diff --git a/src/main/java/com/slatto/domain/notification/service/NotificationService.java b/src/main/java/com/slatto/domain/notification/service/NotificationService.java index eb96b58b..3da29e8d 100644 --- a/src/main/java/com/slatto/domain/notification/service/NotificationService.java +++ b/src/main/java/com/slatto/domain/notification/service/NotificationService.java @@ -461,6 +461,7 @@ public void createNoticeCreatedNotifications( @Transactional public void createFileUploadedNotifications( Long projectId, + Long fileId, String projectTitle, String fileName, String uploaderName, @@ -468,6 +469,7 @@ public void createFileUploadedNotifications( Long actorUserId ) { validateRequiredId(projectId); + validateRequiredId(fileId); validateRequiredId(actorUserId); validateRequiredText(projectTitle); validateRequiredText(fileName); @@ -480,7 +482,7 @@ public void createFileUploadedNotifications( .title(createFileUploadedTitle(projectTitle)) .content(createFileUploadedContent(fileName, uploaderName)) .targetType(NotificationTargetType.PROJECT_FILE) - .targetId(projectId) + .targetId(fileId) .excludeUserId(actorUserId) .build()); } @@ -669,7 +671,7 @@ private NotificationListResponse.NotificationSummary toSummary(Notification noti } private String createScheduleAssignedContent(String scheduleTitle, String assigneeName) { - return assigneeName + "님이 [" + scheduleTitle + "] 담당자로 지정되었어요"; + return assigneeName + "님이 [" + scheduleTitle + "] 담당자로 지명되었어요."; } private String createScheduleAssignedTitle(String projectTitle) { @@ -681,7 +683,7 @@ private String createProjectJoinedTitle(String projectTitle) { } private String createProjectJoinedContent(String joinerName) { - return joinerName + "님이 프로젝트에 합류했어요"; + return joinerName + "님이 프로젝트에 합류했어요."; } private String createScheduleCreatedTitle(String projectTitle) { @@ -689,7 +691,7 @@ private String createScheduleCreatedTitle(String projectTitle) { } private String createScheduleCreatedContent(String scheduleTitle, String creatorName) { - return creatorName + "님이 [" + scheduleTitle + "] 일정을 등록했어요"; + return creatorName + "님이 [" + scheduleTitle + "] 일정을 등록했어요."; } private String createVideoFeedbackCommentedTitle(String projectTitle) { @@ -702,10 +704,10 @@ private String createVideoFeedbackCommentedContent( int groupCount ) { if (groupCount <= 1) { - return commenterName + "님이 [" + videoTitle + "]에 새로운 피드백을 남겼어요"; + return commenterName + "님이 [" + videoTitle + "]에 새로운 피드백을 남겼어요."; } - return "[" + videoTitle + "]에 새로운 피드백 " + groupCount + "건이 등록되었어요"; + return "[" + videoTitle + "]에 새로운 피드백 " + groupCount + "건이 등록되었어요."; } private String createRecruitmentAppliedTitle(String projectTitle) { @@ -718,10 +720,10 @@ private String createRecruitmentAppliedContent( int groupCount ) { if (groupCount <= 1) { - return applicantName + "님이 [" + recruitmentTitle + "]에 지원했어요"; + return applicantName + "님이 [" + recruitmentTitle + "]에 지원했어요."; } - return "[" + recruitmentTitle + "]에 새로운 지원자 " + groupCount + "명이 지원했어요"; + return "[" + recruitmentTitle + "]에 새로운 지원자 " + groupCount + "명이 지원했어요."; } private String createNoticeCreatedTitle(String projectTitle) { @@ -729,7 +731,7 @@ private String createNoticeCreatedTitle(String projectTitle) { } private String createNoticeCreatedContent(String noticeTitle, String creatorName) { - return creatorName + "님이 새 공지를 등록했어요: " + noticeTitle; + return creatorName + "님이 새 공지를 등록했어요: " + noticeTitle + "."; } private String createFileUploadedTitle(String projectTitle) { @@ -737,7 +739,7 @@ private String createFileUploadedTitle(String projectTitle) { } private String createFileUploadedContent(String fileName, String uploaderName) { - return uploaderName + "님이 [" + fileName + "] 파일을 등록했어요"; + return uploaderName + "님이 [" + fileName + "] 파일을 등록했어요."; } private String createFallbackTitle(NotificationType type) { diff --git a/src/main/java/com/slatto/domain/project/service/ProjectFileService.java b/src/main/java/com/slatto/domain/project/service/ProjectFileService.java index a756da95..2e316dcf 100644 --- a/src/main/java/com/slatto/domain/project/service/ProjectFileService.java +++ b/src/main/java/com/slatto/domain/project/service/ProjectFileService.java @@ -1,6 +1,7 @@ package com.slatto.domain.project.service; import com.slatto.domain.notification.service.ActivityLogService; +import com.slatto.domain.notification.service.NotificationService; import com.slatto.domain.project.dto.ProjectFileDownloadResponse; import com.slatto.domain.project.dto.ProjectFileResponse; import com.slatto.domain.project.dto.ProjectFileListResponse; @@ -12,6 +13,7 @@ import com.slatto.domain.project.entity.ProjectMember; import com.slatto.domain.project.exception.ProjectErrorCode; import com.slatto.domain.project.repository.ProjectFileRepository; +import com.slatto.domain.project.repository.ProjectMemberRepository; import com.slatto.domain.user.entity.Users; import com.slatto.global.exception.BaseException; import com.slatto.global.response.code.CommonErrorCode; @@ -53,8 +55,10 @@ public class ProjectFileService { private final ProjectFileRepository projectFileRepository; private final ProjectAccessValidator projectAccessValidator; + private final ProjectMemberRepository projectMemberRepository; private final StorageService storageService; private final ActivityLogService activityLogService; + private final NotificationService notificationService; public ProjectFileListResponse getProjectFiles( Long projectId, @@ -123,6 +127,15 @@ public ProjectFileResponse uploadProjectFile( ProjectFile savedFile = projectFileRepository.save(projectFile); activityLogService.createFileUploadedLog(projectId, currentUserId, savedFile.getId(), savedFile.getFileName()); + notificationService.createFileUploadedNotifications( + projectId, + savedFile.getId(), + project.getTitle(), + savedFile.getFileName(), + currentMember.getUser().getNickname(), + getActiveProjectMemberUserIds(projectId), + currentUserId + ); return toResponse(savedFile); } @@ -321,6 +334,14 @@ private int normalizePageSize(int size) { return Math.min(size, MAX_PAGE_SIZE); } + private List getActiveProjectMemberUserIds(Long projectId) { + return projectMemberRepository.findAllActiveMembersByProjectId(projectId) + .stream() + .map(ProjectMember::getUser) + .map(Users::getId) + .toList(); + } + private ProjectFileResponse toResponse(ProjectFile projectFile) { Users uploader = projectFile.getUploader(); diff --git a/src/main/java/com/slatto/domain/project/service/ProjectInvitationService.java b/src/main/java/com/slatto/domain/project/service/ProjectInvitationService.java index 618879a9..ff8edf5b 100644 --- a/src/main/java/com/slatto/domain/project/service/ProjectInvitationService.java +++ b/src/main/java/com/slatto/domain/project/service/ProjectInvitationService.java @@ -15,6 +15,7 @@ import com.slatto.domain.project.repository.ProjectMemberRepository; import com.slatto.domain.project.repository.ProjectUserRoleRepository; import com.slatto.domain.notification.service.ActivityLogService; +import com.slatto.domain.notification.service.NotificationService; import com.slatto.domain.user.entity.Users; import com.slatto.domain.user.enums.RoleName; import com.slatto.domain.user.repository.UserRepository; @@ -50,6 +51,7 @@ public class ProjectInvitationService { private final ProjectAccessValidator projectAccessValidator; private final ProjectInvitationProperties projectInvitationProperties; private final ActivityLogService activityLogService; + private final NotificationService notificationService; private final SecureRandom secureRandom = new SecureRandom(); @Transactional @@ -117,6 +119,12 @@ public ProjectInvitationAcceptResponse acceptInvitation( saveProjectRoles(projectMember, roleNames); projectInvitation.accept(accepter); activityLogService.createProjectMemberJoinedLog(project.getId(), accepter.getId()); + notificationService.createProjectJoinedNotifications( + project.getId(), + project.getTitle(), + accepter.getNickname(), + getActiveProjectMemberUserIds(project.getId()) + ); return ProjectInvitationAcceptResponse.builder() .projectId(project.getId()) @@ -134,6 +142,14 @@ private void saveProjectRoles(ProjectMember projectMember, List roleNa projectUserRoleRepository.saveAll(projectUserRoles); } + private List getActiveProjectMemberUserIds(Long projectId) { + return projectMemberRepository.findAllActiveMembersByProjectId(projectId) + .stream() + .map(ProjectMember::getUser) + .map(Users::getId) + .toList(); + } + private void validateAcceptableInvitation(ProjectInvitation projectInvitation) { if (projectInvitation.isAccepted()) { throw new BaseException(ProjectErrorCode.PROJECT_INVITATION_ALREADY_ACCEPTED); diff --git a/src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java b/src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java index 0c0e0aff..79469abe 100644 --- a/src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java +++ b/src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java @@ -1,6 +1,7 @@ package com.slatto.domain.project.service; import com.slatto.domain.notification.service.ActivityLogService; +import com.slatto.domain.notification.service.NotificationService; import com.slatto.domain.project.dto.ProjectNoticeCreateRequest; import com.slatto.domain.project.dto.ProjectNoticeListResponse; import com.slatto.domain.project.dto.ProjectNoticeReadResponse; @@ -11,6 +12,7 @@ import com.slatto.domain.project.entity.ProjectNotice; import com.slatto.domain.project.entity.ProjectNoticeRead; import com.slatto.domain.project.exception.ProjectErrorCode; +import com.slatto.domain.project.repository.ProjectMemberRepository; import com.slatto.domain.project.repository.ProjectNoticeReadRepository; import com.slatto.domain.project.repository.ProjectNoticeRepository; import com.slatto.domain.user.entity.Users; @@ -35,8 +37,10 @@ public class ProjectNoticeService { private final ProjectNoticeRepository projectNoticeRepository; private final ProjectNoticeReadRepository projectNoticeReadRepository; + private final ProjectMemberRepository projectMemberRepository; private final ProjectAccessValidator projectAccessValidator; private final ActivityLogService activityLogService; + private final NotificationService notificationService; public ProjectNoticeListResponse getProjectNotices( Long projectId, @@ -95,6 +99,15 @@ public ProjectNoticeResponse createProjectNotice( ); ProjectNotice savedNotice = projectNoticeRepository.save(projectNotice); activityLogService.createNoticeCreatedLog(projectId, currentUserId, savedNotice.getId()); + notificationService.createNoticeCreatedNotifications( + projectId, + savedNotice.getId(), + project.getTitle(), + savedNotice.getTitle(), + currentMember.getUser().getNickname(), + getActiveProjectMemberUserIds(projectId), + currentUserId + ); return toResponse(savedNotice, false); } @@ -202,6 +215,14 @@ private Map getReadByNoticeId(Long userId, List pr )); } + private List getActiveProjectMemberUserIds(Long projectId) { + return projectMemberRepository.findAllActiveMembersByProjectId(projectId) + .stream() + .map(ProjectMember::getUser) + .map(Users::getId) + .toList(); + } + private int normalizePageSize(int size) { if (size <= 0) { return DEFAULT_PAGE_SIZE; diff --git a/src/main/java/com/slatto/domain/schedule/service/ScheduleService.java b/src/main/java/com/slatto/domain/schedule/service/ScheduleService.java index a3ab6539..bddba54b 100644 --- a/src/main/java/com/slatto/domain/schedule/service/ScheduleService.java +++ b/src/main/java/com/slatto/domain/schedule/service/ScheduleService.java @@ -211,6 +211,15 @@ public ScheduleResponse createSchedule(Long currentUserId, ScheduleCreateRequest writer.getId() ); if (savedSchedule.isProjectSchedule()) { + notificationService.createScheduleCreatedNotifications( + project.getId(), + savedSchedule.getId(), + project.getTitle(), + savedSchedule.getTitle(), + writer.getNickname(), + getActiveProjectMemberUserIds(project.getId()), + writer.getId() + ); activityLogService.createScheduleCreatedLog( project.getId(), currentUserId, @@ -377,6 +386,14 @@ private List getProjectParticipantUsers(Long projectId, List partic .toList(); } + private List getActiveProjectMemberUserIds(Long projectId) { + return projectMemberRepository.findAllActiveMembersByProjectId(projectId) + .stream() + .map(ProjectMember::getUser) + .map(Users::getId) + .toList(); + } + private Users getActiveUser(Long userId) { return userRepository.findByIdAndDeletedAtIsNull(userId) .orElseThrow(() -> new BaseException(CommonErrorCode.NOT_FOUND)); diff --git a/src/test/java/com/slatto/domain/notification/service/ProjectNoticeActivityFlowIntegrationTest.java b/src/test/java/com/slatto/domain/notification/service/ProjectNoticeActivityFlowIntegrationTest.java index 55d41eb8..42b2e8d0 100644 --- a/src/test/java/com/slatto/domain/notification/service/ProjectNoticeActivityFlowIntegrationTest.java +++ b/src/test/java/com/slatto/domain/notification/service/ProjectNoticeActivityFlowIntegrationTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import java.time.LocalDate; @@ -56,6 +57,9 @@ class ProjectNoticeActivityFlowIntegrationTest { @Autowired private ProjectActivityReadRepository projectActivityReadRepository; + @MockBean + private NotificationService notificationService; + @Autowired private UserRepository userRepository;