Skip to content

Commit 2cb58db

Browse files
committed
update mission history
1 parent 615a1e9 commit 2cb58db

10 files changed

Lines changed: 244 additions & 21 deletions

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,7 @@ src/main/resources
215215
.env
216216

217217
#
218-
.frontend
218+
.frontend
219+
220+
# 프론트엔드 파일 제외
221+
CC_Maker_FE/

.idea/vcs.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CC_Maker_FE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 21ef65725bd6888c40e92e48ed1ba6f6a39329fc

frontend

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.ccapp.ccgo.mission.controller;
2+
3+
import com.ccapp.ccgo.mission.dto.MissionHistoryDto;
4+
import com.ccapp.ccgo.mission.service.SubGroupMissionService;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.*;
9+
10+
import java.util.List;
11+
12+
@RestController
13+
@RequestMapping("/api/mission/history")
14+
@RequiredArgsConstructor
15+
@Slf4j
16+
public class MissionHistoryController {
17+
18+
private final SubGroupMissionService subGroupMissionService;
19+
20+
// 서브그룹의 미션 히스토리 조회
21+
@GetMapping("/subgroup/{subGroupId}")
22+
public ResponseEntity<List<MissionHistoryDto>> getMissionHistoryBySubGroup(@PathVariable Long subGroupId) {
23+
log.info("서브그룹 미션 히스토리 조회 요청: subGroupId = {}", subGroupId);
24+
List<MissionHistoryDto> histories = subGroupMissionService.getMissionHistoryBySubGroup(subGroupId);
25+
return ResponseEntity.ok(histories);
26+
}
27+
28+
// 사용자의 미션 히스토리 조회
29+
@GetMapping("/user/{userId}")
30+
public ResponseEntity<List<MissionHistoryDto>> getMissionHistoryByUser(@PathVariable Long userId) {
31+
log.info("사용자 미션 히스토리 조회 요청: userId = {}", userId);
32+
List<MissionHistoryDto> histories = subGroupMissionService.getMissionHistoryByUser(userId);
33+
return ResponseEntity.ok(histories);
34+
}
35+
36+
// 팀의 미션 히스토리 조회
37+
@GetMapping("/team/{teamId}")
38+
public ResponseEntity<List<MissionHistoryDto>> getMissionHistoryByTeam(@PathVariable Long teamId) {
39+
log.info("팀 미션 히스토리 조회 요청: teamId = {}", teamId);
40+
List<MissionHistoryDto> histories = subGroupMissionService.getMissionHistoryByTeam(teamId);
41+
return ResponseEntity.ok(histories);
42+
}
43+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.ccapp.ccgo.mission.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.time.LocalDateTime;
9+
10+
@Getter
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class MissionHistoryDto {
15+
16+
private Long id;
17+
private Long subGroupId;
18+
private String subGroupName; // 서브그룹 이름 (필요시)
19+
private Long userId;
20+
private String userName; // 사용자 이름
21+
private Long missionTemplateId;
22+
private String missionTitle; // 미션 제목
23+
private String missionDescription; // 미션 설명
24+
private Integer missionScore; // 미션 점수
25+
private LocalDateTime completedAt;
26+
private LocalDateTime createdAt;
27+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.ccapp.ccgo.mission.entity;
2+
3+
import com.ccapp.ccgo.matching.domain.entity.SubGroup;
4+
import com.ccapp.ccgo.user.entity.User;
5+
import jakarta.persistence.*;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
11+
import java.time.LocalDateTime;
12+
13+
@Entity
14+
@Table(name = "mission_history")
15+
@Getter
16+
@Builder
17+
@NoArgsConstructor
18+
@AllArgsConstructor
19+
public class MissionHistory {
20+
21+
@Id
22+
@GeneratedValue(strategy = GenerationType.IDENTITY)
23+
private Long id;
24+
25+
@ManyToOne(fetch = FetchType.LAZY)
26+
@JoinColumn(name = "sub_group_id")
27+
private SubGroup subGroup;
28+
29+
@ManyToOne(fetch = FetchType.LAZY)
30+
@JoinColumn(name = "user_id")
31+
private User user;
32+
33+
@ManyToOne(fetch = FetchType.LAZY)
34+
@JoinColumn(name = "mission_template_id")
35+
private MissionTemplate missionTemplate;
36+
37+
@Column(name = "completed_at", nullable = false)
38+
private LocalDateTime completedAt;
39+
40+
@Column(name = "created_at", nullable = false)
41+
private LocalDateTime createdAt;
42+
43+
@PrePersist
44+
protected void onCreate() {
45+
createdAt = LocalDateTime.now();
46+
if (completedAt == null) {
47+
completedAt = LocalDateTime.now();
48+
}
49+
}
50+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.ccapp.ccgo.mission.repository;
2+
3+
import com.ccapp.ccgo.mission.entity.MissionHistory;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.data.jpa.repository.Query;
6+
import org.springframework.data.repository.query.Param;
7+
8+
import java.util.List;
9+
10+
public interface MissionHistoryRepository extends JpaRepository<MissionHistory, Long> {
11+
12+
// 특정 서브그룹의 미션 히스토리 조회
13+
List<MissionHistory> findBySubGroup_IdOrderByCompletedAtDesc(Long subGroupId);
14+
15+
// 특정 사용자의 미션 히스토리 조회
16+
List<MissionHistory> findByUser_IdOrderByCompletedAtDesc(Long userId);
17+
18+
// 특정 팀의 미션 히스토리 조회
19+
@Query("SELECT mh FROM MissionHistory mh WHERE mh.subGroup.team.teamId = :teamId ORDER BY mh.completedAt DESC")
20+
List<MissionHistory> findByTeamIdOrderByCompletedAtDesc(@Param("teamId") Long teamId);
21+
22+
// 특정 미션 템플릿의 완료 히스토리 조회
23+
List<MissionHistory> findByMissionTemplate_IdOrderByCompletedAtDesc(Long missionTemplateId);
24+
25+
// 특정 서브그룹에서 특정 미션 템플릿이 완료되었는지 확인
26+
boolean existsBySubGroup_IdAndMissionTemplate_Id(Long subGroupId, Long missionTemplateId);
27+
}

src/main/java/com/ccapp/ccgo/mission/repository/SubGroupMissionRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ public interface SubGroupMissionRepository extends JpaRepository<SubGroupMission
1313

1414
Optional<SubGroupMission> findBySubGroupAndMissionTemplateId(SubGroup subGroup, Long missionTemplateId);
1515

16+
// 사용자가 이미 미션을 받았는지 체크하는 메서드
17+
boolean existsBySubGroup_Team_TeamIdAndSubGroup_SubGroupMembers_User_Id(Long teamId, Long userId);
1618

1719
}

src/main/java/com/ccapp/ccgo/mission/service/SubGroupMissionService.java

Lines changed: 89 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package com.ccapp.ccgo.mission.service;
22

33
import com.ccapp.ccgo.matching.domain.entity.SubGroup;
4+
import com.ccapp.ccgo.matching.domain.entity.SubGroupMember;
45
import com.ccapp.ccgo.matching.repository.SubGroupRepository;
6+
import com.ccapp.ccgo.matching.repository.SubGroupMemberRepository;
57
import com.ccapp.ccgo.mission.dto.ScoreboardResponseDto;
68
import com.ccapp.ccgo.mission.dto.SubGroupMissionDto;
79
import com.ccapp.ccgo.mission.dto.SubGroupScoreDto;
10+
import com.ccapp.ccgo.mission.dto.MissionHistoryDto;
811
import com.ccapp.ccgo.mission.entity.MissionTemplate;
912
import com.ccapp.ccgo.mission.entity.SubGroupMission;
13+
import com.ccapp.ccgo.mission.entity.MissionHistory;
1014
import com.ccapp.ccgo.mission.repository.MissionTemplateRepository;
1115
import com.ccapp.ccgo.mission.repository.SubGroupMissionRepository;
16+
import com.ccapp.ccgo.mission.repository.MissionHistoryRepository;
1217
import lombok.RequiredArgsConstructor;
1318
import java.util.concurrent.ThreadLocalRandom;
1419
import org.springframework.stereotype.Service;
@@ -17,14 +22,17 @@
1722
import java.util.Collections;
1823
import java.util.List;
1924
import java.util.stream.Collectors;
25+
import java.time.LocalDateTime;
2026

2127
@Service
2228
@RequiredArgsConstructor
2329
public class SubGroupMissionService {
2430

2531
private final SubGroupRepository subGroupRepository;
32+
private final SubGroupMemberRepository subGroupMemberRepository;
2633
private final MissionTemplateRepository missionTemplateRepository;
2734
private final SubGroupMissionRepository subGroupMissionRepository;
35+
private final MissionHistoryRepository missionHistoryRepository;
2836

2937
// 서브그룹에 미션 부여
3038
@Transactional
@@ -36,33 +44,34 @@ public void assignMissionsToSubGroup(Long subGroupId) {
3644
throw new IllegalStateException("이미 이 서브그룹에 미션이 부여되어 있습니다.");
3745
}
3846

39-
assignMissionsByScore(subGroup, 1, 6);
47+
// 그룹장 중복 미션 방지: 이미 미션을 받은 사용자가 있는지 체크
48+
List<SubGroupMember> groupMembers = subGroupMemberRepository.findBySubGroup_Id(subGroupId);
49+
List<Long> groupMemberIds = groupMembers.stream()
50+
.map(member -> member.getUser().getId())
51+
.collect(Collectors.toList());
52+
53+
for (Long memberId : groupMemberIds) {
54+
boolean hasExistingMission = subGroupMissionRepository.existsBySubGroup_Team_TeamIdAndSubGroup_SubGroupMembers_User_Id(
55+
subGroup.getTeam().getTeamId(), memberId);
56+
if (hasExistingMission) {
57+
throw new IllegalStateException("사용자 ID " + memberId + "가 이미 다른 서브그룹에서 미션을 받았습니다.");
58+
}
59+
}
60+
4061
assignMissionsByScore(subGroup, 3, 6);
4162
assignMissionsByScore(subGroup, 5, 6);
4263
assignMissionsByScore(subGroup, 10, 6);
4364
}
4465

4566
private void assignMissionsByScore(SubGroup subGroup, Integer score, int count) {
46-
// 1. 이미 할당된 미션 Template ID 목록 조회
47-
List<Long> existingMissionTemplateIds = subGroupMissionRepository.findBySubGroup(subGroup).stream()
48-
.map(m -> m.getMissionTemplate().getId())
49-
.toList();
50-
51-
// 2. score 조건에 맞는 미션 템플릿 중 기존 할당 미션 제외
52-
List<MissionTemplate> missions = missionTemplateRepository.findByScore(score).stream()
53-
.filter(m -> !existingMissionTemplateIds.contains(m.getId()))
54-
.toList();
55-
56-
// 3. 미션 개수 체크
67+
List<MissionTemplate> missions = missionTemplateRepository.findByScore(score);
5768
if (missions.size() < count) {
5869
throw new IllegalStateException(score + "점 미션이 최소 " + count + "개 이상 필요합니다.");
5970
}
6071

61-
// 4. 랜덤 섞고 필요한 개수만큼 선택
6272
Collections.shuffle(missions);
6373
List<MissionTemplate> selected = missions.subList(0, count);
6474

65-
// 5. 새 미션 할당
6675
for (MissionTemplate missionTemplate : selected) {
6776
SubGroupMission mission = SubGroupMission.builder()
6877
.subGroup(subGroup)
@@ -73,7 +82,6 @@ private void assignMissionsByScore(SubGroup subGroup, Integer score, int count)
7382
}
7483
}
7584

76-
7785
// 미션 완료 처리
7886
@Transactional
7987
public void completeMission(Long subGroupMissionId) {
@@ -181,8 +189,71 @@ public void completeMission(Long teamId, Long subGroupId, Long missionTemplateId
181189

182190
// 완료 처리
183191
mission.setCompleted(true);
192+
193+
// 미션 히스토리에 저장
194+
saveMissionHistory(subGroup, mission.getMissionTemplate());
195+
}
196+
197+
// 미션 히스토리 저장
198+
private void saveMissionHistory(SubGroup subGroup, MissionTemplate missionTemplate) {
199+
// 서브그룹의 모든 멤버에 대해 히스토리 저장
200+
List<SubGroupMember> members = subGroupMemberRepository.findBySubGroup_Id(subGroup.getId());
201+
202+
for (SubGroupMember member : members) {
203+
MissionHistory history = MissionHistory.builder()
204+
.subGroup(subGroup)
205+
.user(member.getUser())
206+
.missionTemplate(missionTemplate)
207+
.completedAt(LocalDateTime.now())
208+
.build();
209+
210+
missionHistoryRepository.save(history);
211+
}
212+
}
213+
214+
// 서브그룹의 미션 히스토리 조회
215+
@Transactional(readOnly = true)
216+
public List<MissionHistoryDto> getMissionHistoryBySubGroup(Long subGroupId) {
217+
List<MissionHistory> histories = missionHistoryRepository.findBySubGroup_IdOrderByCompletedAtDesc(subGroupId);
218+
219+
return histories.stream()
220+
.map(this::convertToDto)
221+
.collect(Collectors.toList());
222+
}
223+
224+
// 사용자의 미션 히스토리 조회
225+
@Transactional(readOnly = true)
226+
public List<MissionHistoryDto> getMissionHistoryByUser(Long userId) {
227+
List<MissionHistory> histories = missionHistoryRepository.findByUser_IdOrderByCompletedAtDesc(userId);
228+
229+
return histories.stream()
230+
.map(this::convertToDto)
231+
.collect(Collectors.toList());
232+
}
233+
234+
// 팀의 미션 히스토리 조회
235+
@Transactional(readOnly = true)
236+
public List<MissionHistoryDto> getMissionHistoryByTeam(Long teamId) {
237+
List<MissionHistory> histories = missionHistoryRepository.findByTeamIdOrderByCompletedAtDesc(teamId);
238+
239+
return histories.stream()
240+
.map(this::convertToDto)
241+
.collect(Collectors.toList());
242+
}
243+
244+
// MissionHistory를 DTO로 변환
245+
private MissionHistoryDto convertToDto(MissionHistory history) {
246+
return MissionHistoryDto.builder()
247+
.id(history.getId())
248+
.subGroupId(history.getSubGroup().getId())
249+
.userId(history.getUser().getId())
250+
.userName(history.getUser().getName())
251+
.missionTemplateId(history.getMissionTemplate().getId())
252+
.missionTitle(history.getMissionTemplate().getTitle())
253+
.missionDescription(history.getMissionTemplate().getDescription())
254+
.missionScore(history.getMissionTemplate().getScore())
255+
.completedAt(history.getCompletedAt())
256+
.createdAt(history.getCreatedAt())
257+
.build();
184258
}
185-
186-
187-
188259
}

0 commit comments

Comments
 (0)