Skip to content

Commit c1e2b4e

Browse files
authored
Merge pull request #290 from TaskFlow-CLAP/CLAP-257
CLAP-257 아지트 푸시 알림 logic 변경
2 parents 91ed9ed + 6569c9d commit c1e2b4e

File tree

12 files changed

+131
-50
lines changed

12 files changed

+131
-50
lines changed
Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
package clap.server.adapter.outbound.api;
22

33
import clap.server.adapter.outbound.api.dto.PushNotificationTemplate;
4+
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
5+
import clap.server.application.port.inbound.domain.TaskService;
46
import clap.server.application.port.outbound.webhook.SendAgitPort;
7+
import clap.server.application.service.task.UpdateTaskService;
58
import clap.server.common.annotation.architecture.ExternalApiAdapter;
9+
import clap.server.domain.model.task.Task;
10+
import clap.server.exception.ApplicationException;
11+
import clap.server.exception.code.NotificationErrorCode;
12+
import com.fasterxml.jackson.core.JsonProcessingException;
13+
import com.fasterxml.jackson.databind.JsonNode;
14+
import com.fasterxml.jackson.databind.ObjectMapper;
615
import lombok.RequiredArgsConstructor;
716
import org.springframework.beans.factory.annotation.Value;
817
import org.springframework.http.HttpEntity;
9-
import org.springframework.http.HttpHeaders;
1018
import org.springframework.http.HttpMethod;
19+
import org.springframework.http.ResponseEntity;
1120
import org.springframework.web.client.RestTemplate;
1221

1322

