From 387fe582022b7b2599f8e705588362b03e08140f Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 2 Feb 2025 01:45:53 +0900 Subject: [PATCH 1/3] =?UTF-8?q?CLAP-201=20Fix:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=B9=84=EB=8F=99=EA=B8=B0=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20eventListener=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/clap/server/TaskflowApplication.java | 2 + .../adapter/outbound/api/AgitClient.java | 4 +- .../adapter/outbound/api/KakaoWorkClient.java | 4 +- .../outbound/api/ObjectBlockService.java | 15 ++-- .../outbound/api/dto/SendAgitRequest.java | 13 ---- .../api/dto/SendKakaoWorkRequest.java | 14 ---- .../evenlistener/CustomEventListener.java | 52 ------------- .../outbound/webhook/MakeObjectBlockPort.java | 12 +-- .../port/outbound/webhook/SendAgitPort.java | 4 +- .../outbound/webhook/SendKaKaoWorkPort.java | 4 +- .../notification/SendWebhookService.java | 50 ------------ .../service/task/ApprovalTaskService.java | 23 +----- .../service/task/CreateTaskService.java | 25 +----- .../service/task/UpdateTaskService.java | 25 +----- .../service/webhook/SendAgitService.java | 4 +- .../service/webhook/SendKaKaoWorkService.java | 6 +- .../webhook/SendPushNotificationService.java | 77 +++++++++++++++++++ .../SendSseService.java | 4 +- .../SubscribeSseService.java | 3 +- .../clap/server/config/async/AsyncConfig.java | 29 +++++++ 20 files changed, 146 insertions(+), 224 deletions(-) delete mode 100644 src/main/java/clap/server/adapter/outbound/api/dto/SendAgitRequest.java delete mode 100644 src/main/java/clap/server/adapter/outbound/api/dto/SendKakaoWorkRequest.java delete mode 100644 src/main/java/clap/server/adapter/outbound/persistense/evenlistener/CustomEventListener.java delete mode 100644 src/main/java/clap/server/application/service/notification/SendWebhookService.java create mode 100644 src/main/java/clap/server/application/service/webhook/SendPushNotificationService.java rename src/main/java/clap/server/application/service/{notification => webhook}/SendSseService.java (92%) rename src/main/java/clap/server/application/service/{notification => webhook}/SubscribeSseService.java (94%) create mode 100644 src/main/java/clap/server/config/async/AsyncConfig.java diff --git a/src/main/java/clap/server/TaskflowApplication.java b/src/main/java/clap/server/TaskflowApplication.java index ae57f0c1..9d4bdbc7 100644 --- a/src/main/java/clap/server/TaskflowApplication.java +++ b/src/main/java/clap/server/TaskflowApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling +@EnableAsync public class TaskflowApplication { public static void main(String[] args) { diff --git a/src/main/java/clap/server/adapter/outbound/api/AgitClient.java b/src/main/java/clap/server/adapter/outbound/api/AgitClient.java index ee4ff073..7746f58c 100644 --- a/src/main/java/clap/server/adapter/outbound/api/AgitClient.java +++ b/src/main/java/clap/server/adapter/outbound/api/AgitClient.java @@ -1,6 +1,6 @@ package clap.server.adapter.outbound.api; -import clap.server.adapter.outbound.api.dto.SendAgitRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; import clap.server.application.port.outbound.webhook.SendAgitPort; import clap.server.common.annotation.architecture.ExternalApiAdapter; @@ -20,7 +20,7 @@ public class AgitClient implements SendAgitPort { private String AGIT_WEBHOOK_URL; @Override - public void sendAgit(SendAgitRequest request) { + public void sendAgit(SendWebhookRequest request) { RestTemplate restTemplate = new RestTemplate(); String message = null; diff --git a/src/main/java/clap/server/adapter/outbound/api/KakaoWorkClient.java b/src/main/java/clap/server/adapter/outbound/api/KakaoWorkClient.java index aa0a7743..27a05cfe 100644 --- a/src/main/java/clap/server/adapter/outbound/api/KakaoWorkClient.java +++ b/src/main/java/clap/server/adapter/outbound/api/KakaoWorkClient.java @@ -1,6 +1,6 @@ package clap.server.adapter.outbound.api; -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.application.port.outbound.webhook.SendKaKaoWorkPort; import clap.server.common.annotation.architecture.ExternalApiAdapter; @@ -26,7 +26,7 @@ public class KakaoWorkClient implements SendKaKaoWorkPort { private final ObjectBlockService makeObjectBlock; @Override - public void sendKakaoWord(SendKakaoWorkRequest request) { + public void sendKakaoWork(SendWebhookRequest request) { RestTemplate restTemplate = new RestTemplate(); // Payload 생성 diff --git a/src/main/java/clap/server/adapter/outbound/api/ObjectBlockService.java b/src/main/java/clap/server/adapter/outbound/api/ObjectBlockService.java index 77c67eef..9c8451f4 100644 --- a/src/main/java/clap/server/adapter/outbound/api/ObjectBlockService.java +++ b/src/main/java/clap/server/adapter/outbound/api/ObjectBlockService.java @@ -1,7 +1,6 @@ package clap.server.adapter.outbound.api; - -import clap.server.adapter.outbound.api.dto.SendKakaoWorkRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; import clap.server.adapter.outbound.persistense.repository.notification.NotificationRepository; import clap.server.application.port.outbound.webhook.MakeObjectBlockPort; import clap.server.common.annotation.architecture.PersistenceAdapter; @@ -19,7 +18,7 @@ public class ObjectBlockService implements MakeObjectBlockPort { private final NotificationRepository notificationRepository; @Override - public String makeTaskRequestBlock(SendKakaoWorkRequest request) { + public String makeTaskRequestBlock(SendWebhookRequest request) { // Blocks 데이터 생성 Object[] blocks = new Object[]{ // Header 블록 @@ -92,7 +91,7 @@ public String makeTaskRequestBlock(SendKakaoWorkRequest request) { } @Override - public String makeNewProcessorBlock(SendKakaoWorkRequest request) { + public String makeNewProcessorBlock(SendWebhookRequest request) { Object[] blocks = new Object[]{ Map.of( "type", "header", @@ -148,7 +147,7 @@ public String makeNewProcessorBlock(SendKakaoWorkRequest request) { try { payload = "{" + "\"email\":\"" + request.email() + "\"," + - "\"text\":\"작업 담당자 할당 알림\"," + // fallback 메시지 + "\"text\":\"작업 담당자 선정 알림\"," + // fallback 메시지 "\"blocks\":" + objectMapper.writeValueAsString(blocks) + "}"; } catch (JsonProcessingException e) { @@ -159,7 +158,7 @@ public String makeNewProcessorBlock(SendKakaoWorkRequest request) { } @Override - public String makeProcessorChangeBlock(SendKakaoWorkRequest request) { + public String makeProcessorChangeBlock(SendWebhookRequest request) { Object[] blocks = new Object[]{ Map.of( "type", "header", @@ -227,7 +226,7 @@ public String makeProcessorChangeBlock(SendKakaoWorkRequest request) { } @Override - public String makeCommentBlock(SendKakaoWorkRequest request) { + public String makeCommentBlock(SendWebhookRequest request) { Object[] blocks = new Object[]{ Map.of( "type", "header", @@ -305,7 +304,7 @@ public String makeCommentBlock(SendKakaoWorkRequest request) { } @Override - public String makeTaskStatusBlock(SendKakaoWorkRequest request) { + public String makeTaskStatusBlock(SendWebhookRequest request) { Object[] blocks = new Object[]{ Map.of( "type", "header", diff --git a/src/main/java/clap/server/adapter/outbound/api/dto/SendAgitRequest.java b/src/main/java/clap/server/adapter/outbound/api/dto/SendAgitRequest.java deleted file mode 100644 index 65add4ab..00000000 --- a/src/main/java/clap/server/adapter/outbound/api/dto/SendAgitRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package clap.server.adapter.outbound.api.dto; - -import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; - -public record SendAgitRequest( - String email, - NotificationType notificationType, - String taskName, - String senderName, - String message, - String commenterName -) { -} diff --git a/src/main/java/clap/server/adapter/outbound/api/dto/SendKakaoWorkRequest.java b/src/main/java/clap/server/adapter/outbound/api/dto/SendKakaoWorkRequest.java deleted file mode 100644 index 2bf6252d..00000000 --- a/src/main/java/clap/server/adapter/outbound/api/dto/SendKakaoWorkRequest.java +++ /dev/null @@ -1,14 +0,0 @@ -package clap.server.adapter.outbound.api.dto; - -import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; - -public record SendKakaoWorkRequest( - - String email, - NotificationType notificationType, - String taskName, - String senderName, - String message, - String commenterName -) { -} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/evenlistener/CustomEventListener.java b/src/main/java/clap/server/adapter/outbound/persistense/evenlistener/CustomEventListener.java deleted file mode 100644 index 5e88dcad..00000000 --- a/src/main/java/clap/server/adapter/outbound/persistense/evenlistener/CustomEventListener.java +++ /dev/null @@ -1,52 +0,0 @@ -package clap.server.adapter.outbound.persistense.evenlistener; - -import clap.server.adapter.inbound.web.dto.notification.SseRequest; -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.application.service.notification.CreateNotificationService; -import clap.server.application.service.notification.SendSseService; -import clap.server.application.service.webhook.SendAgitService; -import clap.server.application.service.webhook.SendEmailService; -import clap.server.application.service.webhook.SendKaKaoWorkService; -import clap.server.domain.model.notification.Notification; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.springframework.transaction.event.TransactionPhase; -import org.springframework.transaction.event.TransactionalEventListener; - -// 이벤트 발행 시 실행 -@RequiredArgsConstructor -@Component -public class CustomEventListener { - private final CreateNotificationService createNotificationService; - private final SendSseService sendSseService; - private final SendEmailService sendEmailService; - private final SendKaKaoWorkService sendKaKaoWorkService; - private final SendAgitService agitService; - - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - public void handler(Notification request) { - createNotificationService.createNotification(request); - } - - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - public void handleSseNotification(SseRequest sseRequest) { - sendSseService.send(sseRequest); - } - - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - public void handleEmailSend(SendWebhookRequest email) { - sendEmailService.sendEmail(email); - } - - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - public void handleKakaoWorkSend(SendKakaoWorkRequest kakaoWork) { - sendKaKaoWorkService.sendKaKaoWork(kakaoWork); - } - - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - public void handleAgitSend(SendAgitRequest agit) { - agitService.sendAgit(agit); - } -} diff --git a/src/main/java/clap/server/application/port/outbound/webhook/MakeObjectBlockPort.java b/src/main/java/clap/server/application/port/outbound/webhook/MakeObjectBlockPort.java index 9ca0ac86..dfcb3f61 100644 --- a/src/main/java/clap/server/application/port/outbound/webhook/MakeObjectBlockPort.java +++ b/src/main/java/clap/server/application/port/outbound/webhook/MakeObjectBlockPort.java @@ -1,16 +1,16 @@ package clap.server.application.port.outbound.webhook; -import clap.server.adapter.outbound.api.dto.SendKakaoWorkRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; public interface MakeObjectBlockPort { - String makeTaskRequestBlock(SendKakaoWorkRequest request); + String makeTaskRequestBlock(SendWebhookRequest request); - String makeNewProcessorBlock(SendKakaoWorkRequest request); + String makeNewProcessorBlock(SendWebhookRequest request); - String makeProcessorChangeBlock(SendKakaoWorkRequest request); + String makeProcessorChangeBlock(SendWebhookRequest request); - String makeCommentBlock(SendKakaoWorkRequest request); + String makeCommentBlock(SendWebhookRequest request); - String makeTaskStatusBlock(SendKakaoWorkRequest request); + String makeTaskStatusBlock(SendWebhookRequest request); } diff --git a/src/main/java/clap/server/application/port/outbound/webhook/SendAgitPort.java b/src/main/java/clap/server/application/port/outbound/webhook/SendAgitPort.java index f6e7cfd3..c9d39669 100644 --- a/src/main/java/clap/server/application/port/outbound/webhook/SendAgitPort.java +++ b/src/main/java/clap/server/application/port/outbound/webhook/SendAgitPort.java @@ -1,7 +1,7 @@ package clap.server.application.port.outbound.webhook; -import clap.server.adapter.outbound.api.dto.SendAgitRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; public interface SendAgitPort { - void sendAgit(SendAgitRequest request); + void sendAgit(SendWebhookRequest request); } diff --git a/src/main/java/clap/server/application/port/outbound/webhook/SendKaKaoWorkPort.java b/src/main/java/clap/server/application/port/outbound/webhook/SendKaKaoWorkPort.java index 03550a57..077399b6 100644 --- a/src/main/java/clap/server/application/port/outbound/webhook/SendKaKaoWorkPort.java +++ b/src/main/java/clap/server/application/port/outbound/webhook/SendKaKaoWorkPort.java @@ -1,8 +1,8 @@ package clap.server.application.port.outbound.webhook; -import clap.server.adapter.outbound.api.dto.SendKakaoWorkRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; public interface SendKaKaoWorkPort { - void sendKakaoWord(SendKakaoWorkRequest request); + void sendKakaoWork(SendWebhookRequest request); } diff --git a/src/main/java/clap/server/application/service/notification/SendWebhookService.java b/src/main/java/clap/server/application/service/notification/SendWebhookService.java deleted file mode 100644 index efb5a2e2..00000000 --- a/src/main/java/clap/server/application/service/notification/SendWebhookService.java +++ /dev/null @@ -1,50 +0,0 @@ -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 972b6cb4..5cd73d76 100644 --- a/src/main/java/clap/server/application/service/task/ApprovalTaskService.java +++ b/src/main/java/clap/server/application/service/task/ApprovalTaskService.java @@ -1,6 +1,5 @@ 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; @@ -14,22 +13,19 @@ 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.application.service.webhook.SendPushNotificationService; 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 @@ -42,8 +38,7 @@ 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; + private final SendPushNotificationService sendPushNotificationService; @Override @Transactional @@ -76,20 +71,8 @@ public FindApprovalFormResponse findApprovalForm(Long managerId, Long taskId) { 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, + sendPushNotificationService.sendPushNotification(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 7c77a0dd..83f37076 100644 --- a/src/main/java/clap/server/application/service/task/CreateTaskService.java +++ b/src/main/java/clap/server/application/service/task/CreateTaskService.java @@ -1,6 +1,5 @@ package clap.server.application.service.task; -import clap.server.adapter.inbound.web.dto.notification.SseRequest; import clap.server.adapter.inbound.web.dto.task.CreateTaskRequest; import clap.server.adapter.inbound.web.dto.task.CreateTaskResponse; @@ -14,10 +13,9 @@ import clap.server.application.port.outbound.task.CommandAttachmentPort; import clap.server.application.port.outbound.task.CommandTaskPort; -import clap.server.application.service.notification.SendWebhookService; +import clap.server.application.service.webhook.SendPushNotificationService; 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.Attachment; import clap.server.domain.model.task.Category; import clap.server.common.constants.FilePathConstants; @@ -30,9 +28,6 @@ import java.util.List; -import static clap.server.domain.model.notification.Notification.createTaskNotification; - - @ApplicationService @RequiredArgsConstructor public class CreateTaskService implements CreateTaskUsecase { @@ -42,7 +37,7 @@ public class CreateTaskService implements CreateTaskUsecase { private final CommandTaskPort commandTaskPort; private final CommandAttachmentPort commandAttachmentPort; private final S3UploadAdapter s3UploadAdapter; - private final SendWebhookService sendWebhookService; + private final SendPushNotificationService sendPushNotificationService; private final ApplicationEventPublisher applicationEventPublisher; @Override @@ -74,20 +69,8 @@ private void publishNotification(Task task) { // 검토자들 각각에 대한 알림 생성 후 event 발행 for (Member reviewer : reviewers) { - // 알림 저장 - Notification notification = createTaskNotification(task, reviewer, NotificationType.TASK_REQUESTED); - applicationEventPublisher.publishEvent(notification); - - // SSE 실시간 알림 전송 - SseRequest sseRequest = new SseRequest( - notification.getTask().getTitle(), - notification.getType(), - reviewer.getMemberId(), - null - ); - applicationEventPublisher.publishEvent(sseRequest); - - sendWebhookService.sendWebhookNotification(reviewer, NotificationType.TASK_REQUESTED, + + sendPushNotificationService.sendPushNotification(reviewer, NotificationType.TASK_REQUESTED, task, null, null); } } 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 e3aceb36..2e939d29 100644 --- a/src/main/java/clap/server/application/service/task/UpdateTaskService.java +++ b/src/main/java/clap/server/application/service/task/UpdateTaskService.java @@ -1,6 +1,5 @@ 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; @@ -17,11 +16,10 @@ 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.application.service.webhook.SendPushNotificationService; 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; @@ -30,15 +28,12 @@ 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 @@ -48,8 +43,7 @@ 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 SendPushNotificationService sendPushNotificationService; private final CommandTaskPort commandTaskPort; private final LoadAttachmentPort loadAttachmentPort; @@ -139,20 +133,7 @@ private List validateAndGetAttachments(List attachmentIdsToDel 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, + sendPushNotificationService.sendPushNotification(receiver, notificationType, task, message, null); } } diff --git a/src/main/java/clap/server/application/service/webhook/SendAgitService.java b/src/main/java/clap/server/application/service/webhook/SendAgitService.java index 958ce52c..5ae628ce 100644 --- a/src/main/java/clap/server/application/service/webhook/SendAgitService.java +++ b/src/main/java/clap/server/application/service/webhook/SendAgitService.java @@ -1,6 +1,6 @@ package clap.server.application.service.webhook; -import clap.server.adapter.outbound.api.dto.SendAgitRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; import clap.server.application.port.outbound.webhook.SendAgitPort; import clap.server.common.annotation.architecture.ApplicationService; import lombok.RequiredArgsConstructor; @@ -11,7 +11,7 @@ public class SendAgitService { private final SendAgitPort agitPort; - public void sendAgit(SendAgitRequest request) { + public void sendAgit(SendWebhookRequest request) { agitPort.sendAgit(request); } } diff --git a/src/main/java/clap/server/application/service/webhook/SendKaKaoWorkService.java b/src/main/java/clap/server/application/service/webhook/SendKaKaoWorkService.java index 35ea3f22..487026ba 100644 --- a/src/main/java/clap/server/application/service/webhook/SendKaKaoWorkService.java +++ b/src/main/java/clap/server/application/service/webhook/SendKaKaoWorkService.java @@ -1,6 +1,6 @@ package clap.server.application.service.webhook; -import clap.server.adapter.outbound.api.dto.SendKakaoWorkRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; import clap.server.application.port.outbound.webhook.SendKaKaoWorkPort; import clap.server.common.annotation.architecture.ApplicationService; import lombok.RequiredArgsConstructor; @@ -11,7 +11,7 @@ public class SendKaKaoWorkService { private final SendKaKaoWorkPort sendKaKaoWorkPort; - public void sendKaKaoWork(SendKakaoWorkRequest request) { - sendKaKaoWorkPort.sendKakaoWord(request); + public void sendKaKaoWork(SendWebhookRequest request) { + sendKaKaoWorkPort.sendKakaoWork(request); } } diff --git a/src/main/java/clap/server/application/service/webhook/SendPushNotificationService.java b/src/main/java/clap/server/application/service/webhook/SendPushNotificationService.java new file mode 100644 index 00000000..8c3b1d0d --- /dev/null +++ b/src/main/java/clap/server/application/service/webhook/SendPushNotificationService.java @@ -0,0 +1,77 @@ +package clap.server.application.service.webhook; + +import clap.server.adapter.inbound.web.dto.notification.SseRequest; +import clap.server.adapter.outbound.api.dto.SendWebhookRequest; +import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; +import clap.server.application.port.outbound.notification.CommandNotificationPort; +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.Task; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Async; + +import java.util.concurrent.CompletableFuture; + +import static clap.server.domain.model.notification.Notification.createTaskNotification; + +@ApplicationService +@RequiredArgsConstructor +public class SendPushNotificationService { + + private final SendSseService sendSseService; + private final SendAgitService sendAgitService; + private final SendEmailService sendEmailService; + private final SendKaKaoWorkService sendKaKaoWorkService; + private final CommandNotificationPort commandNotificationPort; + + @Async("notificationExecutor") + public void sendPushNotification(Member receiver, NotificationType notificationType, + Task task, String message, String commenterName) { + String email = receiver.getMemberInfo().getEmail(); + String taskTitle = task.getTitle(); + String requesterNickname = task.getRequester().getNickname(); + + Notification notification = createTaskNotification(task, receiver, notificationType); + + SseRequest sseRequest = new SseRequest( + task.getTitle(), + notificationType, + receiver.getMemberId(), + message + ); + + SendWebhookRequest sendWebhookRequest = new SendWebhookRequest( + email, notificationType, taskTitle, requesterNickname, message, commenterName + ); + + CompletableFuture saveNotification = CompletableFuture.runAsync(() -> { + commandNotificationPort.save(notification); + }); + + CompletableFuture sendSseFuture = CompletableFuture.runAsync(() -> { + sendSseService.send(sseRequest); + }); + + CompletableFuture sendEmailFuture = CompletableFuture.runAsync(() -> { + if (receiver.getEmailNotificationEnabled()) { + sendEmailService.sendEmail(sendWebhookRequest); + } + }); + + CompletableFuture sendAgitFuture = CompletableFuture.runAsync(() -> { + if (receiver.getAgitNotificationEnabled()) { + sendAgitService.sendAgit(sendWebhookRequest); + } + }); + + CompletableFuture sendKakaoWorkFuture = CompletableFuture.runAsync(() -> { + if (receiver.getKakaoworkNotificationEnabled()) { + sendKaKaoWorkService.sendKaKaoWork(sendWebhookRequest); + } + }); + + CompletableFuture allOf = CompletableFuture.allOf(saveNotification, sendSseFuture, sendEmailFuture, sendKakaoWorkFuture, sendAgitFuture); + allOf.join(); + } +} diff --git a/src/main/java/clap/server/application/service/notification/SendSseService.java b/src/main/java/clap/server/application/service/webhook/SendSseService.java similarity index 92% rename from src/main/java/clap/server/application/service/notification/SendSseService.java rename to src/main/java/clap/server/application/service/webhook/SendSseService.java index 4139b53d..6774a110 100644 --- a/src/main/java/clap/server/application/service/notification/SendSseService.java +++ b/src/main/java/clap/server/application/service/webhook/SendSseService.java @@ -1,4 +1,4 @@ -package clap.server.application.service.notification; +package clap.server.application.service.webhook; import clap.server.adapter.inbound.web.dto.notification.SseRequest; import clap.server.application.port.inbound.notification.SendSseUsecase; @@ -9,8 +9,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import java.io.IOException; - @ApplicationService @RequiredArgsConstructor public class SendSseService implements SendSseUsecase { diff --git a/src/main/java/clap/server/application/service/notification/SubscribeSseService.java b/src/main/java/clap/server/application/service/webhook/SubscribeSseService.java similarity index 94% rename from src/main/java/clap/server/application/service/notification/SubscribeSseService.java rename to src/main/java/clap/server/application/service/webhook/SubscribeSseService.java index 558e4aa5..d18a254d 100644 --- a/src/main/java/clap/server/application/service/notification/SubscribeSseService.java +++ b/src/main/java/clap/server/application/service/webhook/SubscribeSseService.java @@ -1,4 +1,4 @@ -package clap.server.application.service.notification; +package clap.server.application.service.webhook; import clap.server.application.port.inbound.notification.SubscribeSseUsecase; import clap.server.application.port.outbound.notification.CommandSsePort; @@ -7,7 +7,6 @@ import clap.server.exception.code.NotificationErrorCode; import lombok.RequiredArgsConstructor; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import java.io.IOException; @ApplicationService @RequiredArgsConstructor diff --git a/src/main/java/clap/server/config/async/AsyncConfig.java b/src/main/java/clap/server/config/async/AsyncConfig.java new file mode 100644 index 00000000..8671f0eb --- /dev/null +++ b/src/main/java/clap/server/config/async/AsyncConfig.java @@ -0,0 +1,29 @@ +package clap.server.config.async; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +@EnableAsync +public class AsyncConfig { + + + @Bean(name = "notificationExecutor") + public ThreadPoolTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + // 스레드 풀 크기 설정 (최대 1000명 사용자를 위한 설정 예시) + executor.setCorePoolSize(50); // 기본적으로 유지할 스레드 수 + executor.setMaxPoolSize(200); // 최대 스레드 수 + executor.setQueueCapacity(1000); // 큐의 크기 (백로그) + executor.setThreadNamePrefix("async-task-"); // 스레드 이름 접두사 + + // 스레드가 유휴 상태로 일정 시간이 지나면 종료되도록 설정 + executor.setKeepAliveSeconds(60); // 60초 후 유휴 스레드 종료 + + executor.initialize(); + return executor; + } +} From c55443214f24d157c6b07cbe5424eddf6eeb58f0 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 2 Feb 2025 01:46:17 +0900 Subject: [PATCH 2/3] =?UTF-8?q?CLAP-201=20Fix:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=B2=A8=EB=B6=80=ED=8C=8C=EC=9D=BC=201=EA=B0=9C=EB=A7=8C=20?= =?UTF-8?q?=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/comment/PostCommentController.java | 4 +-- .../inbound/comment/PostCommentUsecase.java | 2 +- .../service/comment/PostCommentService.java | 29 +++++-------------- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/comment/PostCommentController.java b/src/main/java/clap/server/adapter/inbound/web/comment/PostCommentController.java index d269da51..c736eedb 100644 --- a/src/main/java/clap/server/adapter/inbound/web/comment/PostCommentController.java +++ b/src/main/java/clap/server/adapter/inbound/web/comment/PostCommentController.java @@ -44,8 +44,8 @@ public void createComment( public void createAttachmentComment( @AuthenticationPrincipal SecurityUserDetails userInfo, @PathVariable Long taskId, - @RequestPart(name = "attachment") @NotNull List attachments) { - postCommentUsecase.saveCommentAttachment(userInfo.getUserId(), taskId, attachments); + @RequestPart(name = "attachment") @NotNull MultipartFile attachment) { + postCommentUsecase.saveCommentAttachment(userInfo.getUserId(), taskId, attachment); } } diff --git a/src/main/java/clap/server/application/port/inbound/comment/PostCommentUsecase.java b/src/main/java/clap/server/application/port/inbound/comment/PostCommentUsecase.java index e964316e..168dbf37 100644 --- a/src/main/java/clap/server/application/port/inbound/comment/PostCommentUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/comment/PostCommentUsecase.java @@ -9,5 +9,5 @@ public interface PostCommentUsecase { void save(Long userId, Long taskId, PostAndEditCommentRequest request); - void saveCommentAttachment(Long userId, Long taskId, List files); + void saveCommentAttachment(Long userId, Long taskId, MultipartFile file); } 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 c87c2ce2..6eb15918 100644 --- a/src/main/java/clap/server/application/service/comment/PostCommentService.java +++ b/src/main/java/clap/server/application/service/comment/PostCommentService.java @@ -1,6 +1,5 @@ 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; @@ -13,23 +12,21 @@ 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.application.service.webhook.SendPushNotificationService; 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.ArrayList; import java.util.List; -import static clap.server.domain.model.notification.Notification.createTaskNotification; @ApplicationService @RequiredArgsConstructor @@ -41,8 +38,7 @@ 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; + private final SendPushNotificationService sendPushNotificationService; @Transactional @Override @@ -70,13 +66,15 @@ public void save(Long userId, Long taskId, PostAndEditCommentRequest request) { @Transactional @Override - public void saveCommentAttachment(Long userId, Long taskId, List files) { + public void saveCommentAttachment(Long userId, Long taskId, MultipartFile file) { Task task = taskService.findById(taskId); Member member = memberService.findActiveMember(userId); if (Member.checkCommenter(task, member)) { Comment comment = Comment.createComment(member, task, null); Comment savedComment = commandCommentPort.saveComment(comment); + List files = new ArrayList<>(); + files.add(file); saveAttachment(files, task, savedComment); TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.COMMENT_FILE, task, null, member, savedComment); @@ -98,19 +96,6 @@ private void saveAttachment(List files, Task task, Comment commen } 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); + sendPushNotificationService.sendPushNotification(receiver, NotificationType.COMMENT, task, message, commenterName); } } From a191b551b3f0cd0612321e33a77292b2ec49f6fb Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 2 Feb 2025 01:46:38 +0900 Subject: [PATCH 3/3] =?UTF-8?q?CLAP-201=20Fix:=20html=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/templates/processor-assign.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/processor-assign.html b/src/main/resources/templates/processor-assign.html index 0de7fd85..fc97d3f6 100644 --- a/src/main/resources/templates/processor-assign.html +++ b/src/main/resources/templates/processor-assign.html @@ -71,7 +71,7 @@

"" 작업의 담당자가 할당되었습니다.

    -
  • 담당자:
  • +
  • 담당자: