From 572b6c033fa9585235c44eed7cc6ee2d289c490c Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Tue, 22 Jul 2025 18:43:44 +0900 Subject: [PATCH 1/3] =?UTF-8?q?refactor(rank):=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A5=B4=EB=9F=AC=20=EC=8B=9C=EA=B0=84=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistic/scheduler/RankingRefreshScheduler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/scheduler/RankingRefreshScheduler.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/scheduler/RankingRefreshScheduler.java index 9d9920be..f99c4c09 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/scheduler/RankingRefreshScheduler.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/scheduler/RankingRefreshScheduler.java @@ -35,7 +35,7 @@ public void init() { refresh(); } - @Scheduled(cron = "*/30 * * * * *") // 매 5분마다 실행 + @Scheduled(cron = "0 */5 * * * *") // 매 5분마다 실행 public void refresh() { log.info("RankingRefreshScheduler.refresh() called at {}", LocalDateTime.now()); From 4a200ec93e1031c2c161c1c405dd639b0906b793 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Tue, 22 Jul 2025 19:25:28 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor(Statistics):=20LocalDate=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderService.java | 5 +++-- .../controller/StatisticsController.java | 17 ++++++++++++++--- .../statistic/dto/OrderSalesSumDetail.java | 10 +++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java index b2f8c701..105afbfe 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java @@ -1,5 +1,6 @@ package com.nowait.applicationadmin.order.service; +import java.time.LocalDate; import java.util.List; import java.util.stream.Collectors; @@ -81,7 +82,7 @@ public OrderStatusUpdateResponseDto updateOrderStatus(Long orderId, OrderStatus } @Transactional(readOnly = true) - public OrderSalesSumDetail getSaleSumByStoreId(MemberDetails memberDetails) { + public OrderSalesSumDetail getSaleSumByStoreId(MemberDetails memberDetails, LocalDate date) { User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); Long storeId = user.getStoreId(); @@ -89,7 +90,7 @@ public OrderSalesSumDetail getSaleSumByStoreId(MemberDetails memberDetails) { throw new OrderViewUnauthorizedException(); } - return statisticCustomRepository.findSalesSumByStoreId(storeId); + return statisticCustomRepository.findSalesSumByStoreId(storeId, date); } @Transactional(readOnly = true) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/controller/StatisticsController.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/controller/StatisticsController.java index 2fea19c0..e552f624 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/controller/StatisticsController.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/controller/StatisticsController.java @@ -1,12 +1,15 @@ package com.nowait.applicationadmin.statistic.controller; +import java.time.LocalDate; import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.nowait.applicationadmin.order.service.OrderService; @@ -34,10 +37,18 @@ public class StatisticsController { private final PopularMenuRedisService popularMenuRedisService; @GetMapping("/sales") - @Operation(summary = "오늘의 매출 조회", description = "오늘의 매출을 조회합니다.") + @Operation(summary = "지정일 매출 조회", description = "날짜(date) 파라미터로 매출을 조회합니다. 포맷: yyyy-MM-dd") @ApiResponse(responseCode = "200", description = "오늘의 매출 조회 성공") - public ResponseEntity getTodaySales(@AuthenticationPrincipal MemberDetails memberDetails) { - OrderSalesSumDetail sales = orderService.getSaleSumByStoreId(memberDetails); + public ResponseEntity getTodaySales( + @AuthenticationPrincipal MemberDetails memberDetails, + @RequestParam(value = "date", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) { + LocalDate targetDate = (date != null ? date : LocalDate.now()); + OrderSalesSumDetail sales = orderService.getSaleSumByStoreId(memberDetails, targetDate); + + if (sales.isAllZero()) { + return ResponseEntity.ok(ApiUtils.success("해당일 매출 데이터가 없습니다.")); + } return ResponseEntity .status(HttpStatus.OK) diff --git a/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/dto/OrderSalesSumDetail.java b/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/dto/OrderSalesSumDetail.java index e3ac3ef8..e516f403 100644 --- a/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/dto/OrderSalesSumDetail.java +++ b/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/dto/OrderSalesSumDetail.java @@ -1,5 +1,7 @@ package com.nowait.domainadminrdb.statistic.dto; +import java.time.LocalDate; + import lombok.Builder; import lombok.Getter; @@ -10,12 +12,18 @@ public class OrderSalesSumDetail { private Integer todaySalesSum; private Integer yesterdaySalesSum; private Integer cumulativeSalesBeforeYesterday; + private LocalDate date; public OrderSalesSumDetail(Long storeId, Integer todaySalesSum, Integer yesterdaySalesSum, - Integer cumulativeSalesBeforeYesterday) { + Integer cumulativeSalesBeforeYesterday, LocalDate date) { this.storeId = storeId; this.todaySalesSum = todaySalesSum; this.yesterdaySalesSum = yesterdaySalesSum; this.cumulativeSalesBeforeYesterday = cumulativeSalesBeforeYesterday; + this.date = date; + } + + public boolean isAllZero() { + return todaySalesSum == 0 && yesterdaySalesSum == 0 && cumulativeSalesBeforeYesterday == 0; } } From 4b164a20f2dd53d1e18c7a04fc63b74aa2b5c2b0 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Tue, 22 Jul 2025 19:25:51 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor(Statistics):=20=EC=A3=BC=EC=A0=90?= =?UTF-8?q?=EB=B3=84=20=EB=A7=A4=EC=B6=9C=20=EB=9E=AD=ED=82=B9=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=A3=BC=EB=AC=B8=20=EA=B1=B4=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistic/dto/StoreRankingDto.java | 4 +- .../service/impl/RankingServiceImpl.java | 10 +++- .../repository/StatisticCustomRepository.java | 6 +- .../StatisticCustomRepositoryImpl.java | 60 ++++++++++++------- 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/dto/StoreRankingDto.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/dto/StoreRankingDto.java index a32d6162..36a014c1 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/dto/StoreRankingDto.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/dto/StoreRankingDto.java @@ -9,17 +9,19 @@ public class StoreRankingDto { private final Long departmentId; private final String departmentName; private final Integer totalSales; + private final Integer orderCount; private final Long currentRank; private final Integer delta; private final String profileUrl; public StoreRankingDto(Long storeId, String storeName, Long departmentId, String departmentName, Integer totalSales, - Long currentRank, Integer delta, String profileUrl) { + Integer orderCount, Long currentRank, Integer delta, String profileUrl) { this.storeId = storeId; this.storeName = storeName; this.departmentId = departmentId; this.departmentName = departmentName; this.totalSales = totalSales; + this.orderCount = orderCount; this.currentRank = currentRank; this.delta = delta; this.profileUrl = profileUrl; diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/RankingServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/RankingServiceImpl.java index af206acc..82528132 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/RankingServiceImpl.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/RankingServiceImpl.java @@ -31,7 +31,6 @@ public class RankingServiceImpl implements RankingService { private final UserRepository userRepository; private final RankingQueryService rankingQuery; - @Override @Transactional(readOnly = true) public List getStatisticsRankings(MemberDetails memberDetails) { @@ -45,27 +44,32 @@ public List getStatisticsRankings(MemberDetails memberDetails) // 1) Redis에서 Top4+내주점: storeId, totalSales, currentRank, delta List entries = rankingQuery.getRankings(userStoreId, 5); - // 2) redis 에서 storeId 정보 가져오기 + // 2) 주문 건수 조회 List storeIds = entries.stream() .map(RankingEntry::getStoreId) .toList(); + Map orderCountMap = statisticCustomRepository.findOrderCountByStoreIds(storeIds); + + // 3) redis 에서 storeId 정보 가져오기 List infos = statisticCustomRepository.findStoreInfoByIds(storeIds); // StoreInfo를 storeId로 매핑 Map infoMap = infos.stream() .collect(Collectors.toMap(StoreInfo::getStoreId, Function.identity())); - // 3) 매핑 → 최종 DTO + // 4) 매핑 → 최종 DTO return entries.stream() .map(e -> { StoreInfo info = infoMap.get(e.getStoreId()); + Integer orderCount = orderCountMap.getOrDefault(e.getStoreId(), 0); return new StoreRankingDto( e.getStoreId(), info.getStoreName(), info.getDepartmentId(), info.getDepartmentName(), e.getTotalSales(), + orderCount, e.getCurrentRank(), e.getDelta(), info.getProfileUrl() diff --git a/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/repository/StatisticCustomRepository.java b/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/repository/StatisticCustomRepository.java index 8c03bcdd..fcdeeaf7 100644 --- a/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/repository/StatisticCustomRepository.java +++ b/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/repository/StatisticCustomRepository.java @@ -1,6 +1,8 @@ package com.nowait.domainadminrdb.statistic.repository; +import java.time.LocalDate; import java.util.List; +import java.util.Map; import com.nowait.domainadminrdb.statistic.dto.OrderSalesSumDetail; import com.nowait.domainadminrdb.statistic.dto.StoreInfo; @@ -11,10 +13,12 @@ public interface StatisticCustomRepository { - OrderSalesSumDetail findSalesSumByStoreId(Long storeId); + OrderSalesSumDetail findSalesSumByStoreId(Long storeId, LocalDate date); List getTop4PlusMine(Long storeId); + Map findOrderCountByStoreIds(List storeIds); + // redis 사용하는 부분 List findTotalSales(); diff --git a/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/repository/StatisticCustomRepositoryImpl.java b/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/repository/StatisticCustomRepositoryImpl.java index 0985b9b9..bcee06b8 100644 --- a/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/repository/StatisticCustomRepositoryImpl.java +++ b/nowait-domain/domain-admin-rdb/src/main/java/com/nowait/domainadminrdb/statistic/repository/StatisticCustomRepositoryImpl.java @@ -45,25 +45,19 @@ public StatisticCustomRepositoryImpl(JPAQueryFactory queryFactory) { private static final QStoreImage si = QStoreImage.storeImage; @Override - public OrderSalesSumDetail findSalesSumByStoreId(Long storeId) { - // 1. 날짜 기준 설정 (시작은 자정, 끝은 다음 날 자정) - LocalDate today = LocalDate.now(); - LocalDate yesterday = today.minusDays(1); - - LocalDateTime todayStart = today.atStartOfDay(); - LocalDateTime todayEnd = today.plusDays(1).atStartOfDay(); // 내일 00:00:00 - - LocalDateTime yesterdayStart = yesterday.atStartOfDay(); - LocalDateTime yesterdayEnd = today.atStartOfDay(); + public OrderSalesSumDetail findSalesSumByStoreId(Long storeId, LocalDate date) { + // 1) 시작, 끝 날짜 설정 + LocalDateTime start = date.atStartOfDay(); // 해당 날짜의 자정 + LocalDateTime end = date.plusDays(1).atStartOfDay(); - // 2. 오늘 매출 합산 - Integer todaySum = queryFactory + // 2) target 날짜 해당하는 매출 합산 + Integer targetSum = queryFactory .select(u.totalPrice.sum()) .from(u) .where( u.store.storeId.eq(storeId), - u.createdAt.goe(todayStart), - u.createdAt.lt(todayEnd), + u.createdAt.goe(start), + u.createdAt.lt(end), u.status.eq(COOKED) ) .fetchOne(); @@ -74,8 +68,8 @@ public OrderSalesSumDetail findSalesSumByStoreId(Long storeId) { .from(u) .where( u.store.storeId.eq(storeId), - u.createdAt.goe(yesterdayStart), - u.createdAt.lt(yesterdayEnd), + u.createdAt.goe(start), + u.createdAt.lt(start.minusDays(1)), u.status.eq(COOKED) ) .fetchOne(); @@ -85,21 +79,21 @@ public OrderSalesSumDetail findSalesSumByStoreId(Long storeId) { .from(u) .where( u.store.storeId.eq(storeId), - u.createdAt.lt(yesterdayEnd), + u.createdAt.lt(start), u.status.eq(COOKED) ) .fetchOne(); // null 방어 처리 - if (todaySum == null) - todaySum = 0; + if (targetSum == null) + targetSum = 0; if (yesterdaySum == null) yesterdaySum = 0; if (cumulativeSalesBeforeYesterday == null) cumulativeSalesBeforeYesterday = 0; // 4. 응답 객체 생성 - return new OrderSalesSumDetail(storeId, todaySum, yesterdaySum, cumulativeSalesBeforeYesterday); + return new OrderSalesSumDetail(storeId, targetSum, yesterdaySum, cumulativeSalesBeforeYesterday, date); } @Override @@ -148,6 +142,32 @@ public List getTop4PlusMine(Long userStoreId) { return result; } + @Override + public Map findOrderCountByStoreIds(List storeIds) { + LocalDate today = LocalDate.now(); + LocalDateTime start = today.atStartOfDay(); + LocalDateTime end = today.plusDays(1).atStartOfDay(); + + List rows = queryFactory + .select(u.store.storeId, u.count()) + .from(u) + .where( + u.store.storeId.in(storeIds), + u.createdAt.goe(start), + u.createdAt.lt(end), + u.status.eq(COOKED) + ) + .groupBy(u.store.storeId) + .fetch(); + + // Tuple → Map + return rows.stream() + .collect(Collectors.toMap( + t -> t.get(u.store.storeId), + t -> t.get(u.count()).intValue() + )); + } + private List getAllStores(LocalDateTime todayStart, LocalDateTime todayEnd) { return queryFactory .select(u.store.storeId, u.store.name, u.store.departmentId, u.totalPrice.sum())