diff --git a/src/main/java/io/oeid/mogakgo/domain/notification/application/FCMNotificationService.java b/src/main/java/io/oeid/mogakgo/domain/notification/application/FCMNotificationService.java index 24655f26..8d2d3ba8 100644 --- a/src/main/java/io/oeid/mogakgo/domain/notification/application/FCMNotificationService.java +++ b/src/main/java/io/oeid/mogakgo/domain/notification/application/FCMNotificationService.java @@ -1,18 +1,23 @@ package io.oeid.mogakgo.domain.notification.application; +import static io.oeid.mogakgo.domain.notification.domain.enums.NotificationMessage.REVIEW_REQUEST_MESSAGE; + import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.FirebaseMessagingException; import com.google.firebase.messaging.Message; import com.google.firebase.messaging.Notification; import com.google.firebase.messaging.WebpushConfig; import com.google.firebase.messaging.WebpushFcmOptions; +import io.oeid.mogakgo.domain.notification.domain.enums.FCMNotificationType; import io.oeid.mogakgo.domain.notification.domain.vo.FCMToken; import io.oeid.mogakgo.domain.notification.infrastructure.FCMTokenJpaRepository; import io.oeid.mogakgo.domain.user.application.UserCommonService; +import io.oeid.mogakgo.domain.user.domain.User; import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Slf4j @@ -43,58 +48,74 @@ public void manageToken(Long userId, String fcmToken) { log.info("manageToken End"); } + @Transactional(propagation = Propagation.NOT_SUPPORTED) public void sendNotification(Long userId, String title, String body) { - log.info("sendNotification Start"); getFCMToken(userId).ifPresent( fcmToken -> { - Message message = Message.builder() - .setNotification(Notification.builder() - .setTitle(title) - .setBody(body) - .build()) - .setToken(fcmToken) - .build(); - try { - String response = firebaseMessaging.send(message); - log.info("Successfully sent message: " + response); - } catch (FirebaseMessagingException e) { - log.error("Error sending message: " + e.getMessage()); - } + Message message = generateFirebaseMessage(title, body); + sendMessageToFCM(message); } ); - // send notification - log.info("sendNotification End"); } - public void sendNotification(Long userId, String title, String body, String redirectUri) { - log.info("sendNotification Start"); + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public void sendNotification(Long userId, String title, String body, + FCMNotificationType notificationType) { getFCMToken(userId).ifPresent( fcmToken -> { - WebpushConfig webpushConfig = WebpushConfig.builder() - .setFcmOptions(WebpushFcmOptions.builder() - .setLink(clientUrl + redirectUri) - .build()) - .build(); - // send notification - Message message = Message.builder() - .setNotification(Notification.builder() - .setTitle(title) - .setBody(body) - .build()) - .setToken(fcmToken) - .setWebpushConfig(webpushConfig) - .build(); - try { - String response = firebaseMessaging.send(message); - log.info("Successfully sent message: " + response); - } catch (FirebaseMessagingException e) { - log.error("Error sending message: " + e.getMessage()); - } + Message message = generateFirebaseMessage(title, body, + notificationType.getRedirectUri()); + sendMessageToFCM(message); } ); - log.info("sendNotification End"); } + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public void sendNotification(Long userId, User receiver, Long projectId) { + String redirectUrl = + FCMNotificationType.REVIEW_REQUEST.getRedirectUri() + "?receiverId=" + receiver.getId() + + "&projectId=" + projectId; + getFCMToken(userId).ifPresent( + fcmToken -> { + Message message = generateFirebaseMessage(REVIEW_REQUEST_MESSAGE.getTitle(), + receiver.getUsername()+ REVIEW_REQUEST_MESSAGE.getMessage(), redirectUrl); + sendMessageToFCM(message); + } + ); + } + + private void sendMessageToFCM(Message message) { + log.info("sendNotification Start"); + try { + String response = firebaseMessaging.send(message); + log.info("Successfully sent message: " + response); + } catch (FirebaseMessagingException e) { + log.error("Error sending message: " + e.getMessage()); + } + } + + private Message generateFirebaseMessage(String title, String body) { + return Message.builder() + .setNotification(Notification.builder() + .setTitle(title) + .setBody(body) + .build()) + .build(); + } + + private Message generateFirebaseMessage(String title, String body, String redirectUri) { + return Message.builder() + .setNotification(Notification.builder() + .setTitle(title) + .setBody(body) + .build()) + .setWebpushConfig(WebpushConfig.builder() + .setFcmOptions(WebpushFcmOptions.builder() + .setLink(clientUrl + redirectUri) + .build()) + .build()) + .build(); + } private Optional getFCMToken(Long userId) { return fcmTokenRepository.findById(userId) diff --git a/src/main/java/io/oeid/mogakgo/domain/notification/domain/Notification.java b/src/main/java/io/oeid/mogakgo/domain/notification/domain/Notification.java index a6d1d87b..33af3013 100644 --- a/src/main/java/io/oeid/mogakgo/domain/notification/domain/Notification.java +++ b/src/main/java/io/oeid/mogakgo/domain/notification/domain/Notification.java @@ -1,6 +1,7 @@ package io.oeid.mogakgo.domain.notification.domain; import io.oeid.mogakgo.domain.achievement.domain.entity.Achievement; +import io.oeid.mogakgo.domain.notification.domain.enums.NotificationMessage; import io.oeid.mogakgo.domain.notification.domain.enums.NotificationTag; import io.oeid.mogakgo.domain.notification.exception.NotificationException; import io.oeid.mogakgo.domain.project.domain.entity.Project; @@ -33,12 +34,6 @@ @EntityListeners(AuditingEntityListener.class) public class Notification { - private static final String REVIEW_REQUEST_MESSAGE = " 님과의 만남 후기를 작성해주세요!"; - private static final String ACHIEVEMENT_MESSAGE = " 업적을 달성했습니다!"; - private static final String REQUEST_ARRIVAL_MESSAGE = "매칭 참여 요청이 도착했습니다!"; - private static final String MATCHING_SUCCESS_MESSAGE = "매칭이 성공적으로 이루어졌습니다!"; - private static final String MATCHING_FAILED_MESSAGE = "매칭 요청이 거절되었어요 ㅠㅠ"; - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -104,26 +99,29 @@ private Notification(NotificationTag notificationTag, String message, User user, public static Notification newReviewRequestNotification(User user, User receiver, Project project) { - return new Notification(receiver.getUsername() + REVIEW_REQUEST_MESSAGE, user, receiver, - project); + return new Notification( + receiver.getUsername() + NotificationMessage.REVIEW_REQUEST_MESSAGE.getMessage(), user, + receiver, project); } public static Notification newAchievementNotification(User user, Achievement achievement) { - return new Notification(achievement.getTitle() + ACHIEVEMENT_MESSAGE, user, achievement); + return new Notification( + achievement.getTitle() + NotificationMessage.ACHIEVEMENT_MESSAGE.getMessage(), user, + achievement); } public static Notification newRequestArrivalNotification(User user) { - return new Notification(REQUEST_ARRIVAL_MESSAGE, user); + return new Notification(NotificationMessage.REQUEST_ARRIVAL_MESSAGE.getMessage(), user); } public static Notification newMatchingSuccessNotification(User user, Project project) { - return new Notification(NotificationTag.MATCHING_SUCCEEDED, MATCHING_SUCCESS_MESSAGE, user, - project); + return new Notification(NotificationTag.MATCHING_SUCCEEDED, + NotificationMessage.MATCHING_SUCCESS_MESSAGE.getMessage(), user, project); } public static Notification newMatchingFailedNotification(User user, Project project) { - return new Notification(NotificationTag.MATCHING_FAILED, MATCHING_FAILED_MESSAGE, user, - project); + return new Notification(NotificationTag.MATCHING_FAILED, + NotificationMessage.MATCHING_FAILED_MESSAGE.getMessage(), user, project); } private NotificationTag validateNotificationTag(NotificationTag notificationTag) { diff --git a/src/main/java/io/oeid/mogakgo/domain/notification/domain/enums/FCMNotificationType.java b/src/main/java/io/oeid/mogakgo/domain/notification/domain/enums/FCMNotificationType.java new file mode 100644 index 00000000..ede4313e --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/notification/domain/enums/FCMNotificationType.java @@ -0,0 +1,16 @@ +package io.oeid.mogakgo.domain.notification.domain.enums; + +import lombok.Getter; + +@Getter +public enum FCMNotificationType { + MATCHING_SUCCEEDED("/project"), + REVIEW_REQUEST("/review"), + ; + + private final String redirectUri; + + FCMNotificationType(String redirectUri) { + this.redirectUri = redirectUri; + } +} diff --git a/src/main/java/io/oeid/mogakgo/domain/notification/domain/enums/NotificationMessage.java b/src/main/java/io/oeid/mogakgo/domain/notification/domain/enums/NotificationMessage.java new file mode 100644 index 00000000..775c3e78 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/notification/domain/enums/NotificationMessage.java @@ -0,0 +1,22 @@ +package io.oeid.mogakgo.domain.notification.domain.enums; + +import lombok.Getter; + +@Getter +public enum NotificationMessage { + REVIEW_REQUEST_MESSAGE("리뷰 요청", " 님과의 만남 후기를 작성해주세요!"), + ACHIEVEMENT_MESSAGE("업적 달성"," 업적을 달성했습니다!"), + REQUEST_ARRIVAL_MESSAGE("매칭 참여 요청", "매칭 참여 요청이 도착했습니다!"), + MATCHING_SUCCESS_MESSAGE("매칭 성공","매칭이 성공적으로 이루어졌습니다!"), + MATCHING_FAILED_MESSAGE("매칭 실패","매칭 요청이 거절되었어요 ㅠㅠ"), + ; + + private final String title; + private final String message; + + NotificationMessage(String title, String message) { + this.title = title; + this.message = message; + } +} + diff --git a/src/main/java/io/oeid/mogakgo/domain/project/application/ProjectService.java b/src/main/java/io/oeid/mogakgo/domain/project/application/ProjectService.java index 856811c0..8aae55d5 100644 --- a/src/main/java/io/oeid/mogakgo/domain/project/application/ProjectService.java +++ b/src/main/java/io/oeid/mogakgo/domain/project/application/ProjectService.java @@ -22,6 +22,7 @@ import io.oeid.mogakgo.domain.project.presentation.dto.req.ProjectCreateReq; import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectDensityRankRes; import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectDetailAPIRes; +import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectDetailInfoAPIRes; import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectInfoAPIRes; import io.oeid.mogakgo.domain.project_join_req.exception.ProjectJoinRequestException; import io.oeid.mogakgo.domain.project_join_req.infrastructure.ProjectJoinRequestJpaRepository; @@ -179,7 +180,7 @@ public ProjectDensityRankRes getDensityRankProjects() { return new ProjectDensityRankRes(regionRankList); } - public List getLastedProjectByUserId(Long userId, Long id) { + public ProjectDetailInfoAPIRes getLastedProjectByUserId(Long userId, Long id) { User user = getUser(userId); validateProjectCardCreator(user, id); diff --git a/src/main/java/io/oeid/mogakgo/domain/project/infrastructure/ProjectRepositoryCustom.java b/src/main/java/io/oeid/mogakgo/domain/project/infrastructure/ProjectRepositoryCustom.java index 0efc0095..e82be64d 100644 --- a/src/main/java/io/oeid/mogakgo/domain/project/infrastructure/ProjectRepositoryCustom.java +++ b/src/main/java/io/oeid/mogakgo/domain/project/infrastructure/ProjectRepositoryCustom.java @@ -5,6 +5,7 @@ import io.oeid.mogakgo.domain.geo.domain.enums.Region; import io.oeid.mogakgo.domain.project.domain.entity.enums.ProjectStatus; import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectDetailAPIRes; +import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectDetailInfoAPIRes; import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectInfoAPIRes; import java.util.List; @@ -20,5 +21,5 @@ CursorPaginationResult findByCreatorIdWithPagination( List getDensityRankProjectsByRegion(int limit); - List findLatestProjectByUserId(Long userId); + ProjectDetailInfoAPIRes findLatestProjectByUserId(Long userId); } diff --git a/src/main/java/io/oeid/mogakgo/domain/project/infrastructure/ProjectRepositoryCustomImpl.java b/src/main/java/io/oeid/mogakgo/domain/project/infrastructure/ProjectRepositoryCustomImpl.java index e59ae5eb..7df3e5c7 100644 --- a/src/main/java/io/oeid/mogakgo/domain/project/infrastructure/ProjectRepositoryCustomImpl.java +++ b/src/main/java/io/oeid/mogakgo/domain/project/infrastructure/ProjectRepositoryCustomImpl.java @@ -2,6 +2,7 @@ import static io.oeid.mogakgo.domain.project.domain.entity.QProject.project; import static io.oeid.mogakgo.domain.user.domain.QUser.user; +import static io.oeid.mogakgo.domain.matching.domain.entity.QMatching.matching; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -13,6 +14,7 @@ import io.oeid.mogakgo.domain.project.domain.entity.enums.ProjectStatus; import io.oeid.mogakgo.domain.project.presentation.dto.res.MeetingInfoResponse; import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectDetailAPIRes; +import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectDetailInfoAPIRes; import io.oeid.mogakgo.domain.project.presentation.dto.res.ProjectInfoAPIRes; import io.oeid.mogakgo.domain.user.domain.UserDevelopLanguageTag; import io.oeid.mogakgo.domain.user.domain.UserWantedJobTag; @@ -126,7 +128,7 @@ public List getDensityRankProjectsByRegion(int limit) { } @Override - public List findLatestProjectByUserId(Long userId) { + public ProjectDetailInfoAPIRes findLatestProjectByUserId(Long userId) { List entity = jpaQueryFactory.selectFrom(project) .innerJoin(project.creator, user) @@ -134,10 +136,17 @@ public List findLatestProjectByUserId(Long userId) { .where( userIdEq(userId), projectStatusEq(ProjectStatus.PENDING) + .or(projectStatusEq(ProjectStatus.MATCHED)), + createdAtEq(LocalDate.now()) ) .fetch(); - return entity.stream().map( + Long matchingId = entity.isEmpty() ? null : jpaQueryFactory.select(matching.id) + .from(matching) + .where(matching.project.id.eq(entity.get(0).getId())) + .fetchOne(); + + List result = entity.stream().map( project -> new ProjectDetailAPIRes( project.getId(), new UserPublicApiResponse( @@ -165,6 +174,8 @@ public List findLatestProjectByUserId(Long userId) { ) ) ).toList(); + + return ProjectDetailInfoAPIRes.of(matchingId, result); } private BooleanExpression cursorIdCondition(Long cursorId) { @@ -180,7 +191,7 @@ private BooleanExpression regionEq(Region region) { } private BooleanExpression projectStatusEq(ProjectStatus projectStatus) { - return projectStatus != null ? project.projectStatus.eq(projectStatus) : null; + return project.projectStatus.eq(projectStatus); } private BooleanExpression createdAtEq(LocalDate today) { diff --git a/src/main/java/io/oeid/mogakgo/domain/project/presentation/ProjectController.java b/src/main/java/io/oeid/mogakgo/domain/project/presentation/ProjectController.java index d8cb453e..89cd4f78 100644 --- a/src/main/java/io/oeid/mogakgo/domain/project/presentation/ProjectController.java +++ b/src/main/java/io/oeid/mogakgo/domain/project/presentation/ProjectController.java @@ -99,7 +99,7 @@ public ResponseEntity getById( @GetMapping("/{id}/info") public ResponseEntity getByUserId( @UserId Long userId, @PathVariable Long id) { - return ResponseEntity.ok().body(ProjectDetailInfoAPIRes.of(projectService.getLastedProjectByUserId(userId, id))); + return ResponseEntity.ok().body(projectService.getLastedProjectByUserId(userId, id)); } } diff --git a/src/main/java/io/oeid/mogakgo/domain/project/presentation/dto/res/ProjectDetailInfoAPIRes.java b/src/main/java/io/oeid/mogakgo/domain/project/presentation/dto/res/ProjectDetailInfoAPIRes.java index 10598626..038ea15f 100644 --- a/src/main/java/io/oeid/mogakgo/domain/project/presentation/dto/res/ProjectDetailInfoAPIRes.java +++ b/src/main/java/io/oeid/mogakgo/domain/project/presentation/dto/res/ProjectDetailInfoAPIRes.java @@ -11,9 +11,10 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) public class ProjectDetailInfoAPIRes { + private final Long matchingId; private final List response; - public static ProjectDetailInfoAPIRes of(List response) { - return new ProjectDetailInfoAPIRes(response); + public static ProjectDetailInfoAPIRes of(Long matchingId, List response) { + return new ProjectDetailInfoAPIRes(matchingId, response); } } diff --git a/src/main/java/io/oeid/mogakgo/domain/project_join_req/application/ProjectJoinRequestService.java b/src/main/java/io/oeid/mogakgo/domain/project_join_req/application/ProjectJoinRequestService.java index 33c9a83b..38609901 100644 --- a/src/main/java/io/oeid/mogakgo/domain/project_join_req/application/ProjectJoinRequestService.java +++ b/src/main/java/io/oeid/mogakgo/domain/project_join_req/application/ProjectJoinRequestService.java @@ -1,5 +1,7 @@ package io.oeid.mogakgo.domain.project_join_req.application; +import static io.oeid.mogakgo.domain.notification.domain.enums.FCMNotificationType.MATCHING_SUCCEEDED; +import static io.oeid.mogakgo.domain.notification.domain.enums.NotificationMessage.MATCHING_SUCCESS_MESSAGE; import static io.oeid.mogakgo.exception.code.ErrorCode400.INVALID_MATCHING_USER_TO_ACCEPT; import static io.oeid.mogakgo.exception.code.ErrorCode400.INVALID_SENDER_TO_ACCEPT; import static io.oeid.mogakgo.exception.code.ErrorCode400.PROJECT_JOIN_REQUEST_SHOULD_BE_ONLY_ONE; @@ -12,6 +14,8 @@ import io.oeid.mogakgo.common.base.CursorPaginationResult; import io.oeid.mogakgo.domain.matching.application.MatchingService; import io.oeid.mogakgo.domain.matching.application.UserMatchingService; +import io.oeid.mogakgo.domain.notification.application.FCMNotificationService; +import io.oeid.mogakgo.domain.notification.application.NotificationService; import io.oeid.mogakgo.domain.project.domain.entity.Project; import io.oeid.mogakgo.domain.project.exception.ProjectException; import io.oeid.mogakgo.domain.project.infrastructure.ProjectJpaRepository; @@ -38,8 +42,9 @@ public class ProjectJoinRequestService { private final ProjectJpaRepository projectRepository; private final UserMatchingService userMatchingService; private final MatchingService matchingService; - + private final FCMNotificationService fcmNotificationService; private final UserCommonService userCommonService; + private final NotificationService notificationService; @Transactional public Long create(Long userId, ProjectJoinCreateReq request) { @@ -95,7 +100,11 @@ public Long accept(Long userId, Long projectRequestId) { } catch (ProjectJoinRequestException e) { // TODO: 로그 처리 } - + fcmNotificationService.sendNotification(projectJoinRequest.getSender().getId(), + MATCHING_SUCCESS_MESSAGE.getTitle(), MATCHING_SUCCESS_MESSAGE.getMessage(), + MATCHING_SUCCEEDED); + notificationService.createMatchingSuccessNotification(projectJoinRequest.getSender().getId(), + projectJoinRequest.getProject()); return matchingId; }