diff --git a/src/main/java/com/example/repick/domain/clothingSales/api/ClothingSalesController.java b/src/main/java/com/example/repick/domain/clothingSales/api/ClothingSalesController.java index a9494a0d..1832e225 100644 --- a/src/main/java/com/example/repick/domain/clothingSales/api/ClothingSalesController.java +++ b/src/main/java/com/example/repick/domain/clothingSales/api/ClothingSalesController.java @@ -128,5 +128,18 @@ public SuccessResponse getClothingSalesUserInfo(@PathVaria return SuccessResponse.success(clothingSalesService.getClothingSalesUser(clothingSalesId)); } + @Operation(summary = "어드민 대시보드 - 옷장 정리 현황", + description = "최근 1개월 기준") + @GetMapping("/count") + public SuccessResponse getClothingSalesCount() { + return SuccessResponse.success(clothingSalesService.getClothingSalesCount()); + } + + @Operation(summary = "어드민 대시보드 - 오늘의 현황") + @GetMapping("/today") + public SuccessResponse getCountToday() { + return SuccessResponse.success(clothingSalesService.getCountToday()); + } + } diff --git a/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesAndProductOrderCount.java b/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesAndProductOrderCount.java new file mode 100644 index 00000000..63e8e1a7 --- /dev/null +++ b/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesAndProductOrderCount.java @@ -0,0 +1,13 @@ +package com.example.repick.domain.clothingSales.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GetClothingSalesAndProductOrderCount( + @Schema(name = "수거 신청 수") int collectionRequestCount, + @Schema(name = "결제 완료 수") int paymentCompleteCount, + @Schema(name = "반품 요청 수") int returnRequestCount +) { + public static GetClothingSalesAndProductOrderCount of(int collectionRequestCount, int paymentCompleteCount, int returnRequestCount) { + return new GetClothingSalesAndProductOrderCount(collectionRequestCount, paymentCompleteCount, returnRequestCount); + } +} diff --git a/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesCount.java b/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesCount.java new file mode 100644 index 00000000..f15819e5 --- /dev/null +++ b/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesCount.java @@ -0,0 +1,16 @@ +package com.example.repick.domain.clothingSales.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GetClothingSalesCount( + @Schema(name = "수거 신청 수") long collectionRequestCount, + @Schema(name = "상품화 진행 중 수") long productionProgressCount, + @Schema(name = "상품화 완료 수") long productionCompleteCount, + @Schema(name = "판매중 수") long sellingCount, + @Schema(name = "정산 요청 수") long settlementRequestCount + +) { + public static GetClothingSalesCount of(long collectionRequestCount, long productionProgressCount, long productionCompleteCount, long sellingCount, long settlementRequestCount) { + return new GetClothingSalesCount(collectionRequestCount, productionProgressCount, productionCompleteCount, sellingCount, settlementRequestCount); + } +} diff --git a/src/main/java/com/example/repick/domain/clothingSales/repository/ClothingSalesRepository.java b/src/main/java/com/example/repick/domain/clothingSales/repository/ClothingSalesRepository.java index 40846717..5adf41cf 100644 --- a/src/main/java/com/example/repick/domain/clothingSales/repository/ClothingSalesRepository.java +++ b/src/main/java/com/example/repick/domain/clothingSales/repository/ClothingSalesRepository.java @@ -7,7 +7,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; diff --git a/src/main/java/com/example/repick/domain/clothingSales/repository/ClothingSalesStateRepository.java b/src/main/java/com/example/repick/domain/clothingSales/repository/ClothingSalesStateRepository.java index 8e9a9098..57e6f944 100644 --- a/src/main/java/com/example/repick/domain/clothingSales/repository/ClothingSalesStateRepository.java +++ b/src/main/java/com/example/repick/domain/clothingSales/repository/ClothingSalesStateRepository.java @@ -4,10 +4,14 @@ import com.example.repick.domain.clothingSales.entity.ClothingSalesStateType; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; import java.util.List; public interface ClothingSalesStateRepository extends JpaRepository{ List findByClothingSalesId(Long clothingSalesId); boolean existsByClothingSalesIdAndClothingSalesStateType(Long id, ClothingSalesStateType bagDelivered); + List findByCreatedDateAfter(LocalDateTime createdDate); + + int countByClothingSalesStateTypeInAndCreatedDateAfter(List clothingSalesStateTypes, LocalDateTime createdDate); } diff --git a/src/main/java/com/example/repick/domain/clothingSales/service/ClothingSalesService.java b/src/main/java/com/example/repick/domain/clothingSales/service/ClothingSalesService.java index 8067e762..c0eb0dd8 100644 --- a/src/main/java/com/example/repick/domain/clothingSales/service/ClothingSalesService.java +++ b/src/main/java/com/example/repick/domain/clothingSales/service/ClothingSalesService.java @@ -9,6 +9,7 @@ import com.example.repick.domain.clothingSales.validator.ClothingSalesValidator; import com.example.repick.domain.product.entity.Product; import com.example.repick.domain.product.entity.ProductOrder; +import com.example.repick.domain.product.entity.ProductOrderState; import com.example.repick.domain.product.entity.ProductStateType; import com.example.repick.domain.product.repository.ProductOrderRepository; import com.example.repick.domain.product.repository.ProductRepository; @@ -218,5 +219,34 @@ public GetClothingSalesUser getClothingSalesUser(Long clothingSalesId){ return GetClothingSalesUser.of(code, user); } + public GetClothingSalesCount getClothingSalesCount() { + List clothingSalesStateList = clothingSalesStateRepository.findByCreatedDateAfter(LocalDateTime.now().minusMonths(1)); + long collectionRequestCount = 0; + long productionProgressCount = 0; + long productionCompleteCount = 0; + long sellingCount = 0; + long settlementRequestCount = 0; // TODO: 정산 요청 수 (정산금 출금 신청 플로우 구현 후 추가) + for (ClothingSalesState clothingSalesState : clothingSalesStateList) { + switch (clothingSalesState.getClothingSalesStateType()) { + case BOX_COLLECT_REQUEST, BAG_COLLECT_REQUEST -> collectionRequestCount++; + case COLLECTED, SHOOTING, SHOOTED, PRODUCTING -> productionProgressCount++; + case PRODUCTED, PRODUCT_REGISTERED -> productionCompleteCount++; + case SELLING -> sellingCount++; + } + } + return GetClothingSalesCount.of(collectionRequestCount, productionProgressCount, productionCompleteCount, sellingCount, settlementRequestCount); + } + + public GetClothingSalesAndProductOrderCount getCountToday(){ + int collectionRequestCount = clothingSalesStateRepository.countByClothingSalesStateTypeInAndCreatedDateAfter( + List.of(ClothingSalesStateType.BOX_COLLECT_REQUEST, ClothingSalesStateType.BAG_COLLECT_REQUEST), + LocalDate.now().atStartOfDay()); + int paymentCompleteCount = productOrderRepository.countByProductOrderStateAndLastModifiedDateAfter( + ProductOrderState.PAYMENT_COMPLETED, LocalDate.now().atStartOfDay()); + int returnRequestCount = productOrderRepository.countByProductOrderStateAndLastModifiedDateAfter( + ProductOrderState.RETURN_REQUESTED, LocalDate.now().atStartOfDay()); + return GetClothingSalesAndProductOrderCount.of(collectionRequestCount, paymentCompleteCount, returnRequestCount); + } + } diff --git a/src/main/java/com/example/repick/domain/product/api/ProductController.java b/src/main/java/com/example/repick/domain/product/api/ProductController.java index d2194d9e..ad4673a1 100644 --- a/src/main/java/com/example/repick/domain/product/api/ProductController.java +++ b/src/main/java/com/example/repick/domain/product/api/ProductController.java @@ -249,10 +249,8 @@ public SuccessResponse>> getProd """) @GetMapping("/{clothingSalesId}/{productState}") - public SuccessResponse>> getProductsByUserClothingSales(@PathVariable Long clothingSalesId, - @PathVariable String productState, - @ParameterObject PageCondition pageCondition, - @RequestParam(required = false) Boolean isExpired) { + public SuccessResponse>> getProductsByUserClothingSales(@PathVariable Long clothingSalesId, @PathVariable String productState, + @ParameterObject PageCondition pageCondition, @RequestParam(required = false) Boolean isExpired) { return SuccessResponse.success(productService.getProductsByUserClothingSales(clothingSalesId, productState, isExpired, pageCondition)); } diff --git a/src/main/java/com/example/repick/domain/product/api/ProductOrderController.java b/src/main/java/com/example/repick/domain/product/api/ProductOrderController.java index 47d0a901..ff6c8ab8 100644 --- a/src/main/java/com/example/repick/domain/product/api/ProductOrderController.java +++ b/src/main/java/com/example/repick/domain/product/api/ProductOrderController.java @@ -78,4 +78,10 @@ public SuccessResponse updateProductOrderState(@Schema(description = " return SuccessResponse.success(productOrderService.updateProductOrderState(productOrderId, productOrderSateRequest)); } + @Operation(summary = "어드민 대시보드 - 구매 현황", description = "최근 1개월 기준") + @GetMapping("/count") + public SuccessResponse getProductOrderCount() { + return SuccessResponse.success(productOrderService.getProductOrderCount()); + } + } diff --git a/src/main/java/com/example/repick/domain/product/dto/productOrder/GetProductOrderCount.java b/src/main/java/com/example/repick/domain/product/dto/productOrder/GetProductOrderCount.java new file mode 100644 index 00000000..afb18a6c --- /dev/null +++ b/src/main/java/com/example/repick/domain/product/dto/productOrder/GetProductOrderCount.java @@ -0,0 +1,15 @@ +package com.example.repick.domain.product.dto.productOrder; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GetProductOrderCount( + @Schema(name = "구매 요청 수") long paymentCompletedCount, + @Schema(name = "발송 중 수") long shippingCount, + @Schema(name = "구매 확정 대기 수") long confirmWaitCount, + @Schema(name = "구매 확정 수") long confirmedCount, + @Schema(name = "반품 요청 수") long returnRequestCount +) { + public static GetProductOrderCount of(long paymentCompletedCount, long shippingCount, long confirmWaitCount, long confirmedCount, long refundRequestCount) { + return new GetProductOrderCount(paymentCompletedCount, shippingCount, confirmWaitCount, confirmedCount, refundRequestCount); + } +} diff --git a/src/main/java/com/example/repick/domain/product/entity/QualityRate.java b/src/main/java/com/example/repick/domain/product/entity/QualityRate.java index b7d49cf2..5374e71b 100644 --- a/src/main/java/com/example/repick/domain/product/entity/QualityRate.java +++ b/src/main/java/com/example/repick/domain/product/entity/QualityRate.java @@ -2,7 +2,9 @@ import com.example.repick.global.error.exception.CustomException; import com.example.repick.global.error.exception.ErrorCode; +import lombok.Getter; +@Getter public enum QualityRate { S(1, "S"), A(2, "A"), @@ -16,14 +18,6 @@ public enum QualityRate { this.value = value; } - public int getId() { - return id; - } - - public String getValue() { - return value; - } - public static QualityRate fromId(int id) { for (QualityRate qualityRate : values()) { if (qualityRate.getId() == id) { diff --git a/src/main/java/com/example/repick/domain/product/repository/ProductOrderRepository.java b/src/main/java/com/example/repick/domain/product/repository/ProductOrderRepository.java index c3fbc9e7..462f43cd 100644 --- a/src/main/java/com/example/repick/domain/product/repository/ProductOrderRepository.java +++ b/src/main/java/com/example/repick/domain/product/repository/ProductOrderRepository.java @@ -8,6 +8,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import javax.swing.text.html.Option; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -22,4 +23,7 @@ public interface ProductOrderRepository extends JpaRepository findFirstByProductIdOrderByCreatedDateDesc(Long productId); + List findByLastModifiedDateAfter(LocalDateTime localDateTime); + int countByProductOrderStateAndLastModifiedDateAfter(ProductOrderState productOrderState, LocalDateTime localDateTime); + } diff --git a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java index 360385e4..60702dc2 100644 --- a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java +++ b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java @@ -461,7 +461,7 @@ public Page getClothingSalesReturnedProduct(Lon p.getProductCode(), p.getThumbnailImageUrl(), p.getProductName(), - p.getQualityRate().toString(), + p.getQualityRate() != null ? p.getQualityRate().getValue() : null, p.getClothingSales().getReturnRequestDate().format(DateTimeFormatter.ofPattern("MM/dd/yy")) )) .collect(Collectors.toList()); diff --git a/src/main/java/com/example/repick/domain/product/service/ProductOrderService.java b/src/main/java/com/example/repick/domain/product/service/ProductOrderService.java index 5ad2811b..1003244c 100644 --- a/src/main/java/com/example/repick/domain/product/service/ProductOrderService.java +++ b/src/main/java/com/example/repick/domain/product/service/ProductOrderService.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.List; import static com.example.repick.global.error.exception.ErrorCode.*; @@ -267,4 +268,28 @@ public Boolean updateProductOrderState(Long productOrderId, ProductOrderSateRequ return true; } + public GetProductOrderCount getProductOrderCount(){ + List productOrders = productOrderRepository.findByLastModifiedDateAfter(LocalDateTime.now().minusMonths(1)); + long paymentCompletedCount = 0; + long shippingCount = 0; + long confirmWaitCount = 0; + long confirmedCount = 0; + long refundRequestCount = 0; + for (ProductOrder productOrder : productOrders) { + switch (productOrder.getProductOrderState()) { + case PAYMENT_COMPLETED -> paymentCompletedCount++; + case SHIPPING -> shippingCount++; + case DELIVERED -> { + if (productOrder.isConfirmed()) { + confirmedCount++; + } else { + confirmWaitCount++; + } + } + case RETURN_REQUESTED -> refundRequestCount++; + } + } + return GetProductOrderCount.of(paymentCompletedCount, shippingCount, confirmWaitCount, confirmedCount, refundRequestCount); + } + } diff --git a/src/main/java/com/example/repick/domain/user/api/UserController.java b/src/main/java/com/example/repick/domain/user/api/UserController.java index d7a49548..8509da6d 100644 --- a/src/main/java/com/example/repick/domain/user/api/UserController.java +++ b/src/main/java/com/example/repick/domain/user/api/UserController.java @@ -266,6 +266,12 @@ public SuccessResponse getMyPage() { return SuccessResponse.success(userService.getMyPage()); } + @Operation(summary = "어드민 대시보드 - 유저 통계") + @GetMapping("/statistics") + public SuccessResponse getUserStatistics() { + return SuccessResponse.success(userService.getUserStatistics()); + } + @Operation(summary = "푸시 알림 허용 여부 수정하기", description = """ 푸시 알림 허용 여부를 수정합니다. diff --git a/src/main/java/com/example/repick/domain/user/dto/GetUserStatistics.java b/src/main/java/com/example/repick/domain/user/dto/GetUserStatistics.java new file mode 100644 index 00000000..c74b4c06 --- /dev/null +++ b/src/main/java/com/example/repick/domain/user/dto/GetUserStatistics.java @@ -0,0 +1,12 @@ +package com.example.repick.domain.user.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GetUserStatistics( + @Schema(name = "총 유저 수") long totalUserCount, + @Schema(name = "신규 가입 유저 수") long newUserCount +){ + public static GetUserStatistics of(long totalUserCount, long newUserCount) { + return new GetUserStatistics(totalUserCount, newUserCount); + } +} diff --git a/src/main/java/com/example/repick/domain/user/repository/UserRepository.java b/src/main/java/com/example/repick/domain/user/repository/UserRepository.java index 491e7276..41a90d88 100644 --- a/src/main/java/com/example/repick/domain/user/repository/UserRepository.java +++ b/src/main/java/com/example/repick/domain/user/repository/UserRepository.java @@ -3,10 +3,14 @@ import com.example.repick.domain.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; import java.util.Optional; public interface UserRepository extends JpaRepository { Optional findByProviderId(String providerId); + Long countByIsDeletedFalse(); + Long countByIsDeletedFalseAndCreatedDateAfter(LocalDateTime createdDate); + } diff --git a/src/main/java/com/example/repick/domain/user/service/UserService.java b/src/main/java/com/example/repick/domain/user/service/UserService.java index e9df8df3..f8e10a09 100644 --- a/src/main/java/com/example/repick/domain/user/service/UserService.java +++ b/src/main/java/com/example/repick/domain/user/service/UserService.java @@ -159,6 +159,7 @@ public TokenResponse refreshToken(PostTokenRefresh postTokenRefresh) { return new TokenResponse(accessToken, postTokenRefresh.refreshToken()); } + @Transactional(readOnly = true) public GetMyPage getMyPage() { User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND)); @@ -185,6 +186,14 @@ public GetMyPage getMyPage() { return GetMyPage.of(user.getNickname(), user.getSettlement(), preparing, shipping, delivered, confirmed); } + @Transactional(readOnly = true) + public GetUserStatistics getUserStatistics() { + long totalUserCount = userRepository.countByIsDeletedFalse(); + long newUserCount = userRepository.countByIsDeletedFalseAndCreatedDateAfter(LocalDateTime.now().minusMonths(1)); + return GetUserStatistics.of(totalUserCount, newUserCount); + } + + @Transactional public Boolean updatePushAllow(boolean pushAllow) { User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) .orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));