@@ -18,41 +27,33 @@ public class AgitClient implements SendAgitPort {
1827
@Value("${webhook.agit.url}")
1928
private String AGIT_WEBHOOK_URL;
2029

30+
private final AgitTemplateBuilder agitTemplateBuilder;
31+
private final ObjectMapper objectMapper;
32+
private final TaskService taskService;
33+
2134
@Override
22-
public void sendAgit(PushNotificationTemplate request) {
35+
public void sendAgit(PushNotificationTemplate request, Task task) {
2336

24-
HttpHeaders headers = new HttpHeaders();
25-
headers.add("Content-Type", "application/json");
37+
HttpEntity<String> entity = agitTemplateBuilder.createAgitEntity(request, task);
2638

2739
RestTemplate restTemplate = new RestTemplate();
28-
String taskUrl = "https://www.naver.com"; //Todo 작업 상세페이지 url 추가
29-
30-
String message = switch (request.notificationType()) {
31-
case TASK_REQUESTED -> "📌 *새 작업 요청:* `" + request.taskName() + "`\\n"
32-
+ "\\t\\t*•요청자: " + request.senderName() + "*\\n"
33-
+ "\\t\\t[OPEN](" + taskUrl + ")";
34-
case STATUS_SWITCHED -> "⚙️ *작업 상태 변경:* `" + request.taskName() + "\\n"
35-
+ "\\t\\t*•작업 상태: " + request.message() + "*\\n"
36-
+ "\\t\\t[OPEN](" + taskUrl + ")";
37-
case PROCESSOR_CHANGED -> "🔄 *담당자 변경:* `" + request.taskName() + "\\n"
38-
+ "\\t\\t*•새 담당자: " + request.message() + "*\\n"
39-
+ "\\t\\t[OPEN](" + taskUrl + ")";
40-
case PROCESSOR_ASSIGNED -> "👤 *작업 담당자 배정:* `" + request.taskName() + "\\n"
41-
+ "\\t\\t*•담당자: " + request.message() + "*\\n"
42-
+ "\\t\\t[OPEN](" + taskUrl + ")";
43-
case COMMENT -> "💬 *새 댓글:* `" + request.taskName() + "`\\n"
44-
+ "\\t\\t*•작성자: " + request.commenterName() + "\\n"
45-
+ "\\t\\t*•댓글 내용: " + request.message() + "\\n"
46-
+ "\\t\\t[OPEN](" + taskUrl + ")";
47-
default -> null;
48-
};
49-
50-
String payload = "{"
51-
+ "\"text\": \"" + message + "\","
52-
+ "\"mrkdwn\": true" + "}";
53-
54-
HttpEntity<String> entity = new HttpEntity<>(payload, headers);
55-
56-
restTemplate.exchange(AGIT_WEBHOOK_URL, HttpMethod.POST, entity, String.class);
40+
if (request.notificationType() == NotificationType.TASK_REQUESTED) {
41+
ResponseEntity<String> responseEntity = restTemplate.exchange(
42+
AGIT_WEBHOOK_URL, HttpMethod.POST, entity, String.class);
43+
updateAgitPostId(responseEntity, task);
44+
}
45+
else {
46+
restTemplate.exchange(AGIT_WEBHOOK_URL, HttpMethod.POST, entity, String.class);
47+
}
48+
}
49+
50+
private void updateAgitPostId(ResponseEntity<String> responseEntity, Task task) {
51+
try {
52+
JsonNode jsonNode = objectMapper.readTree(responseEntity.getBody());
53+
task.updateAgitPostId(jsonNode.get("id").asLong());
54+
taskService.upsert(task);
55+
} catch (JsonProcessingException e) {
56+
throw new ApplicationException(NotificationErrorCode.AGIT_SEND_FAILED);
57+
}
5758
}
5859
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package clap.server.adapter.outbound.api;
2+
3+
import clap.server.adapter.outbound.api.dto.PushNotificationTemplate;
4+
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
5+
import clap.server.domain.model.task.Task;
6+
import org.springframework.http.HttpEntity;
7+
import org.springframework.http.HttpHeaders;
8+
import org.springframework.stereotype.Component;
9+
10+
@Component
11+
public class AgitTemplateBuilder {
12+
13+
public HttpEntity<String> createAgitEntity(PushNotificationTemplate request, Task task) {
14+
return new HttpEntity<>(createPayLoad(request, task), createHeaders());
15+
}
16+
17+
18+
public HttpHeaders createHeaders() {
19+
HttpHeaders headers = new HttpHeaders();
20+
headers.add("Content-Type", "application/json");
21+
return headers;
22+
}
23+
24+
public String createPayLoad(PushNotificationTemplate request, Task task) {
25+
26+
String payload;
27+
if (request.notificationType() == NotificationType.TASK_REQUESTED) {
28+
payload = "{"
29+
+ "\"text\": \"" + createMessage(request) + "\","
30+
+ "\"mrkdwn\": true" + "}";
31+
}
32+
33+
else {
34+
payload = "{"
35+
+ "\"parent_id\": " + task.getAgitPostId() + ","
36+
+ "\"text\": \"" + createMessage(request) + "\","
37+
+ "\"mrkdwn\": true"
38+
+ "}";
39+
}
40+
return payload;
41+
}
42+
43+
public String createMessage(PushNotificationTemplate request) {
44+
String taskUrl = "https://www.naver.com"; //Todo 작업 상세페이지 url 추가
45+
46+
return switch (request.notificationType()) {
47+
case TASK_REQUESTED -> "📌 *새 작업 요청:* `" + request.taskName() + "`\\n"
48+
+ "\\t\\t*•요청자: " + request.senderName() + "*\\n"
49+
+ "\\t\\t[OPEN](" + taskUrl + ")";
50+
case STATUS_SWITCHED -> "⚙️ *작업 상태 변경:* `" + request.taskName() + "\\n"
51+
+ "\\t\\t*•작업 상태: " + request.message() + "*\\n"
52+
+ "\\t\\t[OPEN](" + taskUrl + ")";
53+
case PROCESSOR_CHANGED -> "🔄 *담당자 변경:* `" + request.taskName() + "\\n"
54+
+ "\\t\\t*•새 담당자: " + request.message() + "*\\n"
55+
+ "\\t\\t[OPEN](" + taskUrl + ")";
56+
case PROCESSOR_ASSIGNED -> "👤 *작업 담당자 배정:* `" + request.taskName() + "\\n"
57+
+ "\\t\\t*•담당자: " + request.message() + "*\\n"
58+
+ "\\t\\t[OPEN](" + taskUrl + ")";
59+
default -> null;
60+
};
61+
}
62+
}

src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
import clap.server.application.port.outbound.task.LoadTaskPort;
1313
import clap.server.common.annotation.architecture.PersistenceAdapter;
1414
import clap.server.domain.model.task.Task;
15+
import clap.server.exception.ApplicationException;
16+
import clap.server.exception.code.NotificationErrorCode;
17+
import com.fasterxml.jackson.core.JsonProcessingException;
18+
import com.fasterxml.jackson.databind.JsonNode;
19+
import com.fasterxml.jackson.databind.ObjectMapper;
1520
import lombok.RequiredArgsConstructor;
1621
import lombok.extern.slf4j.Slf4j;
1722
import org.springframework.data.domain.Page;
@@ -30,6 +35,7 @@
3035
public class TaskPersistenceAdapter implements CommandTaskPort, LoadTaskPort {
3136
private final TaskRepository taskRepository;
3237
private final TaskPersistenceMapper taskPersistenceMapper;
38+
private final ObjectMapper objectMapper;
3339

3440
@Override
3541
public Task save(Task task) {

src/main/java/clap/server/adapter/outbound/persistense/entity/task/TaskEntity.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public class TaskEntity extends BaseTimeEntity {
4545
@Column
4646
private Long processorOrder;
4747

48+
@Column
49+
private Long agitPostId;
50+
4851
@ManyToOne(fetch = FetchType.LAZY)
4952
@JoinColumn(name = "reviewer_id")
5053
private MemberEntity reviewer;

src/main/java/clap/server/application/port/outbound/task/CommandTaskPort.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import clap.server.domain.model.task.Task;
44

5-
import java.util.Optional;
6-
75
public interface CommandTaskPort {
86
Task save(Task task);
97
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package clap.server.application.port.outbound.webhook;
22

33
import clap.server.adapter.outbound.api.dto.PushNotificationTemplate;
4+
import clap.server.domain.model.task.Task;
45

56
public interface SendAgitPort {
6-
void sendAgit(PushNotificationTemplate request);
7+
void sendAgit(PushNotificationTemplate request, Task task);
78
}

src/main/java/clap/server/application/service/history/PostCommentService.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,29 +68,28 @@ public void saveCommentAttachment(Long userId, Long taskId, MultipartFile file)
6868
if (Member.checkCommenter(task, member)) {
6969
Comment comment = Comment.createComment(member, task, null);
7070
Comment savedComment = commandCommentPort.saveComment(comment);
71-
saveAttachment(file, task, savedComment);
71+
String fileName = saveAttachment(file, task, savedComment);
7272

7373
TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.COMMENT_FILE, task, null, member, savedComment);
7474
commandTaskHistoryPort.save(taskHistory);
7575

7676
if (member.getMemberInfo().getRole() == MemberRole.ROLE_USER) {
77-
publishNotification(task.getProcessor(), task, "첨부파일", member.getNickname());
77+
publishNotification(task.getProcessor(), task, fileName + "(첨부파일)", member.getNickname());
7878
} else {
79-
publishNotification(task.getRequester(), task, "첨부파일", task.getProcessor().getNickname());
79+
publishNotification(task.getRequester(), task, fileName + "(첨부파일)", task.getProcessor().getNickname());
8080
}
8181
}
8282
}
8383

84-
private void saveAttachment(MultipartFile file, Task task, Comment comment) {
84+
private String saveAttachment(MultipartFile file, Task task, Comment comment) {
8585
String fileUrl = s3UploadPort.uploadSingleFile(FilePathPolicy.TASK_COMMENT, file);
8686
Attachment attachment = Attachment.createCommentAttachment(task, comment, file.getOriginalFilename(), fileUrl, file.getSize());
8787
commandAttachmentPort.save(attachment);
88+
return file.getOriginalFilename();
8889
}
8990

9091
private void publishNotification(Member receiver, Task task, String message, String commenterName) {
9192
sendNotificationService.sendPushNotification(receiver, NotificationType.COMMENT, task, message, commenterName);
92-
sendNotificationService.sendAgitNotification(NotificationType.COMMENT,
93-
task, message, commenterName);
9493
}
9594

9695
}

src/main/java/clap/server/application/service/task/UpdateTaskService.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@
2525
import clap.server.domain.model.task.*;
2626
import clap.server.domain.policy.attachment.FilePathPolicy;
2727
import clap.server.exception.ApplicationException;
28+
import clap.server.exception.code.NotificationErrorCode;
2829
import clap.server.exception.code.TaskErrorCode;
30+
import com.fasterxml.jackson.core.JsonProcessingException;
31+
import com.fasterxml.jackson.databind.JsonNode;
32+
import com.fasterxml.jackson.databind.ObjectMapper;
2933
import lombok.RequiredArgsConstructor;
3034
import lombok.extern.slf4j.Slf4j;
35+
import org.springframework.http.ResponseEntity;
3136
import org.springframework.transaction.annotation.Transactional;
3237
import org.springframework.web.multipart.MultipartFile;
3338

@@ -51,6 +56,7 @@ public class UpdateTaskService implements UpdateTaskUsecase, UpdateTaskStatusUse
5156
private final CommandAttachmentPort commandAttachmentPort;
5257
private final CommandTaskHistoryPort commandTaskHistoryPort;
5358
private final S3UploadPort s3UploadPort;
59+
private final ObjectMapper objectMapper;
5460

5561
@Override
5662
@Transactional
@@ -140,7 +146,7 @@ private void publishNotification(Task task, NotificationType notificationType, S
140146
sendNotificationService.sendPushNotification(receiver, notificationType,
141147
task, message, null);
142148
});
143-
sendNotificationService.sendAgitNotification(notificationType,
144-
task, message, null);
149+
150+
sendNotificationService.sendAgitNotification(notificationType, task, message, null);
145151
}
146152
}

src/main/java/clap/server/application/service/webhook/SendAgitService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import clap.server.adapter.outbound.api.dto.PushNotificationTemplate;
44
import clap.server.application.port.outbound.webhook.SendAgitPort;
55
import clap.server.common.annotation.architecture.ApplicationService;
6+
import clap.server.domain.model.task.Task;
67
import lombok.RequiredArgsConstructor;
78

89
@ApplicationService
@@ -11,7 +12,7 @@ public class SendAgitService {
1112

1213
private final SendAgitPort agitPort;
1314

14-
public void sendAgit(PushNotificationTemplate request) {
15-
agitPort.sendAgit(request);
15+
public void sendAgit(PushNotificationTemplate request, Task task) {
16+
agitPort.sendAgit(request, task);
1617
}
1718
}

src/main/java/clap/server/application/service/webhook/SendNotificationService.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
import org.springframework.scheduling.annotation.Async;
1313

1414
import java.util.concurrent.CompletableFuture;
15-
import java.util.concurrent.atomic.AtomicBoolean;
16-
1715
import static clap.server.domain.model.notification.Notification.createTaskNotification;
1816

1917
@ApplicationService
@@ -25,7 +23,6 @@ public class SendNotificationService {
2523
private final SendEmailService sendEmailService;
2624
private final SendKaKaoWorkService sendKaKaoWorkService;
2725
private final CommandNotificationPort commandNotificationPort;
28-
private static final AtomicBoolean agitSent = new AtomicBoolean(false);
2926

3027
@Async("notificationExecutor")
3128
public void sendPushNotification(Member receiver, NotificationType notificationType,
@@ -85,6 +82,6 @@ public void sendAgitNotification(NotificationType notificationType,
8582
commenterName
8683
);
8784

88-
sendAgitService.sendAgit(pushNotificationTemplate);
85+
sendAgitService.sendAgit(pushNotificationTemplate, task);
8986
}
9087
}

0 commit comments

Comments
 (0)