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,9 +1,11 @@
package com.slatto.domain.notification.entity;

import com.slatto.domain.common.entity.BaseEntity;
import com.slatto.domain.project.entity.Project;
import com.slatto.domain.notification.enums.ActorType;
import com.slatto.domain.notification.enums.ActivityLogTargetType;
import com.slatto.domain.notification.enums.ActivityLogType;
import com.slatto.domain.notification.model.ActivityActor;
import com.slatto.domain.project.entity.Project;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
Expand Down Expand Up @@ -52,45 +54,41 @@ public class ActivityLog extends BaseEntity {

private ActivityLog(
Project project,
Long actorUserId,
Long actorGuestId,
ActorType actorType,
ActivityActor actor,
ActivityLogType type,
String content,
String targetType,
ActivityLogTargetType targetType,
Long targetId,
String groupKey
) {
this.project = project;
this.actorUserId = actorUserId;
this.actorGuestId = actorGuestId;
this.actorType = actorType;
this.actorUserId = actor.actorUserId();
this.actorGuestId = actor.actorGuestId();
this.actorType = actor.actorType();
this.type = type;
this.content = content;
this.targetType = targetType;
this.targetType = targetType != null ? targetType.name() : null;
this.targetId = targetId;
this.groupKey = groupKey;
}

public static ActivityLog create(
Project project,
Long actorUserId,
ActorType actorType,
ActivityActor actor,
ActivityLogType type,
String content,
String targetType,
ActivityLogTargetType targetType,
Long targetId
) {
return new ActivityLog(
project,
actorUserId,
null,
actorType,
actor,
type,
content,
targetType,
targetId,
null
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.slatto.domain.notification.model;

import com.slatto.domain.notification.enums.ActorType;

public record ActivityActor(
ActorType actorType,
Long actorUserId,
Long actorGuestId,
String displayName
) {

public ActivityActor {
if (actorType == null) {
throw new IllegalArgumentException("최근활동 행위자 타입은 필수입니다.");
}
if (displayName == null || displayName.isBlank()) {
throw new IllegalArgumentException("최근활동 행위자 이름은 필수입니다.");
}

validateActorIds(actorType, actorUserId, actorGuestId);
}

public static ActivityActor user(Long userId, String displayName) {
return new ActivityActor(ActorType.USER, userId, null, displayName);
}

public static ActivityActor clientReviewer(Long guestId, String displayName) {
return new ActivityActor(ActorType.CLIENT_REVIEWER, null, guestId, displayName);
}

public static ActivityActor system(String displayName) {
return new ActivityActor(ActorType.SYSTEM, null, null, displayName);
}

private static void validateActorIds(ActorType actorType, Long actorUserId, Long actorGuestId) {
switch (actorType) {
case USER -> {
requirePositiveId(actorUserId, "사용자");
requireNull(actorGuestId, "회원 행위자는 게스트 ID를 가질 수 없습니다.");
}
case CLIENT_REVIEWER -> {
requireNull(actorUserId, "게스트 행위자는 사용자 ID를 가질 수 없습니다.");
requirePositiveId(actorGuestId, "게스트");
}
case SYSTEM -> {
requireNull(actorUserId, "시스템 행위자는 사용자 ID를 가질 수 없습니다.");
requireNull(actorGuestId, "시스템 행위자는 게스트 ID를 가질 수 없습니다.");
}
}
}

private static void requirePositiveId(Long id, String actorName) {
if (id == null || id <= 0) {
throw new IllegalArgumentException(actorName + " ID는 양수여야 합니다.");
}
}

private static void requireNull(Long id, String message) {
if (id != null) {
throw new IllegalArgumentException(message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import com.slatto.domain.notification.entity.ActivityLog;
import com.slatto.domain.notification.enums.ActivityLogTargetType;
import com.slatto.domain.notification.enums.ActivityLogType;
import com.slatto.domain.notification.enums.ActorType;
import com.slatto.domain.notification.model.ActivityActor;
import com.slatto.domain.notification.repository.ActivityLogRepository;
import com.slatto.domain.project.entity.Project;
import com.slatto.domain.project.repository.ProjectRepository;
import com.slatto.domain.sharelink.entity.Guest;
import com.slatto.domain.user.entity.Users;
import com.slatto.domain.user.repository.UserRepository;
import com.slatto.global.exception.BaseException;
Expand All @@ -23,6 +24,7 @@ public class ActivityLogService {
private final ActivityLogRepository activityLogRepository;
private final ProjectRepository projectRepository;
private final UserRepository userRepository;
private final ActivityMessageFactory activityMessageFactory;

/**
* 프로젝트에 새 참여자가 합류했을 때 최근활동을 저장한다.
Expand All @@ -35,7 +37,7 @@ public void createProjectMemberJoinedLog(Long projectId, Long actorUserId) {
projectId,
actor,
ActivityLogType.PROJECT_MEMBER_JOINED,
createProjectMemberJoinedContent(actor.getNickname()),
activityMessageFactory.projectMemberJoined(actor.getNickname()),
ActivityLogTargetType.PROJECT,
projectId
);
Expand All @@ -59,7 +61,7 @@ public void createProjectStatusChangedLog(
projectId,
actor,
ActivityLogType.PROJECT_STATUS_CHANGED,
createProjectStatusChangedContent(actor.getNickname(), previousStatus, changedStatus),
activityMessageFactory.projectStatusChanged(actor.getNickname(), previousStatus, changedStatus),
ActivityLogTargetType.PROJECT,
projectId
);
Expand All @@ -76,7 +78,7 @@ public void createProjectUpdatedLog(Long projectId, Long actorUserId) {
projectId,
actor,
ActivityLogType.PROJECT_UPDATED,
createProjectUpdatedContent(actor.getNickname()),
activityMessageFactory.projectUpdated(actor.getNickname()),
ActivityLogTargetType.PROJECT,
projectId
);
Expand All @@ -100,7 +102,7 @@ public void createScheduleCreatedLog(
projectId,
actor,
ActivityLogType.SCHEDULE_CREATED,
createScheduleCreatedContent(actor.getNickname(), scheduleTitle),
activityMessageFactory.scheduleCreated(actor.getNickname(), scheduleTitle),
ActivityLogTargetType.SCHEDULE,
scheduleId
);
Expand All @@ -124,7 +126,7 @@ public void createScheduleUpdatedLog(
projectId,
actor,
ActivityLogType.SCHEDULE_UPDATED,
createScheduleUpdatedContent(actor.getNickname(), scheduleTitle),
activityMessageFactory.scheduleUpdated(actor.getNickname(), scheduleTitle),
ActivityLogTargetType.SCHEDULE,
scheduleId
);
Expand All @@ -146,7 +148,7 @@ public void createNoticeCreatedLog(
projectId,
actor,
ActivityLogType.NOTICE_CREATED,
createNoticeCreatedContent(actor.getNickname()),
activityMessageFactory.noticeCreated(actor.getNickname()),
ActivityLogTargetType.NOTICE,
noticeId
);
Expand All @@ -170,7 +172,7 @@ public void createFileUploadedLog(
projectId,
actor,
ActivityLogType.FILE_UPLOADED,
createFileUploadedContent(actor.getNickname(), fileName),
activityMessageFactory.fileUploaded(actor.getNickname(), fileName),
ActivityLogTargetType.FILE,
fileId
);
Expand All @@ -194,7 +196,32 @@ public void createVideoFeedbackCommentedLog(
projectId,
actor,
ActivityLogType.VIDEO_FEEDBACK_COMMENTED,
createVideoFeedbackCommentedContent(actor.getNickname(), videoTitle),
activityMessageFactory.videoFeedbackCommented(actor.getNickname(), videoTitle),
ActivityLogTargetType.VIDEO,
videoId
);
}

/**
* 공유 링크 게스트가 영상 피드백을 남겼을 때 최근활동을 저장한다.
* 피드백 도메인의 실제 호출 연결은 후속 작업에서 추가한다.
*/
@Transactional
public void createGuestVideoFeedbackCommentedLog(
Long projectId,
Guest guest,
Long videoId,
String videoTitle
) {
validateRequiredGuest(guest);
validateRequiredId(videoId);
validateRequiredText(videoTitle);

createActivityLog(
projectId,
ActivityActor.clientReviewer(guest.getId(), guest.getName()),
ActivityLogType.VIDEO_FEEDBACK_COMMENTED,
activityMessageFactory.videoFeedbackCommented(guest.getName(), videoTitle),
ActivityLogTargetType.VIDEO,
videoId
);
Expand All @@ -207,56 +234,37 @@ private void createUserActivityLog(
String content,
ActivityLogTargetType targetType,
Long targetId
) {
createActivityLog(
projectId,
ActivityActor.user(actor.getId(), actor.getNickname()),
type,
content,
targetType,
targetId
);
}

private void createActivityLog(
Long projectId,
ActivityActor actor,
ActivityLogType type,
String content,
ActivityLogTargetType targetType,
Long targetId
) {
Project project = getActiveProject(projectId);

activityLogRepository.save(ActivityLog.create(
project,
actor.getId(),
ActorType.USER,
actor,
type,
content,
getTargetTypeName(targetType),
targetType,
targetId
));
}

private String createProjectMemberJoinedContent(String actorName) {
return actorName + "님이 프로젝트에 합류했어요";
}

private String createProjectStatusChangedContent(
String actorName,
String previousStatus,
String changedStatus
) {
return actorName + "님이 프로젝트 단계를 '" + previousStatus + "'에서 '" + changedStatus + "'으로 변경했어요";
}

private String createProjectUpdatedContent(String actorName) {
return actorName + "님이 프로젝트 정보를 수정했어요";
}

private String createScheduleCreatedContent(String actorName, String scheduleTitle) {
return actorName + "님이 [" + scheduleTitle + "] 일정을 등록했어요";
}

private String createScheduleUpdatedContent(String actorName, String scheduleTitle) {
return actorName + "님이 [" + scheduleTitle + "] 일정을 수정했어요";
}

private String createNoticeCreatedContent(String actorName) {
return actorName + "님이 새 공지를 등록했어요";
}

private String createFileUploadedContent(String actorName, String fileName) {
return actorName + "님이 [" + fileName + "] 파일을 등록했어요";
}

private String createVideoFeedbackCommentedContent(String actorName, String videoTitle) {
return actorName + "님이 [" + videoTitle + "]에 피드백을 남겼어요";
}

private Project getActiveProject(Long projectId) {
validateRequiredId(projectId);

Expand All @@ -283,7 +291,10 @@ private void validateRequiredText(String text) {
}
}

private String getTargetTypeName(ActivityLogTargetType targetType) {
return targetType != null ? targetType.name() : null;
private void validateRequiredGuest(Guest guest) {
if (guest == null) {
throw new BaseException(CommonErrorCode.BAD_REQUEST);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.slatto.domain.notification.service;

import org.springframework.stereotype.Component;

@Component
public class ActivityMessageFactory {

public String projectMemberJoined(String actorName) {
return actorName + "님이 프로젝트에 합류했어요";
}

public String projectStatusChanged(String actorName, String previousStatus, String changedStatus) {
return actorName + "님이 프로젝트 단계를 '" + previousStatus + "'에서 '" + changedStatus + "'으로 변경했어요";
}

public String projectUpdated(String actorName) {
return actorName + "님이 프로젝트 정보를 수정했어요";
}

public String scheduleCreated(String actorName, String scheduleTitle) {
return actorName + "님이 [" + scheduleTitle + "] 일정을 등록했어요";
}

public String scheduleUpdated(String actorName, String scheduleTitle) {
return actorName + "님이 [" + scheduleTitle + "] 일정을 수정했어요";
}

public String noticeCreated(String actorName) {
return actorName + "님이 새 공지를 등록했어요";
}

public String fileUploaded(String actorName, String fileName) {
return actorName + "님이 [" + fileName + "] 파일을 등록했어요";
}

public String videoFeedbackCommented(String actorName, String videoTitle) {
return actorName + "님이 [" + videoTitle + "]에 피드백을 남겼어요";
}
}
Loading
Loading