Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLAP-370 작업 취소시 알림에서 발생하는 오류 해결 #483

Merged
merged 2 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,19 @@ public String createPayLoad(PushNotificationTemplate request, Task task, String

public String createMessage(PushNotificationTemplate request, String taskDetailUrl) {

return switch (request.notificationType()) {
case TASK_REQUESTED -> "📌 *새 작업 요청:* `" + request.taskName() + "`\\n"
+ "\\t\\t*•요청자: " + request.senderName() + "*\\n"
+ "[확인하러 가기](" + taskDetailUrl + ")";
case STATUS_SWITCHED -> "⚙️ *작업 상태 변경:* `" + request.taskName() + "\\n"
+ "\\t\\t*•작업 상태: " + request.message() + "*\\n"
+ "[확인하러 가기](" + taskDetailUrl + ")";
case PROCESSOR_CHANGED -> "🔄 *담당자 변경:* `" + request.taskName() + "\\n"
+ "\\t\\t*•새 담당자: " + request.message() + "*\\n"
+ "[확인하러 가기](" + taskDetailUrl + ")";
case PROCESSOR_ASSIGNED -> "👤 *작업 담당자 배정:* `" + request.taskName() + "\\n"
+ "\\t\\t*•담당자: " + request.message() + "*\\n"
+ "[확인하러 가기](" + taskDetailUrl + ")";
return switch (request.notificationType()) {
case TASK_REQUESTED -> "📌 *새 작업이 요청되었습니다.*\\n"
+ "\\t\\t*• 🔖 작업명:* " + "*" + request.taskName() + "*" + "\\n"
+ "\\t\\t*• 🙋 요청자:* " + "*" + request.senderName() + "*" + "\\n\\n"
+ "\\t[자세히 보기](" + taskDetailUrl + ")";

case STATUS_SWITCHED -> "작업 상태가 " + "*" + request.message() + "*" + "으로 변경되었습니다.";

case PROCESSOR_CHANGED -> "담당자가 " + "*" + request.message() + "*" + "으로 변경되었습니다.";

case PROCESSOR_ASSIGNED -> "작업이 *승인*되었습니다.*\n"
+ "\\t\\t*• 👤 담당자:* " + "*" + request.message() + "*";

default -> null;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public record PushNotificationTemplate(
String taskName,
String senderName,
String message,
String commenterName
String commenterName,
String reason
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,20 @@ public EmailTemplate createWebhookTemplate(PushNotificationTemplate request, Str
context.setVariable("title", request.taskName());
break;
case STATUS_SWITCHED:
templateName = "status-switched";
subject = "[TaskFlow] "+ request.taskName()+ " 작업의 상태가 " + request.message() + "으로 변경되었습니다.";
context.setVariable("taskDetailUrl", taskDetailUrl);
context.setVariable("receiverName", request.senderName());
context.setVariable("title", request.taskName());
context.setVariable("status", request.message());
if (request.message().equals("TERMINATED")) {
templateName = "task-terminated";
subject = "[TaskFlow] " + request.taskName() + " 작업이 종료되었습니다.";
context.setVariable("taskDetailUrl", taskDetailUrl);
context.setVariable("reason", request.reason());
context.setVariable("title", request.taskName());
} else {
templateName = "status-switched";
subject = "[TaskFlow] "+ request.taskName()+ " 작업의 상태가 " + request.message() + "으로 변경되었습니다.";
context.setVariable("taskDetailUrl", taskDetailUrl);
context.setVariable("receiverName", request.senderName());
context.setVariable("title", request.taskName());
context.setVariable("status", request.message());
}
break;
case PROCESSOR_CHANGED:
templateName = "processor-changed";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ public class KakaoWorkBlockBuilder {
public String makeObjectBlock(PushNotificationTemplate request, String taskDetailUrl){
return switch (request.notificationType()) {
case TASK_REQUESTED -> makeTaskRequestBlock(request, taskDetailUrl);
case STATUS_SWITCHED -> makeTaskStatusBlock(request, taskDetailUrl);
case STATUS_SWITCHED -> switch (request.message()) { // getStatusChangeType() 메서드로 추가 분기
case "TERMINATED" -> makeTerminatedStatusBlock(request, taskDetailUrl);
default -> makeTaskStatusBlock(request, taskDetailUrl);
};
case PROCESSOR_CHANGED -> makeProcessorChangeBlock(request, taskDetailUrl);
case PROCESSOR_ASSIGNED -> makeNewProcessorBlock(request, taskDetailUrl);
case COMMENT -> makeCommentBlock(request, taskDetailUrl);
Expand Down Expand Up @@ -366,4 +369,72 @@ private String makeTaskStatusBlock(PushNotificationTemplate request, String task

return payload;
}

private String makeTerminatedStatusBlock(PushNotificationTemplate request, String taskDetailUrl) {
Object[] blocks = new Object[]{
Map.of(
"type", "header",
"text", "TaskFlow 알림 서비스",
"style", "blue"
),
Map.of(
"type", "text",
"text", "TaskFlow 작업 종료 알림",
"inlines", new Object[]{
Map.of(
"type", "styled",
"text", "TaskFlow 작업 종료 알림",
"bold", true
)
}
),
Map.of(
"type", "text",
"text", "TaskFlow 작업 종료 알림",
"inlines", new Object[]{
Map.of(
"type", "styled",
"text", " - Task Title : " + request.taskName(),
"bold", false
)
}
),
Map.of(
"type", "text",
"text", "TaskFlow 작업 종료 알림",
"inlines", new Object[]{
Map.of(
"type", "styled",
"text", " - 거절 사유 : " + request.reason(),
"bold", false
)
}
),
Map.of(
"type", "button",
"text", "확인하기",
"style", "default",
"action", Map.of(
"type", "open_system_browser",
"name", "button1",
"value", taskDetailUrl
)
)
};

String payload;
try {
payload = "{" +
"\"email\":\"" + request.email() + "\"," +
"\"text\":\"작업 종료 알림\"," +
"\"blocks\":" + objectMapper.writeValueAsString(blocks) +
"}";
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}

return payload;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,6 @@ private String saveAttachment(MultipartFile file, Task task, Comment comment) {

private void publishNotification(Member receiver, Task task, String message, String commenterName) {
boolean isManager = receiver.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER;
sendNotificationService.sendPushNotification(receiver, NotificationType.COMMENT, task, message, commenterName, isManager);
sendNotificationService.sendPushNotification(receiver, NotificationType.COMMENT, task, message, null, commenterName, isManager);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private void publishNotification(List<Member> receivers, Task task, String proce
receivers.forEach(receiver -> {
boolean isManager = receiver.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER;
sendNotificationService.sendPushNotification(receiver, NotificationType.PROCESSOR_ASSIGNED,
task, processorName, null, isManager);
task, processorName, null, null, isManager);
});
sendNotificationService.sendAgitNotification(NotificationType.PROCESSOR_ASSIGNED,
task, processorName, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private void publishNotification(Task task) {
reviewers.forEach(reviewer -> {
boolean isManager = reviewer.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER;
sendNotificationService.sendPushNotification(reviewer, NotificationType.TASK_REQUESTED,
task, null, null, isManager);
task, null, null, null, isManager);
});

sendNotificationService.sendAgitNotification(NotificationType.TASK_REQUESTED,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
package clap.server.application.service.task;

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.port.inbound.domain.MemberService;
import clap.server.application.port.inbound.domain.TaskService;
import clap.server.application.port.inbound.task.TerminateTaskUsecase;
import clap.server.application.port.outbound.taskhistory.CommandTaskHistoryPort;
import clap.server.application.service.webhook.SendNotificationService;
import clap.server.common.annotation.architecture.ApplicationService;
import clap.server.domain.model.member.Member;
import clap.server.domain.model.task.Task;
import clap.server.domain.model.task.TaskHistory;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@ApplicationService
@RequiredArgsConstructor
@Transactional
public class TerminateTaskService implements TerminateTaskUsecase {
private final MemberService memberService;
private final TaskService taskService;
private final CommandTaskHistoryPort commandTaskHistoryPort;
private final SendNotificationService sendNotificationService;

@Override
public void terminateTask(Long memberId, Long taskId, String reason) {
Expand All @@ -28,5 +35,12 @@ public void terminateTask(Long memberId, Long taskId, String reason) {

TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.TASK_TERMINATED, task, reason, null, null);
commandTaskHistoryPort.save(taskHistory);

publishNotification(task.getRequester(), task, String.valueOf(task.getTaskStatus()), reason);

}

private void publishNotification(Member receiver, Task task, String message, String reason) {
sendNotificationService.sendPushNotification(receiver, NotificationType.STATUS_SWITCHED, task, message, reason, null, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public void updateTaskOrderAndStatus(Long processorId, UpdateTaskOrderRequest re

TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.STATUS_SWITCHED, updatedTask, targetStatus.getDescription(), null,null);
commandTaskHistoryPort.save(taskHistory);
publishNotification(targetTask, NotificationType.STATUS_SWITCHED, String.valueOf(updatedTask.getTaskStatus()));
publishNotification(targetTask);
}

/**
Expand All @@ -181,14 +181,14 @@ public void validateRequest(UpdateTaskOrderRequest request, TaskStatus targetSta
}
}

private void publishNotification(Task task, NotificationType notificationType, String message) {
private void publishNotification(Task task) {
List<Member> receivers = List.of(task.getRequester(), task.getProcessor());
receivers.forEach(receiver -> {
boolean isManager = receiver.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER;
sendNotificationService.sendPushNotification(receiver, notificationType, task, message, null, isManager);
sendNotificationService.sendPushNotification(receiver, NotificationType.STATUS_SWITCHED, task, String.valueOf(task.getTaskStatus()), null, null, isManager);
});
sendNotificationService.sendAgitNotification(notificationType,
task, message, null);
sendNotificationService.sendAgitNotification(NotificationType.STATUS_SWITCHED,
task, String.valueOf(task.getTaskStatus()), null);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private void publishNotification(List<Member> receivers, Task task, Notification
receivers.forEach(receiver -> {
boolean isManager = receiver.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER;
sendNotificationService.sendPushNotification(receiver, notificationType,
task, message, null, isManager);
task, message, null, null, isManager);
});

sendNotificationService.sendAgitNotification(notificationType, task, message, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class SendNotificationService {

@Async("notificationExecutor")
public void sendPushNotification(Member receiver, NotificationType notificationType,
Task task, String message, String commenterName, Boolean isManager) {
Task task, String message, String reason, String commenterName, Boolean isManager) {

String email = receiver.getMemberInfo().getEmail();
String taskTitle = task.getTitle();
Expand All @@ -48,7 +48,7 @@ public void sendPushNotification(Member receiver, NotificationType notificationT
Notification notification = createTaskNotification(task, receiver, notificationType, message, taskTitle);

PushNotificationTemplate pushNotificationTemplate = new PushNotificationTemplate(
email, notificationType, taskTitle, requesterNickname, message, commenterName
email, notificationType, taskTitle, requesterNickname, message, commenterName, reason
);

CompletableFuture<Void> saveNotification = CompletableFuture.runAsync(() -> {
Expand Down Expand Up @@ -95,7 +95,8 @@ public void sendAgitNotification(NotificationType notificationType,
task.getTitle(),
task.getRequester().getNickname(),
message,
commenterName
commenterName,
null
);

String taskDetailUrl = extractTaskUrl(notificationType, task, true);
Expand Down
86 changes: 86 additions & 0 deletions src/main/resources/templates/task-terminated.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Notion Notification</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
background-color: #f9f9f9;
margin: 0;
padding: 0;
}
.email-container {
max-width: 500px;
margin: 20px auto;
background: #ffffff;
border: 1px solid #eaeaea;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.header {
background-color: #7879EB;
color: #ffffff;
padding: 15px;
text-align: center;
}
.content {
padding: 20px;
color: #333333;
}
.content p {
margin: 10px 0;
}
.cta-button {
text-align: center;
margin: 20px 0;
}
.cta-button a {
background-color: #7879EB;
color: #ffffff;
text-decoration: none;
padding: 10px 20px;
border-radius: 5px;
font-weight: bold;
}
.cta-button a:hover {
background-color: #18181B;
}
.footer {
text-align: center;
padding: 10px;
font-size: 0.9em;
color: #777777;
background-color: #f4f4f4;
border-top: 1px solid #eaeaea;
}
.footer .taskflow {
font-size: 1.2em;
font-weight: bold;
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
TaskFlow 알림 서비스
</div>
<div class="content">
<p><strong th:text="${title}"></strong> 작업이 종료되었습니다.</p>
<ul>
<li>종료 사유: <span th:text="${reason}"></span></li>
</ul>
<div class="cta-button">
<a href="https://example.com/task/1" target="_blank" th:href="${taskDetailUrl}">확인하기</a>
</div>
</div>
<div class="footer">
<span class="taskflow">TaskFlow</span><br>
업무 관리의 혁신을 이끄는<br>
"스마트한 서비스"
</div>
</div>
</body>
</html>