1212import fitfit .global .apiPayload .ApiResponse ;
1313import fitfit .global .apiPayload .code .status .SuccessStatus ;
1414import io .swagger .v3 .oas .annotations .Operation ;
15+ import io .swagger .v3 .oas .annotations .Parameter ;
1516import io .swagger .v3 .oas .annotations .media .Content ;
1617import io .swagger .v3 .oas .annotations .media .Schema ;
1718import io .swagger .v3 .oas .annotations .responses .ApiResponses ;
1819import io .swagger .v3 .oas .annotations .tags .Tag ;
1920import jakarta .validation .Valid ;
2021import lombok .RequiredArgsConstructor ;
2122import org .springframework .data .domain .Page ;
23+ import org .springframework .security .core .parameters .P ;
2224import org .springframework .web .bind .annotation .*;
2325
2426import java .io .IOException ;
@@ -84,6 +86,30 @@ public ApiResponse<ClothesResponseDTO.UpdateResponse> updateClothes(
8486 return ApiResponse .onSuccess (response );
8587 }
8688
89+ @ GetMapping ("/filter" )
90+ @ Operation (summary = "옷 목록 필터링 조회 API" , description = """
91+ 카테고리, 스타일, 가격대, 위치를 기준으로 옷 목록을 동적 필터링여 조회합니다.
92+ - `page`는 0부터 시작합니다.
93+ - `FilterClothesRequest` DTO는 쿼리 파라미터로 전달됩니다. (주소창에 직접 입력하거나, JS에서 URLSearchParams로 전송)
94+ """ )
95+ @ ApiResponses ({
96+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "200" , description = "OK, 성공" ),
97+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "400" , description = "Bad Request, 잘못된 요청 형식" , content = @ Content (schema = @ Schema (implementation = ApiResponse .class ))),
98+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "401" , description = "Unauthorized, 유효하지 않은 토큰" , content = @ Content (schema = @ Schema (implementation = ApiResponse .class )))
99+ })
100+ public ApiResponse <ClothesResponseDTO .ClothesPageDTO > getFilteredClothes (
101+ @ RequestHeader (value = "Authorization" ) String authorization ,
102+ @ Valid @ ModelAttribute ClothesRequestDTO .FilterClothesRequest request ,
103+ @ RequestParam (name = "page" ) @ Parameter (description = "페이지 번호 (0부터 시작)" , example = "0" ) Integer page
104+ ) {
105+ // 1. 필터링된 옷 목록 조회
106+ Page <Clothes > clothesPage = clothesQueryService .getFilteredClothes (request , page );
107+
108+ // 2. 응답 DTO로 변환
109+ return ApiResponse .onSuccess (ClothesConverter .toClothesPageDTO (clothesPage ));
110+ }
111+
112+
87113 @ GetMapping ("/search" )
88114 @ Operation (summary = "판매 옷 일반 검색 API" , description = """
89115 키워드 검색을 통해 판매 옷 정보를 가져오는 API입니다. (존재하지 않는 키워드인 경우 빈 리스트로 응답)
@@ -97,7 +123,7 @@ public ApiResponse<ClothesResponseDTO.UpdateResponse> updateClothes(
97123 - `totalElements`: 전체 아이템 수입니다.
98124 - `isFirst`: 현재 페이지가 첫 페이지인지 여부입니다. (true/false)
99125 - `isLast`: 현재 페이지가 마지막 페이지인지 여부입니다. (true/false)
100- """ )
126+ """ , tags = { "Search" } )
101127 @ ApiResponses ({
102128 @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "200" , description = "OK, 성공" ),
103129 @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "400" , description = "Bad Request, 잘못된 요청 형식" , content = @ Content (schema = @ Schema (implementation = ApiResponse .class ))),
@@ -128,7 +154,7 @@ public ApiResponse<ClothesResponseDTO.ClothesPageDTO> searchClothes(
128154 - `totalElements`: 전체 아이템 수입니다.
129155 - `isFirst`: 현재 페이지가 첫 페이지인지 여부입니다. (true/false)
130156 - `isLast`: 현재 페이지가 마지막 페이지인지 여부입니다. (true/false)
131- """ )
157+ """ , tags = { "Search" } )
132158 @ ApiResponses ({
133159 @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "200" , description = "OK, 성공" ),
134160 @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "401" , description = "Unauthorized, 유효하지 않은 토큰" , content = @ Content (schema = @ Schema (implementation = ApiResponse .class ))),
@@ -168,4 +194,69 @@ public ApiResponse<List<ClothesResponseDTO.MapMarkerDTO>> getMapMarkers() {
168194 List <ClothesResponseDTO .MapMarkerDTO > result = clothesQueryService .getMapMarkers ();
169195 return ApiResponse .of (SuccessStatus ._OK , result );
170196 }
197+
198+ @ GetMapping ("/{clothesId}" )
199+ @ Operation (
200+ summary = "옷 상세 페이지 조회 API" ,
201+ description = """
202+ 상품 ID로 상세 정보를 조회합니다.
203+ - **기능:** 조회수 1 증가, 상품 정보, 판매자 정보, 이미지 리스트 반환
204+ - **상태값:** `status` (SELLING, MATCHED, SOLD_OUT)에 따라 버튼 활성화 여부를 결정하세요.
205+ """
206+ )
207+ @ ApiResponses ({
208+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "200" , description = "성공 (상세 정보 반환)" , content = @ Content (schema = @ Schema (implementation = ClothesResponseDTO .ClothesDetailDTO .class ))),
209+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "404" , description = "존재하지 않는 상품 (CLOTHES4004)" , content = @ Content (schema = @ Schema (implementation = ApiResponse .class )))
210+ })
211+ public ApiResponse <ClothesResponseDTO .ClothesDetailDTO > getClothesDetail (
212+ @ RequestHeader (value = "Authorization" ) String authorization ,
213+ @ Parameter (description = "조회할 상품 ID" ) @ PathVariable Long clothesId
214+ ) {
215+
216+ ClothesResponseDTO .ClothesDetailDTO result = clothesQueryService .getClothesDetail (clothesId , authorization );
217+ return ApiResponse .of (SuccessStatus ._OK , result );
218+ }
219+
220+ // 옷장 넣기/빼기 API (토글)
221+ @ PostMapping ("/{clothesId}/wear-room" )
222+ @ Operation (summary = "옷장 넣기/빼기 (토글)" , description = "상품을 내 옷장에 추가하거나 삭제합니다. (이미 있으면 삭제, 없으면 추가)" +
223+ "is added = true로 오면 옷장에 추가된것임 false로 오면 옷장에서 빠진거" )
224+ @ ApiResponses ({
225+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "200" , description = "성공" ),
226+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (responseCode = "404" , description = "존재하지 않는 상품/회원" )
227+ })
228+ public ApiResponse <ClothesResponseDTO .ToggleWearRoomDTO > toggleCloset (
229+ @ RequestHeader (value = "Authorization" ) String authorization ,
230+ @ Parameter (description = "상품 ID" ) @ PathVariable Long clothesId
231+ ){
232+ ClothesResponseDTO .ToggleWearRoomDTO result = clothesCommandService .toggleWearRoom (authorization ,clothesId );
233+ return ApiResponse .of (SuccessStatus ._OK , result );
234+ }
235+
236+ @ GetMapping ("/wear-room" )
237+ @ Operation (
238+ summary = "내 옷장(찜 목록) 조회 API" ,
239+ description = """
240+ 내가 옷장에 담은(찜한) 상품 목록을 **담은 순서의 역순(최신순)**으로 조회합니다.
241+ """
242+ )
243+ @ ApiResponses ({
244+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (
245+ responseCode = "200" ,
246+ description = "성공 (옷장 리스트 반환)" ,
247+ content = @ Content (schema = @ Schema (implementation = ClothesResponseDTO .WearRoomListDTO .class ))
248+ ),
249+ @ io .swagger .v3 .oas .annotations .responses .ApiResponse (
250+ responseCode = "401" ,
251+ description = "인증 실패 (로그인 필요)" ,
252+ content = @ Content (schema = @ Schema (implementation = ApiResponse .class ))
253+ )
254+ })
255+ public ApiResponse <ClothesResponseDTO .WearRoomListDTO > getMyWearRoom (
256+ @ RequestHeader (value = "Authorization" ) String authorization
257+ ) {
258+ ClothesResponseDTO .WearRoomListDTO result = clothesQueryService .getMyWearRoomList (authorization );
259+
260+ return ApiResponse .of (SuccessStatus ._OK , result );
261+ }
171262}
0 commit comments