1+ package com .workingdead .meet .controller ;
2+
3+ import com .workingdead .meet .dto .*;
4+ import com .workingdead .meet .entity .*;
5+ import com .workingdead .meet .repository .*;
6+ import com .workingdead .meet .service .*;
7+ import com .workingdead .meet .dto .PriorityDtos .*;
8+ import io .swagger .v3 .oas .annotations .*;
9+ import io .swagger .v3 .oas .annotations .media .*;
10+ import io .swagger .v3 .oas .annotations .responses .*;
11+ import io .swagger .v3 .oas .annotations .tags .Tag ;
12+ import jakarta .validation .Valid ;
13+ import jakarta .servlet .http .HttpSession ;
14+ import org .springframework .http .ResponseEntity ;
15+ import org .springframework .web .bind .annotation .*;
16+
17+ import java .util .List ;
18+ import java .util .Map ;
19+ import java .lang .reflect .Field ;
20+ import org .springframework .util .ReflectionUtils ;
21+
22+ @ Tag (name = "Participant" , description = "참여자 관리 API" )
23+ @ RestController
24+ @ RequestMapping ("" )
25+ public class ParticipantController {
26+ private final ParticipantService participantService ;
27+ private final PriorityService priorityService ;
28+ private final ParticipantRepository participantRepository ;
29+
30+ public ParticipantController (
31+ ParticipantService participantService ,
32+ PriorityService priorityService ,
33+ ParticipantRepository participantRepository ) {
34+ this .participantService = participantService ;
35+ this .priorityService = priorityService ;
36+ this .participantRepository = participantRepository ;
37+ }
38+
39+ // 0.2 참여자 추가/삭제
40+ @ Operation (
41+ summary = "참여자 추가" ,
42+ description = "특정 투표에 새로운 참여자를 추가합니다. displayName을 기반으로 참여자 칩이 생성됩니다."
43+ )
44+ @ ApiResponses ({
45+ @ ApiResponse (responseCode = "200" , description = "참여자 추가 성공" ,
46+ content = @ Content (schema = @ Schema (implementation = ParticipantDtos .ParticipantRes .class ))),
47+ @ ApiResponse (responseCode = "400" , description = "잘못된 요청 (displayName이 비어있는 경우)" , content = @ Content ),
48+ @ ApiResponse (responseCode = "404" , description = "투표를 찾을 수 없음" , content = @ Content )
49+ })
50+ @ PostMapping ("/votes/{voteId}/participants" )
51+ public ResponseEntity <ParticipantDtos .ParticipantRes > add (
52+ @ PathVariable Long voteId ,
53+ @ RequestBody @ Valid ParticipantDtos .CreateParticipantReq req ) {
54+ var res = participantService .add (voteId , req .displayName ());
55+ return ResponseEntity .ok (res );
56+ }
57+
58+ @ Operation (
59+ summary = "참여자 삭제" ,
60+ description = "특정 참여자를 삭제합니다."
61+ )
62+ @ ApiResponses ({
63+ @ ApiResponse (responseCode = "204" , description = "참여자 삭제 성공" , content = @ Content ),
64+ @ ApiResponse (responseCode = "404" , description = "참여자를 찾을 수 없음" , content = @ Content )
65+ })
66+ @ DeleteMapping ("/participants/{participantId}" )
67+ public ResponseEntity <Void > remove (@ PathVariable Long participantId ) {
68+ participantService .remove (participantId );
69+ return ResponseEntity .noContent ().build ();
70+ }
71+
72+ @ Operation (
73+ summary = "참여자 목록 조회 (로그인 칩)" ,
74+ description = "어드민이 등록한 참여자 목록을 읽어와 로그인 칩 형태로 제공합니다. " +
75+ "현재 로그인한 참여자는 loggedIn 상태로 표시됩니다."
76+ )
77+ @ ApiResponses ({
78+ @ ApiResponse (responseCode = "200" , description = "참여자 목록 조회 성공" ),
79+ @ ApiResponse (responseCode = "404" , description = "투표를 찾을 수 없음" )
80+ })
81+ @ GetMapping ("/votes/{voteId}/participants" )
82+ public ResponseEntity <List <ParticipantDtos .ParticipantRes >> getParticipants (
83+ @ PathVariable Long voteId ,
84+ @ RequestParam (required = false ) Long currentParticipantId
85+ ) {
86+ List <ParticipantDtos .ParticipantRes > participants =
87+ participantService .getParticipantsForVote (voteId , currentParticipantId );
88+ return ResponseEntity .ok (participants );
89+ }
90+
91+ /**
92+ * PATCH /participants/{id}/info
93+ * 참여자 정보 부분 수정 (리플렉션)
94+ */
95+ @ PatchMapping ("/participants/{id}/info" )
96+ public ResponseEntity <ParticipantDtos .ParticipantRes > updateParticipant (
97+ @ PathVariable Long id ,
98+ @ RequestBody Map <String , Object > updates ) {
99+
100+ Participant participant = participantRepository .findById (id )
101+ .orElseThrow (() -> new RuntimeException ("Not found" ));
102+
103+ updates .forEach ((key , value ) -> {
104+ Field field = ReflectionUtils .findField (Participant .class , key );
105+ if (field != null ) {
106+ field .setAccessible (true );
107+ ReflectionUtils .setField (field , participant , value );
108+ }
109+ });
110+
111+ Participant saved = participantRepository .save (participant );
112+
113+ // DTO로 변환해서 반환!
114+ ParticipantDtos .ParticipantRes response = new ParticipantDtos .ParticipantRes (
115+ saved .getId (),
116+ saved .getDisplayName (),
117+ false // loggedIn 상태
118+ );
119+
120+ return ResponseEntity .ok (response );
121+ }
122+
123+ /**
124+ * POST /participants/{participantId}
125+ * 우선순위 설정 (최대 3개)
126+ */
127+ @ PostMapping ("/participants/{participantId}" )
128+ public ResponseEntity <PriorityResponse > setPriorities (
129+ @ PathVariable Long participantId ,
130+ @ RequestParam Long voteId ,
131+ @ Valid @ RequestBody PriorityRequest request ,
132+ @ RequestParam (required = false , defaultValue = "db" ) String storage ,
133+ @ RequestParam (required = false , defaultValue = "false" ) boolean dryRun ,
134+ HttpSession session ) {
135+
136+ PriorityResponse response = priorityService .setPriorities (
137+ participantId ,
138+ voteId ,
139+ request ,
140+ storage ,
141+ dryRun ,
142+ session
143+ );
144+
145+ return ResponseEntity .ok (response );
146+ }
147+
148+ /**
149+ * PATCH /participants/{participantId}/schedule
150+ * 일정 제출
151+ */
152+ @ PatchMapping ("/participants/{participantId}/schedule" )
153+ public ResponseEntity <ParticipantDtos .ParticipantScheduleRes > submitSchedule (
154+ @ PathVariable Long participantId ,
155+ @ Valid @ RequestBody ParticipantDtos .SubmitScheduleReq request ) {
156+
157+ ParticipantDtos .ParticipantScheduleRes response =
158+ participantService .submitSchedule (participantId , request );
159+ return ResponseEntity .ok (response );
160+ }
161+ }
0 commit comments