From b0ebfc92a686f4f031e3ef7df265f7021efccff8 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:51:17 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat(event):=20event=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nowait-app-admin-api/build.gradle | 1 + nowait-domain/domain-redis/build.gradle | 1 + nowait-event/build.gradle | 57 +++++++++++++++++++++++++ settings.gradle | 1 + 4 files changed, 60 insertions(+) create mode 100644 nowait-event/build.gradle diff --git a/nowait-app-admin-api/build.gradle b/nowait-app-admin-api/build.gradle index 028c8818..5568bb77 100644 --- a/nowait-app-admin-api/build.gradle +++ b/nowait-app-admin-api/build.gradle @@ -22,6 +22,7 @@ dependencies { implementation project(':nowait-domain:domain-admin-rdb') implementation project(':nowait-domain:domain-core-rdb') implementation project(':nowait-domain:domain-redis') + implementation project(':nowait-event') // Spring Boot Starter implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/nowait-domain/domain-redis/build.gradle b/nowait-domain/domain-redis/build.gradle index 8a6470e6..2beb40e2 100644 --- a/nowait-domain/domain-redis/build.gradle +++ b/nowait-domain/domain-redis/build.gradle @@ -22,6 +22,7 @@ repositories { dependencies { implementation project(':nowait-common') + implementation project(':nowait-event') implementation 'org.springframework.boot:spring-boot-starter-data-redis' diff --git a/nowait-event/build.gradle b/nowait-event/build.gradle new file mode 100644 index 00000000..6d9bc8dc --- /dev/null +++ b/nowait-event/build.gradle @@ -0,0 +1,57 @@ +import org.gradle.jvm.toolchain.JavaLanguageVersion + +plugins { + id 'java-library' +} + +jar { + enabled = true +} + + +group = 'com.nowait' +version = rootProject.version + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':nowait-common') + implementation project(':nowait-domain:domain-core-rdb') + + api 'org.springframework.boot:spring-boot-starter-data-jpa' + api 'jakarta.persistence:jakarta.persistence-api:3.1.0' + + // SPRING SECURITY + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-validation' + + + // SWAGGER + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' + + compileOnly 'org.projectlombok:lombok:1.18.26' + annotationProcessor 'org.projectlombok:lombok:1.18.26' + + //QueryDsl + // TODO Q클래스 생성시 오류 발생 해결 필요 + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + // Jackson + api 'com.fasterxml.jackson.core:jackson-databind:2.15.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2' + + testImplementation 'org.junit.jupiter:junit-jupiter' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' +} diff --git a/settings.gradle b/settings.gradle index 89991c90..79aeb646 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,4 +7,5 @@ include 'nowait-domain:domain-core-rdb' include 'nowait-domain:domain-admin-rdb' include 'nowait-domain:domain-user-rdb' include 'nowait-domain:domain-redis' +include 'nowait-event' include 'nowait-infra' From bfa73da7a2e35e18860cb7a3b12d74bbc06b131a Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:51:34 +0900 Subject: [PATCH 02/15] =?UTF-8?q?feat(event):=20=EC=9D=B8=EA=B8=B0=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EC=9D=B4=EB=B2=A4=ED=8A=B8,=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=EB=84=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/CookingCompleteListener.java | 31 +++++++++++++++++++ .../order/event/CookingCompleteEvent.java | 20 ++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/listener/CookingCompleteListener.java create mode 100644 nowait-event/src/main/java/com/nowait/nowaitevent/order/event/CookingCompleteEvent.java diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/listener/CookingCompleteListener.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/listener/CookingCompleteListener.java new file mode 100644 index 00000000..1555fd0d --- /dev/null +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/listener/CookingCompleteListener.java @@ -0,0 +1,31 @@ +package com.nowait.domaincoreredis.rank.listener; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.nowait.domaincoreredis.rank.service.MenuCounterService; +import com.nowait.nowaitevent.order.event.CookingCompleteEvent; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class CookingCompleteListener { + private final MenuCounterService menuCounterService; + + @TransactionalEventListener( + classes = CookingCompleteEvent.class, + phase = TransactionPhase.AFTER_COMMIT + ) + public void onCookingComplete(CookingCompleteEvent event) { + Long storeId = event.getStoreId(); + for (CookingCompleteEvent.Item item : event.getItems()) { + menuCounterService.incrementMenuCounter( + item.getMenuId(), + storeId, + item.getQuantity() + ); + } + } +} diff --git a/nowait-event/src/main/java/com/nowait/nowaitevent/order/event/CookingCompleteEvent.java b/nowait-event/src/main/java/com/nowait/nowaitevent/order/event/CookingCompleteEvent.java new file mode 100644 index 00000000..0b1a16b2 --- /dev/null +++ b/nowait-event/src/main/java/com/nowait/nowaitevent/order/event/CookingCompleteEvent.java @@ -0,0 +1,20 @@ +package com.nowait.nowaitevent.order.event; + +import java.util.List; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class CookingCompleteEvent { + private final Long storeId; + private final List items; + + @Getter + @RequiredArgsConstructor + public static class Item { + private final Long menuId; + private final int quantity; + } +} From aa8d0e40b7a99b1baa336c461f459605962ae1d6 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:51:46 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat(event):=20MenuCounterService=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rank/service/MenuCounterService.java | 12 +++++ .../rank/service/MenuCounterServiceImpl.java | 52 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterService.java create mode 100644 nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterService.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterService.java new file mode 100644 index 00000000..d1024785 --- /dev/null +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterService.java @@ -0,0 +1,12 @@ +package com.nowait.domaincoreredis.rank.service; + +import java.util.Set; + +import org.springframework.data.redis.core.ZSetOperations; + +public interface MenuCounterService { + + void incrementMenuCounter(Long menuId, Long storeId, int qty); + + Set> getTopMenus(Long storeId, int topN); +} diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java new file mode 100644 index 00000000..c49366b7 --- /dev/null +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java @@ -0,0 +1,52 @@ +package com.nowait.domaincoreredis.rank.service; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Service; + +import com.nowait.domaincoreredis.common.util.RedisKeyUtils; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class MenuCounterServiceImpl implements MenuCounterService { + + private final String KEY_FMT = RedisKeyUtils.buildMenuKey(); + private final DateTimeFormatter DTF = RedisKeyUtils.buildMenuDateKey(); + private final RedisTemplate redis; + + @Override + public void incrementMenuCounter(Long menuId, Long storeId, int qty) { + + String date = LocalDate.now().format(DTF); + String key = String.format(KEY_FMT, storeId, date); + + redis.opsForZSet().incrementScore(key, menuId.toString(), qty); + + Long expirationTime = redis.getExpire(key); + if (expirationTime == null || expirationTime < 0) { + long secondsUntilMidnight = Duration.between( + LocalDateTime.now(), + LocalDate.now().plusDays(1).atStartOfDay() + ).getSeconds(); + + redis.expire(key, secondsUntilMidnight, TimeUnit.SECONDS); + } + } + + @Override + public Set> getTopMenus(Long storeId, int topN) { + String date = LocalDate.now().format(DTF); + String key = String.format(KEY_FMT, storeId, date); + + return redis.opsForZSet().reverseRangeWithScores(key, 0, topN - 1); + } +} From 416c8a25223f7c68cd1984aa933c40fe8e9eeb36 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:51:56 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat(event):=20PopularMenuDto=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/PopularMenuDto.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/dto/PopularMenuDto.java diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/dto/PopularMenuDto.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/dto/PopularMenuDto.java new file mode 100644 index 00000000..828be84a --- /dev/null +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/dto/PopularMenuDto.java @@ -0,0 +1,12 @@ +package com.nowait.applicationadmin.statistic.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class PopularMenuDto { + private Long menuId; + private String menuName; + private Long soldCount; +} From 359182f49857c14a9bc5377baef9b48b05bbbc46 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:52:06 +0900 Subject: [PATCH 05/15] =?UTF-8?q?feat(event):=20PopularMenuRedisService=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/PopularMenuRedisService.java | 10 ++++ .../impl/PopularMenuRedisServiceImpl.java | 53 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/PopularMenuRedisService.java create mode 100644 nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/PopularMenuRedisServiceImpl.java diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/PopularMenuRedisService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/PopularMenuRedisService.java new file mode 100644 index 00000000..95fd281c --- /dev/null +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/PopularMenuRedisService.java @@ -0,0 +1,10 @@ +package com.nowait.applicationadmin.statistic.service; + +import java.util.List; + +import com.nowait.applicationadmin.statistic.dto.PopularMenuDto; +import com.nowait.domaincorerdb.user.entity.MemberDetails; + +public interface PopularMenuRedisService { + List getTodayTop5(MemberDetails memberDetails); +} diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/PopularMenuRedisServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/PopularMenuRedisServiceImpl.java new file mode 100644 index 00000000..9d922992 --- /dev/null +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/PopularMenuRedisServiceImpl.java @@ -0,0 +1,53 @@ +package com.nowait.applicationadmin.statistic.service.impl; + +import java.util.List; +import java.util.Set; + +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Service; + +import com.nowait.applicationadmin.statistic.dto.PopularMenuDto; +import com.nowait.applicationadmin.statistic.service.PopularMenuRedisService; +import com.nowait.common.enums.Role; +import com.nowait.domainadminrdb.statistic.exception.StatisticViewUnauthorizedException; +import com.nowait.domaincorerdb.menu.repository.MenuRepository; +import com.nowait.domaincorerdb.user.entity.MemberDetails; +import com.nowait.domaincorerdb.user.entity.User; +import com.nowait.domaincorerdb.user.exception.UserNotFoundException; +import com.nowait.domaincorerdb.user.repository.UserRepository; +import com.nowait.domaincoreredis.rank.service.MenuCounterService; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class PopularMenuRedisServiceImpl implements PopularMenuRedisService { + + private final MenuCounterService menuCounterService; + private final MenuRepository menuRepository; + private final UserRepository userRepository; + + @Override + public List getTodayTop5(MemberDetails memberDetails) { + + User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + Long storeId = user.getStoreId(); + + if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { + throw new StatisticViewUnauthorizedException(); + } + + Set> tuples = menuCounterService.getTopMenus(storeId, 5); + + return tuples.stream() + .map(tuple -> { + Long menuId = Long.parseLong(tuple.getValue()); + String menuName = menuRepository.findById(menuId) + .map(menu -> menu.getName()) + .orElse("Unknown Menu"); + + return new PopularMenuDto(menuId, menuName, tuple.getScore().longValue()); + }) + .toList(); + } +} From dc692e6f1500b124fc023eeb09b2a076815ff9ea Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:52:14 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat(event):=20=EC=9D=B8=EA=B8=B0=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EA=B4=80=EB=A0=A8=20=ED=82=A4=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 --- .../domaincoreredis/common/util/RedisKeyUtils.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java index fd05a3cb..7c584af4 100644 --- a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java @@ -1,11 +1,19 @@ package com.nowait.domaincoreredis.common.util; +import java.time.format.DateTimeFormatter; + public class RedisKeyUtils { + // Store rank keys private static final String KEY_CURRENT = "nowait:store:rank:current"; private static final String KEY_PREVIOUS = "nowait:store:rank:previous"; private static final String KEY_NEXT = "nowait:store:rank:next"; + // Menu rank keys + private static final String KEY_FMT = "popular:%d:%s"; + private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyyMMdd"); + + private RedisKeyUtils() { throw new UnsupportedOperationException("유틸리티 서비스는 인스턴스화 할 수 없습니다."); } @@ -21,4 +29,8 @@ public static String buildPreviousKey() { public static String buildNextKey() { return KEY_NEXT; } + + public static String buildMenuKey() { return KEY_FMT; } + + public static DateTimeFormatter buildMenuDateKey() { return DTF; } } From 45fd931740176a8e0a53746c92c02a4097b8fe6d Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:52:21 +0900 Subject: [PATCH 07/15] =?UTF-8?q?feat(event):=20=EC=9D=B8=EA=B8=B0=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EA=B4=80=EB=A0=A8=20=EC=97=94=EB=93=9C=20?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/StatisticsController.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 0ad9f0e2..2fea19c0 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 @@ -10,7 +10,9 @@ import org.springframework.web.bind.annotation.RestController; import com.nowait.applicationadmin.order.service.OrderService; +import com.nowait.applicationadmin.statistic.dto.PopularMenuDto; import com.nowait.applicationadmin.statistic.dto.StoreRankingDto; +import com.nowait.applicationadmin.statistic.service.PopularMenuRedisService; import com.nowait.applicationadmin.statistic.service.RankingService; import com.nowait.common.api.ApiUtils; import com.nowait.domainadminrdb.statistic.dto.OrderSalesSumDetail; @@ -29,6 +31,7 @@ public class StatisticsController { private final OrderService orderService; private final RankingService rankingService; + private final PopularMenuRedisService popularMenuRedisService; @GetMapping("/sales") @Operation(summary = "오늘의 매출 조회", description = "오늘의 매출을 조회합니다.") @@ -58,4 +61,20 @@ public ResponseEntity getTopSalesStores(@AuthenticationPrincipal MemberDetail ) ); } + + @GetMapping("/popular-menu") + @Operation(summary = "인기 메뉴 조회", description = "인기 메뉴를 조회합니다.") + @ApiResponse(responseCode = "200", description = "인기 메뉴 조회 성공") + public ResponseEntity getPopularMenu(@AuthenticationPrincipal MemberDetails memberDetails) { + + List popularMenu = popularMenuRedisService.getTodayTop5(memberDetails); + + return ResponseEntity + .status(HttpStatus.OK) + .body( + ApiUtils.success( + popularMenu + ) + ); + } } From 31468eb4b9af8e3fddc89d1bdd3b3f3a741ea9b4 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:52:45 +0900 Subject: [PATCH 08/15] =?UTF-8?q?refactor(Statistics):=20StoreRankingDto?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EA=B0=84=EA=B2=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../applicationadmin/statistic/dto/StoreRankingDto.java | 6 +++--- 1 file changed, 3 insertions(+), 3 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 0c40c84f..a32d6162 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 @@ -4,12 +4,12 @@ @Getter public class StoreRankingDto { - private final Long storeId; + private final Long storeId; private final String storeName; - private final Long departmentId; + private final Long departmentId; private final String departmentName; private final Integer totalSales; - private final Long currentRank; + private final Long currentRank; private final Integer delta; private final String profileUrl; From e5adca2ec16c3aea9e6b1590d4ca66ccd6e46ce2 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 00:53:16 +0900 Subject: [PATCH 09/15] =?UTF-8?q?refactor(Order):=20=EC=A3=BC=EB=AC=B8=20C?= =?UTF-8?q?OOKED=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20Redis?= =?UTF-8?q?=EC=97=90=20=EC=A0=80=EC=9E=A5=ED=95=98=EB=8A=94=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=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 | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 9f808caa..b2f8c701 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 @@ -3,6 +3,7 @@ import java.util.List; import java.util.stream.Collectors; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,6 +25,7 @@ import com.nowait.domaincorerdb.user.entity.User; import com.nowait.domaincorerdb.user.exception.UserNotFoundException; import com.nowait.domaincorerdb.user.repository.UserRepository; +import com.nowait.nowaitevent.order.event.CookingCompleteEvent; import lombok.RequiredArgsConstructor; @@ -34,6 +36,7 @@ public class OrderService { private final StatisticCustomRepository statisticCustomRepository; private final UserRepository userRepository; private final StoreRepository storeRepository; + private final ApplicationEventPublisher publisher; @Transactional(readOnly = true) public List findAllOrders(Long storeId, MemberDetails memberDetails) { @@ -57,6 +60,23 @@ public OrderStatusUpdateResponseDto updateOrderStatus(Long orderId, OrderStatus throw new OrderUpdateUnauthorizedException(); } userOrder.updateStatus(newStatus); + + if (OrderStatus.COOKED.equals(newStatus)) { + List items = userOrder.getOrderItems().stream() + .map(item -> new CookingCompleteEvent.Item( + item.getMenu().getId(), + item.getQuantity() + )) + .toList(); + + publisher.publishEvent( + new CookingCompleteEvent( + userOrder.getStore().getStoreId(), + items + ) + ); + } + return OrderStatusUpdateResponseDto.fromEntity(userOrder); } From 8d053723979c950be80491921b55341633d42886 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 01:08:05 +0900 Subject: [PATCH 10/15] =?UTF-8?q?refactor(Statistics):=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nowaitevent/order/event/CookingCompleteEvent.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nowait-event/src/main/java/com/nowait/nowaitevent/order/event/CookingCompleteEvent.java b/nowait-event/src/main/java/com/nowait/nowaitevent/order/event/CookingCompleteEvent.java index 0b1a16b2..4db8b75d 100644 --- a/nowait-event/src/main/java/com/nowait/nowaitevent/order/event/CookingCompleteEvent.java +++ b/nowait-event/src/main/java/com/nowait/nowaitevent/order/event/CookingCompleteEvent.java @@ -2,19 +2,28 @@ import java.util.List; +import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor public class CookingCompleteEvent { + + @NotNull private final Long storeId; + + @NotNull private final List items; @Getter @RequiredArgsConstructor public static class Item { + + @NotNull private final Long menuId; + + @NotNull private final int quantity; } } From 619d8ae1ae74160aa28e0973f0369a4d63249d99 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 01:08:14 +0900 Subject: [PATCH 11/15] =?UTF-8?q?fix(Statistics):=20N=20+=201=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/PopularMenuRedisServiceImpl.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/PopularMenuRedisServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/PopularMenuRedisServiceImpl.java index 9d922992..01119cbb 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/PopularMenuRedisServiceImpl.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/statistic/service/impl/PopularMenuRedisServiceImpl.java @@ -1,7 +1,9 @@ package com.nowait.applicationadmin.statistic.service.impl; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Service; @@ -10,6 +12,7 @@ import com.nowait.applicationadmin.statistic.service.PopularMenuRedisService; import com.nowait.common.enums.Role; import com.nowait.domainadminrdb.statistic.exception.StatisticViewUnauthorizedException; +import com.nowait.domaincorerdb.menu.entity.Menu; import com.nowait.domaincorerdb.menu.repository.MenuRepository; import com.nowait.domaincorerdb.user.entity.MemberDetails; import com.nowait.domaincorerdb.user.entity.User; @@ -39,12 +42,22 @@ public List getTodayTop5(MemberDetails memberDetails) { Set> tuples = menuCounterService.getTopMenus(storeId, 5); + List menuIds = tuples.stream() + .map(tuple -> Long.parseLong(tuple.getValue())) + .toList(); + + Map menuIdToNameMap = menuRepository.findAllById(menuIds) + .stream() + .collect(Collectors.toMap( + Menu::getId, + Menu::getName + )); + + return tuples.stream() .map(tuple -> { Long menuId = Long.parseLong(tuple.getValue()); - String menuName = menuRepository.findById(menuId) - .map(menu -> menu.getName()) - .orElse("Unknown Menu"); + String menuName = menuIdToNameMap.getOrDefault(menuId, "Unknown Menu"); return new PopularMenuDto(menuId, menuName, tuple.getScore().longValue()); }) From f865597fa259e2bc4f8b937c10d076c8681a5ed4 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 01:15:07 +0900 Subject: [PATCH 12/15] =?UTF-8?q?feat(Statistics):=20MenuCounter=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/nowait/common/exception/ErrorMessage.java | 1 + .../rank/exception/MenuCounterUpdateException.java | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/exception/MenuCounterUpdateException.java diff --git a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java index fa83423b..db722455 100644 --- a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java +++ b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java @@ -60,6 +60,7 @@ public enum ErrorMessage { // Statistics STATISTIC_VIEW_UNAUTHORIZED("통계 보기 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "statistics001"), + MENU_COUNTER_UPDATE("메뉴 카운터 업데이트 실패", "statistics002"), // image IMAGE_FILE_EMPTY("이미지 파일을 업로드 해주세요", "image001"), diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/exception/MenuCounterUpdateException.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/exception/MenuCounterUpdateException.java new file mode 100644 index 00000000..071c0b33 --- /dev/null +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/exception/MenuCounterUpdateException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincoreredis.rank.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class MenuCounterUpdateException extends RuntimeException { + public MenuCounterUpdateException(String message) { + super(ErrorMessage.MENU_COUNTER_UPDATE.getMessage()); + } +} From b8ceeb1606dc0a7a876f6da8495b1b6ae217a570 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 01:15:30 +0900 Subject: [PATCH 13/15] =?UTF-8?q?refactor(Statistics):=20TTL=20=EB=8D=94?= =?UTF-8?q?=20=EC=95=88=EC=A0=84=ED=95=9C=20=EB=B0=A9=EC=8B=9D=EC=9C=BC?= =?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 --- .../rank/service/MenuCounterServiceImpl.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java index c49366b7..b286b637 100644 --- a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java @@ -12,11 +12,14 @@ import org.springframework.stereotype.Service; import com.nowait.domaincoreredis.common.util.RedisKeyUtils; +import com.nowait.domaincoreredis.rank.exception.MenuCounterUpdateException; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Service @RequiredArgsConstructor +@Slf4j public class MenuCounterServiceImpl implements MenuCounterService { private final String KEY_FMT = RedisKeyUtils.buildMenuKey(); @@ -26,19 +29,25 @@ public class MenuCounterServiceImpl implements MenuCounterService { @Override public void incrementMenuCounter(Long menuId, Long storeId, int qty) { - String date = LocalDate.now().format(DTF); - String key = String.format(KEY_FMT, storeId, date); + try { + String date = LocalDate.now().format(DTF); + String key = String.format(KEY_FMT, storeId, date); + + redis.opsForZSet().incrementScore(key, menuId.toString(), qty); - redis.opsForZSet().incrementScore(key, menuId.toString(), qty); + Long expirationTime = redis.getExpire(key); + if (expirationTime == null || expirationTime < 0) { + long secondsUntilMidnight = Duration.between( + LocalDateTime.now(), + LocalDate.now().plusDays(1).atStartOfDay() + ).getSeconds(); - Long expirationTime = redis.getExpire(key); - if (expirationTime == null || expirationTime < 0) { - long secondsUntilMidnight = Duration.between( - LocalDateTime.now(), - LocalDate.now().plusDays(1).atStartOfDay() - ).getSeconds(); + redis.expire(key, Duration.ofSeconds(secondsUntilMidnight)); + } - redis.expire(key, secondsUntilMidnight, TimeUnit.SECONDS); + } catch (Exception e) { + log.error("Failed to increment menu counter for menuId: {}, storeId: {}", + menuId, storeId, e); + throw new MenuCounterUpdateException("메뉴 카운터 업데이트 실패"); } } From 704d39ed4309e0d69cde0a94219f7849369b9cb6 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 01:17:59 +0900 Subject: [PATCH 14/15] =?UTF-8?q?refactor(Statistics):=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nowait-event/build.gradle | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/nowait-event/build.gradle b/nowait-event/build.gradle index 6d9bc8dc..067d51c3 100644 --- a/nowait-event/build.gradle +++ b/nowait-event/build.gradle @@ -29,29 +29,12 @@ dependencies { api 'org.springframework.boot:spring-boot-starter-data-jpa' api 'jakarta.persistence:jakarta.persistence-api:3.1.0' - // SPRING SECURITY - implementation 'org.springframework.boot:spring-boot-starter-security' - testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-validation' - - - // SWAGGER - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' + testImplementation 'org.springframework.boot:spring-boot-starter-test' compileOnly 'org.projectlombok:lombok:1.18.26' annotationProcessor 'org.projectlombok:lombok:1.18.26' - //QueryDsl - // TODO Q클래스 생성시 오류 발생 해결 필요 - implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' - annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" - annotationProcessor "jakarta.persistence:jakarta.persistence-api" - - // Jackson - api 'com.fasterxml.jackson.core:jackson-databind:2.15.2' - api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2' - testImplementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' } From 3c716de2bec63370e7c50e6e73fdb4a030d6fab7 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 18 Jul 2025 01:22:08 +0900 Subject: [PATCH 15/15] =?UTF-8?q?refactor(Statistics):=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rank/exception/MenuCounterUpdateException.java | 4 +--- .../domaincoreredis/rank/service/MenuCounterServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/exception/MenuCounterUpdateException.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/exception/MenuCounterUpdateException.java index 071c0b33..fc8c13ab 100644 --- a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/exception/MenuCounterUpdateException.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/exception/MenuCounterUpdateException.java @@ -3,7 +3,5 @@ import com.nowait.common.exception.ErrorMessage; public class MenuCounterUpdateException extends RuntimeException { - public MenuCounterUpdateException(String message) { - super(ErrorMessage.MENU_COUNTER_UPDATE.getMessage()); - } + public MenuCounterUpdateException() { super(ErrorMessage.MENU_COUNTER_UPDATE.getMessage()); } } diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java index b286b637..f2ffbce3 100644 --- a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/rank/service/MenuCounterServiceImpl.java @@ -47,7 +47,7 @@ public void incrementMenuCounter(Long menuId, Long storeId, int qty) { } catch (Exception e) { log.error("Failed to increment menu counter for menuId: {}, storeId: {}", + menuId, storeId, e); - throw new MenuCounterUpdateException("메뉴 카운터 업데이트 실패"); + throw new MenuCounterUpdateException(); } }