From cfd7195048c493c0b2f4fb35804393fed5915bae Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 31 Jan 2025 17:48:17 +0900 Subject: [PATCH 1/4] =?UTF-8?q?CLAP-190=20fit:=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/comment/CommandCommentController.java | 6 +- .../web/dto/task/DeleteCommentRequest.java | 13 ----- .../AttachmentPersistenceAdapter.java | 13 +++-- .../CommentPersistenceAdapter.java | 5 +- .../entity/task/CommentEntity.java | 4 +- .../repository/task/AttachmentRepository.java | 4 +- .../comment/CommandCommentUsecase.java | 5 +- .../outbound/task/CommandCommentPort.java | 2 + .../outbound/task/LoadAttachmentPort.java | 4 +- .../comment/CommandCommentService.java | 57 ++++--------------- .../service/comment/PostCommentService.java | 25 +------- .../server/domain/model/member/Member.java | 22 +++++++ .../server/domain/model/task/Comment.java | 1 + .../exception/code/CommentErrorCode.java | 4 +- 14 files changed, 63 insertions(+), 102 deletions(-) delete mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/task/DeleteCommentRequest.java diff --git a/src/main/java/clap/server/adapter/inbound/web/comment/CommandCommentController.java b/src/main/java/clap/server/adapter/inbound/web/comment/CommandCommentController.java index 144203df..e3768092 100644 --- a/src/main/java/clap/server/adapter/inbound/web/comment/CommandCommentController.java +++ b/src/main/java/clap/server/adapter/inbound/web/comment/CommandCommentController.java @@ -1,7 +1,6 @@ package clap.server.adapter.inbound.web.comment; import clap.server.adapter.inbound.security.SecurityUserDetails; -import clap.server.adapter.inbound.web.dto.task.DeleteCommentRequest; import clap.server.adapter.inbound.web.dto.task.PostAndEditCommentRequest; import clap.server.application.port.inbound.comment.CommandCommentUsecase; import clap.server.common.annotation.architecture.WebAdapter; @@ -40,9 +39,8 @@ public void editComment( @Secured({"ROLE_MANAGER", "ROLE_USER"}) public void deleteComment( @AuthenticationPrincipal SecurityUserDetails userInfo, - @PathVariable Long commentId, - @RequestBody DeleteCommentRequest request) { - commandCommentUsecase.deleteComment(userInfo.getUserId(), commentId, request); + @PathVariable Long commentId) { + commandCommentUsecase.deleteComment(userInfo.getUserId(), commentId); } } diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/DeleteCommentRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/DeleteCommentRequest.java deleted file mode 100644 index 05c717a6..00000000 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/DeleteCommentRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package clap.server.adapter.inbound.web.dto.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; - -import java.util.List; - -public record DeleteCommentRequest( - @Schema(description = "삭제할 파일 ID 목록, 없을 경우 emptylist 전송") - @NotNull - List attachmentsToDelete -) { -} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java index 41ee5492..5c6c9d2e 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java @@ -54,11 +54,9 @@ public List findAllByTaskIdAndCommentIsNullAndAttachmentId(final Lon } @Override - public List findAllyByTaskIdAndCommentIdAndAttachmentId(Long taskId, Long commentId, List attachmentIds) { - List attachmentEntities = attachmentRepository.findActiveAttachmentsByTask_TaskIdAndComment_CommentIdAndAttachmentIdIn(taskId, commentId, attachmentIds); - return attachmentEntities.stream() - .map(attachmentPersistenceMapper::toDomain) - .collect(Collectors.toList()); + public Optional findByCommentId(Long commentId) { + Optional attachmentEntity = attachmentRepository.findByComment_CommentIdAndIsDeletedFalse(commentId); + return attachmentEntity.map(attachmentPersistenceMapper::toDomain); } @Override @@ -68,4 +66,9 @@ public List findAllByTaskIdAndCommentIsNotNull(final Long taskId) { .map(attachmentPersistenceMapper::toDomain) .collect(Collectors.toList()); } + + @Override + public boolean exitsByCommentId(Long commentId) { + return attachmentRepository.existsByComment_CommentId(commentId); + } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/CommentPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/CommentPersistenceAdapter.java index b50ca262..77cbf428 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/CommentPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/CommentPersistenceAdapter.java @@ -31,8 +31,7 @@ public Comment saveComment(Comment comment) { } @Override - public Comment saveComment(Comment comment) { - CommentEntity commentEntity = commentRepository.save(commentPersistenceMapper.toEntity(comment)); - return commentPersistenceMapper.toDomain(commentEntity); + public void deleteComment(Comment comment) { + commentRepository.delete(commentPersistenceMapper.toEntity(comment)); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/CommentEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/CommentEntity.java index 1b508c50..ba3ef2e8 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/CommentEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/CommentEntity.java @@ -7,12 +7,14 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.SQLDelete; @Entity @Table(name = "comment") @Getter @SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) +@SQLDelete(sql = "UPDATE Comment SET is_Deleted = true WHERE comment_id = ?") public class CommentEntity extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -34,5 +36,5 @@ public class CommentEntity extends BaseTimeEntity { private boolean isModified; @Column(name="is_deleted", nullable = false) - private boolean isDeleted; + private boolean isDeleted = Boolean.FALSE; } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java index f44f96e8..9e8d9336 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java @@ -4,11 +4,13 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface AttachmentRepository extends JpaRepository { List findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalse(Long taskId); List findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalseAndAttachmentIdIn(Long task_taskId, List attachmentId); - List findActiveAttachmentsByTask_TaskIdAndComment_CommentIdAndAttachmentIdIn(Long task_taskId, Long comment_commentId, List attachmentIds); + Optional findByComment_CommentIdAndIsDeletedFalse(Long commentId); + boolean existsByComment_CommentId(Long commentId); List findAllByTask_TaskIdAndCommentIsNotNullAndIsDeletedIsFalse(Long taskId); } \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/inbound/comment/CommandCommentUsecase.java b/src/main/java/clap/server/application/port/inbound/comment/CommandCommentUsecase.java index 4fcd653f..6c3e2238 100644 --- a/src/main/java/clap/server/application/port/inbound/comment/CommandCommentUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/comment/CommandCommentUsecase.java @@ -1,13 +1,10 @@ package clap.server.application.port.inbound.comment; -import clap.server.adapter.inbound.web.dto.task.DeleteCommentRequest; import clap.server.adapter.inbound.web.dto.task.PostAndEditCommentRequest; -import java.util.List; - public interface CommandCommentUsecase { void updateComment(Long userId, Long commentId, PostAndEditCommentRequest request); - void deleteComment(Long userId, Long commentId, DeleteCommentRequest request); + void deleteComment(Long userId, Long commentId); } diff --git a/src/main/java/clap/server/application/port/outbound/task/CommandCommentPort.java b/src/main/java/clap/server/application/port/outbound/task/CommandCommentPort.java index 834732f3..55c74d1b 100644 --- a/src/main/java/clap/server/application/port/outbound/task/CommandCommentPort.java +++ b/src/main/java/clap/server/application/port/outbound/task/CommandCommentPort.java @@ -5,4 +5,6 @@ public interface CommandCommentPort { Comment saveComment(Comment comment); + + void deleteComment(Comment comment); } diff --git a/src/main/java/clap/server/application/port/outbound/task/LoadAttachmentPort.java b/src/main/java/clap/server/application/port/outbound/task/LoadAttachmentPort.java index 023f4db6..127c4fad 100644 --- a/src/main/java/clap/server/application/port/outbound/task/LoadAttachmentPort.java +++ b/src/main/java/clap/server/application/port/outbound/task/LoadAttachmentPort.java @@ -3,11 +3,13 @@ import clap.server.domain.model.task.Attachment; import java.util.List; +import java.util.Optional; public interface LoadAttachmentPort { List findAllByTaskIdAndCommentIsNull(Long taskId); List findAllByTaskIdAndCommentIsNullAndAttachmentId(Long taskId, List attachmentIds); - List findAllyByTaskIdAndCommentIdAndAttachmentId(Long taskId, Long commentId, List attachmentIds); + Optional findByCommentId(Long commentId); List findAllByTaskIdAndCommentIsNotNull(Long taskId); + boolean exitsByCommentId(Long commentId); } diff --git a/src/main/java/clap/server/application/service/comment/CommandCommentService.java b/src/main/java/clap/server/application/service/comment/CommandCommentService.java index 6312c90b..b0c3bd40 100644 --- a/src/main/java/clap/server/application/service/comment/CommandCommentService.java +++ b/src/main/java/clap/server/application/service/comment/CommandCommentService.java @@ -1,8 +1,6 @@ package clap.server.application.service.comment; -import clap.server.adapter.inbound.web.dto.task.DeleteCommentRequest; import clap.server.adapter.inbound.web.dto.task.PostAndEditCommentRequest; -import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole; import clap.server.application.port.inbound.comment.CommandCommentUsecase; import clap.server.application.port.inbound.domain.MemberService; import clap.server.application.port.outbound.task.CommandAttachmentPort; @@ -13,16 +11,11 @@ import clap.server.domain.model.member.Member; import clap.server.domain.model.task.Attachment; import clap.server.domain.model.task.Comment; -import clap.server.domain.model.task.Task; import clap.server.exception.ApplicationException; import clap.server.exception.code.CommentErrorCode; -import clap.server.exception.code.MemberErrorCode; -import clap.server.exception.code.TaskErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @ApplicationService @RequiredArgsConstructor public class CommandCommentService implements CommandCommentUsecase { @@ -42,7 +35,7 @@ public void updateComment(Long userId, Long commentId, PostAndEditCommentRequest Comment comment = loadCommentPort.findById(commentId) .orElseThrow(() -> new ApplicationException(CommentErrorCode.COMMENT_NOT_FOUND)); - if (checkCommenter(comment.getTask(), member)) { + if (Member.checkCommenter(comment.getTask(), member)) { comment.updateComment(request.content()); commandCommentPort.saveComment(comment); @@ -51,53 +44,25 @@ public void updateComment(Long userId, Long commentId, PostAndEditCommentRequest @Transactional @Override - public void deleteComment(Long userId, Long commentId, DeleteCommentRequest request) { + public void deleteComment(Long userId, Long commentId) { Member member = memberService.findActiveMember(userId); Comment comment = loadCommentPort.findById(commentId) .orElseThrow(() -> new ApplicationException(CommentErrorCode.COMMENT_NOT_FOUND)); - if (checkCommenter(comment.getTask(), member)) { - - // 삭제할 댓글이 첨부파일일 경우 - if (!request.attachmentsToDelete().isEmpty()) { - deleteAttachments(request.attachmentsToDelete(), comment.getTask(), comment.getCommentId()); + if (Member.checkCommenter(comment.getTask(), member)) { + if (loadAttachmentPort.exitsByCommentId(commentId)) { + deleteAttachments(commentId); } - - comment.softDelete(); - commandCommentPort.saveComment(comment); + commandCommentPort.deleteComment(comment); }; } - private void deleteAttachments(List attachmentIdsToDelete, Task task, Long commentId) { - List attachmentsToDelete = validateAndGetAttachments(attachmentIdsToDelete, task, commentId); - attachmentsToDelete.forEach(Attachment::softDelete); - commandAttachmentPort.saveAll(attachmentsToDelete); - } - - private List validateAndGetAttachments(List attachmentIdsToDelete, Task task, Long commentId) { - List attachmentsOfTask = loadAttachmentPort.findAllyByTaskIdAndCommentIdAndAttachmentId(task.getTaskId(), commentId, attachmentIdsToDelete); - if (attachmentsOfTask.size() != attachmentIdsToDelete.size()) { - throw new ApplicationException(TaskErrorCode.TASK_ATTACHMENT_NOT_FOUND); - } - return attachmentsOfTask; - } - - public Boolean checkCommenter(Task task, Member member) { - // 일반 회원일 경우 => 요청자인지 확인 - // 담당자일 경우 => 처리자인지 확인 - if ((member.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER) - && !(member.getMemberId() == task.getProcessor().getMemberId())) { - throw new ApplicationException(MemberErrorCode.NOT_A_COMMENTER); - } - - else if ((member.getMemberInfo().getRole() == MemberRole.ROLE_USER) - && !(member.getMemberId() == task.getRequester().getMemberId())) { - throw new ApplicationException(MemberErrorCode.NOT_A_COMMENTER); - } - else { - return true; - } + private void deleteAttachments(Long commentId) { + Attachment attachment = loadAttachmentPort.findByCommentId(commentId) + .orElseThrow(() -> new ApplicationException(CommentErrorCode.COMMENT_ATTACHMENT_NOT_FOUND)); + attachment.softDelete(); + commandAttachmentPort.save(attachment); } } diff --git a/src/main/java/clap/server/application/service/comment/PostCommentService.java b/src/main/java/clap/server/application/service/comment/PostCommentService.java index 0d9ca297..54888cae 100644 --- a/src/main/java/clap/server/application/service/comment/PostCommentService.java +++ b/src/main/java/clap/server/application/service/comment/PostCommentService.java @@ -2,7 +2,6 @@ import clap.server.adapter.inbound.web.dto.task.PostAndEditCommentRequest; import clap.server.adapter.outbound.infrastructure.s3.S3UploadAdapter; -import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskHistoryType; import clap.server.application.mapper.AttachmentMapper; import clap.server.application.port.inbound.comment.PostCommentUsecase; @@ -18,8 +17,6 @@ import clap.server.domain.model.task.Comment; import clap.server.domain.model.task.Task; import clap.server.domain.model.task.TaskHistory; -import clap.server.exception.ApplicationException; -import clap.server.exception.code.MemberErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -45,7 +42,7 @@ public void save(Long userId, Long taskId, PostAndEditCommentRequest request) { // 일반 회원일 경우 => 요청자인지 확인 // 담당자일 경우 => 처리자인지 확인 - if (checkCommenter(task, member)) { + if (Member.checkCommenter(task, member)) { Comment comment = Comment.createComment(member, task, request.content()); Comment savedComment = commandCommentPort.saveComment(comment); @@ -60,7 +57,7 @@ public void saveCommentAttachment(Long userId, Long taskId, List Task task = taskService.findById(taskId); Member member = memberService.findActiveMember(userId); - if (checkCommenter(task, member)) { + if (Member.checkCommenter(task, member)) { Comment comment = Comment.createComment(member, task, "Attachment"); Comment savedComment = commandCommentPort.saveComment(comment); saveAttachment(files, task, savedComment); @@ -75,22 +72,4 @@ private void saveAttachment(List files, Task task, Comment commen List attachments = AttachmentMapper.toCommentAttachments(task, comment, files, fileUrls); commandAttachmentPort.saveAll(attachments); } - - public Boolean checkCommenter(Task task, Member member) { - // 일반 회원일 경우 => 요청자인지 확인 - // 담당자일 경우 => 처리자인지 확인 - if ((member.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER) - && !(member.getMemberId() == task.getProcessor().getMemberId())) { - throw new ApplicationException(MemberErrorCode.NOT_A_COMMENTER); - } - - else if ((member.getMemberInfo().getRole() == MemberRole.ROLE_USER) - && !(member.getMemberId() == task.getRequester().getMemberId())) { - throw new ApplicationException(MemberErrorCode.NOT_A_COMMENTER); - } - else { - return true; - } - - } } diff --git a/src/main/java/clap/server/domain/model/member/Member.java b/src/main/java/clap/server/domain/model/member/Member.java index f3279b13..bce03b3e 100644 --- a/src/main/java/clap/server/domain/model/member/Member.java +++ b/src/main/java/clap/server/domain/model/member/Member.java @@ -1,7 +1,11 @@ package clap.server.domain.model.member; +import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole; import clap.server.adapter.outbound.persistense.entity.member.constant.MemberStatus; import clap.server.domain.model.common.BaseTime; +import clap.server.domain.model.task.Task; +import clap.server.exception.ApplicationException; +import clap.server.exception.code.MemberErrorCode; import lombok.*; import lombok.experimental.SuperBuilder; @@ -78,4 +82,22 @@ public void updateMemberInfo(String name, Boolean agitNotificationEnabled, Boole public void setStatusDeleted() { this.status = MemberStatus.DELETED; } + + public static Boolean checkCommenter(Task task, Member member) { + // 일반 회원일 경우 => 요청자인지 확인 + // 담당자일 경우 => 처리자인지 확인 + if ((member.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER) + && !(member.getMemberId() == task.getProcessor().getMemberId())) { + throw new ApplicationException(MemberErrorCode.NOT_A_COMMENTER); + } + + else if ((member.getMemberInfo().getRole() == MemberRole.ROLE_USER) + && !(member.getMemberId() == task.getRequester().getMemberId())) { + throw new ApplicationException(MemberErrorCode.NOT_A_COMMENTER); + } + else { + return true; + } + + } } diff --git a/src/main/java/clap/server/domain/model/task/Comment.java b/src/main/java/clap/server/domain/model/task/Comment.java index 25ce0b46..fda3fe4a 100644 --- a/src/main/java/clap/server/domain/model/task/Comment.java +++ b/src/main/java/clap/server/domain/model/task/Comment.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.SQLDelete; @Getter @SuperBuilder diff --git a/src/main/java/clap/server/exception/code/CommentErrorCode.java b/src/main/java/clap/server/exception/code/CommentErrorCode.java index 97aa1baf..1f74a967 100644 --- a/src/main/java/clap/server/exception/code/CommentErrorCode.java +++ b/src/main/java/clap/server/exception/code/CommentErrorCode.java @@ -8,7 +8,9 @@ @AllArgsConstructor public enum CommentErrorCode implements BaseErrorCode { - COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMENT_001", "댓글을 찾을 수 없습니다."); + COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMENT_001", "댓글을 찾을 수 없습니다."), + COMMENT_ATTACHMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMENT_002", "댓글 첨부파일을 찾을 수 없습니다.") + ; private final HttpStatus httpStatus; From 7612b7f771da95a8349500a540f64d6e13bac684 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 31 Jan 2025 21:37:35 +0900 Subject: [PATCH 2/4] =?UTF-8?q?CLAP-190=20Feat:=20=EC=B9=B4=EC=B9=B4?= =?UTF-8?q?=EC=98=A4=EC=9B=8C=ED=81=AC,=20=EC=95=84=EC=A7=80=ED=8A=B8,=20?= =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=95=8C=EB=A6=BC=20api=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ManagementNotificationController.java | 24 ++++++++++++ .../entity/member/MemberEntity.java | 10 ++++- .../notification/EnableAgitUsecase.java | 6 +++ .../notification/EnableEmailUsecase.java | 6 +++ .../notification/EnableKakaoUsecase.java | 6 +++ .../UpdateNotificationService.java | 39 +++++++++++++++++++ .../server/domain/model/member/Member.java | 27 +++++++++++++ 7 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/main/java/clap/server/application/port/inbound/notification/EnableAgitUsecase.java create mode 100644 src/main/java/clap/server/application/port/inbound/notification/EnableEmailUsecase.java create mode 100644 src/main/java/clap/server/application/port/inbound/notification/EnableKakaoUsecase.java create mode 100644 src/main/java/clap/server/application/service/notification/UpdateNotificationService.java diff --git a/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java b/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java index 255bfb9e..72bceb67 100644 --- a/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java +++ b/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java @@ -1,6 +1,9 @@ package clap.server.adapter.inbound.web.notification; import clap.server.adapter.inbound.security.SecurityUserDetails; +import clap.server.application.port.inbound.notification.EnableAgitUsecase; +import clap.server.application.port.inbound.notification.EnableEmailUsecase; +import clap.server.application.port.inbound.notification.EnableKakaoUsecase; import clap.server.application.port.inbound.notification.UpdateNotificationUsecase; import clap.server.common.annotation.architecture.WebAdapter; import io.swagger.v3.oas.annotations.Operation; @@ -22,6 +25,9 @@ public class ManagementNotificationController { private final UpdateNotificationUsecase updateNotificationUsecase; + private final EnableKakaoUsecase enableKakaoUsecase; + private final EnableAgitUsecase enableAgitUsecase; + private final EnableEmailUsecase enableEmailUsecase; @Operation(summary = "알림 목록에서 한 개 눌렀을 때 읽음 처리") @Parameter(name = "notificationId", description = "알림 고유 ID", required = true, in = ParameterIn.PATH) @@ -35,4 +41,22 @@ public void updateNotificationIsRead(@PathVariable Long notificationId) { public void updateAllNotificationIsRead(@AuthenticationPrincipal SecurityUserDetails userInfo) { updateNotificationUsecase.updateAllNotification(userInfo.getUserId()); } + + @Operation(summary = "카카오 푸시 알림 활성화/비활성화 API", description = "알림 거부였을 시 -> 승인으로 변경, 알림 승인이였을 시 -> 거부로 변경") + @PatchMapping("/kakao") + public void enableKaKaoWork(@AuthenticationPrincipal SecurityUserDetails userInfo) { + enableKakaoUsecase.enableKakao(userInfo.getUserId()); + } + + @Operation(summary = "아지트 푸시 알림 활성화/비활성화 API", description = "알림 거부였을 시 -> 승인으로 변경, 알림 승인이였을 시 -> 거부로 변경") + @PatchMapping("/agit") + public void enableAgit(@AuthenticationPrincipal SecurityUserDetails userInfo) { + enableAgitUsecase.enableAgit(userInfo.getUserId()); + } + + @Operation(summary = "이메일 푸시 알림 활성화/비활성화 API", description = "알림 거부였을 시 -> 승인으로 변경, 알림 승인이였을 시 -> 거부로 변경") + @PatchMapping("/email") + public void enableEmail(@AuthenticationPrincipal SecurityUserDetails userInfo) { + enableEmailUsecase.enableEmail(userInfo.getUserId()); + } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java index ba9862ad..4898a3c4 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/member/MemberEntity.java @@ -52,8 +52,14 @@ public class MemberEntity extends BaseTimeEntity { @Column private String imageUrl; - @Column - private Boolean notificationEnabled; + @Column(name = "kakaowork_notification_enabled") + private Boolean kakaoworkNotificationEnabled = Boolean.TRUE;; + + @Column(name = "agit_notification_enabled") + private Boolean agitNotificationEnabled = Boolean.TRUE;; + + @Column(name = "email_notification_enabled") + private Boolean emailNotificationEnabled = Boolean.TRUE;; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "admin_id") diff --git a/src/main/java/clap/server/application/port/inbound/notification/EnableAgitUsecase.java b/src/main/java/clap/server/application/port/inbound/notification/EnableAgitUsecase.java new file mode 100644 index 00000000..d172c371 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/notification/EnableAgitUsecase.java @@ -0,0 +1,6 @@ +package clap.server.application.port.inbound.notification; + +public interface EnableAgitUsecase { + + void enableAgit(Long userId); +} diff --git a/src/main/java/clap/server/application/port/inbound/notification/EnableEmailUsecase.java b/src/main/java/clap/server/application/port/inbound/notification/EnableEmailUsecase.java new file mode 100644 index 00000000..e0ae34bc --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/notification/EnableEmailUsecase.java @@ -0,0 +1,6 @@ +package clap.server.application.port.inbound.notification; + +public interface EnableEmailUsecase { + + void enableEmail(Long userId); +} diff --git a/src/main/java/clap/server/application/port/inbound/notification/EnableKakaoUsecase.java b/src/main/java/clap/server/application/port/inbound/notification/EnableKakaoUsecase.java new file mode 100644 index 00000000..28d49a7b --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/notification/EnableKakaoUsecase.java @@ -0,0 +1,6 @@ +package clap.server.application.port.inbound.notification; + +public interface EnableKakaoUsecase { + + void enableKakao(Long userId); +} diff --git a/src/main/java/clap/server/application/service/notification/UpdateNotificationService.java b/src/main/java/clap/server/application/service/notification/UpdateNotificationService.java new file mode 100644 index 00000000..53305eb6 --- /dev/null +++ b/src/main/java/clap/server/application/service/notification/UpdateNotificationService.java @@ -0,0 +1,39 @@ +package clap.server.application.service.notification; + +import clap.server.application.port.inbound.domain.MemberService; +import clap.server.application.port.inbound.notification.EnableAgitUsecase; +import clap.server.application.port.inbound.notification.EnableEmailUsecase; +import clap.server.application.port.inbound.notification.EnableKakaoUsecase; +import clap.server.application.port.outbound.member.CommandMemberPort; +import clap.server.common.annotation.architecture.ApplicationService; +import clap.server.domain.model.member.Member; +import lombok.RequiredArgsConstructor; + +@ApplicationService +@RequiredArgsConstructor +public class UpdateNotificationService implements EnableKakaoUsecase, EnableAgitUsecase, EnableEmailUsecase { + + private final MemberService memberService; + private final CommandMemberPort commandMemberPort; + + @Override + public void enableAgit(Long userId) { + Member member = memberService.findActiveMember(userId); + member.updateAgitEnabled(); + commandMemberPort.save(member); + } + + @Override + public void enableEmail(Long userId) { + Member member = memberService.findActiveMember(userId); + member.updateEmailEnabled(); + commandMemberPort.save(member); + } + + @Override + public void enableKakao(Long userId) { + Member member = memberService.findActiveMember(userId); + member.updateKaKaoEnabled(); + commandMemberPort.save(member); + } +} diff --git a/src/main/java/clap/server/domain/model/member/Member.java b/src/main/java/clap/server/domain/model/member/Member.java index bce03b3e..852758a2 100644 --- a/src/main/java/clap/server/domain/model/member/Member.java +++ b/src/main/java/clap/server/domain/model/member/Member.java @@ -83,6 +83,33 @@ public void setStatusDeleted() { this.status = MemberStatus.DELETED; } + public void updateKaKaoEnabled() { + if (!this.kakaoworkNotificationEnabled) { + this.kakaoworkNotificationEnabled = true; + } + else { + this.kakaoworkNotificationEnabled = false; + } + } + + public void updateAgitEnabled() { + if (!this.agitNotificationEnabled) { + this.agitNotificationEnabled = true; + } + else { + this.agitNotificationEnabled = false; + } + } + + public void updateEmailEnabled() { + if (!this.emailNotificationEnabled) { + this.emailNotificationEnabled = true; + } + else { + this.emailNotificationEnabled = false; + } + } + public static Boolean checkCommenter(Task task, Member member) { // 일반 회원일 경우 => 요청자인지 확인 // 담당자일 경우 => 처리자인지 확인 From 58e770c224ed2d2307d2d3680f0592a705c989f5 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 31 Jan 2025 23:44:00 +0900 Subject: [PATCH 3/4] =?UTF-8?q?CLAP-190=20Feat:=20=ED=91=B8=EC=8B=9C=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20logic=20=EC=95=8C=EB=A6=BC=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EB=90=98=EB=8A=94=20method=EC=97=90=20add?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/comment/PostCommentService.java | 45 ++++++++++++++++- .../notification/SendWebhookService.java | 50 +++++++++++++++++++ .../service/task/ApprovalTaskService.java | 39 ++++++++++++++- .../service/task/CreateTaskService.java | 43 +++------------- .../service/task/UpdateTaskService.java | 47 +++++++++++++++-- 5 files changed, 179 insertions(+), 45 deletions(-) create mode 100644 src/main/java/clap/server/application/service/notification/SendWebhookService.java diff --git a/src/main/java/clap/server/application/service/comment/PostCommentService.java b/src/main/java/clap/server/application/service/comment/PostCommentService.java index 54888cae..c87c2ce2 100644 --- a/src/main/java/clap/server/application/service/comment/PostCommentService.java +++ b/src/main/java/clap/server/application/service/comment/PostCommentService.java @@ -1,7 +1,10 @@ package clap.server.application.service.comment; +import clap.server.adapter.inbound.web.dto.notification.SseRequest; import clap.server.adapter.inbound.web.dto.task.PostAndEditCommentRequest; import clap.server.adapter.outbound.infrastructure.s3.S3UploadAdapter; +import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole; +import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskHistoryType; import clap.server.application.mapper.AttachmentMapper; import clap.server.application.port.inbound.comment.PostCommentUsecase; @@ -10,19 +13,24 @@ import clap.server.application.port.outbound.task.CommandAttachmentPort; import clap.server.application.port.outbound.task.CommandCommentPort; import clap.server.application.port.outbound.taskhistory.CommandTaskHistoryPort; +import clap.server.application.service.notification.SendWebhookService; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.common.constants.FilePathConstants; import clap.server.domain.model.member.Member; +import clap.server.domain.model.notification.Notification; import clap.server.domain.model.task.Attachment; import clap.server.domain.model.task.Comment; import clap.server.domain.model.task.Task; import clap.server.domain.model.task.TaskHistory; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.util.List; +import static clap.server.domain.model.notification.Notification.createTaskNotification; + @ApplicationService @RequiredArgsConstructor public class PostCommentService implements PostCommentUsecase { @@ -33,6 +41,8 @@ public class PostCommentService implements PostCommentUsecase { private final S3UploadAdapter s3UploadAdapter; private final CommandAttachmentPort commandAttachmentPort; private final CommandTaskHistoryPort commandTaskHistoryPort; + private final ApplicationEventPublisher applicationEventPublisher; + private final SendWebhookService sendWebhookService; @Transactional @Override @@ -46,8 +56,15 @@ public void save(Long userId, Long taskId, PostAndEditCommentRequest request) { Comment comment = Comment.createComment(member, task, request.content()); Comment savedComment = commandCommentPort.saveComment(comment); - TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.COMMENT, task, null, member,savedComment); + TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.COMMENT, task, null, member, savedComment); commandTaskHistoryPort.save(taskHistory); + + if (member.getMemberInfo().getRole() == MemberRole.ROLE_USER) { + publishNotification(task.getProcessor(), task, comment.getContent(), member.getNickname()); + } + else { + publishNotification(task.getRequester(), task, comment.getContent(), task.getProcessor().getNickname()); + } } } @@ -58,12 +75,19 @@ public void saveCommentAttachment(Long userId, Long taskId, List Member member = memberService.findActiveMember(userId); if (Member.checkCommenter(task, member)) { - Comment comment = Comment.createComment(member, task, "Attachment"); + Comment comment = Comment.createComment(member, task, null); Comment savedComment = commandCommentPort.saveComment(comment); saveAttachment(files, task, savedComment); TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.COMMENT_FILE, task, null, member, savedComment); commandTaskHistoryPort.save(taskHistory); + + if (member.getMemberInfo().getRole() == MemberRole.ROLE_USER) { + publishNotification(task.getProcessor(), task, "첨부파일", member.getNickname()); + } + else { + publishNotification(task.getRequester(), task, "첨부파일", task.getProcessor().getNickname()); + } } } @@ -72,4 +96,21 @@ private void saveAttachment(List files, Task task, Comment commen List attachments = AttachmentMapper.toCommentAttachments(task, comment, files, fileUrls); commandAttachmentPort.saveAll(attachments); } + + private void publishNotification(Member receiver, Task task, String message, String commenterName){ + // 알림 저장 + Notification notification = createTaskNotification(task, receiver, NotificationType.COMMENT); + applicationEventPublisher.publishEvent(notification); + + // SSE 실시간 알림 전송 + SseRequest sseRequest = new SseRequest( + notification.getTask().getTitle(), + notification.getType(), + receiver.getMemberId(), + message + ); + applicationEventPublisher.publishEvent(sseRequest); + + sendWebhookService.sendWebhookNotification(receiver, NotificationType.COMMENT, task, message, commenterName); + } } diff --git a/src/main/java/clap/server/application/service/notification/SendWebhookService.java b/src/main/java/clap/server/application/service/notification/SendWebhookService.java new file mode 100644 index 00000000..efb5a2e2 --- /dev/null +++ b/src/main/java/clap/server/application/service/notification/SendWebhookService.java @@ -0,0 +1,50 @@ +package clap.server.application.service.notification; + +import clap.server.adapter.outbound.api.dto.SendAgitRequest; +import clap.server.adapter.outbound.api.dto.SendKakaoWorkRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; +import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; +import clap.server.common.annotation.architecture.ApplicationService; +import clap.server.domain.model.member.Member; +import clap.server.domain.model.task.Task; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; + +@ApplicationService +@RequiredArgsConstructor +public class SendWebhookService { + + private final ApplicationEventPublisher applicationEventPublisher; + + public void sendWebhookNotification(Member receiver, NotificationType notificationType, + Task task, String message, String commenterName) { + // 공통 데이터 + String email = receiver.getMemberInfo().getEmail(); + String taskTitle = task.getTitle(); + String requesterNickname = task.getRequester().getNickname(); + + // 이메일 전송 + if (receiver.getEmailNotificationEnabled()) { + SendWebhookRequest sendWebhookRequest = new SendWebhookRequest( + email, notificationType, taskTitle, requesterNickname, message, commenterName + ); + applicationEventPublisher.publishEvent(sendWebhookRequest); + } + + // 카카오워크 전송 + if (receiver.getKakaoworkNotificationEnabled()) { + SendKakaoWorkRequest sendKakaoWorkRequest = new SendKakaoWorkRequest( + email, notificationType, taskTitle, requesterNickname, message, commenterName + ); + applicationEventPublisher.publishEvent(sendKakaoWorkRequest); + } + + // 아지트 전송 + if (receiver.getAgitNotificationEnabled()) { + SendAgitRequest sendAgitRequest = new SendAgitRequest( + email, notificationType, taskTitle, requesterNickname, message, commenterName + ); + applicationEventPublisher.publishEvent(sendAgitRequest); + } + } +} diff --git a/src/main/java/clap/server/application/service/task/ApprovalTaskService.java b/src/main/java/clap/server/application/service/task/ApprovalTaskService.java index 124c6527..972b6cb4 100644 --- a/src/main/java/clap/server/application/service/task/ApprovalTaskService.java +++ b/src/main/java/clap/server/application/service/task/ApprovalTaskService.java @@ -1,8 +1,10 @@ package clap.server.application.service.task; +import clap.server.adapter.inbound.web.dto.notification.SseRequest; import clap.server.adapter.inbound.web.dto.task.ApprovalTaskRequest; import clap.server.adapter.inbound.web.dto.task.ApprovalTaskResponse; import clap.server.adapter.inbound.web.dto.task.FindApprovalFormResponse; +import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskHistoryType; import clap.server.application.mapper.TaskMapper; import clap.server.application.port.inbound.domain.CategoryService; @@ -12,15 +14,23 @@ import clap.server.application.port.inbound.task.ApprovalTaskUsecase; import clap.server.application.port.outbound.task.CommandTaskPort; import clap.server.application.port.outbound.taskhistory.CommandTaskHistoryPort; +import clap.server.application.service.notification.SendWebhookService; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.member.Member; +import clap.server.domain.model.notification.Notification; import clap.server.domain.model.task.Category; import clap.server.domain.model.task.Label; import clap.server.domain.model.task.Task; import clap.server.domain.model.task.TaskHistory; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; +import java.util.List; + +import static clap.server.domain.model.notification.Notification.createTaskNotification; + @ApplicationService @RequiredArgsConstructor @Transactional(readOnly = true) @@ -32,6 +42,8 @@ public class ApprovalTaskService implements ApprovalTaskUsecase { private final LabelService labelService; private final CommandTaskPort commandTaskPort; private final CommandTaskHistoryPort commandTaskHistoryPort; + private final ApplicationEventPublisher applicationEventPublisher; + private final SendWebhookService sendWebhookService; @Override @Transactional @@ -45,10 +57,15 @@ public ApprovalTaskResponse approvalTaskByReviewer(Long reviewerId, Long taskId, task.approveTask(reviewer, processor, approvalTaskRequest.dueDate(), category, label); TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.PROCESSOR_ASSIGNED, task, null, processor,null); commandTaskHistoryPort.save(taskHistory); + + List receivers = new ArrayList<>(); + receivers.add(task.getRequester()); + receivers.add(task.getProcessor()); + + publishNotification(receivers, task); return TaskMapper.toApprovalTaskResponse(commandTaskPort.save(task)); } - @Override public FindApprovalFormResponse findApprovalForm(Long managerId, Long taskId) { memberService.findActiveMember(managerId); @@ -56,4 +73,24 @@ public FindApprovalFormResponse findApprovalForm(Long managerId, Long taskId) { task.validateTaskRequested(); return TaskMapper.toFindApprovalFormResponse(task); } + + private void publishNotification(List receivers, Task task){ + for (Member receiver : receivers) { + // 알림 저장 + Notification notification = createTaskNotification(task, receiver, NotificationType.PROCESSOR_ASSIGNED); + applicationEventPublisher.publishEvent(notification); + + // SSE 실시간 알림 전송 + SseRequest sseRequest = new SseRequest( + notification.getTask().getTitle(), + notification.getType(), + receiver.getMemberId(), + task.getProcessor().getNickname() + ); + applicationEventPublisher.publishEvent(sseRequest); + + sendWebhookService.sendWebhookNotification(receiver, NotificationType.PROCESSOR_ASSIGNED, + task, task.getProcessor().getNickname(), null); + } + } } diff --git a/src/main/java/clap/server/application/service/task/CreateTaskService.java b/src/main/java/clap/server/application/service/task/CreateTaskService.java index 60e4ec18..4f6296d3 100644 --- a/src/main/java/clap/server/application/service/task/CreateTaskService.java +++ b/src/main/java/clap/server/application/service/task/CreateTaskService.java @@ -4,9 +4,6 @@ import clap.server.adapter.inbound.web.dto.task.CreateTaskRequest; import clap.server.adapter.inbound.web.dto.task.CreateTaskResponse; -import clap.server.adapter.outbound.api.dto.SendAgitRequest; -import clap.server.adapter.outbound.api.dto.SendKakaoWorkRequest; -import clap.server.adapter.outbound.api.dto.SendWebhookRequest; import clap.server.adapter.outbound.infrastructure.s3.S3UploadAdapter; import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; import clap.server.application.mapper.AttachmentMapper; @@ -17,7 +14,7 @@ import clap.server.application.port.outbound.task.CommandAttachmentPort; import clap.server.application.port.outbound.task.CommandTaskPort; -import clap.server.application.service.webhook.SendKaKaoWorkService; +import clap.server.application.service.notification.SendWebhookService; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.member.Member; import clap.server.domain.model.notification.Notification; @@ -45,7 +42,7 @@ public class CreateTaskService implements CreateTaskUsecase { private final CommandTaskPort commandTaskPort; private final CommandAttachmentPort commandAttachmentPort; private final S3UploadAdapter s3UploadAdapter; - private final SendKaKaoWorkService sendKaKaoWorkService; + private final SendWebhookService sendWebhookService; private final ApplicationEventPublisher applicationEventPublisher; @Override @@ -87,39 +84,11 @@ private void publishNotification(Task task){ ); applicationEventPublisher.publishEvent(sseRequest); - //Email Webhook 실시간 전송 - SendWebhookRequest sendWebhookRequest = new SendWebhookRequest( - reviewer.getMemberInfo().getEmail(), - NotificationType.TASK_REQUESTED, - task.getTitle(), - task.getRequester().getNickname(), - null, - null - ); - applicationEventPublisher.publishEvent(sendWebhookRequest); - - //Kakao Webhook 실시간 전송 - SendKakaoWorkRequest sendKakaoWorkRequest = new SendKakaoWorkRequest( - reviewer.getMemberInfo().getEmail(), - NotificationType.TASK_REQUESTED, - task.getTitle(), - task.getRequester().getNickname(), - null, - null - ); - applicationEventPublisher.publishEvent(sendKakaoWorkRequest); - - //아지트 Webhook 실시간 전송 - SendAgitRequest sendAgitRequest = new SendAgitRequest( - reviewer.getMemberInfo().getEmail(), - NotificationType.TASK_REQUESTED, - task.getTitle(), - task.getRequester().getNickname(), - null, - null - ); - applicationEventPublisher.publishEvent(sendAgitRequest); + sendWebhookService.sendWebhookNotification(reviewer, NotificationType.TASK_REQUESTED, + task, null, null); } } + + } \ No newline at end of file diff --git a/src/main/java/clap/server/application/service/task/UpdateTaskService.java b/src/main/java/clap/server/application/service/task/UpdateTaskService.java index b25e355f..9e770156 100644 --- a/src/main/java/clap/server/application/service/task/UpdateTaskService.java +++ b/src/main/java/clap/server/application/service/task/UpdateTaskService.java @@ -1,7 +1,9 @@ package clap.server.application.service.task; +import clap.server.adapter.inbound.web.dto.notification.SseRequest; import clap.server.adapter.inbound.web.dto.task.*; import clap.server.adapter.outbound.infrastructure.s3.S3UploadAdapter; +import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; import clap.server.application.mapper.AttachmentMapper; import clap.server.application.mapper.TaskMapper; import clap.server.application.port.inbound.domain.CategoryService; @@ -15,9 +17,11 @@ import clap.server.application.port.outbound.task.CommandAttachmentPort; import clap.server.application.port.outbound.task.CommandTaskPort; import clap.server.application.port.outbound.task.LoadAttachmentPort; +import clap.server.application.service.notification.SendWebhookService; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.common.constants.FilePathConstants; import clap.server.domain.model.member.Member; +import clap.server.domain.model.notification.Notification; import clap.server.domain.model.task.Attachment; import clap.server.domain.model.task.Category; import clap.server.domain.model.task.Label; @@ -26,11 +30,15 @@ import clap.server.exception.code.TaskErrorCode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.util.ArrayList; import java.util.List; +import static clap.server.domain.model.notification.Notification.createTaskNotification; + @ApplicationService @RequiredArgsConstructor @@ -40,6 +48,8 @@ public class UpdateTaskService implements UpdateTaskUsecase, UpdateTaskStatusUse private final MemberService memberService; private final CategoryService categoryService; private final TaskService taskService; + private final ApplicationEventPublisher applicationEventPublisher; + private final SendWebhookService sendWebhookService; private final CommandTaskPort commandTaskPort; private final LoadAttachmentPort loadAttachmentPort; @@ -70,23 +80,28 @@ public UpdateTaskResponse updateTaskState(Long memberId, Long taskId, UpdateTask Task task = taskService.findById(taskId); task.updateTaskStatus(updateTaskStatusRequest.taskStatus()); Task updateTask = commandTaskPort.save(task); + List receiver = new ArrayList<>(); + receiver.add(task.getRequester()); + publishNotification(receiver, updateTask, NotificationType.STATUS_SWITCHED, String.valueOf(updateTask.getTaskStatus())); return TaskMapper.toUpdateTaskResponse(updateTask); - - // TODO : 알림 생성 로직 및 푸시 알림 로직 추가 } @Transactional @Override public UpdateTaskResponse updateTaskProcessor(Long taskId, Long userId, UpdateTaskProcessorRequest request) { - Member reviewer = memberService.findReviewer(userId); + memberService.findReviewer(userId); Member processor = memberService.findById(request.processorId()); Task task = taskService.findById(taskId); task.updateProcessor(processor); Task updateTask = commandTaskPort.save(task); - return TaskMapper.toUpdateTaskResponse(updateTask); - // TODO : 알림 생성 로직 및 푸시 알림 로직 추가 + List receivers = new ArrayList<>(); + receivers.add(updateTask.getRequester()); + receivers.add(updateTask.getProcessor()); + + publishNotification(receivers, updateTask, NotificationType.PROCESSOR_CHANGED, updateTask.getProcessor().getNickname()); + return TaskMapper.toUpdateTaskResponse(updateTask); } @Transactional @@ -101,6 +116,8 @@ public UpdateTaskResponse updateTaskLabel(Long taskId, Long userId, UpdateTaskLa return TaskMapper.toUpdateTaskResponse(updatetask); } + + private void updateAttachments(List attachmentIdsToDelete, List files, Task task) { List attachmentsToDelete = validateAndGetAttachments(attachmentIdsToDelete, task); attachmentsToDelete.forEach(Attachment::softDelete); @@ -117,4 +134,24 @@ private List validateAndGetAttachments(List attachmentIdsToDel } return attachmentsOfTask; } + + private void publishNotification(List receivers, Task task, NotificationType notificationType, String message){ + for (Member receiver : receivers) { + // 알림 저장 + Notification notification = createTaskNotification(task, receiver, notificationType); + applicationEventPublisher.publishEvent(notification); + + // SSE 실시간 알림 전송 + SseRequest sseRequest = new SseRequest( + notification.getTask().getTitle(), + notification.getType(), + receiver.getMemberId(), + message + ); + applicationEventPublisher.publishEvent(sseRequest); + + sendWebhookService.sendWebhookNotification(receiver, notificationType, + task, message, null); + } + } } From 418f8be102cf7e7d3668ec9859da8f094c7c9120 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 31 Jan 2025 23:46:50 +0900 Subject: [PATCH 4/4] =?UTF-8?q?CLAP-190=20Fix:=20=ED=99=9C=EC=84=B1?= =?UTF-8?q?=ED=99=94=EB=90=9C=20=ED=9A=8C=EC=9B=90=EC=9D=B8=EC=A7=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/notification/ManagementNotificationController.java | 5 +++-- .../inbound/notification/UpdateNotificationUsecase.java | 2 +- .../service/notification/ReadNotificationService.java | 7 ++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java b/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java index 72bceb67..79db55ca 100644 --- a/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java +++ b/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java @@ -32,8 +32,9 @@ public class ManagementNotificationController { @Operation(summary = "알림 목록에서 한 개 눌렀을 때 읽음 처리") @Parameter(name = "notificationId", description = "알림 고유 ID", required = true, in = ParameterIn.PATH) @PatchMapping("/{notificationId}") - public void updateNotificationIsRead(@PathVariable Long notificationId) { - updateNotificationUsecase.updateNotification(notificationId); + public void updateNotificationIsRead(@AuthenticationPrincipal SecurityUserDetails userInfo, + @PathVariable Long notificationId) { + updateNotificationUsecase.updateNotification(userInfo.getUserId(), notificationId); } @Operation(summary = "알림 목록에서 전체 읽음 버튼을 눌렀을 때 전체 읽음 처리") diff --git a/src/main/java/clap/server/application/port/inbound/notification/UpdateNotificationUsecase.java b/src/main/java/clap/server/application/port/inbound/notification/UpdateNotificationUsecase.java index 858b2cb4..c7949842 100644 --- a/src/main/java/clap/server/application/port/inbound/notification/UpdateNotificationUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/notification/UpdateNotificationUsecase.java @@ -1,7 +1,7 @@ package clap.server.application.port.inbound.notification; public interface UpdateNotificationUsecase { - void updateNotification(Long notificationId); + void updateNotification(Long userId, Long notificationId); void updateAllNotification(Long memberId); } diff --git a/src/main/java/clap/server/application/service/notification/ReadNotificationService.java b/src/main/java/clap/server/application/service/notification/ReadNotificationService.java index 80e27a7e..142a469f 100644 --- a/src/main/java/clap/server/application/service/notification/ReadNotificationService.java +++ b/src/main/java/clap/server/application/service/notification/ReadNotificationService.java @@ -1,5 +1,6 @@ package clap.server.application.service.notification; +import clap.server.application.port.inbound.domain.MemberService; import clap.server.application.port.inbound.notification.UpdateNotificationUsecase; import clap.server.application.port.outbound.notification.CommandNotificationPort; import clap.server.application.port.outbound.notification.LoadNotificationPort; @@ -16,13 +17,16 @@ @RequiredArgsConstructor public class ReadNotificationService implements UpdateNotificationUsecase { + private final MemberService memberService; private final LoadNotificationPort loadNotificationPort; private final CommandNotificationPort commandNotificationPort; @Transactional @Override - public void updateNotification(Long notificationId) { + public void updateNotification(Long userId, Long notificationId) { + + memberService.findActiveMember(userId); Notification notification = loadNotificationPort.findById(notificationId) .orElseThrow(() -> new ApplicationException(NotificationErrorCode.NOTIFICATION_NOT_FOUND)); notification.updateNotificationIsRead(); @@ -32,6 +36,7 @@ public void updateNotification(Long notificationId) { @Transactional @Override public void updateAllNotification(Long memberId) { + memberService.findActiveMember(memberId); List notificationList = loadNotificationPort.findNotificationsByMemberId(memberId); for (Notification notification : notificationList) { notification.updateNotificationIsRead();