From d55ac0778ee6331b91df09b79b7f4095306033e5 Mon Sep 17 00:00:00 2001 From: kjh0718 Date: Wed, 14 Jan 2026 01:19:57 +0900 Subject: [PATCH 1/8] =?UTF-8?q?rank=5F=EA=B8=B0=EB=8A=A5=5F=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20:=20feat=20:=20{=EB=9E=AD=ED=81=AC=20=EB=8B=A8?= =?UTF-8?q?=EC=88=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84}=20https://?= =?UTF-8?q?github.com/CampusTable/campus-table-be/issues/66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menu/controller/MenuController.java | 116 ++++++++++-------- .../be/domain/menu/dto/TopMenuResponse.java | 21 ++++ .../be/domain/menu/service/MenuService.java | 43 +++++++ .../be/domain/order/service/OrderService.java | 10 ++ 4 files changed, 137 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/campustable/be/domain/menu/dto/TopMenuResponse.java diff --git a/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java b/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java index 291ec2c..3536ea3 100644 --- a/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java +++ b/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java @@ -3,6 +3,7 @@ import com.campustable.be.domain.menu.dto.MenuRequest; import com.campustable.be.domain.menu.dto.MenuResponse; import com.campustable.be.domain.menu.dto.MenuUpdateRequest; +import com.campustable.be.domain.menu.dto.TopMenuResponse; import com.campustable.be.domain.menu.service.MenuService; import com.campustable.be.global.aop.LogMonitoringInvocation; import jakarta.validation.Valid; @@ -19,78 +20,87 @@ public class MenuController implements MenuControllerDocs { - private final MenuService menuService; + private final MenuService menuService; - @Override - @GetMapping - @LogMonitoringInvocation - public ResponseEntity> getAllMenus(){ + @Override + @GetMapping + @LogMonitoringInvocation + public ResponseEntity> getAllMenus() { - List menus = menuService.getAllMenus(); + List menus = menuService.getAllMenus(); - return ResponseEntity.ok(menus); + return ResponseEntity.ok(menus); - } + } - @Override - @LogMonitoringInvocation - @GetMapping("/category/{category_id}") - public ResponseEntity> getAllMenusByCategoryId( - @PathVariable(name = "category_id") Long categoryId){ + @Override + @LogMonitoringInvocation + @GetMapping("/category/{category_id}") + public ResponseEntity> getAllMenusByCategoryId( + @PathVariable(name = "category_id") Long categoryId) { - List menus = menuService.getAllMenusByCategory(categoryId); + List menus = menuService.getAllMenusByCategory(categoryId); - return ResponseEntity.ok(menus); + return ResponseEntity.ok(menus); - } + } - @Override - @LogMonitoringInvocation - @GetMapping("/{menuId}") - public ResponseEntity getMenuById(@PathVariable Long menuId){ - return ResponseEntity.ok(menuService.getMenuById(menuId)); - } + @Override + @LogMonitoringInvocation + @GetMapping("/{menuId}") + public ResponseEntity getMenuById(@PathVariable Long menuId) { + return ResponseEntity.ok(menuService.getMenuById(menuId)); + } - @Override - @LogMonitoringInvocation - @GetMapping("/cafeteria/{cafeteria-id}") - public ResponseEntity> getAllMenusByCafeteriaId( + @Override + @LogMonitoringInvocation + @GetMapping("/cafeteria/{cafeteria-id}") + public ResponseEntity> getAllMenusByCafeteriaId( @PathVariable(name = "cafeteria-id") Long cafeteriaId - ) { - return ResponseEntity.ok(menuService.getAllMenusByCafeteriaId(cafeteriaId)); - } + ) { + return ResponseEntity.ok(menuService.getAllMenusByCafeteriaId(cafeteriaId)); + } - @Override - @PostMapping - @LogMonitoringInvocation - public ResponseEntity createMenu(@Valid @RequestBody MenuRequest createRequest){ - MenuResponse createMenu = menuService.createMenu(createRequest); + @Override + @PostMapping + @LogMonitoringInvocation + public ResponseEntity createMenu(@Valid @RequestBody MenuRequest createRequest) { + MenuResponse createMenu = menuService.createMenu(createRequest); - return ResponseEntity.status(HttpStatus.CREATED).body(createMenu); - } + return ResponseEntity.status(HttpStatus.CREATED).body(createMenu); + } - @Override - @PatchMapping("/{menu_id}") - @LogMonitoringInvocation - public ResponseEntity updateMenu( - @PathVariable(name = "menu_id") Long menuId, - @RequestBody MenuUpdateRequest updateRequest){ + @Override + @PatchMapping("/{menu_id}") + @LogMonitoringInvocation + public ResponseEntity updateMenu( + @PathVariable(name = "menu_id") Long menuId, + @RequestBody MenuUpdateRequest updateRequest) { - MenuResponse updateMenu = menuService.updateMenu(menuId, updateRequest); + MenuResponse updateMenu = menuService.updateMenu(menuId, updateRequest); - return ResponseEntity.ok(updateMenu); - } + return ResponseEntity.ok(updateMenu); + } - @Override - @LogMonitoringInvocation - @DeleteMapping("/{menu_id}") - public ResponseEntity deleteMenu( - @PathVariable(name = "menu_id") Long menuId) { + @Override + @LogMonitoringInvocation + @DeleteMapping("/{menu_id}") + public ResponseEntity deleteMenu( + @PathVariable(name = "menu_id") Long menuId) { - menuService.deleteMenu(menuId); + menuService.deleteMenu(menuId); - return ResponseEntity.noContent().build(); - } + return ResponseEntity.noContent().build(); + } + + @GetMapping("/top") + @LogMonitoringInvocation + public ResponseEntity> getTopMenus() { + + List topMenus = menuService.getTopMenus(); + + return ResponseEntity.ok(topMenus); + } } diff --git a/src/main/java/com/campustable/be/domain/menu/dto/TopMenuResponse.java b/src/main/java/com/campustable/be/domain/menu/dto/TopMenuResponse.java new file mode 100644 index 0000000..a8799cb --- /dev/null +++ b/src/main/java/com/campustable/be/domain/menu/dto/TopMenuResponse.java @@ -0,0 +1,21 @@ +package com.campustable.be.domain.menu.dto; + +import com.campustable.be.domain.menu.entity.Menu; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class TopMenuResponse { + + private Long rank; + private MenuResponse menu; + + public static TopMenuResponse of(Long rank, Menu menu) { + return TopMenuResponse.builder() + .rank(rank) + .menu(MenuResponse.from(menu)) + .build(); + } + +} diff --git a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java index 146c24e..41b2409 100644 --- a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java +++ b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java @@ -8,12 +8,20 @@ import com.campustable.be.domain.menu.dto.MenuRequest; import com.campustable.be.domain.menu.dto.MenuResponse; import com.campustable.be.domain.menu.dto.MenuUpdateRequest; +import com.campustable.be.domain.menu.dto.TopMenuResponse; import com.campustable.be.domain.menu.entity.Menu; import com.campustable.be.domain.menu.repository.MenuRepository; import com.campustable.be.global.exception.CustomException; import com.campustable.be.global.exception.ErrorCode; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -29,6 +37,7 @@ public class MenuService { private final MenuRepository menuRepository; private final CategoryRepository categoryRepository; private final CafeteriaService cafeteriaService; + private final StringRedisTemplate stringRedisTemplate; @Transactional @@ -142,5 +151,39 @@ public void deleteMenu(Long menuId) { } } + + @Transactional + public List getTopMenus(){ + + Set topMenus = stringRedisTemplate.opsForZSet().reverseRange("menu:rank",0,2); + + if(topMenus == null || topMenus.isEmpty()){ + return List.of(); + } + + List topMenuIds = topMenus.stream() + .map(Long::parseLong) + .toList(); + + List menus = menuRepository.findAllById(topMenuIds); + + Map topMenusMap = menus.stream() + .collect(Collectors.toMap(Menu::getId, Function.identity())); + + List topMenusResponse = new ArrayList<>(); + + for(int i=0;i<3;i++){ + Long topMenuId = topMenuIds.get(i); + Menu menu = topMenusMap.get(topMenuId); + + if(menu != null){ + topMenusResponse.add(TopMenuResponse.of((long)(i+1),menu)); + } + } + + return topMenusResponse; + + } + } diff --git a/src/main/java/com/campustable/be/domain/order/service/OrderService.java b/src/main/java/com/campustable/be/domain/order/service/OrderService.java index fa4135a..b29e0cf 100644 --- a/src/main/java/com/campustable/be/domain/order/service/OrderService.java +++ b/src/main/java/com/campustable/be/domain/order/service/OrderService.java @@ -3,6 +3,7 @@ import com.campustable.be.domain.cart.entity.Cart; import com.campustable.be.domain.cart.repository.CartRepository; import com.campustable.be.domain.menu.entity.Menu; +import com.campustable.be.domain.menu.repository.MenuRepository; import com.campustable.be.domain.order.dto.OrderResponse; import com.campustable.be.domain.order.entity.Order; import com.campustable.be.domain.order.entity.OrderItem; @@ -16,6 +17,8 @@ import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,6 +32,7 @@ public class OrderService { private final CartRepository cartRepository; private final UserRepository userRepository; private final OrderItemRepository orderItemRepository; + private final StringRedisTemplate stringRedisTemplate; public OrderResponse createOrder() { Long userId = SecurityUtil.getCurrentUserId(); @@ -63,6 +67,12 @@ public OrderResponse createOrder() { user.setCart(null); cartRepository.delete(cart); + // 주문된 메뉴 랭킹 점수 증가 + for(OrderItem orderItem : orderItems) { + stringRedisTemplate.opsForZSet() + .incrementScore("menu:rank",String.valueOf(orderItem.getMenu().getId()),orderItem.getQuantity()); + } + return OrderResponse.from(order); } From 9699b0aa40ade115513216fd996f4b7659523c33 Mon Sep 17 00:00:00 2001 From: kjh0718 Date: Wed, 14 Jan 2026 13:28:06 +0900 Subject: [PATCH 2/8] =?UTF-8?q?rank=5F=EA=B8=B0=EB=8A=A5=5F=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20:=20feat=20:=20{=EC=8B=9D=EB=8B=B9=EB=B3=84=20top3?= =?UTF-8?q?=20=EB=82=98=EC=98=A4=EA=B2=8C=20=EA=B5=AC=ED=98=84}=20https://?= =?UTF-8?q?github.com/CampusTable/campus-table-be/issues/66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../be/domain/menu/controller/MenuController.java | 6 +++--- .../campustable/be/domain/menu/dto/MenuRequest.java | 1 + .../campustable/be/domain/menu/dto/MenuResponse.java | 4 +++- .../be/domain/menu/service/MenuService.java | 8 +++++--- .../be/domain/order/service/OrderService.java | 12 +++++++++++- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java b/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java index 3536ea3..21ac8ee 100644 --- a/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java +++ b/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java @@ -94,11 +94,11 @@ public ResponseEntity deleteMenu( return ResponseEntity.noContent().build(); } - @GetMapping("/top") + @GetMapping("/cafeteria/{cafeteria-id}/top-menus") @LogMonitoringInvocation - public ResponseEntity> getTopMenus() { + public ResponseEntity> getTopMenus(@PathVariable(name = "cafeteria-id") Long cafeteriaId) { - List topMenus = menuService.getTopMenus(); + List topMenus = menuService.getTopMenusByCafeteriaId(cafeteriaId); return ResponseEntity.ok(topMenus); } diff --git a/src/main/java/com/campustable/be/domain/menu/dto/MenuRequest.java b/src/main/java/com/campustable/be/domain/menu/dto/MenuRequest.java index 5f42a07..8b6968b 100644 --- a/src/main/java/com/campustable/be/domain/menu/dto/MenuRequest.java +++ b/src/main/java/com/campustable/be/domain/menu/dto/MenuRequest.java @@ -43,6 +43,7 @@ public Menu toEntity(Category category) { .menuUrl(this.getMenuUrl()) .available(this.getAvailable()) .stockQuantity(this.getStockQuantity()) + .build(); } diff --git a/src/main/java/com/campustable/be/domain/menu/dto/MenuResponse.java b/src/main/java/com/campustable/be/domain/menu/dto/MenuResponse.java index af03eaa..17a52c4 100644 --- a/src/main/java/com/campustable/be/domain/menu/dto/MenuResponse.java +++ b/src/main/java/com/campustable/be/domain/menu/dto/MenuResponse.java @@ -24,6 +24,7 @@ public class MenuResponse { private Boolean available; private Integer stockQuantity; private LocalDateTime createdDate; + private Long cafeteriaId; public static MenuResponse from(Menu menu) { return new MenuResponse( @@ -34,7 +35,8 @@ public static MenuResponse from(Menu menu) { menu.getMenuUrl(), menu.getAvailable(), menu.getStockQuantity(), - menu.getCreatedAt() + menu.getCreatedAt(), + menu.getCategory().getCafeteria().getCafeteriaId() ); } diff --git a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java index 41b2409..40e6db3 100644 --- a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java +++ b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java @@ -153,9 +153,11 @@ public void deleteMenu(Long menuId) { @Transactional - public List getTopMenus(){ + public List getTopMenusByCafeteriaId(Long cafeteriaId) { - Set topMenus = stringRedisTemplate.opsForZSet().reverseRange("menu:rank",0,2); + String key = "cafeteria:"+cafeteriaId+"menu:rank"; + + Set topMenus = stringRedisTemplate.opsForZSet().reverseRange(key,0,2); if(topMenus == null || topMenus.isEmpty()){ return List.of(); @@ -172,7 +174,7 @@ public List getTopMenus(){ List topMenusResponse = new ArrayList<>(); - for(int i=0;i<3;i++){ + for(int i=0;i Date: Wed, 14 Jan 2026 13:39:36 +0900 Subject: [PATCH 3/8] =?UTF-8?q?rank=5F=EA=B8=B0=EB=8A=A5=5F=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20:=20feat=20:=20{key=20=EA=B0=92=EC=97=90=20?= =?UTF-8?q?=EC=BD=9C=EB=A1=A0=20=EC=B6=94=EA=B0=80}=20https://github.com/C?= =?UTF-8?q?ampusTable/campus-table-be/issues/66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/campustable/be/domain/menu/service/MenuService.java | 2 +- .../com/campustable/be/domain/order/service/OrderService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java index 40e6db3..fcb6f50 100644 --- a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java +++ b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java @@ -155,7 +155,7 @@ public void deleteMenu(Long menuId) { @Transactional public List getTopMenusByCafeteriaId(Long cafeteriaId) { - String key = "cafeteria:"+cafeteriaId+"menu:rank"; + String key = "cafeteria:"+cafeteriaId+":menu:rank"; Set topMenus = stringRedisTemplate.opsForZSet().reverseRange(key,0,2); diff --git a/src/main/java/com/campustable/be/domain/order/service/OrderService.java b/src/main/java/com/campustable/be/domain/order/service/OrderService.java index 8530951..2a44556 100644 --- a/src/main/java/com/campustable/be/domain/order/service/OrderService.java +++ b/src/main/java/com/campustable/be/domain/order/service/OrderService.java @@ -77,7 +77,7 @@ public OrderResponse createOrder() { Cafeteria cafeteria = category.getCafeteria(); Long cafeteriaId = cafeteria.getCafeteriaId(); - String key = "cafeteria:" + cafeteriaId + "menu:rank"; + String key = "cafeteria:" + cafeteriaId + ":menu:rank"; stringRedisTemplate.opsForZSet() .incrementScore(key,String.valueOf(menu.getId()),orderItem.getQuantity()); From 2660e5075091c381673ac4a0652b240b31afafae Mon Sep 17 00:00:00 2001 From: kjh0718 Date: Wed, 14 Jan 2026 15:16:34 +0900 Subject: [PATCH 4/8] =?UTF-8?q?rank=5F=EA=B8=B0=EB=8A=A5=5F=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20:=20feat=20:=20{=EC=8B=9D=EB=8B=B9=20=EC=97=86?= =?UTF-8?q?=EC=9D=84=EB=95=8C=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?+=20docs}=20https://github.com/CampusTable/campus-table-be/issu?= =?UTF-8?q?es/66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menu/controller/MenuController.java | 5 +++-- .../menu/controller/MenuControllerDocs.java | 19 +++++++++++++++++++ .../be/domain/menu/service/MenuService.java | 4 +++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java b/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java index 21ac8ee..99b5761 100644 --- a/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java +++ b/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java @@ -94,11 +94,12 @@ public ResponseEntity deleteMenu( return ResponseEntity.noContent().build(); } + @Override @GetMapping("/cafeteria/{cafeteria-id}/top-menus") @LogMonitoringInvocation - public ResponseEntity> getTopMenus(@PathVariable(name = "cafeteria-id") Long cafeteriaId) { + public ResponseEntity> getTop3MenusByCafeteriaId(@PathVariable(name = "cafeteria-id") Long cafeteriaId) { - List topMenus = menuService.getTopMenusByCafeteriaId(cafeteriaId); + List topMenus = menuService.getTop3MenusByCafeteriaId(cafeteriaId); return ResponseEntity.ok(topMenus); } diff --git a/src/main/java/com/campustable/be/domain/menu/controller/MenuControllerDocs.java b/src/main/java/com/campustable/be/domain/menu/controller/MenuControllerDocs.java index a1d3479..2320817 100644 --- a/src/main/java/com/campustable/be/domain/menu/controller/MenuControllerDocs.java +++ b/src/main/java/com/campustable/be/domain/menu/controller/MenuControllerDocs.java @@ -3,6 +3,7 @@ import com.campustable.be.domain.menu.dto.MenuRequest; import com.campustable.be.domain.menu.dto.MenuResponse; import com.campustable.be.domain.menu.dto.MenuUpdateRequest; +import com.campustable.be.domain.menu.dto.TopMenuResponse; import com.campustable.be.global.exception.ErrorResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -123,4 +124,22 @@ ResponseEntity updateMenu( ResponseEntity deleteMenu( @Parameter(description = "삭제할 메뉴 ID", example = "1") Long menuId ); + + /** + * 특정 식당의 인기 메뉴(Top 3)를 조회합니다. + * Redis ZSet을 기반으로 식당별 주문량이 가장 많은 Top3 메뉴 정보와 랭킹을 반환 + * @param cafeteriaId 식당 고유 식별자 + * @return 랭킹과 메뉴 상세 정보 리스트를 담은 ResponseEntity + */ + @Operation(summary = "식당별 인기 메뉴 조회(Top 3)", description = "특정 식당 ID의 Top 3 인기 메뉴를 조회합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "조회 성공"), + @ApiResponse(responseCode = "404", description = "해당 식당을 찾을 수 없습니다.", + content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + ResponseEntity> getTop3MenusByCafeteriaId( + @Parameter(description = "조회할 식당 ID",example = "1") Long cafeteriaId + ); + + } \ No newline at end of file diff --git a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java index fcb6f50..f29d971 100644 --- a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java +++ b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java @@ -153,7 +153,9 @@ public void deleteMenu(Long menuId) { @Transactional - public List getTopMenusByCafeteriaId(Long cafeteriaId) { + public List getTop3MenusByCafeteriaId(Long cafeteriaId) { + + cafeteriaService.findCafeteriaById(cafeteriaId); String key = "cafeteria:"+cafeteriaId+":menu:rank"; From f153f03aaf4182573e8a27c82e8608235e4591bf Mon Sep 17 00:00:00 2001 From: kjh0718 Date: Thu, 15 Jan 2026 16:55:31 +0900 Subject: [PATCH 5/8] =?UTF-8?q?rank=5F=EA=B8=B0=EB=8A=A5=5F=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20:=20feat=20:=20{=EB=A9=94=EB=89=B4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=8B=9C=20redis=20=EB=9E=AD=ED=82=B9=200=EC=A0=90=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94}=20https://github.com/CampusTable/c?= =?UTF-8?q?ampus-table-be/issues/66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../be/domain/menu/service/MenuService.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java index f29d971..03a64c3 100644 --- a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java +++ b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java @@ -60,7 +60,15 @@ public MenuResponse createMenu(MenuRequest request) { } Menu menu = request.toEntity(category); - return MenuResponse.from(menuRepository.save(menu)); + + Menu savedMenu = menuRepository.save(menu); + + Long cafeteriaId = savedMenu.getCategory().getCafeteria().getCafeteriaId(); + String key = "cafeteria:" + cafeteriaId + ":menu:rank"; + + stringRedisTemplate.opsForZSet().add(key, String.valueOf(savedMenu.getId()), 0.0); + + return MenuResponse.from(savedMenu); } @@ -68,7 +76,7 @@ public MenuResponse createMenu(MenuRequest request) { public MenuResponse getMenuById(Long menuId) { Menu menu = menuRepository.findById(menuId) - .orElseThrow(()->{ + .orElseThrow(() -> { log.error("getMenuById : 유효하지않은 menuId"); return new CustomException(ErrorCode.MENU_NOT_FOUND); }); @@ -155,13 +163,13 @@ public void deleteMenu(Long menuId) { @Transactional public List getTop3MenusByCafeteriaId(Long cafeteriaId) { - cafeteriaService.findCafeteriaById(cafeteriaId); + cafeteriaService.findCafeteriaById(cafeteriaId); - String key = "cafeteria:"+cafeteriaId+":menu:rank"; + String key = "cafeteria:" + cafeteriaId + ":menu:rank"; - Set topMenus = stringRedisTemplate.opsForZSet().reverseRange(key,0,2); + Set topMenus = stringRedisTemplate.opsForZSet().reverseRange(key, 0, 2); - if(topMenus == null || topMenus.isEmpty()){ + if (topMenus == null || topMenus.isEmpty()) { return List.of(); } @@ -171,17 +179,17 @@ public List getTop3MenusByCafeteriaId(Long cafeteriaId) { List menus = menuRepository.findAllById(topMenuIds); - Map topMenusMap = menus.stream() + Map topMenusMap = menus.stream() .collect(Collectors.toMap(Menu::getId, Function.identity())); List topMenusResponse = new ArrayList<>(); - for(int i=0;i Date: Sun, 25 Jan 2026 21:01:22 +0900 Subject: [PATCH 6/8] =?UTF-8?q?rank=5F=EA=B8=B0=EB=8A=A5=5F=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20:=20feat=20:=20{coderabbit=20=EC=88=98=EC=A0=95}=20?= =?UTF-8?q?https://github.com/CampusTable/campus-table-be/issues/66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../be/domain/menu/service/MenuService.java | 13 +++++-- .../be/domain/order/service/OrderService.java | 38 +++++++++++++------ 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java index 03a64c3..b9fb2a7 100644 --- a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java +++ b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java @@ -63,10 +63,17 @@ public MenuResponse createMenu(MenuRequest request) { Menu savedMenu = menuRepository.save(menu); - Long cafeteriaId = savedMenu.getCategory().getCafeteria().getCafeteriaId(); - String key = "cafeteria:" + cafeteriaId + ":menu:rank"; + try{ + Long cafeteriaId = savedMenu.getCategory().getCafeteria().getCafeteriaId(); + String key = "cafeteria:" + cafeteriaId + ":menu:rank"; + + stringRedisTemplate.opsForZSet().add(key, String.valueOf(savedMenu.getId()), 0.0); + } + catch (Exception e){ - stringRedisTemplate.opsForZSet().add(key, String.valueOf(savedMenu.getId()), 0.0); + log.error("메뉴 생성 후 Redis 랭킹 등록 실패. (Menu Id: {}), 원인 :{}",savedMenu.getId(),e.getMessage()); + + } return MenuResponse.from(savedMenu); diff --git a/src/main/java/com/campustable/be/domain/order/service/OrderService.java b/src/main/java/com/campustable/be/domain/order/service/OrderService.java index 2a44556..f558638 100644 --- a/src/main/java/com/campustable/be/domain/order/service/OrderService.java +++ b/src/main/java/com/campustable/be/domain/order/service/OrderService.java @@ -23,6 +23,8 @@ import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; @Service @RequiredArgsConstructor @@ -69,21 +71,33 @@ public OrderResponse createOrder() { user.setCart(null); cartRepository.delete(cart); - // 주문된 메뉴 랭킹 점수 증가 - for(OrderItem orderItem : orderItems) { - - Menu menu = orderItem.getMenu(); - Category category = menu.getCategory(); - Cafeteria cafeteria = category.getCafeteria(); - Long cafeteriaId = cafeteria.getCafeteriaId(); + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + updateMenuRanking(orderItems); + } + }); - String key = "cafeteria:" + cafeteriaId + ":menu:rank"; + return OrderResponse.from(order); + } - stringRedisTemplate.opsForZSet() - .incrementScore(key,String.valueOf(menu.getId()),orderItem.getQuantity()); + private void updateMenuRanking(List orderItems) { + for(OrderItem orderItem : orderItems) { + try { + Menu menu = orderItem.getMenu(); + Category category = menu.getCategory(); + Cafeteria cafeteria = category.getCafeteria(); + Long cafeteriaId = cafeteria.getCafeteriaId(); + + String key = "cafeteria:" + cafeteriaId + ":menu:rank"; + + stringRedisTemplate.opsForZSet() + .incrementScore(key, String.valueOf(menu.getId()), orderItem.getQuantity()); + } + catch (Exception e) { + log.error("랭킹 점수 반영 실패: {}",e.getMessage()); + } } - - return OrderResponse.from(order); } public void updateCategoryToReady(Long orderId,Long categoryId) { From f8698abe31b2726aa2abcd98b922e054a8b207d9 Mon Sep 17 00:00:00 2001 From: kjh0718 Date: Sun, 25 Jan 2026 21:16:13 +0900 Subject: [PATCH 7/8] =?UTF-8?q?rank=5F=EA=B8=B0=EB=8A=A5=5F=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20:=20feat=20:=20{coderabbit=20=EC=88=98=EC=A0=95=20-?= =?UTF-8?q?=20redis=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B3=80=ED=99=98=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC}=20https://github.com/C?= =?UTF-8?q?ampusTable/campus-table-be/issues/66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../be/domain/menu/service/MenuService.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java index b9fb2a7..acc78bb 100644 --- a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java +++ b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java @@ -63,15 +63,14 @@ public MenuResponse createMenu(MenuRequest request) { Menu savedMenu = menuRepository.save(menu); - try{ + try { Long cafeteriaId = savedMenu.getCategory().getCafeteria().getCafeteriaId(); String key = "cafeteria:" + cafeteriaId + ":menu:rank"; stringRedisTemplate.opsForZSet().add(key, String.valueOf(savedMenu.getId()), 0.0); - } - catch (Exception e){ + } catch (Exception e) { - log.error("메뉴 생성 후 Redis 랭킹 등록 실패. (Menu Id: {}), 원인 :{}",savedMenu.getId(),e.getMessage()); + log.error("메뉴 생성 후 Redis 랭킹 등록 실패. (Menu Id: {}), 원인 :{}", savedMenu.getId(), e.getMessage()); } @@ -181,7 +180,15 @@ public List getTop3MenusByCafeteriaId(Long cafeteriaId) { } List topMenuIds = topMenus.stream() - .map(Long::parseLong) + .map(id -> { + try { + return Long.parseLong(id); + } catch (NumberFormatException e) { + log.warn("Redis에 잘못된 메뉴 ID 형식: {}", id); + return null; + } + }) + .filter(Objects::nonNull) .toList(); List menus = menuRepository.findAllById(topMenuIds); From f580fa8c917941a5a5ada271f558a1ee81015676 Mon Sep 17 00:00:00 2001 From: kjh0718 Date: Wed, 4 Feb 2026 01:53:10 +0900 Subject: [PATCH 8/8] =?UTF-8?q?rank=5F=EA=B8=B0=EB=8A=A5=5F=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20:=20feat=20:=20{=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20+=20rank}=20https://github.com/Ca?= =?UTF-8?q?mpusTable/campus-table-be/issues/66?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../be/domain/menu/controller/MenuController.java | 11 +++++++++-- .../be/domain/menu/service/MenuService.java | 5 ----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java b/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java index 9de1222..61c5263 100644 --- a/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java +++ b/src/main/java/com/campustable/be/domain/menu/controller/MenuController.java @@ -36,7 +36,15 @@ public ResponseEntity> getAllMenus(){ } - @Override + @Override + @LogMonitoringInvocation + @GetMapping("/menus/{menuId}") + public ResponseEntity getMenuById(@PathVariable Long menuId){ + return ResponseEntity.ok(menuService.getMenuById(menuId)); + } + + + @Override @LogMonitoringInvocation @GetMapping("/category/{category_id}/menus") public ResponseEntity> getAllMenusByCategoryId( @@ -49,7 +57,6 @@ public ResponseEntity> getAllMenusByCategoryId( } - @Override @LogMonitoringInvocation @GetMapping("/cafeteria/{cafeteria-id}") diff --git a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java index 03d5892..0515b18 100644 --- a/src/main/java/com/campustable/be/domain/menu/service/MenuService.java +++ b/src/main/java/com/campustable/be/domain/menu/service/MenuService.java @@ -61,11 +61,6 @@ public MenuResponse createMenu(MenuRequest request, MultipartFile image) { Menu menu = request.toEntity(category); Menu savedMenu = menuRepository.save(menu); - - Menu menu = request.toEntity(category); - - Menu savedMenu = menuRepository.save(menu); - try { Long cafeteriaId = savedMenu.getCategory().getCafeteria().getCafeteriaId(); String key = "cafeteria:" + cafeteriaId + ":menu:rank";