From d60aec5bee46c567b3c0c94bab3adc82b9cbd2e3 Mon Sep 17 00:00:00 2001 From: JeongHyun Lee <86969518+hyunihs@users.noreply.github.com> Date: Thu, 22 Aug 2024 22:11:46 +0900 Subject: [PATCH] fix: admin api (#146) * fix: getUserStatistics * fix: clothingSalesCount admin dashboard * fix: change state to selling when refund * refactor: productService --- .../GetClothingSalesAndProductOrderCount.java | 2 +- .../dto/GetClothingSalesCount.java | 8 ++-- .../service/ClothingSalesService.java | 10 ++-- .../product/scheduler/ProductScheduler.java | 4 +- .../product/service/ProductOrderService.java | 16 +++++-- .../product/service/ProductService.java | 13 ++---- .../domain/user/dto/GetUserStatistics.java | 8 ++-- .../user/repository/UserRepository.java | 5 +- .../user/repository/UserRepositoryCustom.java | 8 ++++ .../user/repository/UserRepositoryImpl.java | 46 +++++++++++++++++++ .../domain/user/service/UserService.java | 4 +- 11 files changed, 87 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/example/repick/domain/user/repository/UserRepositoryCustom.java create mode 100644 src/main/java/com/example/repick/domain/user/repository/UserRepositoryImpl.java 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 index 63e8e1a7..24e43063 100644 --- a/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesAndProductOrderCount.java +++ b/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesAndProductOrderCount.java @@ -3,7 +3,7 @@ import io.swagger.v3.oas.annotations.media.Schema; public record GetClothingSalesAndProductOrderCount( - @Schema(name = "수거 신청 수") int collectionRequestCount, + @Schema(name = "수거 신청 수") int collectionRequestCount, // 수거 신청 수 = 리픽백 배송 신청 수 @Schema(name = "결제 완료 수") int paymentCompleteCount, @Schema(name = "반품 요청 수") int 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 index f15819e5..a3548f46 100644 --- a/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesCount.java +++ b/src/main/java/com/example/repick/domain/clothingSales/dto/GetClothingSalesCount.java @@ -3,14 +3,14 @@ import io.swagger.v3.oas.annotations.media.Schema; public record GetClothingSalesCount( - @Schema(name = "수거 신청 수") long collectionRequestCount, - @Schema(name = "상품화 진행 중 수") long productionProgressCount, + @Schema(name = "수거 신청 수") long collectionRequestCount, // 수거 신청 수 = 리픽백 배송 신청 수 + @Schema(name = "진행 요청 수") long collectingCount, // 진행 요청 수 = 리픽백/박스 수거 신청 수 @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); + public static GetClothingSalesCount of(long collectionRequestCount, long collectingCount, long productionCompleteCount, long sellingCount, long settlementRequestCount) { + return new GetClothingSalesCount(collectionRequestCount, collectingCount, productionCompleteCount, sellingCount, settlementRequestCount); } } 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 300d456f..a821c6dd 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 @@ -241,24 +241,24 @@ public GetClothingSalesUser getClothingSalesUser(Long clothingSalesId){ public GetClothingSalesCount getClothingSalesCount() { List clothingSalesStateList = clothingSalesStateRepository.findByCreatedDateAfter(LocalDateTime.now().minusMonths(1)); long collectionRequestCount = 0; - long productionProgressCount = 0; + long collectingCount = 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 BAG_INIT_REQUEST -> collectionRequestCount++; + case BOX_COLLECT_REQUEST, BAG_COLLECT_REQUEST -> collectingCount++; case PRODUCTED, PRODUCT_REGISTERED -> productionCompleteCount++; case SELLING -> sellingCount++; } } - return GetClothingSalesCount.of(collectionRequestCount, productionProgressCount, productionCompleteCount, sellingCount, settlementRequestCount); + return GetClothingSalesCount.of(collectionRequestCount, collectingCount, productionCompleteCount, sellingCount, settlementRequestCount); } public GetClothingSalesAndProductOrderCount getCountToday(){ int collectionRequestCount = clothingSalesStateRepository.countByClothingSalesStateTypeInAndCreatedDateAfter( - List.of(ClothingSalesStateType.BOX_COLLECT_REQUEST, ClothingSalesStateType.BAG_COLLECT_REQUEST), + List.of(ClothingSalesStateType.BAG_INIT_REQUEST), LocalDate.now().atStartOfDay()); int paymentCompleteCount = productOrderRepository.countByProductOrderStateAndLastModifiedDateAfter( ProductOrderState.PAYMENT_COMPLETED, LocalDate.now().atStartOfDay()); diff --git a/src/main/java/com/example/repick/domain/product/scheduler/ProductScheduler.java b/src/main/java/com/example/repick/domain/product/scheduler/ProductScheduler.java index d953186a..4a4abaca 100644 --- a/src/main/java/com/example/repick/domain/product/scheduler/ProductScheduler.java +++ b/src/main/java/com/example/repick/domain/product/scheduler/ProductScheduler.java @@ -46,9 +46,7 @@ public void updateProductDiscountRate() { private void handleExpiredSales(ClothingSales clothingSales) { clothingSales.getProductList().forEach(p -> { - productService.addProductSellingState(p.getId(), ProductStateType.SELLING_END); - p.updateProductState(ProductStateType.SELLING_END); - productRepository.save(p); + productService.changeSellingState(p, ProductStateType.SELLING_END); }); ClothingSalesState clothingSalesState = ClothingSalesState.of(clothingSales.getId(), ClothingSalesStateType.SELLING_EXPIRED); clothingSales.updateClothingSalesState(ClothingSalesStateType.SELLING_EXPIRED); 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 1003244c..72d64c1e 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 @@ -40,6 +40,7 @@ public class ProductOrderService { private final IamportClient iamportClient; private final ProductValidator productValidator; private final ProductStateRepository productStateRepository; + private final ProductService productService; @Transactional public GetProductOrderPreparation prepareProductOrder(PostProductOrder postProductOrder) { @@ -135,12 +136,9 @@ public Boolean validatePayment(PostPayment postPayment){ productOrders.forEach(productOrder -> { productOrder.updateProductOrderState(ProductOrderState.PAYMENT_COMPLETED); productCartRepository.deleteByUserIdAndProductId(productOrder.getUserId(), productOrder.getProductId()); - productStateRepository.save(ProductState.of(productOrder.getProductId(), ProductStateType.SOLD_OUT)); - // Product 엔티티의 productState 업데이트 Product product = productRepository.findById(productOrder.getProductId()) .orElseThrow(() -> new CustomException(INVALID_PRODUCT_ID)); - product.updateProductState(ProductStateType.SOLD_OUT); - productRepository.save(product); + productService.changeSellingState(product, ProductStateType.SOLD_OUT); }); productOrderRepository.saveAll(productOrders); @@ -263,8 +261,16 @@ public Boolean registerTrackingNumber(Long productOrderId, TrackingNumberRequest public Boolean updateProductOrderState(Long productOrderId, ProductOrderSateRequest productOrderStateRequest){ ProductOrder productOrder = productOrderRepository.findById(productOrderId) .orElseThrow(() -> new CustomException(PRODUCT_ORDER_NOT_FOUND)); - productOrder.updateProductOrderState(ProductOrderState.fromValue(productOrderStateRequest.state())); + ProductOrderState productOrderState = ProductOrderState.fromValue(productOrderStateRequest.state()); + // productOrderState 변경 + productOrder.updateProductOrderState(productOrderState); productOrderRepository.save(productOrder); + // 환불되었을 경우 상품 다시 판매중으로 변경 + if(productOrderState == ProductOrderState.REFUND_COMPLETED){ + Product product = productRepository.findById(productOrder.getProductId()) + .orElseThrow(() -> new CustomException(INVALID_PRODUCT_ID)); + productService.changeSellingState(product, ProductStateType.SELLING); + } return true; } diff --git a/src/main/java/com/example/repick/domain/product/service/ProductService.java b/src/main/java/com/example/repick/domain/product/service/ProductService.java index cb65bfe3..4763f9a0 100644 --- a/src/main/java/com/example/repick/domain/product/service/ProductService.java +++ b/src/main/java/com/example/repick/domain/product/service/ProductService.java @@ -4,7 +4,6 @@ import com.example.repick.domain.clothingSales.entity.ClothingSales; import com.example.repick.domain.clothingSales.repository.ClothingSalesRepository; import com.example.repick.domain.product.dto.product.*; -import com.example.repick.domain.product.dto.productClothingSales.GetKgSellProductClothingSales; import com.example.repick.domain.product.dto.productOrder.GetProductCart; import com.example.repick.domain.product.entity.*; import com.example.repick.domain.product.repository.*; @@ -86,11 +85,6 @@ private void addMaterials(List materials, Product product) { } } - - public void addProductSellingState(Long productId, ProductStateType productStateType) { - productStateRepository.save(ProductState.of(productId, productStateType)); - } - @Transactional public ProductResponse registerProduct(List images, PostProduct postProduct) { User user = userRepository.findById(postProduct.userId()) @@ -121,7 +115,7 @@ public ProductResponse registerProduct(List images, PostProduct p if (!postProduct.materials().isEmpty()) addMaterials(postProduct.materials(), product); // productSellingState - addProductSellingState(product.getId(), ProductStateType.PREPARING); + productStateRepository.save(ProductState.of(product.getId(), ProductStateType.PREPARING)); return ProductResponse.fromProduct(product); @@ -138,7 +132,7 @@ private Product handleRejectedProduct(PostProduct postProduct, User user, List> getCarted(PageCondition pageCondition) } public void changeSellingState(Product product, ProductStateType sellingState) { - addProductSellingState(product.getId(), sellingState); + productStateRepository.save(ProductState.of(product.getId(), sellingState)); product.updateProductState(sellingState); + productRepository.save(product); } public void calculateDiscountPriceAndPredictDiscountRateAndSave(Product product) { 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 index c74b4c06..5ca43b3b 100644 --- a/src/main/java/com/example/repick/domain/user/dto/GetUserStatistics.java +++ b/src/main/java/com/example/repick/domain/user/dto/GetUserStatistics.java @@ -4,9 +4,11 @@ public record GetUserStatistics( @Schema(name = "총 유저 수") long totalUserCount, - @Schema(name = "신규 가입 유저 수") long newUserCount + @Schema(name = "신규 가입 유저 수") long newUserCount, + @Schema(name = "여성 유저 비율") double femaleUserRatio, + @Schema(name = "남성 유저 비율") double maleUserRatio ){ - public static GetUserStatistics of(long totalUserCount, long newUserCount) { - return new GetUserStatistics(totalUserCount, newUserCount); + public static GetUserStatistics of(long totalUserCount, long newUserCount, double femaleUserRatio, double maleUserRatio) { + return new GetUserStatistics(totalUserCount, newUserCount, femaleUserRatio, maleUserRatio); } } 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 41a90d88..cb0cee50 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 @@ -6,11 +6,8 @@ import java.time.LocalDateTime; import java.util.Optional; -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository, UserRepositoryCustom { Optional findByProviderId(String providerId); - Long countByIsDeletedFalse(); - Long countByIsDeletedFalseAndCreatedDateAfter(LocalDateTime createdDate); - } diff --git a/src/main/java/com/example/repick/domain/user/repository/UserRepositoryCustom.java b/src/main/java/com/example/repick/domain/user/repository/UserRepositoryCustom.java new file mode 100644 index 00000000..9cc8f4c1 --- /dev/null +++ b/src/main/java/com/example/repick/domain/user/repository/UserRepositoryCustom.java @@ -0,0 +1,8 @@ +package com.example.repick.domain.user.repository; + +import com.example.repick.domain.user.dto.GetUserStatistics; + + +public interface UserRepositoryCustom { + GetUserStatistics getUserStatistics(); +} diff --git a/src/main/java/com/example/repick/domain/user/repository/UserRepositoryImpl.java b/src/main/java/com/example/repick/domain/user/repository/UserRepositoryImpl.java new file mode 100644 index 00000000..fc5c6a2b --- /dev/null +++ b/src/main/java/com/example/repick/domain/user/repository/UserRepositoryImpl.java @@ -0,0 +1,46 @@ +package com.example.repick.domain.user.repository; + +import com.example.repick.domain.user.dto.GetUserStatistics; +import com.example.repick.domain.user.entity.Gender; +import com.querydsl.core.Tuple; +import com.querydsl.core.types.dsl.CaseBuilder; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDateTime; + +import static com.example.repick.domain.user.entity.QUser.user; + +@RequiredArgsConstructor +public class UserRepositoryImpl implements UserRepositoryCustom{ + + private final JPAQueryFactory queryFactory; + + @Override + public GetUserStatistics getUserStatistics() { + Tuple result = queryFactory + .select( + user.count(), // 총 유저 수 + new CaseBuilder() + .when(user.createdDate.after(LocalDateTime.now().minusMonths(1))).then(1).otherwise(0).sum(), // 신규 유저 수 + new CaseBuilder() + .when(user.gender.eq(Gender.FEMALE)).then(1).otherwise(0).sum(), // 여성 유저 수 + new CaseBuilder() + .when(user.gender.eq(Gender.MALE)).then(1).otherwise(0).sum() // 남성 유저 수 + ) + .from(user) + .where(user.isDeleted.isFalse()) + .fetchOne(); + + long totalUserCount = result.get(0, Number.class).longValue(); + long newUserCount = result.get(1, Number.class).longValue(); + long femaleUserCount = result.get(2, Number.class).longValue(); + long maleUserCount = result.get(3, Number.class).longValue(); + + double femaleRatio = (double) femaleUserCount / totalUserCount; + double maleRatio = (double) maleUserCount / totalUserCount; + + return GetUserStatistics.of(totalUserCount, newUserCount, femaleRatio, maleRatio); + } + +} \ No newline at end of file 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 f8e10a09..97fb109f 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 @@ -188,9 +188,7 @@ public GetMyPage getMyPage() { @Transactional(readOnly = true) public GetUserStatistics getUserStatistics() { - long totalUserCount = userRepository.countByIsDeletedFalse(); - long newUserCount = userRepository.countByIsDeletedFalseAndCreatedDateAfter(LocalDateTime.now().minusMonths(1)); - return GetUserStatistics.of(totalUserCount, newUserCount); + return userRepository.getUserStatistics(); } @Transactional