From 66e3abff31887b38cb982a157798afad5039650e Mon Sep 17 00:00:00 2001 From: oroi2009 Date: Mon, 18 Aug 2025 18:03:28 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=93=9D=20=20docs=20:=20menu=20&=20sto?= =?UTF-8?q?re=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EB=AA=85=EC=84=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/menu/web/controller/MenuApi.java | 158 ++++++++++++++++++ .../menu/web/controller/MenuController.java | 6 +- .../domain/store/web/controller/StoreApi.java | 123 ++++++++++++++ .../store/web/controller/StoreController.java | 5 +- 4 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/example/Centralthon/domain/menu/web/controller/MenuApi.java create mode 100644 src/main/java/com/example/Centralthon/domain/store/web/controller/StoreApi.java diff --git a/src/main/java/com/example/Centralthon/domain/menu/web/controller/MenuApi.java b/src/main/java/com/example/Centralthon/domain/menu/web/controller/MenuApi.java new file mode 100644 index 0000000..95c51b4 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/menu/web/controller/MenuApi.java @@ -0,0 +1,158 @@ +package com.example.Centralthon.domain.menu.web.controller; + +import com.example.Centralthon.domain.menu.web.dto.MenuDetailsRes; +import com.example.Centralthon.domain.menu.web.dto.MenuIdsReq; +import com.example.Centralthon.domain.menu.web.dto.NearbyMenusRes; +import com.example.Centralthon.domain.menu.web.dto.StoresByMenuRes; +import com.example.Centralthon.global.response.SuccessResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +@Tag(name = "Menus", description = "반찬 관련 API") +public interface MenuApi { + @Operation( + summary = "맞춤 추천용 메뉴 목록 조회", + description ="사용자 위치 기준 2km 이내에 가게에서 판매중인 반찬 매물을 반환합니다.
"+ + "중복을 제외하고 { 반찬명, 카테고리 }를 반환합니다.") + @ApiResponse( + responseCode = "200", + description = "맞춤 추천용 메뉴 목록 조회 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SuccessResponse.class), + examples = @ExampleObject( + name = "SUCCESS_200", + value = """ + { + "timestamp": "2025-08-15 04:22:54", + "code": "SUCCESS_200", + "httpStatus": 200, + "message": "호출에 성공하였습니다.", + "data": [ + { + "name": "진미채 볶음", + "category" : "STIR_FRY" + }, + { + "name": "두부 조림", + "category" : "BRAISED" + } + ], + "isSuccess": true + } + """ + ) + ) + ) + ResponseEntity>> nearbyMenus( + @Parameter(name = "lat", description = "사용자 위도", example = "37.468355", required = true) + @RequestParam("lat") Double lat, + @Parameter(name = "lng", description = "사용자 경도", example = "127.039073", required = true) + @RequestParam("lng") Double lng); + + @Operation( + summary = "특정 메뉴 판매 가게 조회", + description = "반찬명을 기준으로 해당 메뉴를 판매하는 가게를 조회합니다. 공백까지 포함하여 동일 여부를 판단합니다.
"+ + "{ 메뉴Id값, 가게명, 사용자-가게 거리(km), 할인가, 수량}을 반환합니다.") + @ApiResponse( + responseCode = "200", + description = "특정 메뉴 판매 가게 조회 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SuccessResponse.class), + examples = @ExampleObject( + name = "SUCCESS_200", + value = """ + { + "timestamp": "2025-08-15 04:22:54", + "code": "SUCCESS_200", + "httpStatus": 200, + "message": "호출에 성공하였습니다.", + "data": [ + { + "menuId": 15, + "storeName": "초록찬 비건키친", + "distance": 1.7047542239779305, + "salePrice": 4000, + "quantity": 7 + }, + { + "menuId": 1, + "storeName": "우찬이네 반찬", + "distance": 1.0348092111962985, + "salePrice": 4200, + "quantity": 10 + } + ], + "isSuccess": true + } + """ + ) + ) + ) + ResponseEntity>> storesByMenu( + @Parameter(name = "name", description = "메뉴 이름(공백 포함하여 완전일치)", example = "두부 조림", required = true) + @RequestParam("name") String name, + @Parameter(name = "lat", description = "사용자 위도", example = "37.468355", required = true) + @RequestParam("lat") Double lat, + @Parameter(name = "lng", description = "사용자 경도", example = "127.039073", required = true) + @RequestParam("lng") Double lng); + + @Operation( + summary = "메뉴 상세 조회", + description = "사용자가 추가한 각 메뉴들의 상세 정보를 반환합니다.
"+ + "{메뉴Id값, 메뉴 이름, 카테고리, 원가, 할인가, 할인율, 가게명}을 반환합니다." + ) + @ApiResponse( + responseCode = "200", + description = "메뉴 상세 조회 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SuccessResponse.class), + examples = @ExampleObject( + name = "SUCCESS_200", + value = """ + { + "timestamp": "2025-08-15 04:22:54", + "code": "SUCCESS_200", + "httpStatus": 200, + "message": "호출에 성공하였습니다.", + "data": [ + { + "menuId": 1, + "name": "진미채 볶음", + "category" : "STIR_FRY", + "costPrice" : 3000, + "salePrice" : 2400, + "salePercent" : 20, + "storeName" : "우찬이네 밥상" + }, + { + "menuId": 4, + "name": "두부 조림", + "category" : "BRAISED", + "costPrice" : 5000, + "salePrice" : 4000, + "salePercent" : 20, + "storeName" : "희망 식당" + } + ], + "isSuccess": true + } + """ + ) + ) + ) + ResponseEntity>> details(@RequestBody @Valid MenuIdsReq menus); +} diff --git a/src/main/java/com/example/Centralthon/domain/menu/web/controller/MenuController.java b/src/main/java/com/example/Centralthon/domain/menu/web/controller/MenuController.java index b442449..3598235 100644 --- a/src/main/java/com/example/Centralthon/domain/menu/web/controller/MenuController.java +++ b/src/main/java/com/example/Centralthon/domain/menu/web/controller/MenuController.java @@ -3,6 +3,7 @@ import com.example.Centralthon.domain.menu.service.MenuService; import com.example.Centralthon.domain.menu.web.dto.*; +import com.example.Centralthon.domain.order.web.controller.OrderApi; import com.example.Centralthon.global.response.SuccessResponse; import jakarta.validation.Valid; @@ -21,11 +22,12 @@ @RestController @RequestMapping("/api/menus") @RequiredArgsConstructor -public class MenuController { +public class MenuController implements MenuApi { private final MenuService menuService; //맞춤 추천용 메뉴 목록 조회 @GetMapping("") + @Override public ResponseEntity>> nearbyMenus( @RequestParam("lat") Double lat, @RequestParam("lng") Double lng) { @@ -37,6 +39,7 @@ public ResponseEntity>> nearbyMenus( //특정 메뉴를 판매 하는 가게 조회 @GetMapping("/stores") + @Override public ResponseEntity>> storesByMenu( @RequestParam("name") String name, @RequestParam("lat") Double lat, @@ -49,6 +52,7 @@ public ResponseEntity>> storesByMenu( //메뉴 상세 조회 @PostMapping("/details") + @Override public ResponseEntity>> details(@RequestBody @Valid MenuIdsReq menus){ List menuList = menuService.details(menus); diff --git a/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreApi.java b/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreApi.java new file mode 100644 index 0000000..92f0098 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreApi.java @@ -0,0 +1,123 @@ +package com.example.Centralthon.domain.store.web.controller; + +import com.example.Centralthon.domain.order.web.dto.CompleteOrderReq; +import com.example.Centralthon.domain.order.web.dto.CreateOrderReq; +import com.example.Centralthon.domain.order.web.dto.CreateOrderRes; +import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes; +import com.example.Centralthon.domain.store.web.dto.StoreMenusRes; +import com.example.Centralthon.global.response.SuccessResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +@Tag(name = "Stores", description = "가게 관련 API") +public interface StoreApi { + @Operation( + summary = "근처 가게 위치 목록 조회", + description = "사용자 위치 기준 2km 반경 내에 가게 목록을 조회합니다.
가게들의 기본키와 좌표값(위도, 경도)을 반환합니다." + ) + @ApiResponse( + responseCode = "200", + description = "근처 가게 조회 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SuccessResponse.class), + examples = @ExampleObject( + name = "SUCCESS_200", + value = """ + { + "timestamp": "2025-08-14 15:54:04", + "code": "SUCCESS_200", + "httpStatus": 200, + "message": "호출에 성공하였습니다.", + "data": [ + { + "storeId": 1, + "lat": 37.59, + "lng": 127.0164 + }, + { + "storeId": 2, + "lat": 37.577, + "lng": 127.0204 + } + ], + "isSuccess": true + } + """ + ) + ) + ) + ResponseEntity>> nearbyStores( + @Parameter(name = "lat", description = "사용자 위도", example = "37.468355", required = true) + @RequestParam("lat") Double lat, + @Parameter(name = "lng", description = "사용자 경도", example = "127.039073", required = true) + @RequestParam("lng") Double lng); + + @Operation( + summary = "가게에서 판매 중인 메뉴 조회", + description ="현재 재고가 있고 마감 기한을 넘지 않은 메뉴들의 목록을 반환합니다. distacne = 사용자와 가게간의 거리(km)
" + + "{ 메뉴 Id값, 이름, 카테고리, 원가, 할인가, 할인율, 수량} 을 반환합니다.") + @ApiResponse( + responseCode = "200", + description = "판매 메뉴 목록 조회 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = SuccessResponse.class), + examples = @ExampleObject( + name = "SUCCESS_200", + value = """ + { + "timestamp": "2025-08-15 04:45:14", + "code": "SUCCESS_200", + "httpStatus": 200, + "message": "호출에 성공하였습니다.", + "data": { + "name": "오색퓨전찬", + "category": "FUSION_SIDE_DISH", + "distance": 1.0419556581490452, + "menus": [ + { + "menuId": 4, + "name": "진미채 볶음", + "category": "STIR_FRY", + "costPrice": 5000, + "salePrice": 4500, + "salePercent": 10, + "quantity": 7 + }, + { + "menuId": 6, + "name": "순두부찌개", + "category": "SOUP", + "costPrice": 6200, + "salePrice": 5900, + "salePercent": 5, + "quantity": 4 + } + ] + }, + "isSuccess": true + } + """ + ) + ) + ) + ResponseEntity> getStoreMenus( + @Parameter(name = "storeId", in = ParameterIn.PATH, description = "가게 ID", example = "1", required = true) + @PathVariable Long storeId, + @Parameter(name = "lat", in = ParameterIn.QUERY, description = "사용자 위도", example = "37.468355", required = true) + @RequestParam("lat") Double lat, + @Parameter(name = "lng", in = ParameterIn.QUERY, description = "사용자 경도", example = "127.039073", required = true) + @RequestParam("lng") Double lng); +} diff --git a/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java b/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java index c5bc02e..6ffff81 100644 --- a/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java +++ b/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java @@ -1,5 +1,6 @@ package com.example.Centralthon.domain.store.web.controller; +import com.example.Centralthon.domain.order.web.controller.OrderApi; import com.example.Centralthon.domain.store.service.StoreService; import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes; import com.example.Centralthon.domain.store.web.dto.StoreMenusRes; @@ -14,11 +15,12 @@ @RestController @RequestMapping("/api/stores") @RequiredArgsConstructor -public class StoreController { +public class StoreController implements StoreApi { private final StoreService storeService; // 근처 가게 위치 목록 조회 @GetMapping("") + @Override public ResponseEntity>> nearbyStores( @RequestParam("lat") Double lat, @RequestParam("lng") Double lng){ @@ -30,6 +32,7 @@ public ResponseEntity>> nearbyStores( // 가게 판매 메뉴 목록 조회 @GetMapping("/{storeId}/menus") + @Override public ResponseEntity> getStoreMenus( @PathVariable Long storeId, @RequestParam("lat") Double lat, From 1f963b8324b7d31d1b372cc29b4cb1056bb7299f Mon Sep 17 00:00:00 2001 From: frombunny Date: Mon, 18 Aug 2025 18:16:37 +0900 Subject: [PATCH 2/2] =?UTF-8?q?:memo:=20docs:=20swagger=20ui=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a57e3d2..a24dd91 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -15,7 +15,7 @@ spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.use_sql_comments=true # Swagger -springdoc.swagger-ui.path=/swagger-ui.html +springdoc.swagger-ui.path=/api/swagger-ui.html springdoc.api-docs.path=/api-docs springdoc.group-configs[0].group=default springdoc.group-configs[0].paths-to-match=/**