diff --git a/src/main/java/com/moing/backend/domain/mission/application/service/MissionRemindAlarmUseCase.java b/src/main/java/com/moing/backend/domain/mission/application/service/MissionRemindAlarmUseCase.java index 5bcfa309..2a43ef79 100644 --- a/src/main/java/com/moing/backend/domain/mission/application/service/MissionRemindAlarmUseCase.java +++ b/src/main/java/com/moing/backend/domain/mission/application/service/MissionRemindAlarmUseCase.java @@ -12,7 +12,6 @@ import com.moing.backend.global.config.fcm.dto.request.MultiRequest; import com.moing.backend.global.config.fcm.service.MultiMessageSender; import lombok.RequiredArgsConstructor; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import javax.transaction.Transactional; @@ -31,7 +30,6 @@ public class MissionRemindAlarmUseCase { private final MissionArchiveScheduleQueryService missionArchiveScheduleQueryService; private final MissionQueryService missionQueryService; - private final ApplicationEventPublisher eventPublisher; private final MultiMessageSender multiMessageSender; private final SaveMultiAlarmHistoryUseCase saveMultiAlarmHistoryUseCase; @@ -39,7 +37,7 @@ public class MissionRemindAlarmUseCase { String REMIND_NAME = "미션 리마인드"; - public Boolean sendRemindMissionAlarm() { + public void sendRemindMissionAlarm() { Random random = new Random(System.currentTimeMillis()); String title = getTitle(random.nextInt(4)); @@ -50,16 +48,12 @@ public Boolean sendRemindMissionAlarm() { Optional> memberIdAndTokens = mapToMemberAndToken(remainMissionPeople); Optional> pushMemberIdAndToken = isPushMemberIdAndToken(remainMissionPeople); -// eventPublisher.publishEvent(new MultiFcmEvent(title, message, pushMemberIdAndToken, memberIdAndTokens, -// "",REMIND_NAME, AlarmType.REMIND, PagePath.MISSION_ALL_PTAH.getValue())); - if (pushMemberIdAndToken.isPresent() && !pushMemberIdAndToken.get().isEmpty()) { multiMessageSender.send(new MultiRequest(pushMemberIdAndToken.get(), title, message, "", REMIND_NAME, AlarmType.REMIND, PagePath.MISSION_ALL_PTAH.getValue())); } if (memberIdAndTokens.isPresent() && !memberIdAndTokens.get().isEmpty()) { saveMultiAlarmHistoryUseCase.saveAlarmHistories(AlarmHistoryMapper.getMemberIds(memberIdAndTokens.get()),"",title,message,REMIND_NAME, AlarmType.REMIND, PagePath.MISSION_ALL_PTAH.getValue()); } - return true; } @@ -137,7 +131,7 @@ public Boolean sendRepeatMissionRemindOnMonday() { } - public Optional> mapToMemberAndToken(List members) { + private Optional> mapToMemberAndToken(List members) { return Optional.of(members.stream() .map(member -> MemberIdAndToken.builder() .fcmToken(member.getFcmToken()) @@ -145,7 +139,7 @@ public Optional> mapToMemberAndToken(List members .build()) .collect(Collectors.toList())); } - public Optional> isPushMemberIdAndToken(List members) { + private Optional> isPushMemberIdAndToken(List members) { return Optional.of(members.stream() .map(member -> { if (member.isRemindPush() && !member.isSignOut()) { diff --git a/src/main/java/com/moing/backend/domain/mission/application/service/MissionScheduleUseCase.java b/src/main/java/com/moing/backend/domain/mission/application/service/MissionScheduleUseCase.java new file mode 100644 index 00000000..1223dba5 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/mission/application/service/MissionScheduleUseCase.java @@ -0,0 +1,44 @@ +package com.moing.backend.domain.mission.application.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Profile; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +@Slf4j +@Service +@Transactional +@EnableAsync +@EnableScheduling +@RequiredArgsConstructor +@Profile("prod") +public class MissionScheduleUseCase { + + private final MissionRemindAlarmUseCase missionRemindAlarmUseCase; + private final MissionUpdateUseCase missionUpdateUseCase; + + /** + * 단일 미션 마감 + * 해당 시간 미션 마감 + * 한시간 마다 실행 + */ + @Scheduled(cron = "0 1 * * * *") + public void singleMissionEndRoutine() { + missionUpdateUseCase.terminateMissionByAdmin(); + } + + /** + * 리마인드 알림 + * 인증하지 않은 미션이 있는 경우 알림 + * 매일 오후 8시 + */ + @Scheduled(cron = "0 0 20 * * *") + public void MissionRemindAlarm() { + missionRemindAlarmUseCase.sendRemindMissionAlarm(); + } +} \ No newline at end of file diff --git a/src/main/java/com/moing/backend/domain/mission/application/service/MissionTerminationUseCase.java b/src/main/java/com/moing/backend/domain/mission/application/service/MissionTerminationUseCase.java deleted file mode 100644 index 243582d5..00000000 --- a/src/main/java/com/moing/backend/domain/mission/application/service/MissionTerminationUseCase.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.moing.backend.domain.mission.application.service; - -import com.moing.backend.domain.member.domain.service.MemberGetService; -import com.moing.backend.domain.mission.domain.entity.Mission; -import com.moing.backend.domain.mission.domain.entity.constant.MissionStatus; -import com.moing.backend.domain.mission.domain.service.MissionQueryService; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -@Transactional -@RequiredArgsConstructor -public class MissionTerminationUseCase { - - private final MemberGetService memberGetService; - private final MissionQueryService missionQueryService; - - // 스케쥴러에서 호출 - public void terminateMission() { - - List missionByDueTo = missionQueryService.findMissionByDueTo(); - - missionByDueTo.stream().forEach( - // 미션 종료 처리 - mission -> mission.updateStatus(MissionStatus.END) - - ); - } - - // 미션 점수 반영 -> MissionState - - - - // MissionState 조회 해서 미션 점수 현황조회 - - public void getMissionScoreStatus() { - - } - - // 팀별 점수 반영 - public void updateMissionScore() { - - } - -} diff --git a/src/main/java/com/moing/backend/domain/mission/application/service/MissionUpdateUseCase.java b/src/main/java/com/moing/backend/domain/mission/application/service/MissionUpdateUseCase.java index 37a42829..6e3f6bc6 100644 --- a/src/main/java/com/moing/backend/domain/mission/application/service/MissionUpdateUseCase.java +++ b/src/main/java/com/moing/backend/domain/mission/application/service/MissionUpdateUseCase.java @@ -10,8 +10,6 @@ import com.moing.backend.domain.mission.domain.entity.constant.MissionStatus; import com.moing.backend.domain.mission.domain.service.MissionQueryService; import com.moing.backend.domain.mission.domain.service.MissionSaveService; -import com.moing.backend.domain.mission.exception.NoAccessCreateMission; -import com.moing.backend.domain.mission.exception.NoAccessDeleteMission; import com.moing.backend.domain.mission.exception.NoAccessUpdateMission; import com.moing.backend.domain.team.domain.entity.Team; import lombok.RequiredArgsConstructor; @@ -19,6 +17,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.List; @Service @Transactional @@ -31,17 +30,12 @@ public class MissionUpdateUseCase { public MissionCreateRes updateMission(String userSocialId, Long missionId, MissionReq missionReq) { - Member member = memberGetService.getMemberBySocialId(userSocialId); Mission mission = missionQueryService.findMissionById(missionId); Team team = mission.getTeam(); Long memberId = member.getMemberId(); - /** - * 미션 생성자 확인 - */ - if (!((memberId.equals(mission.getMakerId())) || memberId.equals(team.getLeaderId())) ) { throw new NoAccessUpdateMission(); } @@ -51,11 +45,11 @@ public MissionCreateRes updateMission(String userSocialId, Long missionId, Missi } - public MissionReadRes updateMissionStatus(String userSocialId, Long missionId) { - + public MissionReadRes terminateMissionByUser(String userSocialId, Long missionId) { Member member = memberGetService.getMemberBySocialId(userSocialId); Long memberId = member.getMemberId(); + Mission findMission = missionQueryService.findMissionById(missionId); Team team = findMission.getTeam(); @@ -69,4 +63,10 @@ public MissionReadRes updateMissionStatus(String userSocialId, Long missionId) { return MissionMapper.mapToMissionReadRes(findMission,member); } + + public void terminateMissionByAdmin() { + missionQueryService.findMissionByDueTo().stream().forEach( + mission -> mission.updateStatus(MissionStatus.END) + ); + } } diff --git a/src/main/java/com/moing/backend/domain/mission/domain/repository/MissionCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/mission/domain/repository/MissionCustomRepositoryImpl.java index ebf9471c..1baa1d3a 100644 --- a/src/main/java/com/moing/backend/domain/mission/domain/repository/MissionCustomRepositoryImpl.java +++ b/src/main/java/com/moing/backend/domain/mission/domain/repository/MissionCustomRepositoryImpl.java @@ -128,13 +128,11 @@ private JPQLQuery RepeatMissionDonePeopleByWeek(NumberPath missionId @Override public Optional> findMissionByDueTo() { - LocalDateTime now = LocalDateTime.now(); - return Optional.ofNullable(queryFactory .selectFrom(mission) .where( - mission.dueTo.before(now), mission.status.eq(MissionStatus.WAIT).or(mission.status.eq(MissionStatus.ONGOING)), + mission.dueTo.before(LocalDateTime.now()), mission.type.eq(MissionType.ONCE) ).fetch()); } diff --git a/src/main/java/com/moing/backend/domain/mission/presentation/MissionController.java b/src/main/java/com/moing/backend/domain/mission/presentation/MissionController.java index f041b45b..56dccc52 100644 --- a/src/main/java/com/moing/backend/domain/mission/presentation/MissionController.java +++ b/src/main/java/com/moing/backend/domain/mission/presentation/MissionController.java @@ -20,14 +20,11 @@ @RequestMapping("/api/team/{teamId}/missions") public class MissionController { - private final MissionCreateUseCase missionCreateUseCase; private final MissionReadUseCase missionReadUseCase; private final MissionUpdateUseCase missionUpdateUseCase; private final MissionDeleteUseCase missionDeleteUseCase; -// private final MissionRemindAlarmUseCase missionRemindAlarmUseCase; - /** * 미션 조회 * [GET] {teamId}/missions/{missionId} @@ -39,8 +36,6 @@ public ResponseEntity> getMission(@Authenticatio return ResponseEntity.ok(SuccessResponse.create(READ_MISSION_SUCCESS.getMessage(), this.missionReadUseCase.getMission(user.getSocialId(),missionId))); } - - /** * 미션 생성 * [POST] {teamId}/missions @@ -68,8 +63,8 @@ public ResponseEntity> updateMission(@Authenti * 작성자 : 정승연 */ @PutMapping("/{missionId}/end") - public ResponseEntity> endMission(@AuthenticationPrincipal User user,@PathVariable("teamId") Long teamId,@PathVariable Long missionId) { - return ResponseEntity.ok(SuccessResponse.create(END_MISSION_SUCCESS.getMessage(), this.missionUpdateUseCase.updateMissionStatus(user.getSocialId(),missionId))); + public ResponseEntity> terminateMission(@AuthenticationPrincipal User user, @PathVariable("teamId") Long teamId, @PathVariable Long missionId) { + return ResponseEntity.ok(SuccessResponse.create(END_MISSION_SUCCESS.getMessage(), this.missionUpdateUseCase.terminateMissionByUser(user.getSocialId(),missionId))); } /** @@ -78,7 +73,7 @@ public ResponseEntity> endMission(@Authenticatio * 작성자 : 정승연 */ @DeleteMapping("/{missionId}") - public ResponseEntity> deleteMission(@AuthenticationPrincipal User user,@PathVariable Long missionId) { + public ResponseEntity> deleteMission(@AuthenticationPrincipal User user,@PathVariable("teamId") Long teamId,@PathVariable Long missionId) { return ResponseEntity.ok(SuccessResponse.create(DELETE_MISSION_SUCCESS.getMessage(), this.missionDeleteUseCase.deleteMission(user.getSocialId(),missionId))); } @@ -92,6 +87,7 @@ public ResponseEntity> deleteMission(@AuthenticationPrinci public ResponseEntity> recommendMission(@AuthenticationPrincipal User user,@PathVariable Long teamId) { return ResponseEntity.ok(SuccessResponse.create(RECOMMEND_MISSION_SUCCESS.getMessage(), this.missionReadUseCase.getTeamCategory(teamId))); } + /** * 미션 추천 * [GET] {teamId}/missions/isLeader @@ -103,10 +99,6 @@ public ResponseEntity> isLeader(@AuthenticationPrincipa return ResponseEntity.ok(SuccessResponse.create(RECOMMEND_MISSION_SUCCESS.getMessage(), this.missionCreateUseCase.getIsLeader(user.getSocialId(),teamId))); } -// @PostMapping("/remind") -// public ResponseEntity> remindAlarm(@AuthenticationPrincipal User user,@PathVariable Long teamId) { -// return ResponseEntity.ok(SuccessResponse.create(RECOMMEND_MISSION_SUCCESS.getMessage(), this.missionRemindAlarmUseCase.sendRepeatMissionRemind())); -// } /** * 미션 설명 확인 (미션 읽음 처리) @@ -124,5 +116,4 @@ public ResponseEntity> confirmMissionExplanat - } diff --git a/src/main/java/com/moing/backend/domain/missionArchive/domain/repository/MissionArchiveRepository.java b/src/main/java/com/moing/backend/domain/missionArchive/domain/repository/MissionArchiveRepository.java index 7d9726a6..280bf848 100644 --- a/src/main/java/com/moing/backend/domain/missionArchive/domain/repository/MissionArchiveRepository.java +++ b/src/main/java/com/moing/backend/domain/missionArchive/domain/repository/MissionArchiveRepository.java @@ -1,85 +1,18 @@ package com.moing.backend.domain.missionArchive.domain.repository; -import com.moing.backend.domain.history.application.dto.response.MemberIdAndToken; -import com.moing.backend.domain.member.domain.entity.Member; -import com.moing.backend.domain.mission.domain.entity.constant.MissionStatus; import com.moing.backend.domain.missionArchive.domain.entity.MissionArchive; -import com.moing.backend.domain.missionArchive.domain.entity.MissionArchiveStatus; -import com.querydsl.core.Tuple; import feign.Param; -import org.hibernate.annotations.NamedNativeQuery; -import org.hibernate.annotations.Parameter; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import javax.persistence.ColumnResult; -import javax.persistence.ConstructorResult; -import javax.persistence.SqlResultSetMapping; import java.util.List; -import java.util.Map; import java.util.Optional; @Repository public interface MissionArchiveRepository extends JpaRepository,MissionArchiveCustomRepository { - @Query("select m from MissionArchive as m where m.member =:memberId" ) - Optional> findByMemberId(@Param("memberId") Long memberId); - @Query("select m from MissionArchive as m where m.mission.id = :missionId and m.member.memberId =:memberId order by m.createdDate") Optional> findArchivesByMissionIdAndMemberId(@Param("memberId") Long memberId, @Param("missionId")Long missionId); - @Query("select m from MissionArchive as m where m.mission.id = :missionId and m.member.memberId =:memberId") - Optional findByMissionIdAndMemberId(@Param("memberId") Long memberId, @Param("missionId")Long missionId); - - - @Query("select m from MissionArchive as m where m.mission.id IN :missionIds and m.member.memberId =:memberId") - Optional> findRepeatMissionArchivesByMission (@Param("memberId") Long memberId, @Param("missionIds") List missionIds); - - -// @Query(value = "SELECT distinct tmSub.fcm_token, tmSub.member_id" + -// "FROM ( " + -// " SELECT distinct COALESCE(tm.member_id, 0) AS member_id, t.team_id, me.fcm_token " + -// " FROM mission m " + -// " LEFT JOIN team t ON m.team_id = t.team_id " + -// " LEFT JOIN team_member tm ON t.team_id = tm.team_id AND tm.is_deleted = 'False' " + -// " LEFT JOIN member me on tm.member_id = me.member_id " + -// " ) tmSub " + -// " LEFT JOIN mission m ON NOT (m.status = 'END' OR m.status = 'SUCCESS') and m.team_id = tmSub.team_id " + -// " LEFT JOIN mission_archive ms ON m.mission_id = ms.mission_id and ms.member_id = tmSub.member_id " + -// "GROUP BY tmSub.member_id, m.mission_id, m.number " + -// "having COUNT(ms.mission_archive_id) < m.number", nativeQuery = true) -// Optional> findHavingRemainMissions(); - - - @Query(value = "SELECT distinct COALESCE(tmSub.fcm_token,'undef') as fcmToken, tmSub.member_id as memberId " + - "FROM (SELECT distinct COALESCE(tm.member_id, 0) AS member_id, t.team_id, me.fcm_token " + - "FROM mission m " + - "LEFT JOIN team t ON m.team_id = t.team_id " + - "LEFT JOIN team_member tm ON t.team_id = tm.team_id AND tm.is_deleted = 'False' " + - "LEFT JOIN member me on tm.member_id = me.member_id) tmSub " + - "LEFT JOIN mission m ON NOT (m.status = 'END' OR m.status = 'SUCCESS') and m.team_id = tmSub.team_id " + - "LEFT JOIN mission_archive ms ON m.mission_id = ms.mission_id and ms.member_id = tmSub.member_id " + - "GROUP BY tmSub.member_id, m.mission_id, m.number " + - "HAVING COUNT(ms.mission_archive_id) < m.number", nativeQuery = true - ) - Optional>> findHavingRemainMissions(); - - -// @Query(value = "SELECT distinct tmSub.fcm_token as fcmToken, tmSub.member_id as memberId " + -// "FROM (SELECT distinct COALESCE(tm.member_id, 0) AS member_id, t.team_id, me.fcm_token " + -// "FROM mission m " + -// "LEFT JOIN team t ON m.team_id = t.team_id " + -// "LEFT JOIN team_member tm ON t.team_id = tm.team_id AND tm.is_deleted = 'False' " + -// "LEFT JOIN member me on tm.member_id = me.member_id) tmSub " + -// "LEFT JOIN mission m ON NOT (m.status = 'END' OR m.status = 'SUCCESS') and m.team_id = tmSub.team_id " + -// "LEFT JOIN mission_archive ms ON m.mission_id = ms.mission_id and ms.member_id = tmSub.member_id " + -// "GROUP BY tmSub.member_id, m.mission_id, m.number " + -// "HAVING COUNT(ms.mission_archive_id) < m.number", nativeQuery = true -// ) -// Optional> findHavingRemainMissions(); - - - - } diff --git a/src/test/java/com/moing/backend/domain/mission/representation/MissionControllerTest.java b/src/test/java/com/moing/backend/domain/mission/representation/MissionControllerTest.java index 83d8cc49..5ec529f4 100644 --- a/src/test/java/com/moing/backend/domain/mission/representation/MissionControllerTest.java +++ b/src/test/java/com/moing/backend/domain/mission/representation/MissionControllerTest.java @@ -9,19 +9,14 @@ import com.moing.backend.domain.mission.application.service.MissionDeleteUseCase; import com.moing.backend.domain.mission.application.service.MissionReadUseCase; import com.moing.backend.domain.mission.application.service.MissionUpdateUseCase; -import com.moing.backend.domain.mission.domain.repository.MissionRepository; import com.moing.backend.domain.mission.domain.service.MissionQueryService; import com.moing.backend.domain.mission.presentation.MissionController; import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; -import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import static com.moing.backend.domain.mission.presentation.constant.MissionResponseMessage.CONFIRM_MISSION_SUCCESS; @@ -35,7 +30,6 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @WebMvcTest(MissionController.class) public class MissionControllerTest extends CommonControllerTest { @@ -366,7 +360,7 @@ public class MissionControllerTest extends CommonControllerTest { .isLeader(Boolean.FALSE) .build(); - given(missionUpdateUseCase.updateMissionStatus(any(),any())).willReturn(output); + given(missionUpdateUseCase.terminateMissionByUser(any(),any())).willReturn(output); Long teamId = 2L; Long missionId = 1L;