Skip to content

Commit 6bddbc1

Browse files
authored
Merge pull request #120 from prgrms-web-devcourse-final-project/feat/mission
feat: 개발용 시간 설정 가능
2 parents b132666 + abd50e3 commit 6bddbc1

File tree

14 files changed

+239
-25
lines changed

14 files changed

+239
-25
lines changed

backend/src/main/java/com/back/domain/mission/controller/ApiV1MissionController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,6 @@ public ResponseEntity<ApiResponse<MissionResponse>> getMissionAllDetail(
100100
.status(HttpStatus.OK)
101101
.body(ApiResponse.success("200", "미션 상세 조회 성공", response));
102102
}
103+
104+
103105
}

backend/src/main/java/com/back/domain/mission/repository/MissionRepository.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,19 @@ List<Mission> findByMemberIdAndIsCompletedWithParty(
5252
ORDER BY m.createDate DESC
5353
""")
5454
List<Mission> findAllWithParty();
55+
56+
@Query("""
57+
SELECT DISTINCT m FROM Mission m
58+
LEFT JOIN FETCH m.party p
59+
LEFT JOIN PartyMember pm ON pm.party.id = p.id
60+
WHERE (m.member.id = :memberId
61+
OR (pm.member.id = :memberId
62+
AND pm.status = 'ACCEPTED'))
63+
AND m.isCompleted = :isCompleted
64+
ORDER BY m.createDate DESC
65+
""")
66+
List<Mission> findMyMissionsWithParty(
67+
@Param("memberId") Integer memberId,
68+
@Param("isCompleted") boolean isCompleted
69+
);
5570
}

backend/src/main/java/com/back/domain/mission/scheduler/MissionEndScheduler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.back.domain.mission.service.MissionCompletionService;
66
import com.back.domain.party.party.entity.PartyMember;
77
import com.back.domain.party.party.entity.PartyMemberStatus;
8+
import com.back.global.util.TimeProvider;
89
import lombok.RequiredArgsConstructor;
910
import lombok.extern.slf4j.Slf4j;
1011
import org.springframework.scheduling.annotation.Scheduled;
@@ -17,7 +18,7 @@
1718
@Component
1819
@RequiredArgsConstructor
1920
public class MissionEndScheduler {
20-
21+
private final TimeProvider timeProvider;
2122
private final MissionRepository missionRepository;
2223
private final MissionCompletionService missionCompletionService;
2324

@@ -26,7 +27,7 @@ public class MissionEndScheduler {
2627
public void completeEndedMissions() {
2728
log.info("미션 종료 처리 스케줄러 시작");
2829

29-
LocalDate yesterday = LocalDate.now().minusDays(1);
30+
LocalDate yesterday = timeProvider.today().minusDays(1);
3031
List<Mission> endedMissions = missionRepository.findByEndDate(yesterday);
3132

3233
if (endedMissions.isEmpty()) {

backend/src/main/java/com/back/domain/mission/scheduler/TaskAutoFailScheduler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.back.domain.mission.entity.Task;
44
import com.back.domain.mission.repository.TaskRepository;
55
import com.back.domain.mission.service.TaskAutoFailService;
6+
import com.back.global.util.TimeProvider;
67
import lombok.RequiredArgsConstructor;
78
import lombok.extern.slf4j.Slf4j;
89
import org.springframework.data.domain.PageRequest;
@@ -16,7 +17,7 @@
1617
@Component
1718
@RequiredArgsConstructor
1819
public class TaskAutoFailScheduler {
19-
20+
private final TimeProvider timeProvider;
2021
private final TaskRepository taskRepository;
2122
private final TaskAutoFailService taskAutoFailService;
2223

@@ -25,7 +26,7 @@ public class TaskAutoFailScheduler {
2526
public void autoFailExpiredTasks() {
2627
log.info("자동 실패 처리 스케줄러 시작");
2728

28-
LocalDate yesterday = LocalDate.now().minusDays(1);
29+
LocalDate yesterday = timeProvider.today().minusDays(1);
2930
int yesterdayDayOfWeek = yesterday.getDayOfWeek().getValue();
3031

3132
log.info("처리 대상 날짜: {}, 요일: {}", yesterday, yesterdayDayOfWeek);

backend/src/main/java/com/back/domain/mission/service/MissionCalculateService.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.back.domain.mission.repository.TaskLogRepository;
99
import com.back.domain.party.party.entity.PartyMember;
1010
import com.back.domain.party.party.entity.PartyMemberStatus;
11+
import com.back.global.util.TimeProvider;
1112
import lombok.RequiredArgsConstructor;
1213
import org.springframework.stereotype.Service;
1314

@@ -20,12 +21,13 @@
2021
@RequiredArgsConstructor
2122
public class MissionCalculateService {
2223

24+
private final TimeProvider timeProvider;
2325
private final TaskLogRepository taskLogRepository;
2426

2527
// 날짜 계산
2628
// 시작 날짜 계산
2729
public LocalDate calculateStartDate() {
28-
LocalDate today = LocalDate.now();
30+
LocalDate today = timeProvider.today();
2931
DayOfWeek todayDayOfWeek = today.getDayOfWeek();
3032

3133
if (todayDayOfWeek == DayOfWeek.MONDAY) {
@@ -47,7 +49,7 @@ public LocalDate calculateEndDate(LocalDate startDate, Integer weeks) {
4749

4850
//현재 몇 주차인지 계산( 시작 전 0, 후 null)
4951
public Integer calculateCurrentWeek(Mission mission) {
50-
LocalDate today = LocalDate.now();
52+
LocalDate today = timeProvider.today();
5153
if (today.isBefore(mission.getStartDate())) {
5254
return 0;
5355
}
@@ -70,7 +72,7 @@ public int getCurrentWeekNumber(Mission mission, LocalDate date) {
7072

7173
// 오늘 할 일인지 여부
7274
public boolean isToday(Task task) {
73-
LocalDate today = LocalDate.now();
75+
LocalDate today = timeProvider.today();
7476
DayOfWeek todayDayOfWeek = today.getDayOfWeek();
7577
int todayDayNum = todayDayOfWeek.getValue();
7678

@@ -216,7 +218,7 @@ public Integer calculatePartyWeekAverage(SubGoal subGoal) {
216218
}
217219

218220
//특정 테스크를 특정 날짜에 완료한 팀원 수 계산
219-
public TaskResponse.PartyCompletionDto calculateTaskCompletion(Task task, LocalDate date) {
221+
public TaskResponse.PartyCompletionDto calculateTaskCompletion(Task task, LocalDate date) {
220222
Mission mission = task.getSubGoal().getMission();
221223

222224
if (!mission.isPartyMission()) {

backend/src/main/java/com/back/domain/mission/service/MissionCompletionService.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.back.domain.mission.exception.MissionException;
88
import com.back.domain.mission.repository.MissionCompletionLogRepository;
99
import com.back.domain.mission.repository.MissionRepository;
10+
import com.back.global.util.TimeProvider;
1011
import lombok.RequiredArgsConstructor;
1112
import org.springframework.context.ApplicationEventPublisher;
1213
import org.springframework.stereotype.Service;
@@ -17,14 +18,15 @@
1718
@Service
1819
@RequiredArgsConstructor
1920
public class MissionCompletionService {
20-
21+
private final TimeProvider timeProvider;
2122
private final MissionRepository missionRepository;
2223
private final MissionCalculateService missionCalculateService;
2324
private final ApplicationEventPublisher eventPublisher;
2425
private final MissionCompletionLogRepository completionLogRepository;
2526

2627
@Transactional
2728
public void checkAndCompleteMission(Integer missionId, Integer memberId) {
29+
LocalDate today = timeProvider.today();
2830
Mission mission = missionRepository.findById(missionId)
2931
.orElseThrow(() -> new MissionException(MissionErrorCode.MISSION_NOT_FOUND));
3032

@@ -37,27 +39,27 @@ public void checkAndCompleteMission(Integer missionId, Integer memberId) {
3739
if (!mission.isPartyMission()) {
3840
if (!mission.isCompleted()) { // 개인 미션은 한 번만
3941
mission.setCompleted(true);
40-
publishCompletionEvent(mission, memberId);
41-
saveCompletionLog(mission.getId(), memberId);
42+
publishCompletionEvent(mission, memberId, today);
43+
saveCompletionLog(mission.getId(), memberId, today);
4244
}
4345
}
4446
// 파티 미션
4547
else {
4648
if (!hasAlreadyCompleted(mission.getId(), memberId)) { // 중복 체크
47-
publishCompletionEvent(mission, memberId);
48-
saveCompletionLog(mission.getId(), memberId);
49+
publishCompletionEvent(mission, memberId, today);
50+
saveCompletionLog(mission.getId(), memberId, today);
4951
}
5052
}
5153
}
5254
}
5355

54-
private void publishCompletionEvent(Mission mission, Integer memberId) {
56+
private void publishCompletionEvent(Mission mission, Integer memberId, LocalDate completedDate) {
5557
eventPublisher.publishEvent(MissionCompletedEvent.builder()
5658
.missionId(mission.getId())
5759
.memberId(memberId)
5860
.isPartyMission(mission.isPartyMission())
5961
.partyId(mission.isPartyMission() ? mission.getParty().getId() : null)
60-
.completedDate(LocalDate.now())
62+
.completedDate(completedDate)
6163
.build());
6264
}
6365

@@ -67,11 +69,11 @@ private boolean hasAlreadyCompleted(Integer missionId, Integer memberId) {
6769
}
6870

6971
// 완료 로그 저장
70-
private void saveCompletionLog(Integer missionId, Integer memberId) {
72+
private void saveCompletionLog(Integer missionId, Integer memberId, LocalDate completedDate) {
7173
MissionCompletionLog log = MissionCompletionLog.builder()
7274
.missionId(missionId)
7375
.memberId(memberId)
74-
.completedDate(LocalDate.now())
76+
.completedDate(completedDate)
7577
.build();
7678
completionLogRepository.save(log);
7779
}

backend/src/main/java/com/back/domain/mission/service/MissionService.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ public class MissionService {
3131
private final PartyMissionService partyMissionService;
3232
// 특정 회원의 전체 미션 조회 ( 진행 중과 완료로 분리)
3333
public MissionOverviewResponse getMissions(Integer memberId) {
34-
List<Mission> activeMissions = missionRepository.findByMemberIdAndIsCompletedWithParty(memberId, false);
35-
List<Mission> completedMissions = missionRepository.findByMemberIdAndIsCompletedWithParty(memberId, true);
36-
34+
List<Mission> activeMissions = missionRepository
35+
.findMyMissionsWithParty(memberId, false);
36+
List<Mission> completedMissions = missionRepository
37+
.findMyMissionsWithParty(memberId, true);
3738
List<MissionResponse> activeResponses = activeMissions.stream()
3839
.map(m -> partyMissionService.convertToSimpleResponse(m, memberId))
3940
.collect(Collectors.toList());

backend/src/main/java/com/back/domain/mission/service/PartyMissionService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,5 @@ private SubGoalResponse buildSubGoalResponse(SubGoal sg, Mission mission, Intege
257257

258258
return builder.build();
259259
}
260+
260261
}

backend/src/main/java/com/back/domain/mission/service/TaskService.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.back.domain.mission.repository.TaskLogRepository;
1717
import com.back.domain.mission.repository.TaskRepository;
1818
import com.back.domain.party.party.entity.PartyMemberStatus;
19+
import com.back.global.util.TimeProvider;
1920
import lombok.RequiredArgsConstructor;
2021
import org.springframework.context.ApplicationEventPublisher;
2122
import org.springframework.stereotype.Service;
@@ -32,7 +33,7 @@
3233
@RequiredArgsConstructor
3334
@Transactional
3435
public class TaskService {
35-
36+
private final TimeProvider timeProvider;
3637
private final TaskRepository taskRepository;
3738
private final TaskLogRepository taskLogRepository;
3839
private final MissionCalculateService calculateService;
@@ -47,7 +48,7 @@ public class TaskService {
4748
public TaskCompleteResponse completeTask(Integer memberId, TaskCompleteRequest request) {
4849
// 1. Task 조회
4950
Task task = findTaskById(request.getTaskId());
50-
LocalDate today = LocalDate.now();
51+
LocalDate today = timeProvider.today();
5152
Mission mission = task.getSubGoal().getMission();
5253
SubGoal subGoal = task.getSubGoal();
5354

@@ -109,7 +110,7 @@ public TaskCompleteResponse completeTask(Integer memberId, TaskCompleteRequest r
109110
// 오늘의 태스크 조회
110111
@Transactional(readOnly = true)
111112
public List<TaskResponse> getTodayTasks(Integer memberId) {
112-
LocalDate today = LocalDate.now();
113+
LocalDate today = timeProvider.today();
113114
int todayDayNum = today.getDayOfWeek().getValue();
114115

115116
List<Task> tasks = taskRepository.findTodayTasks(memberId, today, todayDayNum);
@@ -142,7 +143,7 @@ public TaskResponse updateTask(Integer memberId, Integer taskId, String newTitle
142143
// Task 엔티티 내부에서 수정 가능 여부 검증
143144
task.updateContent(newTitle);
144145

145-
return toTaskResponse(task, memberId, LocalDate.now());
146+
return toTaskResponse(task, memberId, timeProvider.today());
146147
}
147148

148149
// 주차별 태스크 일괄 수정
@@ -161,7 +162,7 @@ public List<TaskResponse> updateWeekTasks(Integer memberId, WeekTaskUpdateReques
161162
}
162163

163164
task.updateContent(taskDto.getTitle());
164-
responses.add(toTaskResponse(task, memberId, LocalDate.now()));
165+
responses.add(toTaskResponse(task, memberId, timeProvider.today()));
165166
}
166167

167168
return responses;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.back.global.controller;
2+
3+
import com.back.global.util.DevTimeProvider;
4+
import io.swagger.v3.oas.annotations.Operation;
5+
import io.swagger.v3.oas.annotations.tags.Tag;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.context.annotation.Profile;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.*;
10+
11+
import java.time.LocalDate;
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
@RestController
16+
@RequestMapping("/api/v1/dev")
17+
@RequiredArgsConstructor
18+
@Profile("dev") // 개발 환경에서만 활성화
19+
@Tag(name = "DevController", description = "[개발용] 시간 조작 API")
20+
public class DevController {
21+
22+
private final DevTimeProvider timeProvider;
23+
24+
@PostMapping("/time/set")
25+
@Operation(summary = "[개발용] 시간 고정", description = "시스템 시간을 특정 날짜로 고정합니다")
26+
public ResponseEntity<?> setFixedTime(@RequestParam String date) {
27+
LocalDate parsed = LocalDate.parse(date);
28+
timeProvider.setFixedDate(parsed);
29+
30+
return ResponseEntity.ok(Map.of(
31+
"message", "시간이 " + date + "로 고정되었습니다",
32+
"fixedDate", date
33+
));
34+
}
35+
36+
@DeleteMapping("/time/reset")
37+
@Operation(summary = "[개발용] 시간 고정 해제", description = "실제 시간으로 복원합니다")
38+
public ResponseEntity<?> resetTime() {
39+
timeProvider.reset();
40+
41+
return ResponseEntity.ok(Map.of(
42+
"message", "실제 시간으로 복원되었습니다"
43+
));
44+
}
45+
46+
@GetMapping("/time/current")
47+
@Operation(summary = "[개발용] 현재 시간 확인", description = "현재 시스템이 인식하는 시간을 확인합니다")
48+
public ResponseEntity<?> getCurrentTime() {
49+
LocalDate current = timeProvider.today();
50+
LocalDate fixed = timeProvider.getFixedDate();
51+
52+
Map<String, Object> response = new HashMap<>();
53+
response.put("fixed", fixed != null);
54+
response.put("fixedDate", fixed);
55+
response.put("currentDate", current);
56+
response.put("dayOfWeek", current.getDayOfWeek().toString());
57+
58+
return ResponseEntity.ok(response);
59+
}
60+
}

0 commit comments

Comments
 (0)