From 755aba928ab189fe2a07cda43918d61d0412e323 Mon Sep 17 00:00:00 2001 From: Sanghyun Yi Date: Tue, 13 Jan 2026 23:31:11 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20statics=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20api=20=EC=A0=9C=EA=B1=B0=20=ED=9B=84=20?= =?UTF-8?q?=EC=9D=BC=EC=9E=90=EB=B3=84=20=ED=86=B5=EA=B3=84=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ItemDailyStatisticsService.java | 34 +++++------ .../service/ItemWeeklyStatisticsService.java | 34 +++++------ .../SubcategoryDailyStatisticsService.java | 32 +++++----- .../SubcategoryWeeklyStatisticsService.java | 32 +++++----- .../TopCategoryDailyStatisticsService.java | 29 ++++----- .../TopCategoryWeeklyStatisticsService.java | 29 ++++----- .../entity/daily/ItemDailyStatistics.java | 8 +++ .../entity/weekly/ItemWeeklyStatistics.java | 8 +++ .../ItemDailyStatisticsController.java | 29 ++++----- .../ItemWeeklyStatisticsController.java | 31 +++++----- .../SubcategoryDailyStatisticsController.java | 31 +++++----- ...SubcategoryWeeklyStatisticsController.java | 32 +++++----- .../TopCategoryDailyStatisticsController.java | 26 ++++---- ...TopCategoryWeeklyStatisticsController.java | 28 ++++----- .../ItemDailyStatisticsSearchRequest.java | 27 ++++++++ .../ItemWeeklyStatisticsSearchRequest.java | 27 ++++++++ ...bcategoryDailyStatisticsSearchRequest.java | 24 ++++++++ ...categoryWeeklyStatisticsSearchRequest.java | 24 ++++++++ ...pCategoryDailyStatisticsSearchRequest.java | 21 +++++++ ...CategoryWeeklyStatisticsSearchRequest.java | 21 +++++++ .../daily/ItemDailyStatisticsRepository.java | 25 ++++++++ .../SubcategoryDailyStatisticsRepository.java | 20 ++++++ .../TopCategoryDailyStatisticsRepository.java | 20 ++++++ .../ItemWeeklyStatisticsRepository.java | 25 ++++++++ ...SubcategoryWeeklyStatisticsRepository.java | 20 ++++++ ...TopCategoryWeeklyStatisticsRepository.java | 20 ++++++ .../statistics/util/DateRangeValidator.java | 61 +++++++++++++++++++ .../statistics/util/WeekConverter.java | 42 +++++++++++++ 28 files changed, 563 insertions(+), 197 deletions(-) create mode 100644 src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java create mode 100644 src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java create mode 100644 src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java create mode 100644 src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java create mode 100644 src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java create mode 100644 src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java create mode 100644 src/main/java/until/the/eternity/statistics/util/DateRangeValidator.java create mode 100644 src/main/java/until/the/eternity/statistics/util/WeekConverter.java diff --git a/src/main/java/until/the/eternity/statistics/application/service/ItemDailyStatisticsService.java b/src/main/java/until/the/eternity/statistics/application/service/ItemDailyStatisticsService.java index 923325ba..ab63326a 100644 --- a/src/main/java/until/the/eternity/statistics/application/service/ItemDailyStatisticsService.java +++ b/src/main/java/until/the/eternity/statistics/application/service/ItemDailyStatisticsService.java @@ -20,24 +20,24 @@ public class ItemDailyStatisticsService { private final ItemDailyStatisticsRepository repository; private final ItemDailyStatisticsMapper mapper; - /** 아이템별 일간 통계 전체 조회 (페이징) */ + /** 아이템별 일간 통계 조회 (itemName, subCategory, topCategory, 날짜 범위) */ @Transactional(readOnly = true) - public PageResponseDto findAll(Pageable pageable) { - Page page = repository.findAll(pageable); - Page dtoPage = page.map(mapper::toDto); - return PageResponseDto.of(dtoPage); - } + public java.util.List search( + String itemName, + String subCategory, + String topCategory, + java.time.LocalDate startDate, + java.time.LocalDate endDate) { + // 날짜 범위 검증 (최대 30일) + until.the.eternity.statistics.util.DateRangeValidator.validateDailyDateRange( + startDate, endDate); - /** 아이템별 일간 통계 ID로 단건 조회 */ - @Transactional(readOnly = true) - public ItemDailyStatisticsResponse findById(Long id) { - ItemDailyStatistics entity = - repository - .findById(id) - .orElseThrow( - () -> - new IllegalArgumentException( - "ItemDailyStatistics not found: " + id)); - return mapper.toDto(entity); + // 조회 + java.util.List results = + repository.findByItemAndDateRange( + itemName, subCategory, topCategory, startDate, endDate); + + // DTO 변환 + return results.stream().map(mapper::toDto).collect(java.util.stream.Collectors.toList()); } } diff --git a/src/main/java/until/the/eternity/statistics/application/service/ItemWeeklyStatisticsService.java b/src/main/java/until/the/eternity/statistics/application/service/ItemWeeklyStatisticsService.java index 9d3099fa..75502a7f 100644 --- a/src/main/java/until/the/eternity/statistics/application/service/ItemWeeklyStatisticsService.java +++ b/src/main/java/until/the/eternity/statistics/application/service/ItemWeeklyStatisticsService.java @@ -20,24 +20,24 @@ public class ItemWeeklyStatisticsService { private final ItemWeeklyStatisticsRepository repository; private final ItemWeeklyStatisticsMapper mapper; - /** 아이템별 주간 통계 전체 조회 (페이징) */ + /** 아이템별 주간 통계 조회 (itemName, subCategory, topCategory, 날짜 범위) */ @Transactional(readOnly = true) - public PageResponseDto findAll(Pageable pageable) { - Page page = repository.findAll(pageable); - Page dtoPage = page.map(mapper::toDto); - return PageResponseDto.of(dtoPage); - } + public java.util.List search( + String itemName, + String subCategory, + String topCategory, + java.time.LocalDate startDate, + java.time.LocalDate endDate) { + // 날짜 범위 검증 (최대 4개월) + until.the.eternity.statistics.util.DateRangeValidator.validateWeeklyDateRange( + startDate, endDate); - /** 아이템별 주간 통계 ID로 단건 조회 */ - @Transactional(readOnly = true) - public ItemWeeklyStatisticsResponse findById(Long id) { - ItemWeeklyStatistics entity = - repository - .findById(id) - .orElseThrow( - () -> - new IllegalArgumentException( - "ItemWeeklyStatistics not found: " + id)); - return mapper.toDto(entity); + // 조회 + java.util.List results = + repository.findByItemAndDateRange( + itemName, subCategory, topCategory, startDate, endDate); + + // DTO 변환 + return results.stream().map(mapper::toDto).collect(java.util.stream.Collectors.toList()); } } diff --git a/src/main/java/until/the/eternity/statistics/application/service/SubcategoryDailyStatisticsService.java b/src/main/java/until/the/eternity/statistics/application/service/SubcategoryDailyStatisticsService.java index 9d8638db..50f865bc 100644 --- a/src/main/java/until/the/eternity/statistics/application/service/SubcategoryDailyStatisticsService.java +++ b/src/main/java/until/the/eternity/statistics/application/service/SubcategoryDailyStatisticsService.java @@ -20,24 +20,22 @@ public class SubcategoryDailyStatisticsService { private final SubcategoryDailyStatisticsRepository repository; private final SubcategoryDailyStatisticsMapper mapper; - /** 서브카테고리별 일간 통계 전체 조회 (페이징) */ + /** 서브카테고리별 일간 통계 조회 (subCategory, 날짜 범위) */ @Transactional(readOnly = true) - public PageResponseDto findAll(Pageable pageable) { - Page page = repository.findAll(pageable); - Page dtoPage = page.map(mapper::toDto); - return PageResponseDto.of(dtoPage); - } + public java.util.List search( + String topCategory, // topCategory는 파라미터로 받지만 조회에는 사용하지 않음 (DB 구조상) + String subCategory, + java.time.LocalDate startDate, + java.time.LocalDate endDate) { + // 날짜 범위 검증 (최대 30일) + until.the.eternity.statistics.util.DateRangeValidator.validateDailyDateRange( + startDate, endDate); - /** 서브카테고리별 일간 통계 ID로 단건 조회 */ - @Transactional(readOnly = true) - public SubcategoryDailyStatisticsResponse findById(Long id) { - SubcategoryDailyStatistics entity = - repository - .findById(id) - .orElseThrow( - () -> - new IllegalArgumentException( - "SubcategoryDailyStatistics not found: " + id)); - return mapper.toDto(entity); + // 조회 + java.util.List results = + repository.findBySubcategoryAndDateRange(subCategory, startDate, endDate); + + // DTO 변환 + return results.stream().map(mapper::toDto).collect(java.util.stream.Collectors.toList()); } } diff --git a/src/main/java/until/the/eternity/statistics/application/service/SubcategoryWeeklyStatisticsService.java b/src/main/java/until/the/eternity/statistics/application/service/SubcategoryWeeklyStatisticsService.java index 676c1c98..ea9df092 100644 --- a/src/main/java/until/the/eternity/statistics/application/service/SubcategoryWeeklyStatisticsService.java +++ b/src/main/java/until/the/eternity/statistics/application/service/SubcategoryWeeklyStatisticsService.java @@ -20,24 +20,22 @@ public class SubcategoryWeeklyStatisticsService { private final SubcategoryWeeklyStatisticsRepository repository; private final SubcategoryWeeklyStatisticsMapper mapper; - /** 서브카테고리별 주간 통계 전체 조회 (페이징) */ + /** 서브카테고리별 주간 통계 조회 (subCategory, 날짜 범위) */ @Transactional(readOnly = true) - public PageResponseDto findAll(Pageable pageable) { - Page page = repository.findAll(pageable); - Page dtoPage = page.map(mapper::toDto); - return PageResponseDto.of(dtoPage); - } + public java.util.List search( + String topCategory, // topCategory는 파라미터로 받지만 조회에는 사용하지 않음 (DB 구조상) + String subCategory, + java.time.LocalDate startDate, + java.time.LocalDate endDate) { + // 날짜 범위 검증 (최대 4개월) + until.the.eternity.statistics.util.DateRangeValidator.validateWeeklyDateRange( + startDate, endDate); - /** 서브카테고리별 주간 통계 ID로 단건 조회 */ - @Transactional(readOnly = true) - public SubcategoryWeeklyStatisticsResponse findById(Long id) { - SubcategoryWeeklyStatistics entity = - repository - .findById(id) - .orElseThrow( - () -> - new IllegalArgumentException( - "SubcategoryWeeklyStatistics not found: " + id)); - return mapper.toDto(entity); + // 조회 + java.util.List results = + repository.findBySubcategoryAndDateRange(subCategory, startDate, endDate); + + // DTO 변환 + return results.stream().map(mapper::toDto).collect(java.util.stream.Collectors.toList()); } } diff --git a/src/main/java/until/the/eternity/statistics/application/service/TopCategoryDailyStatisticsService.java b/src/main/java/until/the/eternity/statistics/application/service/TopCategoryDailyStatisticsService.java index f51801f4..eea5eafb 100644 --- a/src/main/java/until/the/eternity/statistics/application/service/TopCategoryDailyStatisticsService.java +++ b/src/main/java/until/the/eternity/statistics/application/service/TopCategoryDailyStatisticsService.java @@ -20,24 +20,19 @@ public class TopCategoryDailyStatisticsService { private final TopCategoryDailyStatisticsRepository repository; private final TopCategoryDailyStatisticsMapper mapper; - /** 탑카테고리별 일간 통계 전체 조회 (페이징) */ + /** 탑카테고리별 일간 통계 조회 (topCategory, 날짜 범위) */ @Transactional(readOnly = true) - public PageResponseDto findAll(Pageable pageable) { - Page page = repository.findAll(pageable); - Page dtoPage = page.map(mapper::toDto); - return PageResponseDto.of(dtoPage); - } + public java.util.List search( + String topCategory, java.time.LocalDate startDate, java.time.LocalDate endDate) { + // 날짜 범위 검증 (최대 30일) + until.the.eternity.statistics.util.DateRangeValidator.validateDailyDateRange( + startDate, endDate); - /** 탑카테고리별 일간 통계 ID로 단건 조회 */ - @Transactional(readOnly = true) - public TopCategoryDailyStatisticsResponse findById(Long id) { - TopCategoryDailyStatistics entity = - repository - .findById(id) - .orElseThrow( - () -> - new IllegalArgumentException( - "TopCategoryDailyStatistics not found: " + id)); - return mapper.toDto(entity); + // 조회 + java.util.List results = + repository.findByTopCategoryAndDateRange(topCategory, startDate, endDate); + + // DTO 변환 + return results.stream().map(mapper::toDto).collect(java.util.stream.Collectors.toList()); } } diff --git a/src/main/java/until/the/eternity/statistics/application/service/TopCategoryWeeklyStatisticsService.java b/src/main/java/until/the/eternity/statistics/application/service/TopCategoryWeeklyStatisticsService.java index a586ea5b..de9dbe13 100644 --- a/src/main/java/until/the/eternity/statistics/application/service/TopCategoryWeeklyStatisticsService.java +++ b/src/main/java/until/the/eternity/statistics/application/service/TopCategoryWeeklyStatisticsService.java @@ -20,24 +20,19 @@ public class TopCategoryWeeklyStatisticsService { private final TopCategoryWeeklyStatisticsRepository repository; private final TopCategoryWeeklyStatisticsMapper mapper; - /** 탑카테고리별 주간 통계 전체 조회 (페이징) */ + /** 탑카테고리별 주간 통계 조회 (topCategory, 날짜 범위) */ @Transactional(readOnly = true) - public PageResponseDto findAll(Pageable pageable) { - Page page = repository.findAll(pageable); - Page dtoPage = page.map(mapper::toDto); - return PageResponseDto.of(dtoPage); - } + public java.util.List search( + String topCategory, java.time.LocalDate startDate, java.time.LocalDate endDate) { + // 날짜 범위 검증 (최대 4개월) + until.the.eternity.statistics.util.DateRangeValidator.validateWeeklyDateRange( + startDate, endDate); - /** 탑카테고리별 주간 통계 ID로 단건 조회 */ - @Transactional(readOnly = true) - public TopCategoryWeeklyStatisticsResponse findById(Long id) { - TopCategoryWeeklyStatistics entity = - repository - .findById(id) - .orElseThrow( - () -> - new IllegalArgumentException( - "TopCategoryWeeklyStatistics not found: " + id)); - return mapper.toDto(entity); + // 조회 + java.util.List results = + repository.findByTopCategoryAndDateRange(topCategory, startDate, endDate); + + // DTO 변환 + return results.stream().map(mapper::toDto).collect(java.util.stream.Collectors.toList()); } } diff --git a/src/main/java/until/the/eternity/statistics/domain/entity/daily/ItemDailyStatistics.java b/src/main/java/until/the/eternity/statistics/domain/entity/daily/ItemDailyStatistics.java index 49f5b17a..361df9c1 100644 --- a/src/main/java/until/the/eternity/statistics/domain/entity/daily/ItemDailyStatistics.java +++ b/src/main/java/until/the/eternity/statistics/domain/entity/daily/ItemDailyStatistics.java @@ -36,6 +36,14 @@ public class ItemDailyStatistics { @Schema(description = "아이템 이름", example = "켈틱 로열 나이트 소드") private String itemName; + @Column(name = "item_top_category", nullable = false, length = 255) + @Schema(description = "아이템 탑 카테고리", example = "무기") + private String itemTopCategory; + + @Column(name = "item_sub_category", nullable = false, length = 255) + @Schema(description = "아이템 서브 카테고리", example = "한손검") + private String itemSubCategory; + @Column(name = "date_auction_buy", nullable = false) @Schema(description = "거래 일자", example = "2025-07-01") private LocalDate dateAuctionBuy; diff --git a/src/main/java/until/the/eternity/statistics/domain/entity/weekly/ItemWeeklyStatistics.java b/src/main/java/until/the/eternity/statistics/domain/entity/weekly/ItemWeeklyStatistics.java index b1ae76bb..6970f18e 100644 --- a/src/main/java/until/the/eternity/statistics/domain/entity/weekly/ItemWeeklyStatistics.java +++ b/src/main/java/until/the/eternity/statistics/domain/entity/weekly/ItemWeeklyStatistics.java @@ -36,6 +36,14 @@ public class ItemWeeklyStatistics { @Schema(description = "아이템 이름", example = "켈틱 로열 나이트 소드") private String itemName; + @Column(name = "item_top_category", nullable = false, length = 255) + @Schema(description = "아이템 탑 카테고리", example = "무기") + private String itemTopCategory; + + @Column(name = "item_sub_category", nullable = false, length = 255) + @Schema(description = "아이템 서브 카테고리", example = "한손검") + private String itemSubCategory; + @Column(name = "year", nullable = false) @Schema(description = "연도", example = "2025") private Integer year; diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java index 892ad3b3..1d6ee170 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java @@ -22,19 +22,20 @@ public class ItemDailyStatisticsController { @GetMapping @Operation( - summary = "아이템별 일간 통계 목록 조회", - description = "아이템별 일간 거래 통계 목록을 페이징하여 조회합니다. 최저가, 최고가, 평균가, 거래 총량, 거래 수량 정보를 포함합니다.") - public ResponseEntity> getItemDailyStatistics( - @ParameterObject @ModelAttribute PageRequestDto pageDto) { - PageResponseDto result = service.findAll(pageDto.toPageable()); - return ResponseEntity.ok(result); - } - - @GetMapping("/{id}") - @Operation(summary = "아이템별 일간 통계 단건 조회", description = "ID를 통해 특정 아이템의 일간 거래 통계를 조회합니다.") - public ResponseEntity getItemDailyStatisticsById( - @Parameter(description = "통계 ID", example = "1") @PathVariable Long id) { - ItemDailyStatisticsResponse result = service.findById(id); - return ResponseEntity.ok(result); + summary = "아이템별 일간 통계 조회", + description = "아이템 이름, 서브 카테고리, 탑 카테고리로 일간 통계를 조회합니다. 최대 30일까지 조회 가능합니다.") + public ResponseEntity> searchItemDailyStatistics( + @ParameterObject @ModelAttribute + @jakarta.validation.Valid + until.the.eternity.statistics.interfaces.rest.dto.request.ItemDailyStatisticsSearchRequest + request) { + java.util.List results = + service.search( + request.itemName(), + request.subCategory(), + request.topCategory(), + request.startDate(), + request.endDate()); + return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemWeeklyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemWeeklyStatisticsController.java index af8cad29..dcafa264 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemWeeklyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemWeeklyStatisticsController.java @@ -22,21 +22,20 @@ public class ItemWeeklyStatisticsController { @GetMapping @Operation( - summary = "아이템별 주간 통계 목록 조회", - description = - "아이템별 주간 거래 통계 목록을 페이징하여 조회합니다. 최저가, 최고가, 평균가, 거래 총량, 거래 수량, 연도, 주차 정보를 포함합니다.") - public ResponseEntity> getItemWeeklyStatistics( - @ParameterObject @ModelAttribute PageRequestDto pageDto) { - PageResponseDto result = - service.findAll(pageDto.toPageable()); - return ResponseEntity.ok(result); - } - - @GetMapping("/{id}") - @Operation(summary = "아이템별 주간 통계 단건 조회", description = "ID를 통해 특정 아이템의 주간 거래 통계를 조회합니다.") - public ResponseEntity getItemWeeklyStatisticsById( - @Parameter(description = "통계 ID", example = "1") @PathVariable Long id) { - ItemWeeklyStatisticsResponse result = service.findById(id); - return ResponseEntity.ok(result); + summary = "아이템별 주간 통계 조회", + description = "아이템 이름, 서브 카테고리, 탑 카테고리로 주간 통계를 조회합니다. 최대 4개월까지 조회 가능합니다.") + public ResponseEntity> searchItemWeeklyStatistics( + @ParameterObject @ModelAttribute + @jakarta.validation.Valid + until.the.eternity.statistics.interfaces.rest.dto.request.ItemWeeklyStatisticsSearchRequest + request) { + java.util.List results = + service.search( + request.itemName(), + request.subCategory(), + request.topCategory(), + request.startDate(), + request.endDate()); + return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryDailyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryDailyStatisticsController.java index 2f8df45f..c4a9d376 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryDailyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryDailyStatisticsController.java @@ -22,21 +22,20 @@ public class SubcategoryDailyStatisticsController { @GetMapping @Operation( - summary = "서브카테고리별 일간 통계 목록 조회", - description = - "서브카테고리별 일간 거래 통계 목록을 페이징하여 조회합니다. 최저가, 최고가, 평균가, 거래 총량, 거래 수량 정보를 포함합니다.") - public ResponseEntity> - getSubcategoryDailyStatistics(@ParameterObject @ModelAttribute PageRequestDto pageDto) { - PageResponseDto result = - service.findAll(pageDto.toPageable()); - return ResponseEntity.ok(result); - } - - @GetMapping("/{id}") - @Operation(summary = "서브카테고리별 일간 통계 단건 조회", description = "ID를 통해 특정 서브카테고리의 일간 거래 통계를 조회합니다.") - public ResponseEntity getSubcategoryDailyStatisticsById( - @Parameter(description = "통계 ID", example = "1") @PathVariable Long id) { - SubcategoryDailyStatisticsResponse result = service.findById(id); - return ResponseEntity.ok(result); + summary = "서브카테고리별 일간 통계 조회", + description = "탑 카테고리와 서브 카테고리로 일간 통계를 조회합니다. 최대 30일까지 조회 가능합니다.") + public ResponseEntity> + searchSubcategoryDailyStatistics( + @ParameterObject @ModelAttribute + @jakarta.validation.Valid + until.the.eternity.statistics.interfaces.rest.dto.request.SubcategoryDailyStatisticsSearchRequest + request) { + java.util.List results = + service.search( + request.topCategory(), + request.subCategory(), + request.startDate(), + request.endDate()); + return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryWeeklyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryWeeklyStatisticsController.java index 43c70397..4af20f58 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryWeeklyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryWeeklyStatisticsController.java @@ -22,22 +22,20 @@ public class SubcategoryWeeklyStatisticsController { @GetMapping @Operation( - summary = "서브카테고리별 주간 통계 목록 조회", - description = - "서브카테고리별 주간 거래 통계 목록을 페이징하여 조회합니다. 최저가, 최고가, 평균가, 거래 총량, 거래 수량, 연도, 주차 정보를 포함합니다.") - public ResponseEntity> - getSubcategoryWeeklyStatistics( - @ParameterObject @ModelAttribute PageRequestDto pageDto) { - PageResponseDto result = - service.findAll(pageDto.toPageable()); - return ResponseEntity.ok(result); - } - - @GetMapping("/{id}") - @Operation(summary = "서브카테고리별 주간 통계 단건 조회", description = "ID를 통해 특정 서브카테고리의 주간 거래 통계를 조회합니다.") - public ResponseEntity getSubcategoryWeeklyStatisticsById( - @Parameter(description = "통계 ID", example = "1") @PathVariable Long id) { - SubcategoryWeeklyStatisticsResponse result = service.findById(id); - return ResponseEntity.ok(result); + summary = "서브카테고리별 주간 통계 조회", + description = "탑 카테고리와 서브 카테고리로 주간 통계를 조회합니다. 최대 4개월까지 조회 가능합니다.") + public ResponseEntity> + searchSubcategoryWeeklyStatistics( + @ParameterObject @ModelAttribute + @jakarta.validation.Valid + until.the.eternity.statistics.interfaces.rest.dto.request.SubcategoryWeeklyStatisticsSearchRequest + request) { + java.util.List results = + service.search( + request.topCategory(), + request.subCategory(), + request.startDate(), + request.endDate()); + return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryDailyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryDailyStatisticsController.java index f0c9c59c..48340b59 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryDailyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryDailyStatisticsController.java @@ -22,20 +22,16 @@ public class TopCategoryDailyStatisticsController { @GetMapping @Operation( - summary = "탑카테고리별 일간 통계 목록 조회", - description = "탑카테고리별 일간 거래 통계 목록을 페이징하여 조회합니다. 최저가, 최고가, 평균가, 거래 총량, 거래 수량 정보를 포함합니다.") - public ResponseEntity> - getTopCategoryDailyStatistics(@ParameterObject @ModelAttribute PageRequestDto pageDto) { - PageResponseDto result = - service.findAll(pageDto.toPageable()); - return ResponseEntity.ok(result); - } - - @GetMapping("/{id}") - @Operation(summary = "탑카테고리별 일간 통계 단건 조회", description = "ID를 통해 특정 탑카테고리의 일간 거래 통계를 조회합니다.") - public ResponseEntity getTopCategoryDailyStatisticsById( - @Parameter(description = "통계 ID", example = "1") @PathVariable Long id) { - TopCategoryDailyStatisticsResponse result = service.findById(id); - return ResponseEntity.ok(result); + summary = "탑카테고리별 일간 통계 조회", + description = "탑 카테고리로 일간 통계를 조회합니다. 최대 30일까지 조회 가능합니다.") + public ResponseEntity> + searchTopCategoryDailyStatistics( + @ParameterObject @ModelAttribute + @jakarta.validation.Valid + until.the.eternity.statistics.interfaces.rest.dto.request.TopCategoryDailyStatisticsSearchRequest + request) { + java.util.List results = + service.search(request.topCategory(), request.startDate(), request.endDate()); + return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryWeeklyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryWeeklyStatisticsController.java index e63033e2..ba870332 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryWeeklyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryWeeklyStatisticsController.java @@ -22,22 +22,16 @@ public class TopCategoryWeeklyStatisticsController { @GetMapping @Operation( - summary = "탑카테고리별 주간 통계 목록 조회", - description = - "탑카테고리별 주간 거래 통계 목록을 페이징하여 조회합니다. 최저가, 최고가, 평균가, 거래 총량, 거래 수량, 연도, 주차 정보를 포함합니다.") - public ResponseEntity> - getTopCategoryWeeklyStatistics( - @ParameterObject @ModelAttribute PageRequestDto pageDto) { - PageResponseDto result = - service.findAll(pageDto.toPageable()); - return ResponseEntity.ok(result); - } - - @GetMapping("/{id}") - @Operation(summary = "탑카테고리별 주간 통계 단건 조회", description = "ID를 통해 특정 탑카테고리의 주간 거래 통계를 조회합니다.") - public ResponseEntity getTopCategoryWeeklyStatisticsById( - @Parameter(description = "통계 ID", example = "1") @PathVariable Long id) { - TopCategoryWeeklyStatisticsResponse result = service.findById(id); - return ResponseEntity.ok(result); + summary = "탑카테고리별 주간 통계 조회", + description = "탑 카테고리로 주간 통계를 조회합니다. 최대 4개월까지 조회 가능합니다.") + public ResponseEntity> + searchTopCategoryWeeklyStatistics( + @ParameterObject @ModelAttribute + @jakarta.validation.Valid + until.the.eternity.statistics.interfaces.rest.dto.request.TopCategoryWeeklyStatisticsSearchRequest + request) { + java.util.List results = + service.search(request.topCategory(), request.startDate(), request.endDate()); + return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java new file mode 100644 index 00000000..757d52ff --- /dev/null +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java @@ -0,0 +1,27 @@ +package until.the.eternity.statistics.interfaces.rest.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; + +@Schema(description = "아이템별 일간 통계 검색 요청") +public record ItemDailyStatisticsSearchRequest( + @NotBlank(message = "아이템 이름은 필수입니다") + @Schema(description = "아이템 이름", example = "켈틱 로열 나이트 소드", required = true) + String itemName, + @NotBlank(message = "서브 카테고리는 필수입니다") + @Schema(description = "아이템 서브 카테고리", example = "한손검", required = true) + String subCategory, + @NotBlank(message = "탑 카테고리는 필수입니다") + @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + String topCategory, + @NotNull(message = "시작 일자는 필수입니다") + @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate startDate, + @NotNull(message = "종료 일자는 필수입니다") + @Schema(description = "검색 종료 일자", example = "2025-01-31", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java new file mode 100644 index 00000000..8781ce22 --- /dev/null +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java @@ -0,0 +1,27 @@ +package until.the.eternity.statistics.interfaces.rest.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; + +@Schema(description = "아이템별 주간 통계 검색 요청") +public record ItemWeeklyStatisticsSearchRequest( + @NotBlank(message = "아이템 이름은 필수입니다") + @Schema(description = "아이템 이름", example = "켈틱 로열 나이트 소드", required = true) + String itemName, + @NotBlank(message = "서브 카테고리는 필수입니다") + @Schema(description = "아이템 서브 카테고리", example = "한손검", required = true) + String subCategory, + @NotBlank(message = "탑 카테고리는 필수입니다") + @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + String topCategory, + @NotNull(message = "시작 일자는 필수입니다") + @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate startDate, + @NotNull(message = "종료 일자는 필수입니다") + @Schema(description = "검색 종료 일자", example = "2025-04-30", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java new file mode 100644 index 00000000..c25ca553 --- /dev/null +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java @@ -0,0 +1,24 @@ +package until.the.eternity.statistics.interfaces.rest.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; + +@Schema(description = "서브카테고리별 일간 통계 검색 요청") +public record SubcategoryDailyStatisticsSearchRequest( + @NotBlank(message = "탑 카테고리는 필수입니다") + @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + String topCategory, + @NotBlank(message = "서브 카테고리는 필수입니다") + @Schema(description = "아이템 서브 카테고리", example = "한손검", required = true) + String subCategory, + @NotNull(message = "시작 일자는 필수입니다") + @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate startDate, + @NotNull(message = "종료 일자는 필수입니다") + @Schema(description = "검색 종료 일자", example = "2025-01-31", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java new file mode 100644 index 00000000..629dbb4a --- /dev/null +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java @@ -0,0 +1,24 @@ +package until.the.eternity.statistics.interfaces.rest.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; + +@Schema(description = "서브카테고리별 주간 통계 검색 요청") +public record SubcategoryWeeklyStatisticsSearchRequest( + @NotBlank(message = "탑 카테고리는 필수입니다") + @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + String topCategory, + @NotBlank(message = "서브 카테고리는 필수입니다") + @Schema(description = "아이템 서브 카테고리", example = "한손검", required = true) + String subCategory, + @NotNull(message = "시작 일자는 필수입니다") + @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate startDate, + @NotNull(message = "종료 일자는 필수입니다") + @Schema(description = "검색 종료 일자", example = "2025-04-30", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java new file mode 100644 index 00000000..1fe04a55 --- /dev/null +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java @@ -0,0 +1,21 @@ +package until.the.eternity.statistics.interfaces.rest.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; + +@Schema(description = "탑카테고리별 일간 통계 검색 요청") +public record TopCategoryDailyStatisticsSearchRequest( + @NotBlank(message = "탑 카테고리는 필수입니다") + @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + String topCategory, + @NotNull(message = "시작 일자는 필수입니다") + @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate startDate, + @NotNull(message = "종료 일자는 필수입니다") + @Schema(description = "검색 종료 일자", example = "2025-01-31", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java new file mode 100644 index 00000000..220c583e --- /dev/null +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java @@ -0,0 +1,21 @@ +package until.the.eternity.statistics.interfaces.rest.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; +import org.springframework.format.annotation.DateTimeFormat; + +@Schema(description = "탑카테고리별 주간 통계 검색 요청") +public record TopCategoryWeeklyStatisticsSearchRequest( + @NotBlank(message = "탑 카테고리는 필수입니다") + @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + String topCategory, + @NotNull(message = "시작 일자는 필수입니다") + @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate startDate, + @NotNull(message = "종료 일자는 필수입니다") + @Schema(description = "검색 종료 일자", example = "2025-04-30", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") + LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/repository/daily/ItemDailyStatisticsRepository.java b/src/main/java/until/the/eternity/statistics/repository/daily/ItemDailyStatisticsRepository.java index 96894e09..d78b5837 100644 --- a/src/main/java/until/the/eternity/statistics/repository/daily/ItemDailyStatisticsRepository.java +++ b/src/main/java/until/the/eternity/statistics/repository/daily/ItemDailyStatisticsRepository.java @@ -1,13 +1,38 @@ package until.the.eternity.statistics.repository.daily; +import java.time.LocalDate; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import until.the.eternity.statistics.domain.entity.daily.ItemDailyStatistics; public interface ItemDailyStatisticsRepository extends JpaRepository { + /** + * 아이템별 일간 통계 조회 + * + * @param itemName 아이템 이름 + * @param subCategory 서브 카테고리 + * @param topCategory 탑 카테고리 + * @param startDate 시작 일자 + * @param endDate 종료 일자 + * @return 해당 조건의 일간 통계 리스트 + */ + @Query( + "SELECT i FROM ItemDailyStatistics i WHERE i.itemName = :itemName " + + "AND i.itemSubCategory = :subCategory AND i.itemTopCategory = :topCategory " + + "AND i.dateAuctionBuy BETWEEN :startDate AND :endDate " + + "ORDER BY i.dateAuctionBuy ASC") + List findByItemAndDateRange( + @Param("itemName") String itemName, + @Param("subCategory") String subCategory, + @Param("topCategory") String topCategory, + @Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate); + /** * 당일 거래된 각 아이템의 통계를 item_daily_statistics 테이블에 upsert * AuctionHistoryScheduler가 실행될 때마다 당일 통계만 업데이트 diff --git a/src/main/java/until/the/eternity/statistics/repository/daily/SubcategoryDailyStatisticsRepository.java b/src/main/java/until/the/eternity/statistics/repository/daily/SubcategoryDailyStatisticsRepository.java index fdb790d8..207547a2 100644 --- a/src/main/java/until/the/eternity/statistics/repository/daily/SubcategoryDailyStatisticsRepository.java +++ b/src/main/java/until/the/eternity/statistics/repository/daily/SubcategoryDailyStatisticsRepository.java @@ -1,14 +1,34 @@ package until.the.eternity.statistics.repository.daily; +import java.time.LocalDate; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import until.the.eternity.statistics.domain.entity.daily.SubcategoryDailyStatistics; public interface SubcategoryDailyStatisticsRepository extends JpaRepository { + /** + * 서브카테고리별 일간 통계 조회 + * + * @param subCategory 서브 카테고리 + * @param startDate 시작 일자 + * @param endDate 종료 일자 + * @return 해당 조건의 일간 통계 리스트 + */ + @Query( + "SELECT s FROM SubcategoryDailyStatistics s WHERE s.itemSubCategory = :subCategory " + + "AND s.dateAuctionBuy BETWEEN :startDate AND :endDate " + + "ORDER BY s.dateAuctionBuy ASC") + List findBySubcategoryAndDateRange( + @Param("subCategory") String subCategory, + @Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate); + /** * 당일의 ItemDailyStatistics 데이터를 기반으로 서브카테고리별 통계를 집계하여 upsert * item_daily_statistics 테이블만 사용하여 효율적으로 집계 diff --git a/src/main/java/until/the/eternity/statistics/repository/daily/TopCategoryDailyStatisticsRepository.java b/src/main/java/until/the/eternity/statistics/repository/daily/TopCategoryDailyStatisticsRepository.java index 555ca79c..148fad05 100644 --- a/src/main/java/until/the/eternity/statistics/repository/daily/TopCategoryDailyStatisticsRepository.java +++ b/src/main/java/until/the/eternity/statistics/repository/daily/TopCategoryDailyStatisticsRepository.java @@ -1,14 +1,34 @@ package until.the.eternity.statistics.repository.daily; +import java.time.LocalDate; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import until.the.eternity.statistics.domain.entity.daily.TopCategoryDailyStatistics; public interface TopCategoryDailyStatisticsRepository extends JpaRepository { + /** + * 탑카테고리별 일간 통계 조회 + * + * @param topCategory 탑 카테고리 + * @param startDate 시작 일자 + * @param endDate 종료 일자 + * @return 해당 조건의 일간 통계 리스트 + */ + @Query( + "SELECT t FROM TopCategoryDailyStatistics t WHERE t.itemTopCategory = :topCategory " + + "AND t.dateAuctionBuy BETWEEN :startDate AND :endDate " + + "ORDER BY t.dateAuctionBuy ASC") + List findByTopCategoryAndDateRange( + @Param("topCategory") String topCategory, + @Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate); + /** * 당일의 SubcategoryDailyStatistics 데이터를 기반으로 탑카테고리별 통계를 집계하여 upsert * item_daily_statistics 테이블을 사용하여 top_category 정보를 가져옴 diff --git a/src/main/java/until/the/eternity/statistics/repository/weekly/ItemWeeklyStatisticsRepository.java b/src/main/java/until/the/eternity/statistics/repository/weekly/ItemWeeklyStatisticsRepository.java index b77af6e5..cfb3e186 100644 --- a/src/main/java/until/the/eternity/statistics/repository/weekly/ItemWeeklyStatisticsRepository.java +++ b/src/main/java/until/the/eternity/statistics/repository/weekly/ItemWeeklyStatisticsRepository.java @@ -1,13 +1,38 @@ package until.the.eternity.statistics.repository.weekly; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import until.the.eternity.statistics.domain.entity.weekly.ItemWeeklyStatistics; +import until.the.eternity.statistics.util.WeekConverter; public interface ItemWeeklyStatisticsRepository extends JpaRepository { + /** + * 아이템별 주간 통계 조회 + * + * @param itemName 아이템 이름 + * @param subCategory 서브 카테고리 + * @param topCategory 탑 카테고리 + * @param startDate 시작 날짜 + * @param endDate 종료 날짜 + * @return 해당 조건의 주간 통계 리스트 + */ + @Query( + "SELECT i FROM ItemWeeklyStatistics i WHERE i.itemName = :itemName " + + "AND i.itemSubCategory = :subCategory AND i.itemTopCategory = :topCategory " + + "AND i.weekStartDate BETWEEN :startDate AND :endDate " + + "ORDER BY i.year ASC, i.weekNumber ASC") + List findByItemAndDateRange( + @Param("itemName") String itemName, + @Param("subCategory") String subCategory, + @Param("topCategory") String topCategory, + @Param("startDate") java.time.LocalDate startDate, + @Param("endDate") java.time.LocalDate endDate); + /** 전주(지난 주 월~일)의 ItemDailyStatistics 데이터를 기반으로 아이템별 주간 통계를 집계하여 upsert */ @Modifying(clearAutomatically = true, flushAutomatically = true) @Transactional diff --git a/src/main/java/until/the/eternity/statistics/repository/weekly/SubcategoryWeeklyStatisticsRepository.java b/src/main/java/until/the/eternity/statistics/repository/weekly/SubcategoryWeeklyStatisticsRepository.java index d2b7559a..8fee807b 100644 --- a/src/main/java/until/the/eternity/statistics/repository/weekly/SubcategoryWeeklyStatisticsRepository.java +++ b/src/main/java/until/the/eternity/statistics/repository/weekly/SubcategoryWeeklyStatisticsRepository.java @@ -1,14 +1,34 @@ package until.the.eternity.statistics.repository.weekly; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import until.the.eternity.statistics.domain.entity.weekly.SubcategoryWeeklyStatistics; +import until.the.eternity.statistics.util.WeekConverter; public interface SubcategoryWeeklyStatisticsRepository extends JpaRepository { + /** + * 서브카테고리별 주간 통계 조회 + * + * @param subCategory 서브 카테고리 + * @param startDate 시작 날짜 + * @param endDate 종료 날짜 + * @return 해당 조건의 주간 통계 리스트 + */ + @Query( + "SELECT s FROM SubcategoryWeeklyStatistics s WHERE s.itemSubCategory = :subCategory " + + "AND s.weekStartDate BETWEEN :startDate AND :endDate " + + "ORDER BY s.year ASC, s.weekNumber ASC") + List findBySubcategoryAndDateRange( + @Param("subCategory") String subCategory, + @Param("startDate") java.time.LocalDate startDate, + @Param("endDate") java.time.LocalDate endDate); + /** 전주의 ItemWeeklyStatistics 데이터를 기반으로 서브카테고리별 주간 통계를 집계하여 upsert */ @Modifying(clearAutomatically = true, flushAutomatically = true) @Transactional diff --git a/src/main/java/until/the/eternity/statistics/repository/weekly/TopCategoryWeeklyStatisticsRepository.java b/src/main/java/until/the/eternity/statistics/repository/weekly/TopCategoryWeeklyStatisticsRepository.java index b6bb0dc1..a13c2420 100644 --- a/src/main/java/until/the/eternity/statistics/repository/weekly/TopCategoryWeeklyStatisticsRepository.java +++ b/src/main/java/until/the/eternity/statistics/repository/weekly/TopCategoryWeeklyStatisticsRepository.java @@ -1,14 +1,34 @@ package until.the.eternity.statistics.repository.weekly; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import until.the.eternity.statistics.domain.entity.weekly.TopCategoryWeeklyStatistics; +import until.the.eternity.statistics.util.WeekConverter; public interface TopCategoryWeeklyStatisticsRepository extends JpaRepository { + /** + * 탑카테고리별 주간 통계 조회 + * + * @param topCategory 탑 카테고리 + * @param startDate 시작 날짜 + * @param endDate 종료 날짜 + * @return 해당 조건의 주간 통계 리스트 + */ + @Query( + "SELECT t FROM TopCategoryWeeklyStatistics t WHERE t.itemTopCategory = :topCategory " + + "AND t.weekStartDate BETWEEN :startDate AND :endDate " + + "ORDER BY t.year ASC, t.weekNumber ASC") + List findByTopCategoryAndDateRange( + @Param("topCategory") String topCategory, + @Param("startDate") java.time.LocalDate startDate, + @Param("endDate") java.time.LocalDate endDate); + /** 전주의 SubcategoryWeeklyStatistics 데이터를 기반으로 탑카테고리별 주간 통계를 집계하여 upsert */ @Modifying(clearAutomatically = true, flushAutomatically = true) @Transactional diff --git a/src/main/java/until/the/eternity/statistics/util/DateRangeValidator.java b/src/main/java/until/the/eternity/statistics/util/DateRangeValidator.java new file mode 100644 index 00000000..4aa75f71 --- /dev/null +++ b/src/main/java/until/the/eternity/statistics/util/DateRangeValidator.java @@ -0,0 +1,61 @@ +package until.the.eternity.statistics.util; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; + +public class DateRangeValidator { + + private static final long DAILY_MAX_DAYS = 30; + private static final long WEEKLY_MAX_DAYS = 120; // 4개월 (약 120일) + + /** + * Daily 통계 조회 날짜 범위 검증 (최대 30일) + * + * @param startDate 시작 일자 + * @param endDate 종료 일자 + * @throws IllegalArgumentException 날짜 범위가 유효하지 않은 경우 + */ + public static void validateDailyDateRange(LocalDate startDate, LocalDate endDate) { + validateBasicDateRange(startDate, endDate); + + long daysBetween = ChronoUnit.DAYS.between(startDate, endDate); + if (daysBetween > DAILY_MAX_DAYS) { + throw new IllegalArgumentException( + String.format( + "일간 통계 조회는 최대 %d일까지만 가능합니다. 요청 기간: %d일", + DAILY_MAX_DAYS, daysBetween)); + } + } + + /** + * Weekly 통계 조회 날짜 범위 검증 (최대 4개월, 약 120일) + * + * @param startDate 시작 일자 + * @param endDate 종료 일자 + * @throws IllegalArgumentException 날짜 범위가 유효하지 않은 경우 + */ + public static void validateWeeklyDateRange(LocalDate startDate, LocalDate endDate) { + validateBasicDateRange(startDate, endDate); + + long daysBetween = ChronoUnit.DAYS.between(startDate, endDate); + if (daysBetween > WEEKLY_MAX_DAYS) { + throw new IllegalArgumentException( + String.format( + "주간 통계 조회는 최대 4개월(%d일)까지만 가능합니다. 요청 기간: %d일", + WEEKLY_MAX_DAYS, daysBetween)); + } + } + + /** + * 기본 날짜 범위 검증 + * + * @param startDate 시작 일자 + * @param endDate 종료 일자 + * @throws IllegalArgumentException 시작일이 종료일보다 늦은 경우 + */ + private static void validateBasicDateRange(LocalDate startDate, LocalDate endDate) { + if (startDate.isAfter(endDate)) { + throw new IllegalArgumentException("시작 일자는 종료 일자보다 이전이어야 합니다."); + } + } +} diff --git a/src/main/java/until/the/eternity/statistics/util/WeekConverter.java b/src/main/java/until/the/eternity/statistics/util/WeekConverter.java new file mode 100644 index 00000000..a90adda2 --- /dev/null +++ b/src/main/java/until/the/eternity/statistics/util/WeekConverter.java @@ -0,0 +1,42 @@ +package until.the.eternity.statistics.util; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.IsoFields; +import java.time.temporal.TemporalAdjusters; +import java.util.ArrayList; +import java.util.List; + +public class WeekConverter { + + /** + * 날짜 범위를 Year-Week 리스트로 변환 ISO 8601 주 번호 체계 사용 (월요일 시작) + * + * @param startDate 시작 일자 + * @param endDate 종료 일자 + * @return Year-Week 조합 리스트 + */ + public static List convertToYearWeekList(LocalDate startDate, LocalDate endDate) { + List yearWeeks = new ArrayList<>(); + + // 시작 날짜가 속한 주의 월요일 + LocalDate current = startDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + + // 종료 날짜가 속한 주의 월요일 + LocalDate endWeekStart = endDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + + while (!current.isAfter(endWeekStart)) { + int year = current.get(IsoFields.WEEK_BASED_YEAR); + int weekNumber = current.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); + yearWeeks.add(new YearWeek(year, weekNumber)); + + // 다음 주로 이동 + current = current.plusWeeks(1); + } + + return yearWeeks; + } + + /** Year-Week 조합을 나타내는 record */ + public record YearWeek(int year, int weekNumber) {} +} From 881d0110808ddaa45aba137c6ee62f85b0edeef0 Mon Sep 17 00:00:00 2001 From: dev-ant Date: Wed, 14 Jan 2026 12:37:44 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=EA=B2=BD=EB=A7=A4=EC=9E=A5=20?= =?UTF-8?q?=EA=B1=B0=EB=9E=98=20=EB=82=B4=EC=97=AD=20=ED=86=B5=EA=B3=84=20?= =?UTF-8?q?API=20Swagger=20example=20=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rest/controller/ItemDailyStatisticsController.java | 6 ++++-- .../dto/request/ItemDailyStatisticsSearchRequest.java | 10 +++++----- .../SubcategoryDailyStatisticsSearchRequest.java | 8 ++++---- .../TopCategoryDailyStatisticsSearchRequest.java | 6 +++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java index 1d6ee170..1dee9b8f 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java @@ -3,6 +3,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.ResponseEntity; @@ -10,6 +11,7 @@ import until.the.eternity.common.request.PageRequestDto; import until.the.eternity.common.response.PageResponseDto; import until.the.eternity.statistics.application.service.ItemDailyStatisticsService; +import until.the.eternity.statistics.interfaces.rest.dto.request.ItemDailyStatisticsSearchRequest; import until.the.eternity.statistics.interfaces.rest.dto.response.ItemDailyStatisticsResponse; @RestController @@ -26,8 +28,8 @@ public class ItemDailyStatisticsController { description = "아이템 이름, 서브 카테고리, 탑 카테고리로 일간 통계를 조회합니다. 최대 30일까지 조회 가능합니다.") public ResponseEntity> searchItemDailyStatistics( @ParameterObject @ModelAttribute - @jakarta.validation.Valid - until.the.eternity.statistics.interfaces.rest.dto.request.ItemDailyStatisticsSearchRequest + @Valid + ItemDailyStatisticsSearchRequest request) { java.util.List results = service.search( diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java index 757d52ff..51b99063 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java @@ -9,19 +9,19 @@ @Schema(description = "아이템별 일간 통계 검색 요청") public record ItemDailyStatisticsSearchRequest( @NotBlank(message = "아이템 이름은 필수입니다") - @Schema(description = "아이템 이름", example = "켈틱 로열 나이트 소드", required = true) + @Schema(description = "아이템 이름", example = "축복의 포션", required = true) String itemName, @NotBlank(message = "서브 카테고리는 필수입니다") - @Schema(description = "아이템 서브 카테고리", example = "한손검", required = true) + @Schema(description = "아이템 서브 카테고리", example = "포션", required = true) String subCategory, @NotBlank(message = "탑 카테고리는 필수입니다") - @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2025-01-31", required = true) + @Schema(description = "검색 종료 일자", example = "2026-01-31", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java index c25ca553..72efef8f 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java @@ -9,16 +9,16 @@ @Schema(description = "서브카테고리별 일간 통계 검색 요청") public record SubcategoryDailyStatisticsSearchRequest( @NotBlank(message = "탑 카테고리는 필수입니다") - @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, @NotBlank(message = "서브 카테고리는 필수입니다") - @Schema(description = "아이템 서브 카테고리", example = "한손검", required = true) + @Schema(description = "아이템 서브 카테고리", example = "포션", required = true) String subCategory, @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2025-01-31", required = true) + @Schema(description = "검색 종료 일자", example = "2026-01-31", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java index 1fe04a55..1fc146c9 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java @@ -9,13 +9,13 @@ @Schema(description = "탑카테고리별 일간 통계 검색 요청") public record TopCategoryDailyStatisticsSearchRequest( @NotBlank(message = "탑 카테고리는 필수입니다") - @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2025-01-31", required = true) + @Schema(description = "검색 종료 일자", example = "2026-01-31", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {} From ea13562efdc0a86d45bd9a323d8545f796322bc0 Mon Sep 17 00:00:00 2001 From: dev-ant Date: Wed, 14 Jan 2026 12:40:43 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=EA=B2=BD=EB=A7=A4=EC=9E=A5=20?= =?UTF-8?q?=EA=B1=B0=EB=9E=98=20=EB=82=B4=EC=97=AD=20=EC=A3=BC=EA=B0=84=20?= =?UTF-8?q?=ED=86=B5=EA=B3=84=20API=20Swagger=20example=20=EA=B0=92=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/ItemWeeklyStatisticsSearchRequest.java | 10 +++++----- .../SubcategoryWeeklyStatisticsSearchRequest.java | 8 ++++---- .../TopCategoryWeeklyStatisticsSearchRequest.java | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java index 8781ce22..4cb56f08 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java @@ -9,19 +9,19 @@ @Schema(description = "아이템별 주간 통계 검색 요청") public record ItemWeeklyStatisticsSearchRequest( @NotBlank(message = "아이템 이름은 필수입니다") - @Schema(description = "아이템 이름", example = "켈틱 로열 나이트 소드", required = true) + @Schema(description = "아이템 이름", example = "축복의 포션", required = true) String itemName, @NotBlank(message = "서브 카테고리는 필수입니다") - @Schema(description = "아이템 서브 카테고리", example = "한손검", required = true) + @Schema(description = "아이템 서브 카테고리", example = "포션", required = true) String subCategory, @NotBlank(message = "탑 카테고리는 필수입니다") - @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2025-04-30", required = true) + @Schema(description = "검색 종료 일자", example = "2026-04-30", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java index 629dbb4a..6f4f323d 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java @@ -9,16 +9,16 @@ @Schema(description = "서브카테고리별 주간 통계 검색 요청") public record SubcategoryWeeklyStatisticsSearchRequest( @NotBlank(message = "탑 카테고리는 필수입니다") - @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, @NotBlank(message = "서브 카테고리는 필수입니다") - @Schema(description = "아이템 서브 카테고리", example = "한손검", required = true) + @Schema(description = "아이템 서브 카테고리", example = "포션", required = true) String subCategory, @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2025-04-30", required = true) + @Schema(description = "검색 종료 일자", example = "2026-04-30", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java index 220c583e..e10d4f7a 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java @@ -9,13 +9,13 @@ @Schema(description = "탑카테고리별 주간 통계 검색 요청") public record TopCategoryWeeklyStatisticsSearchRequest( @NotBlank(message = "탑 카테고리는 필수입니다") - @Schema(description = "아이템 탑 카테고리", example = "무기", required = true) + @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2025-01-01", required = true) + @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2025-04-30", required = true) + @Schema(description = "검색 종료 일자", example = "2026-04-30", required = true) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {} From f5a6586a406e61d04518f49820641e061ca68b8b Mon Sep 17 00:00:00 2001 From: dev-ant Date: Wed, 14 Jan 2026 20:39:12 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat;=20auction=20history=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ItemDailyStatisticsController.java | 4 ++-- .../ItemWeeklyStatisticsController.java | 4 ++-- .../SubcategoryDailyStatisticsController.java | 4 ++-- .../SubcategoryWeeklyStatisticsController.java | 4 ++-- .../TopCategoryDailyStatisticsController.java | 2 +- .../TopCategoryWeeklyStatisticsController.java | 2 +- .../ItemDailyStatisticsSearchRequest.java | 18 ++++++++++++------ .../ItemWeeklyStatisticsSearchRequest.java | 18 ++++++++++++------ ...ubcategoryDailyStatisticsSearchRequest.java | 18 ++++++++++++------ ...bcategoryWeeklyStatisticsSearchRequest.java | 18 ++++++++++++------ ...opCategoryDailyStatisticsSearchRequest.java | 18 ++++++++++++------ ...pCategoryWeeklyStatisticsSearchRequest.java | 18 ++++++++++++------ 12 files changed, 82 insertions(+), 46 deletions(-) diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java index 1dee9b8f..377f6cd1 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemDailyStatisticsController.java @@ -36,8 +36,8 @@ public ResponseEntity> searchItemDai request.itemName(), request.subCategory(), request.topCategory(), - request.startDate(), - request.endDate()); + request.getStartDateWithDefault(), + request.getEndDateWithDefault()); return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemWeeklyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemWeeklyStatisticsController.java index dcafa264..2c8a5a9b 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemWeeklyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/ItemWeeklyStatisticsController.java @@ -34,8 +34,8 @@ public ResponseEntity> searchItemWe request.itemName(), request.subCategory(), request.topCategory(), - request.startDate(), - request.endDate()); + request.getStartDateWithDefault(), + request.getEndDateWithDefault()); return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryDailyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryDailyStatisticsController.java index c4a9d376..f1d0703b 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryDailyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryDailyStatisticsController.java @@ -34,8 +34,8 @@ public class SubcategoryDailyStatisticsController { service.search( request.topCategory(), request.subCategory(), - request.startDate(), - request.endDate()); + request.getStartDateWithDefault(), + request.getEndDateWithDefault()); return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryWeeklyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryWeeklyStatisticsController.java index 4af20f58..d607cae4 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryWeeklyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/SubcategoryWeeklyStatisticsController.java @@ -34,8 +34,8 @@ public class SubcategoryWeeklyStatisticsController { service.search( request.topCategory(), request.subCategory(), - request.startDate(), - request.endDate()); + request.getStartDateWithDefault(), + request.getEndDateWithDefault()); return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryDailyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryDailyStatisticsController.java index 48340b59..718996d7 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryDailyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryDailyStatisticsController.java @@ -31,7 +31,7 @@ public class TopCategoryDailyStatisticsController { until.the.eternity.statistics.interfaces.rest.dto.request.TopCategoryDailyStatisticsSearchRequest request) { java.util.List results = - service.search(request.topCategory(), request.startDate(), request.endDate()); + service.search(request.topCategory(), request.getStartDateWithDefault(), request.getEndDateWithDefault()); return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryWeeklyStatisticsController.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryWeeklyStatisticsController.java index ba870332..b37a1287 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryWeeklyStatisticsController.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/controller/TopCategoryWeeklyStatisticsController.java @@ -31,7 +31,7 @@ public class TopCategoryWeeklyStatisticsController { until.the.eternity.statistics.interfaces.rest.dto.request.TopCategoryWeeklyStatisticsSearchRequest request) { java.util.List results = - service.search(request.topCategory(), request.startDate(), request.endDate()); + service.search(request.topCategory(), request.getStartDateWithDefault(), request.getEndDateWithDefault()); return ResponseEntity.ok(results); } } diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java index 51b99063..f6f91521 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemDailyStatisticsSearchRequest.java @@ -2,7 +2,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; @@ -17,11 +16,18 @@ public record ItemDailyStatisticsSearchRequest( @NotBlank(message = "탑 카테고리는 필수입니다") @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, - @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) + @Schema(description = "검색 시작 일자 (미입력 시 오늘로부터 2주 전)", example = "2026-01-01") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, - @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2026-01-31", required = true) + @Schema(description = "검색 종료 일자 (미입력 시 오늘)", example = "2026-01-31") @DateTimeFormat(pattern = "yyyy-MM-dd") - LocalDate endDate) {} + LocalDate endDate) { + + public LocalDate getStartDateWithDefault() { + return startDate != null ? startDate : LocalDate.now().minusDays(14); + } + + public LocalDate getEndDateWithDefault() { + return endDate != null ? endDate : LocalDate.now(); + } +} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java index 4cb56f08..1360de10 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/ItemWeeklyStatisticsSearchRequest.java @@ -2,7 +2,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; @@ -17,11 +16,18 @@ public record ItemWeeklyStatisticsSearchRequest( @NotBlank(message = "탑 카테고리는 필수입니다") @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, - @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) + @Schema(description = "검색 시작 일자 (미입력 시 오늘로부터 2달 전)", example = "2026-01-01") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, - @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2026-04-30", required = true) + @Schema(description = "검색 종료 일자 (미입력 시 오늘)", example = "2026-04-30") @DateTimeFormat(pattern = "yyyy-MM-dd") - LocalDate endDate) {} + LocalDate endDate) { + + public LocalDate getStartDateWithDefault() { + return startDate != null ? startDate : LocalDate.now().minusMonths(2); + } + + public LocalDate getEndDateWithDefault() { + return endDate != null ? endDate : LocalDate.now(); + } +} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java index 72efef8f..47c2c966 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryDailyStatisticsSearchRequest.java @@ -2,7 +2,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; @@ -14,11 +13,18 @@ public record SubcategoryDailyStatisticsSearchRequest( @NotBlank(message = "서브 카테고리는 필수입니다") @Schema(description = "아이템 서브 카테고리", example = "포션", required = true) String subCategory, - @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) + @Schema(description = "검색 시작 일자 (미입력 시 오늘로부터 2주 전)", example = "2026-01-01") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, - @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2026-01-31", required = true) + @Schema(description = "검색 종료 일자 (미입력 시 오늘)", example = "2026-01-31") @DateTimeFormat(pattern = "yyyy-MM-dd") - LocalDate endDate) {} + LocalDate endDate) { + + public LocalDate getStartDateWithDefault() { + return startDate != null ? startDate : LocalDate.now().minusDays(14); + } + + public LocalDate getEndDateWithDefault() { + return endDate != null ? endDate : LocalDate.now(); + } +} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java index 6f4f323d..9d7bc716 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/SubcategoryWeeklyStatisticsSearchRequest.java @@ -2,7 +2,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; @@ -14,11 +13,18 @@ public record SubcategoryWeeklyStatisticsSearchRequest( @NotBlank(message = "서브 카테고리는 필수입니다") @Schema(description = "아이템 서브 카테고리", example = "포션", required = true) String subCategory, - @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) + @Schema(description = "검색 시작 일자 (미입력 시 오늘로부터 2달 전)", example = "2026-01-01") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, - @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2026-04-30", required = true) + @Schema(description = "검색 종료 일자 (미입력 시 오늘)", example = "2026-04-30") @DateTimeFormat(pattern = "yyyy-MM-dd") - LocalDate endDate) {} + LocalDate endDate) { + + public LocalDate getStartDateWithDefault() { + return startDate != null ? startDate : LocalDate.now().minusMonths(2); + } + + public LocalDate getEndDateWithDefault() { + return endDate != null ? endDate : LocalDate.now(); + } +} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java index 1fc146c9..4b8e25d7 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryDailyStatisticsSearchRequest.java @@ -2,7 +2,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; @@ -11,11 +10,18 @@ public record TopCategoryDailyStatisticsSearchRequest( @NotBlank(message = "탑 카테고리는 필수입니다") @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, - @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) + @Schema(description = "검색 시작 일자 (미입력 시 오늘로부터 2주 전)", example = "2026-01-01") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, - @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2026-01-31", required = true) + @Schema(description = "검색 종료 일자 (미입력 시 오늘)", example = "2026-01-31") @DateTimeFormat(pattern = "yyyy-MM-dd") - LocalDate endDate) {} + LocalDate endDate) { + + public LocalDate getStartDateWithDefault() { + return startDate != null ? startDate : LocalDate.now().minusDays(14); + } + + public LocalDate getEndDateWithDefault() { + return endDate != null ? endDate : LocalDate.now(); + } +} diff --git a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java index e10d4f7a..ad1c6b41 100644 --- a/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java +++ b/src/main/java/until/the/eternity/statistics/interfaces/rest/dto/request/TopCategoryWeeklyStatisticsSearchRequest.java @@ -2,7 +2,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; @@ -11,11 +10,18 @@ public record TopCategoryWeeklyStatisticsSearchRequest( @NotBlank(message = "탑 카테고리는 필수입니다") @Schema(description = "아이템 탑 카테고리", example = "소모품", required = true) String topCategory, - @NotNull(message = "시작 일자는 필수입니다") - @Schema(description = "검색 시작 일자", example = "2026-01-01", required = true) + @Schema(description = "검색 시작 일자 (미입력 시 오늘로부터 2달 전)", example = "2026-01-01") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, - @NotNull(message = "종료 일자는 필수입니다") - @Schema(description = "검색 종료 일자", example = "2026-04-30", required = true) + @Schema(description = "검색 종료 일자 (미입력 시 오늘)", example = "2026-04-30") @DateTimeFormat(pattern = "yyyy-MM-dd") - LocalDate endDate) {} + LocalDate endDate) { + + public LocalDate getStartDateWithDefault() { + return startDate != null ? startDate : LocalDate.now().minusMonths(2); + } + + public LocalDate getEndDateWithDefault() { + return endDate != null ? endDate : LocalDate.now(); + } +} From 6fb6066716ae930875d402fa69c9615834b9a482 Mon Sep 17 00:00:00 2001 From: dev-ant Date: Thu, 15 Jan 2026 12:47:04 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20statics=20test=20code=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ItemDailyStatisticsServiceTest.java | 122 ------------------ .../ItemWeeklyStatisticsServiceTest.java | 98 -------------- ...SubcategoryDailyStatisticsServiceTest.java | 94 -------------- ...ubcategoryWeeklyStatisticsServiceTest.java | 36 ------ ...TopCategoryDailyStatisticsServiceTest.java | 36 ------ ...opCategoryWeeklyStatisticsServiceTest.java | 36 ------ 6 files changed, 422 deletions(-) delete mode 100644 src/test/java/until/the/eternity/statistics/application/service/ItemDailyStatisticsServiceTest.java delete mode 100644 src/test/java/until/the/eternity/statistics/application/service/ItemWeeklyStatisticsServiceTest.java delete mode 100644 src/test/java/until/the/eternity/statistics/application/service/SubcategoryDailyStatisticsServiceTest.java delete mode 100644 src/test/java/until/the/eternity/statistics/application/service/SubcategoryWeeklyStatisticsServiceTest.java delete mode 100644 src/test/java/until/the/eternity/statistics/application/service/TopCategoryDailyStatisticsServiceTest.java delete mode 100644 src/test/java/until/the/eternity/statistics/application/service/TopCategoryWeeklyStatisticsServiceTest.java diff --git a/src/test/java/until/the/eternity/statistics/application/service/ItemDailyStatisticsServiceTest.java b/src/test/java/until/the/eternity/statistics/application/service/ItemDailyStatisticsServiceTest.java deleted file mode 100644 index d9c3cbc5..00000000 --- a/src/test/java/until/the/eternity/statistics/application/service/ItemDailyStatisticsServiceTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package until.the.eternity.statistics.application.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.*; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import until.the.eternity.common.response.PageResponseDto; -import until.the.eternity.statistics.domain.entity.daily.ItemDailyStatistics; -import until.the.eternity.statistics.domain.mapper.ItemDailyStatisticsMapper; -import until.the.eternity.statistics.interfaces.rest.dto.response.ItemDailyStatisticsResponse; -import until.the.eternity.statistics.repository.daily.ItemDailyStatisticsRepository; - -@ExtendWith(MockitoExtension.class) -class ItemDailyStatisticsServiceTest { - - @Mock private ItemDailyStatisticsRepository repository; - @Mock private ItemDailyStatisticsMapper mapper; - - @InjectMocks private ItemDailyStatisticsService service; - - @Test - @DisplayName("findAll은 페이징된 통계 목록을 반환한다") - void findAll_should_return_paged_statistics() { - // given - Pageable pageable = PageRequest.of(0, 10); - ItemDailyStatistics entity = createMockEntity(); - ItemDailyStatisticsResponse response = createMockResponse(); - Page entityPage = new PageImpl<>(List.of(entity), pageable, 1); - - when(repository.findAll(pageable)).thenReturn(entityPage); - when(mapper.toDto(entity)).thenReturn(response); - - // when - PageResponseDto result = service.findAll(pageable); - - // then - assertThat(result.items()).hasSize(1).contains(response); - assertThat(result.meta().totalElements()).isEqualTo(1); - verify(repository).findAll(pageable); - verify(mapper).toDto(entity); - } - - @Test - @DisplayName("findById는 ID에 해당하는 통계를 반환한다") - void findById_should_return_statistics_when_exists() { - // given - Long id = 1L; - ItemDailyStatistics entity = createMockEntity(); - ItemDailyStatisticsResponse response = createMockResponse(); - - when(repository.findById(id)).thenReturn(Optional.of(entity)); - when(mapper.toDto(entity)).thenReturn(response); - - // when - ItemDailyStatisticsResponse result = service.findById(id); - - // then - assertThat(result).isEqualTo(response); - verify(repository).findById(id); - verify(mapper).toDto(entity); - } - - @Test - @DisplayName("findById는 데이터가 없으면 예외를 발생시킨다") - void findById_should_throw_exception_when_not_exists() { - // given - Long id = 999L; - when(repository.findById(id)).thenReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> service.findById(id)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("ItemDailyStatistics not found"); - - verify(repository).findById(id); - verify(mapper, never()).toDto(any()); - } - - private ItemDailyStatistics createMockEntity() { - return ItemDailyStatistics.builder() - .id(1L) - .itemName("Test Item") - .dateAuctionBuy(LocalDate.of(2025, 7, 1)) - .minPrice(100000L) - .maxPrice(150000L) - .avgPrice(new BigDecimal("125000.00")) - .totalVolume(5000000L) - .totalQuantity(100L) - .createdAt(LocalDateTime.now()) - .updatedAt(LocalDateTime.now()) - .build(); - } - - private ItemDailyStatisticsResponse createMockResponse() { - return new ItemDailyStatisticsResponse( - 1L, - "Test Item", - LocalDate.of(2025, 7, 1), - 100000L, - 150000L, - new BigDecimal("125000.00"), - 5000000L, - 100L, - LocalDateTime.now(), - LocalDateTime.now()); - } -} diff --git a/src/test/java/until/the/eternity/statistics/application/service/ItemWeeklyStatisticsServiceTest.java b/src/test/java/until/the/eternity/statistics/application/service/ItemWeeklyStatisticsServiceTest.java deleted file mode 100644 index b7067b2b..00000000 --- a/src/test/java/until/the/eternity/statistics/application/service/ItemWeeklyStatisticsServiceTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package until.the.eternity.statistics.application.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import until.the.eternity.common.response.PageResponseDto; -import until.the.eternity.statistics.domain.entity.weekly.ItemWeeklyStatistics; -import until.the.eternity.statistics.domain.mapper.ItemWeeklyStatisticsMapper; -import until.the.eternity.statistics.interfaces.rest.dto.response.ItemWeeklyStatisticsResponse; -import until.the.eternity.statistics.repository.weekly.ItemWeeklyStatisticsRepository; - -@ExtendWith(MockitoExtension.class) -class ItemWeeklyStatisticsServiceTest { - - @Mock private ItemWeeklyStatisticsRepository repository; - @Mock private ItemWeeklyStatisticsMapper mapper; - - @InjectMocks private ItemWeeklyStatisticsService service; - - @Test - @DisplayName("findAll은 페이징된 주간 통계 목록을 반환한다") - void findAll_should_return_paged_statistics() { - // given - Pageable pageable = PageRequest.of(0, 10); - ItemWeeklyStatistics entity = - ItemWeeklyStatistics.builder() - .id(1L) - .itemName("Test Item") - .year(2025) - .weekNumber(27) - .weekStartDate(LocalDate.of(2025, 7, 1)) - .minPrice(100000L) - .maxPrice(150000L) - .avgPrice(new BigDecimal("125000.00")) - .totalVolume(35000000L) - .totalQuantity(700L) - .createdAt(LocalDateTime.now()) - .updatedAt(LocalDateTime.now()) - .build(); - - ItemWeeklyStatisticsResponse response = - new ItemWeeklyStatisticsResponse( - 1L, - "Test Item", - 2025, - 27, - LocalDate.of(2025, 7, 1), - 100000L, - 150000L, - new BigDecimal("125000.00"), - 35000000L, - 700L, - LocalDateTime.now(), - LocalDateTime.now()); - - Page entityPage = new PageImpl<>(List.of(entity), pageable, 1); - - when(repository.findAll(pageable)).thenReturn(entityPage); - when(mapper.toDto(entity)).thenReturn(response); - - // when - PageResponseDto result = service.findAll(pageable); - - // then - assertThat(result.items()).hasSize(1).contains(response); - verify(repository).findAll(pageable); - } - - @Test - @DisplayName("findById는 데이터가 없으면 예외를 발생시킨다") - void findById_should_throw_exception_when_not_exists() { - // given - Long id = 999L; - when(repository.findById(id)).thenReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> service.findById(id)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("ItemWeeklyStatistics not found"); - } -} diff --git a/src/test/java/until/the/eternity/statistics/application/service/SubcategoryDailyStatisticsServiceTest.java b/src/test/java/until/the/eternity/statistics/application/service/SubcategoryDailyStatisticsServiceTest.java deleted file mode 100644 index 9eff9c8a..00000000 --- a/src/test/java/until/the/eternity/statistics/application/service/SubcategoryDailyStatisticsServiceTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package until.the.eternity.statistics.application.service; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import until.the.eternity.common.response.PageResponseDto; -import until.the.eternity.statistics.domain.entity.daily.SubcategoryDailyStatistics; -import until.the.eternity.statistics.domain.mapper.SubcategoryDailyStatisticsMapper; -import until.the.eternity.statistics.interfaces.rest.dto.response.SubcategoryDailyStatisticsResponse; -import until.the.eternity.statistics.repository.daily.SubcategoryDailyStatisticsRepository; - -@ExtendWith(MockitoExtension.class) -class SubcategoryDailyStatisticsServiceTest { - - @Mock private SubcategoryDailyStatisticsRepository repository; - @Mock private SubcategoryDailyStatisticsMapper mapper; - - @InjectMocks private SubcategoryDailyStatisticsService service; - - @Test - @DisplayName("findAll은 페이징된 통계 목록을 반환한다") - void findAll_should_return_paged_statistics() { - // given - Pageable pageable = PageRequest.of(0, 10); - SubcategoryDailyStatistics entity = - SubcategoryDailyStatistics.builder() - .id(1L) - .itemSubCategory("한손검") - .dateAuctionBuy(LocalDate.of(2025, 7, 1)) - .minPrice(100000L) - .maxPrice(150000L) - .avgPrice(new BigDecimal("125000.00")) - .totalVolume(50000000L) - .totalQuantity(1000L) - .createdAt(LocalDateTime.now()) - .updatedAt(LocalDateTime.now()) - .build(); - - SubcategoryDailyStatisticsResponse response = - new SubcategoryDailyStatisticsResponse( - 1L, - "한손검", - LocalDate.of(2025, 7, 1), - 100000L, - 150000L, - new BigDecimal("125000.00"), - 50000000L, - 1000L, - LocalDateTime.now(), - LocalDateTime.now()); - - Page entityPage = new PageImpl<>(List.of(entity), pageable, 1); - - when(repository.findAll(pageable)).thenReturn(entityPage); - when(mapper.toDto(entity)).thenReturn(response); - - // when - PageResponseDto result = service.findAll(pageable); - - // then - assertThat(result.items()).hasSize(1).contains(response); - verify(repository).findAll(pageable); - } - - @Test - @DisplayName("findById는 데이터가 없으면 예외를 발생시킨다") - void findById_should_throw_exception_when_not_exists() { - // given - Long id = 999L; - when(repository.findById(id)).thenReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> service.findById(id)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("SubcategoryDailyStatistics not found"); - } -} diff --git a/src/test/java/until/the/eternity/statistics/application/service/SubcategoryWeeklyStatisticsServiceTest.java b/src/test/java/until/the/eternity/statistics/application/service/SubcategoryWeeklyStatisticsServiceTest.java deleted file mode 100644 index 889913b3..00000000 --- a/src/test/java/until/the/eternity/statistics/application/service/SubcategoryWeeklyStatisticsServiceTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package until.the.eternity.statistics.application.service; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.when; - -import java.util.Optional; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import until.the.eternity.statistics.domain.mapper.SubcategoryWeeklyStatisticsMapper; -import until.the.eternity.statistics.repository.weekly.SubcategoryWeeklyStatisticsRepository; - -@ExtendWith(MockitoExtension.class) -class SubcategoryWeeklyStatisticsServiceTest { - - @Mock private SubcategoryWeeklyStatisticsRepository repository; - @Mock private SubcategoryWeeklyStatisticsMapper mapper; - - @InjectMocks private SubcategoryWeeklyStatisticsService service; - - @Test - @DisplayName("findById는 데이터가 없으면 예외를 발생시킨다") - void findById_should_throw_exception_when_not_exists() { - // given - Long id = 999L; - when(repository.findById(id)).thenReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> service.findById(id)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("SubcategoryWeeklyStatistics not found"); - } -} diff --git a/src/test/java/until/the/eternity/statistics/application/service/TopCategoryDailyStatisticsServiceTest.java b/src/test/java/until/the/eternity/statistics/application/service/TopCategoryDailyStatisticsServiceTest.java deleted file mode 100644 index 1e1130c6..00000000 --- a/src/test/java/until/the/eternity/statistics/application/service/TopCategoryDailyStatisticsServiceTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package until.the.eternity.statistics.application.service; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.when; - -import java.util.Optional; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import until.the.eternity.statistics.domain.mapper.TopCategoryDailyStatisticsMapper; -import until.the.eternity.statistics.repository.daily.TopCategoryDailyStatisticsRepository; - -@ExtendWith(MockitoExtension.class) -class TopCategoryDailyStatisticsServiceTest { - - @Mock private TopCategoryDailyStatisticsRepository repository; - @Mock private TopCategoryDailyStatisticsMapper mapper; - - @InjectMocks private TopCategoryDailyStatisticsService service; - - @Test - @DisplayName("findById는 데이터가 없으면 예외를 발생시킨다") - void findById_should_throw_exception_when_not_exists() { - // given - Long id = 999L; - when(repository.findById(id)).thenReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> service.findById(id)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("TopCategoryDailyStatistics not found"); - } -} diff --git a/src/test/java/until/the/eternity/statistics/application/service/TopCategoryWeeklyStatisticsServiceTest.java b/src/test/java/until/the/eternity/statistics/application/service/TopCategoryWeeklyStatisticsServiceTest.java deleted file mode 100644 index 1a425c57..00000000 --- a/src/test/java/until/the/eternity/statistics/application/service/TopCategoryWeeklyStatisticsServiceTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package until.the.eternity.statistics.application.service; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.when; - -import java.util.Optional; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import until.the.eternity.statistics.domain.mapper.TopCategoryWeeklyStatisticsMapper; -import until.the.eternity.statistics.repository.weekly.TopCategoryWeeklyStatisticsRepository; - -@ExtendWith(MockitoExtension.class) -class TopCategoryWeeklyStatisticsServiceTest { - - @Mock private TopCategoryWeeklyStatisticsRepository repository; - @Mock private TopCategoryWeeklyStatisticsMapper mapper; - - @InjectMocks private TopCategoryWeeklyStatisticsService service; - - @Test - @DisplayName("findById는 데이터가 없으면 예외를 발생시킨다") - void findById_should_throw_exception_when_not_exists() { - // given - Long id = 999L; - when(repository.findById(id)).thenReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> service.findById(id)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("TopCategoryWeeklyStatistics not found"); - } -}