Skip to content
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
@@ -1,6 +1,6 @@
package clap.server.adapter.inbound.web.history;

import clap.server.adapter.inbound.security.SecurityUserDetails;
import clap.server.adapter.inbound.security.service.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.history.request.EditCommentRequest;
import clap.server.application.port.inbound.history.DeleteCommentUsecase;
import clap.server.application.port.inbound.history.EditCommentUsecase;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package clap.server.adapter.inbound.web.history;

import clap.server.adapter.inbound.security.SecurityUserDetails;
import clap.server.adapter.inbound.security.service.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.history.request.CreateCommentRequest;
import clap.server.application.port.inbound.history.SaveCommentAttachmentUsecase;
import clap.server.application.port.inbound.history.SaveCommentUsecase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ public class ChangeTaskController {
@Operation(summary = "작업 상태 변경")
@Secured("ROLE_MANAGER")
@PatchMapping("/{taskId}/status")
public ResponseEntity<UpdateTaskResponse> updateTaskState(
public void updateTaskState(
@PathVariable @NotNull Long taskId,
@AuthenticationPrincipal SecurityUserDetails userInfo,
@Parameter(description = "변경하고 싶은 작업 상태",
schema = @Schema(allowableValues = {"IN_PROGRESS", "PENDING_COMPLETED", "COMPLETED"}))
@RequestBody TaskStatus taskStatus) {

return ResponseEntity.ok(updateTaskStatusUsecase.updateTaskStatus(userInfo.getUserId(), taskId, taskStatus));
updateTaskStatusUsecase.updateTaskStatus(userInfo.getUserId(), taskId, taskStatus);
}

@Operation(summary = "작업 처리자 변경")
Expand Down
67 changes: 34 additions & 33 deletions src/main/java/clap/server/adapter/outbound/api/AgitClient.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package clap.server.adapter.outbound.api;

import clap.server.adapter.outbound.api.dto.PushNotificationTemplate;
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
import clap.server.application.port.inbound.domain.TaskService;
import clap.server.application.port.outbound.webhook.SendAgitPort;
import clap.server.application.service.task.UpdateTaskService;
import clap.server.common.annotation.architecture.ExternalApiAdapter;
import clap.server.domain.model.task.Task;
import clap.server.exception.ApplicationException;
import clap.server.exception.code.NotificationErrorCode;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;


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

private final AgitTemplateBuilder agitTemplateBuilder;
private final ObjectMapper objectMapper;
private final TaskService taskService;

@Override
public void sendAgit(PushNotificationTemplate request) {
public void sendAgit(PushNotificationTemplate request, Task task) {

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

RestTemplate restTemplate = new RestTemplate();
String taskUrl = "https://www.naver.com"; //Todo 작업 상세페이지 url 추가

String message = switch (request.notificationType()) {
case TASK_REQUESTED -> "📌 *새 작업 요청:* `" + request.taskName() + "`\\n"
+ "\\t\\t*•요청자: " + request.senderName() + "*\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
case STATUS_SWITCHED -> "⚙️ *작업 상태 변경:* `" + request.taskName() + "\\n"
+ "\\t\\t*•작업 상태: " + request.message() + "*\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
case PROCESSOR_CHANGED -> "🔄 *담당자 변경:* `" + request.taskName() + "\\n"
+ "\\t\\t*•새 담당자: " + request.message() + "*\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
case PROCESSOR_ASSIGNED -> "👤 *작업 담당자 배정:* `" + request.taskName() + "\\n"
+ "\\t\\t*•담당자: " + request.message() + "*\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
case COMMENT -> "💬 *새 댓글:* `" + request.taskName() + "`\\n"
+ "\\t\\t*•작성자: " + request.commenterName() + "\\n"
+ "\\t\\t*•댓글 내용: " + request.message() + "\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
default -> null;
};

String payload = "{"
+ "\"text\": \"" + message + "\","
+ "\"mrkdwn\": true" + "}";

HttpEntity<String> entity = new HttpEntity<>(payload, headers);

restTemplate.exchange(AGIT_WEBHOOK_URL, HttpMethod.POST, entity, String.class);
if (request.notificationType() == NotificationType.TASK_REQUESTED) {
ResponseEntity<String> responseEntity = restTemplate.exchange(
AGIT_WEBHOOK_URL, HttpMethod.POST, entity, String.class);
updateAgitPostId(responseEntity, task);
}
else {
restTemplate.exchange(AGIT_WEBHOOK_URL, HttpMethod.POST, entity, String.class);
}
}

private void updateAgitPostId(ResponseEntity<String> responseEntity, Task task) {
try {
JsonNode jsonNode = objectMapper.readTree(responseEntity.getBody());
task.updateAgitPostId(jsonNode.get("id").asLong());
taskService.upsert(task);
} catch (JsonProcessingException e) {
throw new ApplicationException(NotificationErrorCode.AGIT_SEND_FAILED);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package clap.server.adapter.outbound.api;

import clap.server.adapter.outbound.api.dto.PushNotificationTemplate;
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
import clap.server.domain.model.task.Task;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

@Component
public class AgitTemplateBuilder {

public HttpEntity<String> createAgitEntity(PushNotificationTemplate request, Task task) {
return new HttpEntity<>(createPayLoad(request, task), createHeaders());
}


public HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
return headers;
}

public String createPayLoad(PushNotificationTemplate request, Task task) {

String payload;
if (request.notificationType() == NotificationType.TASK_REQUESTED) {
payload = "{"
+ "\"text\": \"" + createMessage(request) + "\","
+ "\"mrkdwn\": true" + "}";
}

else {
payload = "{"
+ "\"parent_id\": " + task.getAgitPostId() + ","
+ "\"text\": \"" + createMessage(request) + "\","
+ "\"mrkdwn\": true"
+ "}";
}
return payload;
}

public String createMessage(PushNotificationTemplate request) {
String taskUrl = "https://www.naver.com"; //Todo 작업 상세페이지 url 추가

return switch (request.notificationType()) {
case TASK_REQUESTED -> "📌 *새 작업 요청:* `" + request.taskName() + "`\\n"
+ "\\t\\t*•요청자: " + request.senderName() + "*\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
case STATUS_SWITCHED -> "⚙️ *작업 상태 변경:* `" + request.taskName() + "\\n"
+ "\\t\\t*•작업 상태: " + request.message() + "*\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
case PROCESSOR_CHANGED -> "🔄 *담당자 변경:* `" + request.taskName() + "\\n"
+ "\\t\\t*•새 담당자: " + request.message() + "*\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
case PROCESSOR_ASSIGNED -> "👤 *작업 담당자 배정:* `" + request.taskName() + "\\n"
+ "\\t\\t*•담당자: " + request.message() + "*\\n"
+ "\\t\\t[OPEN](" + taskUrl + ")";
default -> null;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@
import clap.server.application.port.outbound.task.LoadTaskPort;
import clap.server.common.annotation.architecture.PersistenceAdapter;
import clap.server.domain.model.task.Task;
import clap.server.exception.ApplicationException;
import clap.server.exception.code.NotificationErrorCode;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.http.ResponseEntity;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -29,6 +35,7 @@
public class TaskPersistenceAdapter implements CommandTaskPort, LoadTaskPort {
private final TaskRepository taskRepository;
private final TaskPersistenceMapper taskPersistenceMapper;
private final ObjectMapper objectMapper;

@Override
public Task save(Task task) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public class TaskEntity extends BaseTimeEntity {
@Column
private Long processorOrder;

@Column
private Long agitPostId;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reviewer_id")
private MemberEntity reviewer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;

public interface UpdateTaskStatusUsecase {
UpdateTaskResponse updateTaskStatus(Long memberId, Long taskId, TaskStatus taskStatus);
void updateTaskStatus(Long memberId, Long taskId, TaskStatus taskStatus);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

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

import java.util.Optional;

public interface CommandTaskPort {
Task save(Task task);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package clap.server.application.port.outbound.webhook;

import clap.server.adapter.outbound.api.dto.PushNotificationTemplate;
import clap.server.domain.model.task.Task;

public interface SendAgitPort {
void sendAgit(PushNotificationTemplate request);
void sendAgit(PushNotificationTemplate request, Task task);
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,29 +68,28 @@ public void saveCommentAttachment(Long userId, Long taskId, MultipartFile file)
if (Member.checkCommenter(task, member)) {
Comment comment = Comment.createComment(member, task, null);
Comment savedComment = commandCommentPort.saveComment(comment);
saveAttachment(file, task, savedComment);
String fileName = saveAttachment(file, 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());
publishNotification(task.getProcessor(), task, fileName + "(첨부파일)", member.getNickname());
} else {
publishNotification(task.getRequester(), task, "첨부파일", task.getProcessor().getNickname());
publishNotification(task.getRequester(), task, fileName + "(첨부파일)", task.getProcessor().getNickname());
}
}
}

private void saveAttachment(MultipartFile file, Task task, Comment comment) {
private String saveAttachment(MultipartFile file, Task task, Comment comment) {
String fileUrl = s3UploadPort.uploadSingleFile(FilePathPolicy.TASK_COMMENT, file);
Attachment attachment = Attachment.createCommentAttachment(task, comment, file.getOriginalFilename(), fileUrl, file.getSize());
commandAttachmentPort.save(attachment);
return file.getOriginalFilename();
}

private void publishNotification(Member receiver, Task task, String message, String commenterName) {
sendNotificationService.sendPushNotification(receiver, NotificationType.COMMENT, task, message, commenterName);
sendNotificationService.sendAgitNotification(NotificationType.COMMENT,
task, message, commenterName);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public class ApprovalTaskService implements ApprovalTaskUsecase {
private final TaskService taskService;
private final CategoryService categoryService;
private final LabelService labelService;
private final CommandTaskPort commandTaskPort;
private final RequestedTaskUpdatePolicy requestedTaskUpdatePolicy;
private final CommandTaskHistoryPort commandTaskHistoryPort;
private final SendNotificationService sendNotificationService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import clap.server.adapter.inbound.web.dto.task.request.UpdateTaskRequest;
import clap.server.adapter.inbound.web.dto.task.response.UpdateTaskResponse;
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskHistoryType;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.application.mapper.AttachmentMapper;
import clap.server.application.mapper.TaskResponseMapper;
Expand All @@ -19,24 +20,28 @@
import clap.server.application.port.outbound.s3.S3UploadPort;
import clap.server.application.port.outbound.task.CommandAttachmentPort;
import clap.server.application.port.outbound.task.LoadAttachmentPort;
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.policy.attachment.FilePathPolicy;
import clap.server.domain.model.member.Member;
import clap.server.domain.model.task.Attachment;
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.policy.task.TaskPolicyConstants;
import clap.server.domain.model.task.*;
import clap.server.domain.policy.attachment.FilePathPolicy;
import clap.server.exception.ApplicationException;
import clap.server.exception.code.NotificationErrorCode;
import clap.server.exception.code.TaskErrorCode;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

import static clap.server.domain.policy.task.TaskPolicyConstants.TASK_UPDATABLE_STATUS;


@ApplicationService
@RequiredArgsConstructor
Expand All @@ -51,7 +56,9 @@ public class UpdateTaskService implements UpdateTaskUsecase, UpdateTaskStatusUse
private final LoadAttachmentPort loadAttachmentPort;
private final LabelService labelService;
private final CommandAttachmentPort commandAttachmentPort;
private final CommandTaskHistoryPort commandTaskHistoryPort;
private final S3UploadPort s3UploadPort;
private final ObjectMapper objectMapper;

@Override
@Transactional
Expand All @@ -71,15 +78,22 @@ public UpdateTaskResponse updateTask(Long requesterId, Long taskId, UpdateTaskRe

@Override
@Transactional
public UpdateTaskResponse updateTaskState(Long memberId, Long taskId, UpdateTaskStatusRequest updateTaskStatusRequest) {
public void updateTaskStatus(Long memberId, Long taskId, TaskStatus taskStatus) {
memberService.findActiveMember(memberId);
memberService.findReviewer(memberId);
Task task = taskService.findById(taskId);
task.updateTaskStatus(taskStatus);
Task updateTask = taskService.upsert(task);

publishNotification(updateTask, NotificationType.STATUS_SWITCHED, String.valueOf(updateTask.getTaskStatus()));
return TaskResponseMapper.toUpdateTaskResponse(updateTask);
if(!TASK_UPDATABLE_STATUS.contains(taskStatus)){
throw new ApplicationException(TaskErrorCode.TASK_STATUS_NOT_ALLOWED);
}

if(!task.getTaskStatus().equals(taskStatus)){
task.updateTaskStatus(taskStatus);
Task updateTask = taskService.upsert(task);
TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.STATUS_SWITCHED, task, taskStatus.getDescription(), null,null);
commandTaskHistoryPort.save(taskHistory);
publishNotification(updateTask, NotificationType.STATUS_SWITCHED, String.valueOf(updateTask.getTaskStatus()));
}
}

@Transactional
Expand Down Expand Up @@ -135,7 +149,7 @@ private void publishNotification(Task task, NotificationType notificationType, S
sendNotificationService.sendPushNotification(receiver, notificationType,
task, message, null);
});
sendNotificationService.sendAgitNotification(notificationType,
task, message, null);

sendNotificationService.sendAgitNotification(notificationType, task, message, null);
}
}
Loading