diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/controller/OrderController.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/controller/OrderController.java index 6c2b845d..16246fbc 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/controller/OrderController.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/controller/OrderController.java @@ -17,6 +17,8 @@ import com.nowait.applicationadmin.order.dto.OrderStatusUpdateResponseDto; import com.nowait.applicationadmin.order.service.OrderService; import com.nowait.common.api.ApiUtils; +import com.nowait.domaincorerdb.order.dto.OrderSalesSumDetail; +import com.nowait.domaincorerdb.order.dto.TopSalesStoresDetail; import com.nowait.domaincorerdb.user.entity.MemberDetails; import io.swagger.v3.oas.annotations.Operation; @@ -31,36 +33,66 @@ @RequiredArgsConstructor public class OrderController { - private final OrderService orderService; + private final OrderService orderService; - @GetMapping("/{storeId}") - @Operation(summary = "주점별 주문리스트 조회", description = "특정 주점에 대한 예약리스트 조회") - @ApiResponse(responseCode = "200", description = "주리스트 조회") - public ResponseEntity getOrderListByStoreId(@PathVariable Long storeId, - @AuthenticationPrincipal MemberDetails memberDetails) { - List response = orderService.findAllOrders(storeId,memberDetails); - return ResponseEntity - .status(HttpStatus.OK) - .body( - ApiUtils.success( - response - ) - ); - } + @GetMapping("/{storeId}") + @Operation(summary = "주점별 주문리스트 조회", description = "특정 주점에 대한 예약리스트 조회") + @ApiResponse(responseCode = "200", description = "주리스트 조회") + public ResponseEntity getOrderListByStoreId(@PathVariable Long storeId, + @AuthenticationPrincipal MemberDetails memberDetails) { + List response = orderService.findAllOrders(storeId, memberDetails); + return ResponseEntity + .status(HttpStatus.OK) + .body( + ApiUtils.success( + response + ) + ); + } - @PatchMapping("/status/{orderId}") - @Operation(summary = "주문 상태 변경", description = "특정 주문의 상태를 변경.") - @ApiResponse(responseCode = "200", description = "주문 상태 변경 성공") - @ApiResponse(responseCode = "400", description = "주문을 찾을 수 없음") - public ResponseEntity updateOrderStatus( - @PathVariable Long orderId, - @RequestBody@Valid OrderStatusUpdateRequestDto requestDto, - @AuthenticationPrincipal MemberDetails memberDetails - ) { - OrderStatusUpdateResponseDto response = orderService.updateOrderStatus( - orderId,requestDto.getOrderStatus(),memberDetails); - return ResponseEntity - .status(HttpStatus.OK) - .body(ApiUtils.success(response)); - } + @PatchMapping("/status/{orderId}") + @Operation(summary = "주문 상태 변경", description = "특정 주문의 상태를 변경.") + @ApiResponse(responseCode = "200", description = "주문 상태 변경 성공") + @ApiResponse(responseCode = "400", description = "주문을 찾을 수 없음") + public ResponseEntity updateOrderStatus( + @PathVariable Long orderId, + @RequestBody @Valid OrderStatusUpdateRequestDto requestDto, + @AuthenticationPrincipal MemberDetails memberDetails + ) { + OrderStatusUpdateResponseDto response = orderService.updateOrderStatus( + orderId, requestDto.getOrderStatus(), memberDetails); + return ResponseEntity + .status(HttpStatus.OK) + .body(ApiUtils.success(response)); + } + + @GetMapping("/sales") + @Operation(summary = "오늘의 매출 조회", description = "오늘의 매출을 조회합니다.") + @ApiResponse(responseCode = "200", description = "오늘의 매출 조회 성공") + public ResponseEntity getTodaySales(@AuthenticationPrincipal MemberDetails memberDetails) { + OrderSalesSumDetail sales = orderService.getSaleSumByStoreId(memberDetails); + + return ResponseEntity + .status(HttpStatus.OK) + .body( + ApiUtils.success( + sales + ) + ); + } + + @GetMapping("/top-sales") + @Operation(summary = "오늘의 매출 상위 5개 주점 조회", description = "오늘의 매출이 가장 높은 상위 5개 주점을 조회합니다.") + @ApiResponse(responseCode = "200", description = "오늘의 매출 상위 5개 주점 조회 성공") + public ResponseEntity getTopSalesStores(@AuthenticationPrincipal MemberDetails memberDetails) { + List topSalesStoresDetail = orderService.getTop5StoresBySalesToday(memberDetails); + + return ResponseEntity + .status(HttpStatus.OK) + .body( + ApiUtils.success( + topSalesStoresDetail + ) + ); + } } 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 19ec26c7..c0d3db14 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 @@ -9,6 +9,8 @@ import com.nowait.applicationadmin.order.dto.OrderResponseDto; import com.nowait.applicationadmin.order.dto.OrderStatusUpdateResponseDto; import com.nowait.common.enums.Role; +import com.nowait.domaincorerdb.order.dto.OrderSalesSumDetail; +import com.nowait.domaincorerdb.order.dto.TopSalesStoresDetail; import com.nowait.domaincorerdb.order.entity.OrderStatus; import com.nowait.domaincorerdb.order.entity.UserOrder; import com.nowait.domaincorerdb.order.exception.OrderNotFoundException; @@ -33,13 +35,13 @@ public class OrderService { @Transactional(readOnly = true) public List findAllOrders(Long storeId, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - storeRepository.findByStoreIdAndDeletedFalse(storeId) - .orElseThrow(StoreNotFoundException::new); + User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + storeRepository.findByStoreIdAndDeletedFalse(storeId).orElseThrow(StoreNotFoundException::new); if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { throw new OrderViewUnauthorizedException(); } - return orderRepository.findAllByStore_StoreId(storeId).stream() + return orderRepository.findAllByStore_StoreId(storeId) + .stream() .map(OrderResponseDto::fromEntity) .collect(Collectors.toList()); } @@ -47,13 +49,36 @@ public List findAllOrders(Long storeId, MemberDetails memberDe @Transactional public OrderStatusUpdateResponseDto updateOrderStatus(Long orderId, OrderStatus newStatus, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - UserOrder userOrder = orderRepository.findById(orderId) - .orElseThrow(OrderNotFoundException::new); + User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + UserOrder userOrder = orderRepository.findById(orderId).orElseThrow(OrderNotFoundException::new); if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(userOrder.getStore().getStoreId())) { throw new OrderUpdateUnauthorizedException(); } userOrder.updateStatus(newStatus); return OrderStatusUpdateResponseDto.fromEntity(userOrder); } + + @Transactional(readOnly = true) + public OrderSalesSumDetail getSaleSumByStoreId(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 OrderViewUnauthorizedException(); + } + + return orderRepository.findSalesSumByStoreId(storeId); + } + + @Transactional(readOnly = true) + public List getTop5StoresBySalesToday(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 OrderViewUnauthorizedException(); + } + + return orderRepository.getTop4PlusMine(storeId); + } } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/controller/StorePaymentController.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/controller/StorePaymentController.java index df8bc44a..82c1784a 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/controller/StorePaymentController.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/controller/StorePaymentController.java @@ -1,4 +1,4 @@ -package com.nowait.applicationadmin.storePayment.controller; +package com.nowait.applicationadmin.storepayment.controller; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -10,10 +10,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentCreateRequest; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentCreateResponse; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentUpdateRequest; -import com.nowait.applicationadmin.storePayment.service.StorePaymentService; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentCreateRequest; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentCreateResponse; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentUpdateRequest; +import com.nowait.applicationadmin.storepayment.service.StorePaymentService; import com.nowait.common.api.ApiUtils; import com.nowait.domaincorerdb.user.entity.MemberDetails; diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentCreateRequest.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentCreateRequest.java index 4a292ce3..13801ccb 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentCreateRequest.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentCreateRequest.java @@ -1,4 +1,5 @@ -package com.nowait.applicationadmin.storePayment.dto; +package com.nowait.applicationadmin.storepayment.dto; + import com.nowait.domaincorerdb.storepayment.entity.StorePayment; diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentCreateResponse.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentCreateResponse.java index 250a6b1f..47bead90 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentCreateResponse.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentCreateResponse.java @@ -1,4 +1,4 @@ -package com.nowait.applicationadmin.storePayment.dto; +package com.nowait.applicationadmin.storepayment.dto; import java.time.LocalDateTime; diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentReadDto.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentReadDto.java index 329d799a..04f2e0f6 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentReadDto.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentReadDto.java @@ -1,4 +1,4 @@ -package com.nowait.applicationadmin.storePayment.dto; +package com.nowait.applicationadmin.storepayment.dto; import java.time.LocalDateTime; diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentUpdateRequest.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentUpdateRequest.java index 71f4c979..50320aab 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentUpdateRequest.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/dto/StorePaymentUpdateRequest.java @@ -1,4 +1,4 @@ -package com.nowait.applicationadmin.storePayment.dto; +package com.nowait.applicationadmin.storepayment.dto; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentService.java index b0324dab..4cf3c76f 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentService.java @@ -1,9 +1,9 @@ -package com.nowait.applicationadmin.storePayment.service; +package com.nowait.applicationadmin.storepayment.service; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentCreateRequest; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentCreateResponse; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentReadDto; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentUpdateRequest; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentCreateRequest; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentCreateResponse; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentReadDto; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentUpdateRequest; import com.nowait.domaincorerdb.user.entity.MemberDetails; public interface StorePaymentService { diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentServiceImpl.java index 5eb6fbca..ff95015d 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentServiceImpl.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentServiceImpl.java @@ -1,12 +1,12 @@ -package com.nowait.applicationadmin.storePayment.service; +package com.nowait.applicationadmin.storepayment.service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentCreateRequest; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentCreateResponse; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentReadDto; -import com.nowait.applicationadmin.storePayment.dto.StorePaymentUpdateRequest; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentCreateRequest; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentCreateResponse; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentReadDto; +import com.nowait.applicationadmin.storepayment.dto.StorePaymentUpdateRequest; import com.nowait.common.enums.Role; import com.nowait.domaincorerdb.storepayment.entity.StorePayment; import com.nowait.domaincorerdb.storepayment.exception.StorePaymentAlreadyExistsException; diff --git a/nowait-common/src/main/java/com/nowait/common/api/ApiResult.java b/nowait-common/src/main/java/com/nowait/common/api/ApiResult.java index 73509f56..17247f42 100644 --- a/nowait-common/src/main/java/com/nowait/common/api/ApiResult.java +++ b/nowait-common/src/main/java/com/nowait/common/api/ApiResult.java @@ -19,7 +19,5 @@ public T getResponse() { return response; } - public ApiError getError() { - return error; - } + // public ApiError getError() { return success ? null : error; } } diff --git a/nowait-domain/domain-core-rdb/build.gradle b/nowait-domain/domain-core-rdb/build.gradle index 01bc023b..daa8ad3d 100644 --- a/nowait-domain/domain-core-rdb/build.gradle +++ b/nowait-domain/domain-core-rdb/build.gradle @@ -31,6 +31,12 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-validation' + //QueryDsl + 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" + // SWAGGER implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' @@ -38,7 +44,7 @@ dependencies { annotationProcessor 'org.projectlombok:lombok:1.18.26' // Jackson - api 'com.fasterxml.jackson.core:jackson-databind:2.15.2' // 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' diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/config/QueryDslConfig.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/config/QueryDslConfig.java new file mode 100644 index 00000000..f75d497b --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/config/QueryDslConfig.java @@ -0,0 +1,20 @@ +package com.nowait.domaincorerdb.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +@Configuration +public class QueryDslConfig { + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/department/entity/Department.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/department/entity/Department.java new file mode 100644 index 00000000..51c54577 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/department/entity/Department.java @@ -0,0 +1,24 @@ +package com.nowait.domaincorerdb.department.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "departments") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Department { + + @Id + private Long id; + + @Column(nullable = false) + private String name; +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/dto/OrderSalesSumDetail.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/dto/OrderSalesSumDetail.java new file mode 100644 index 00000000..fe3b25ee --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/dto/OrderSalesSumDetail.java @@ -0,0 +1,21 @@ +package com.nowait.domaincorerdb.order.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class OrderSalesSumDetail { + private Long storeId; + private Integer todaySalesSum; + private Integer yesterdaySalesSum; + private Integer cumulativeSalesBeforeYesterday; + + public OrderSalesSumDetail(Long storeId, Integer todaySalesSum, Integer yesterdaySalesSum, + Integer cumulativeSalesBeforeYesterday) { + this.storeId = storeId; + this.todaySalesSum = todaySalesSum; + this.yesterdaySalesSum = yesterdaySalesSum; + this.cumulativeSalesBeforeYesterday = cumulativeSalesBeforeYesterday; + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/dto/TopSalesStoresDetail.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/dto/TopSalesStoresDetail.java new file mode 100644 index 00000000..acc0d2d3 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/dto/TopSalesStoresDetail.java @@ -0,0 +1,25 @@ +package com.nowait.domaincorerdb.order.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class TopSalesStoresDetail { + private Long storeId; + private String storeName; + private Long departmentId; + private String departmentName; + private Integer totalSales; + private Long storeRank; + + public TopSalesStoresDetail(Long storeId, String storeName, Long departmentId, String departmentName, Integer totalSales, + Long storeRank) { + this.storeId = storeId; + this.storeName = storeName; + this.departmentId = departmentId; + this.departmentName = departmentName; + this.totalSales = totalSales; + this.storeRank = storeRank; + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderCustomRepository.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderCustomRepository.java new file mode 100644 index 00000000..77d2747d --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderCustomRepository.java @@ -0,0 +1,13 @@ +package com.nowait.domaincorerdb.order.repository; + +import java.util.List; + +import com.nowait.domaincorerdb.order.dto.OrderSalesSumDetail; +import com.nowait.domaincorerdb.order.dto.TopSalesStoresDetail; + +public interface OrderCustomRepository { + + OrderSalesSumDetail findSalesSumByStoreId(Long storeId); + + List getTop4PlusMine(Long storeId); +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderCustomRepositoryImpl.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderCustomRepositoryImpl.java new file mode 100644 index 00000000..341940bb --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderCustomRepositoryImpl.java @@ -0,0 +1,258 @@ +package com.nowait.domaincorerdb.order.repository; + +import static com.nowait.domaincorerdb.order.entity.OrderStatus.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.nowait.domaincorerdb.department.entity.QDepartment; +import com.nowait.domaincorerdb.order.dto.OrderSalesSumDetail; +import com.nowait.domaincorerdb.order.dto.TopSalesStoresDetail; +import com.nowait.domaincorerdb.order.entity.QUserOrder; +import com.nowait.domaincorerdb.store.entity.QStore; +import com.querydsl.core.Tuple; +import com.querydsl.jpa.impl.JPAQueryFactory; + +public class OrderCustomRepositoryImpl implements OrderCustomRepository { + + private final JPAQueryFactory queryFactory; + + public OrderCustomRepositoryImpl(JPAQueryFactory queryFactory) { + this.queryFactory = queryFactory; + } + + private static final QUserOrder u = QUserOrder.userOrder; + private static final QStore s = QStore.store; + private static final QDepartment d = QDepartment.department; + + @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(); + + // 2. 오늘 매출 합산 + Integer todaySum = queryFactory + .select(u.totalPrice.sum()) + .from(u) + .where( + u.store.storeId.eq(storeId), + u.createdAt.goe(todayStart), + u.createdAt.lt(todayEnd), + u.status.eq(COOKED) + ) + .fetchOne(); + + // 3. 어제 매출 합산 + Integer yesterdaySum = queryFactory + .select(u.totalPrice.sum()) + .from(u) + .where( + u.store.storeId.eq(storeId), + u.createdAt.goe(yesterdayStart), + u.createdAt.lt(yesterdayEnd), + u.status.eq(COOKED) + ) + .fetchOne(); + + Integer cumulativeSalesBeforeYesterday = queryFactory + .select(u.totalPrice.sum()) + .from(u) + .where( + u.store.storeId.eq(storeId), + u.createdAt.lt(yesterdayEnd), + u.status.eq(COOKED) + ) + .fetchOne(); + + // null 방어 처리 + if (todaySum == null) + todaySum = 0; + if (yesterdaySum == null) + yesterdaySum = 0; + if (cumulativeSalesBeforeYesterday == null) + cumulativeSalesBeforeYesterday = 0; + + // 4. 응답 객체 생성 + return new OrderSalesSumDetail(storeId, todaySum, yesterdaySum, cumulativeSalesBeforeYesterday); + } + + @Override + public List getTop4PlusMine(Long userStoreId) { + LocalDate today = LocalDate.now(); + LocalDateTime todayStart = today.atStartOfDay(); + LocalDateTime todayEnd = today.plusDays(1).atStartOfDay(); // 내일 00:00:00 + + // 1. 전체 주점 및 순위 맵 + List allStores = getAllStores(todayStart, todayEnd); + Map rankMap = getRankMap(allStores); + + // 2. 내 주점의 순위가 5위 이하인지 확인 + Integer userRank = rankMap.get(userStoreId); + + // 3. 상위 5개 주점 조회 (내 주점 포함 여부는 나중에 확인) + List result = getTopNStores(allStores, rankMap, 5); + Set already = result.stream() + .map(TopSalesStoresDetail::getStoreId) + .collect(Collectors.toSet()); + + // 4. 내 주점 추가 (내 주점이 포함되지 않으면 추가) + TopSalesStoresDetail myStore = getMyStoreDetail(allStores, rankMap, userStoreId); + if (myStore == null) + myStore = getZeroSalesStore(userStoreId); + + if (myStore != null && !already.contains(userStoreId)) { + // 내 주점이 5위 이하로 밀린 경우 + if (userRank > 5) { + result = result.stream() + .sorted(Comparator.comparing(TopSalesStoresDetail::getStoreRank, + Comparator.nullsLast(Comparator.naturalOrder()))) + .limit(4) + .collect(Collectors.toList()); + // 5위 자리에 내 주점 추가하고 나머지 4개만 보여줌 + result.add(myStore); + } + } + + // 5. 결과가 6개가 되지 않도록 자르고 5개로 제한 + result = result.stream() + .sorted(Comparator.comparing(TopSalesStoresDetail::getStoreRank, + Comparator.nullsLast(Comparator.naturalOrder()))) + .collect(Collectors.toList()); + + return result; + } + + private List getAllStores(LocalDateTime todayStart, LocalDateTime todayEnd) { + return queryFactory + .select(u.store.storeId, u.store.name, u.store.departmentId, u.totalPrice.sum()) + .from(u) + .where( + u.createdAt.goe(todayStart), + u.createdAt.lt(todayEnd), + u.status.eq(COOKED) + ) + .groupBy(u.store.storeId, u.store.name, u.store.departmentId) + .orderBy(u.totalPrice.sum().desc()) + .fetch(); + } + + private Map getRankMap(List allStores) { + Map rankMap = new HashMap<>(); + int rank = 1; + for (Tuple t : allStores) { + rankMap.put(t.get(u.store.storeId), rank++); + } + return rankMap; + } + + private List getTopNStores(List allStores, Map rankMap, int n) { + // 1. departmentId 목록을 추출 + Set departmentIds = allStores.stream() + .map(t -> t.get(u.store.departmentId)) + .collect(Collectors.toSet()); + + // 2. departmentId에 대한 학과 이름을 한번에 가져오기 + Map departmentNameMap = getDepartmentNames(departmentIds); + + List list = new ArrayList<>(); + for (int i = 0; i < Math.min(n, allStores.size()); i++) { + Tuple t = allStores.get(i); + Long storeId = t.get(u.store.storeId); + String storeName = t.get(u.store.name); + Long departmentId = t.get(u.store.departmentId); + + // 학과 이름 가져오기 + String departmentName = departmentNameMap.getOrDefault(departmentId, "Unknown"); + + list.add(new TopSalesStoresDetail( + storeId, + storeName, + t.get(u.store.departmentId), + departmentName, + t.get(u.totalPrice.sum()), + rankMap.get(storeId).longValue() + )); + } + return list; + } + + private TopSalesStoresDetail getMyStoreDetail(List allStores, Map rankMap, Long userStoreId) { + Tuple myStore = allStores.stream() + .filter(t -> t.get(u.store.storeId).equals(userStoreId)) + .findFirst() + .orElse(null); + + if (myStore == null) + return null; + + Long departmentId = myStore.get(u.store.departmentId); + + // departmentId에 대한 학과 이름을 한번에 가져오기 + Map departmentNameMap = getDepartmentNames(Set.of(departmentId)); + String departmentName = departmentNameMap.getOrDefault(departmentId, "Unknown"); + + Long storeId = myStore.get(u.store.storeId); + return new TopSalesStoresDetail( + storeId, + myStore.get(u.store.name), + myStore.get(u.store.departmentId), + departmentName, + myStore.get(u.totalPrice.sum()), + rankMap.get(storeId).longValue() + ); + } + + private TopSalesStoresDetail getZeroSalesStore(Long userStoreId) { + Tuple zeroTuple = queryFactory + .select(s.storeId, s.name, s.departmentId) + .from(s) + .where(s.storeId.eq(userStoreId)) + .fetchOne(); + + if (zeroTuple == null) + return null; + + Long departmentId = zeroTuple.get(u.store.departmentId); + // departmentId에 대한 학과 이름을 한번에 가져오기 + Map departmentNameMap = getDepartmentNames(Set.of(departmentId)); + String departmentName = departmentNameMap.getOrDefault(departmentId, "Unknown"); + + return new TopSalesStoresDetail( + zeroTuple.get(s.storeId), + zeroTuple.get(s.name), + zeroTuple.get(s.departmentId), + departmentName, + 0, + null + ); + } + + // 한 번에 학과 이름을 가져오는 메서드 + private Map getDepartmentNames(Set departmentIds) { + List departmentTuples = queryFactory + .select(d.id, d.name) + .from(d) + .where(d.id.in(departmentIds)) + .fetch(); + + Map departmentNameMap = new HashMap<>(); + for (Tuple t : departmentTuples) { + departmentNameMap.put(t.get(d.id), t.get(d.name)); + } + return departmentNameMap; + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderRepository.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderRepository.java index 96db42c4..54c2b7b6 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderRepository.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/repository/OrderRepository.java @@ -7,12 +7,12 @@ import org.springframework.stereotype.Repository; import com.nowait.domaincorerdb.order.entity.UserOrder; + @Repository -public interface OrderRepository extends JpaRepository { +public interface OrderRepository extends JpaRepository, OrderCustomRepository { boolean existsBySignatureAndCreatedAtAfter(String signature, LocalDateTime createdAt); List findByStore_StoreIdAndTableIdAndSessionId(Long storeId, Long tableId, String sessionId); - List findAllByStore_StoreId(Long storeId); - + List findAllByStore_StoreId(Long storeId); } diff --git a/nowait-domain/domain-redis/build.gradle b/nowait-domain/domain-redis/build.gradle new file mode 100644 index 00000000..8a6470e6 --- /dev/null +++ b/nowait-domain/domain-redis/build.gradle @@ -0,0 +1,36 @@ +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 'org.springframework.boot:spring-boot-starter-data-redis' + + // 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' + + testImplementation 'org.junit.jupiter:junit-jupiter' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' +} diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/config/RedisConfig.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/config/RedisConfig.java new file mode 100644 index 00000000..57e8b919 --- /dev/null +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/config/RedisConfig.java @@ -0,0 +1,40 @@ +package com.nowait.domaincoreredis.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new StringRedisSerializer()); + + redisTemplate.setDefaultSerializer(new StringRedisSerializer()); + + return redisTemplate; + } +} diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/service/StoreRankCacheService.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/service/StoreRankCacheService.java new file mode 100644 index 00000000..444aedcf --- /dev/null +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/service/StoreRankCacheService.java @@ -0,0 +1,10 @@ +package com.nowait.domaincoreredis.service; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +@Service +public class StoreRankCacheService { + + private RedisTemplate redisTemplate; +} diff --git a/settings.gradle b/settings.gradle index 24f20718..89991c90 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,4 +6,5 @@ include 'nowait-common' 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-infra'