Skip to content

Commit 0299092

Browse files
dev-antCopilot
andauthored
feat: 경매장 거래 내역 검색 조건 테이블 및 조회 API 구현 (#69)
* refactor: auction history search request dto record로 변경 * feat: : price 검색 dto 추가 * fix: auction history search request dto class 변경 사항 코드 반영 * feat: auction search option meta data flyway script 작성 * feat: auction search option meta ì 조회 API 구현 * feat: auction history search에서 price request dto 제거 * feat: auction search meta service 테스트 코드 추가 * fix; auction history search request 주석 오타 수정 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 0903f6c commit 0299092

File tree

16 files changed

+614
-25
lines changed

16 files changed

+614
-25
lines changed

gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
applicationVersion=0.0.1
33
### Project Config ###
44
projectGroup=until.the.eternity
5+
### Gradle Configuration ###
6+
org.gradle.java.installations.auto-download=true
57
### Project Dependency Versions ###
68
javaVersion=21
79
### Spring Dependency Versions ###

src/main/java/until/the/eternity/auctionhistory/infrastructure/persistence/AuctionHistoryQueryDslRepository.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ public Page<AuctionHistory> search(AuctionHistorySearchRequest condition, Pageab
3939

4040
private BooleanBuilder buildPredicate(AuctionHistorySearchRequest c, QAuctionHistory ah) {
4141
BooleanBuilder builder = new BooleanBuilder();
42-
if (c.getItemTopCategory() != null && !c.getItemTopCategory().isBlank()) {
43-
builder.and(ah.itemTopCategory.eq(c.getItemTopCategory()));
42+
if (c.itemTopCategory() != null && !c.itemTopCategory().isBlank()) {
43+
builder.and(ah.itemTopCategory.eq(c.itemTopCategory()));
4444
}
45-
if (c.getItemSubCategory() != null && !c.getItemSubCategory().isBlank()) {
46-
builder.and(ah.itemSubCategory.eq(c.getItemSubCategory()));
45+
if (c.itemSubCategory() != null && !c.itemSubCategory().isBlank()) {
46+
builder.and(ah.itemSubCategory.eq(c.itemSubCategory()));
4747
}
48-
if (c.getItemName() != null && !c.getItemName().isBlank()) {
49-
builder.and(ah.itemName.containsIgnoreCase(c.getItemName()));
48+
if (c.itemName() != null && !c.itemName().isBlank()) {
49+
builder.and(ah.itemName.containsIgnoreCase(c.itemName()));
5050
}
5151
return builder;
5252
}
Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,12 @@
11
package until.the.eternity.auctionhistory.interfaces.rest.dto.request;
22

33
import io.swagger.v3.oas.annotations.media.Schema;
4-
import lombok.*;
54

65
/** 경매 히스토리 검색 조건 DTO - 페이지네이션 포함 */
7-
@Getter
8-
@Setter
9-
@ToString(callSuper = true)
10-
@NoArgsConstructor
11-
@AllArgsConstructor
12-
@Builder
13-
public class AuctionHistorySearchRequest {
14-
15-
@Schema(description = "아이템 이름 (like 검색)", example = "페러시우스 타이탄 블레이드")
16-
private String itemName;
17-
18-
@Schema(description = "대분류 카테고리", example = "근거리 장비")
19-
private String itemTopCategory;
20-
21-
@Schema(description = "소분류 카테고리", example = "검")
22-
private String itemSubCategory;
23-
}
6+
public record AuctionHistorySearchRequest(
7+
@Schema(description = "아이템 이름 (like 검색)", example = "페러시우스 타이탄 블레이드") String itemName,
8+
@Schema(description = "대분류 카테고리", example = "근거리 장비") String itemTopCategory,
9+
@Schema(description = "소분류 카테고리", example = "검") String itemSubCategory,
10+
// TODO: 거래 가격, 거래 일자를 범위 검색으로 변경, 옵션은 별도의 RequestDTO 구현
11+
@Schema(description = "거래 가격", example = "10000000") String auction_price_per_unit,
12+
@Schema(description = "거래 일자", example = "검") String date_auction_buy) {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package until.the.eternity.auctionhistory.interfaces.rest.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
5+
public record PriceSearchRequest(
6+
@Schema(description = "가격 최소값", example = "0", defaultValue = "0") long PriceTo,
7+
@Schema(description = "가격 최대값", example = "9999999999", defaultValue = "9999999999")
8+
long PriceFrom) {}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package until.the.eternity.auctionsearchoption.application.service;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import java.util.List;
6+
import java.util.Map;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.stereotype.Service;
10+
import org.springframework.transaction.annotation.Transactional;
11+
import until.the.eternity.auctionsearchoption.domain.entity.AuctionSearchOptionMetadata;
12+
import until.the.eternity.auctionsearchoption.domain.repository.AuctionSearchOptionRepositoryPort;
13+
import until.the.eternity.auctionsearchoption.interfaces.rest.dto.response.FieldMetadata;
14+
import until.the.eternity.auctionsearchoption.interfaces.rest.dto.response.SearchOptionMetadataResponse;
15+
16+
@Slf4j
17+
@Service
18+
@RequiredArgsConstructor
19+
public class AuctionSearchOptionService {
20+
21+
private final AuctionSearchOptionRepositoryPort repositoryPort;
22+
private final ObjectMapper objectMapper;
23+
24+
/**
25+
* 모든 활성화된 검색 옵션 조회
26+
*
27+
* @return 검색 옵션 메타데이터 리스트
28+
*/
29+
@Transactional(readOnly = true)
30+
public List<SearchOptionMetadataResponse> getAllActiveSearchOptions() {
31+
List<AuctionSearchOptionMetadata> entities = repositoryPort.findAllActive();
32+
33+
return entities.stream().map(this::toResponse).toList();
34+
}
35+
36+
private SearchOptionMetadataResponse toResponse(AuctionSearchOptionMetadata entity) {
37+
Map<String, FieldMetadata> searchCondition =
38+
parseJsonToFieldMetadata(entity.getSearchConditionJson());
39+
40+
return new SearchOptionMetadataResponse(
41+
entity.getId(),
42+
entity.getSearchOptionName(),
43+
searchCondition,
44+
entity.getDisplayOrder());
45+
}
46+
47+
private Map<String, FieldMetadata> parseJsonToFieldMetadata(String json) {
48+
try {
49+
TypeReference<Map<String, FieldMetadata>> typeRef = new TypeReference<>() {};
50+
return objectMapper.readValue(json, typeRef);
51+
} catch (Exception e) {
52+
log.error("Failed to parse JSON to FieldMetadata: {}", json, e);
53+
throw new IllegalStateException("JSON 파싱 실패", e);
54+
}
55+
}
56+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package until.the.eternity.auctionsearchoption.domain.entity;
2+
3+
import jakarta.persistence.*;
4+
import java.time.LocalDateTime;
5+
import lombok.AccessLevel;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
import org.hibernate.annotations.JdbcTypeCode;
9+
import org.hibernate.type.SqlTypes;
10+
11+
@Entity
12+
@Table(name = "auction_search_option_metadata")
13+
@Getter
14+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
15+
public class AuctionSearchOptionMetadata {
16+
17+
@Id
18+
@GeneratedValue(strategy = GenerationType.IDENTITY)
19+
private Long id;
20+
21+
@Column(name = "search_option_name", nullable = false, length = 100)
22+
private String searchOptionName;
23+
24+
@JdbcTypeCode(SqlTypes.JSON)
25+
@Column(name = "search_condition_json", nullable = false, columnDefinition = "JSON")
26+
private String searchConditionJson;
27+
28+
@Column(name = "display_order", nullable = false, unique = true)
29+
private Integer displayOrder;
30+
31+
@Column(name = "is_active", nullable = false)
32+
private Boolean isActive = true;
33+
34+
@Column(name = "created_at", nullable = false, updatable = false)
35+
private LocalDateTime createdAt;
36+
37+
@Column(name = "updated_at", nullable = false)
38+
private LocalDateTime updatedAt;
39+
40+
@PrePersist
41+
protected void onCreate() {
42+
createdAt = LocalDateTime.now();
43+
updatedAt = LocalDateTime.now();
44+
}
45+
46+
@PreUpdate
47+
protected void onUpdate() {
48+
updatedAt = LocalDateTime.now();
49+
}
50+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package until.the.eternity.auctionsearchoption.domain.repository;
2+
3+
import java.util.List;
4+
import until.the.eternity.auctionsearchoption.domain.entity.AuctionSearchOptionMetadata;
5+
6+
public interface AuctionSearchOptionRepositoryPort {
7+
8+
/**
9+
* 모든 활성화된 검색 옵션 조회 (정렬 순서대로)
10+
*
11+
* @return 검색 옵션 메타데이터 리스트
12+
*/
13+
List<AuctionSearchOptionMetadata> findAllActive();
14+
15+
/**
16+
* 모든 검색 옵션 조회 (정렬 순서대로)
17+
*
18+
* @return 검색 옵션 메타데이터 리스트
19+
*/
20+
List<AuctionSearchOptionMetadata> findAll();
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package until.the.eternity.auctionsearchoption.infrastructure.persistence;
2+
3+
import java.util.List;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
import until.the.eternity.auctionsearchoption.domain.entity.AuctionSearchOptionMetadata;
7+
8+
@Repository
9+
interface AuctionSearchOptionJpaRepository
10+
extends JpaRepository<AuctionSearchOptionMetadata, Long> {
11+
12+
List<AuctionSearchOptionMetadata> findByIsActiveTrueOrderByDisplayOrderAsc();
13+
14+
List<AuctionSearchOptionMetadata> findAllByOrderByDisplayOrderAsc();
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package until.the.eternity.auctionsearchoption.infrastructure.persistence;
2+
3+
import java.util.List;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.stereotype.Component;
6+
import until.the.eternity.auctionsearchoption.domain.entity.AuctionSearchOptionMetadata;
7+
import until.the.eternity.auctionsearchoption.domain.repository.AuctionSearchOptionRepositoryPort;
8+
9+
@Component
10+
@RequiredArgsConstructor
11+
class AuctionSearchOptionRepositoryPortImpl implements AuctionSearchOptionRepositoryPort {
12+
13+
private final AuctionSearchOptionJpaRepository jpaRepository;
14+
15+
@Override
16+
public List<AuctionSearchOptionMetadata> findAllActive() {
17+
return jpaRepository.findByIsActiveTrueOrderByDisplayOrderAsc();
18+
}
19+
20+
@Override
21+
public List<AuctionSearchOptionMetadata> findAll() {
22+
return jpaRepository.findAllByOrderByDisplayOrderAsc();
23+
}
24+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package until.the.eternity.auctionsearchoption.interfaces.rest;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.tags.Tag;
5+
import java.util.List;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RestController;
11+
import until.the.eternity.auctionsearchoption.application.service.AuctionSearchOptionService;
12+
import until.the.eternity.auctionsearchoption.interfaces.rest.dto.response.SearchOptionMetadataResponse;
13+
import until.the.eternity.common.response.ApiResponse;
14+
15+
@Tag(name = "Auction Search Option", description = "경매 검색 옵션 API")
16+
@RestController
17+
@RequestMapping("/api/search-option")
18+
@RequiredArgsConstructor
19+
public class AuctionSearchOptionController {
20+
21+
private final AuctionSearchOptionService service;
22+
23+
@Operation(summary = "검색 옵션 메타데이터 조회", description = "경매 검색에 사용 가능한 모든 옵션 메타데이터를 조회합니다.")
24+
@GetMapping
25+
public ResponseEntity<ApiResponse<List<SearchOptionMetadataResponse>>> getSearchOptions() {
26+
List<SearchOptionMetadataResponse> searchOptions = service.getAllActiveSearchOptions();
27+
28+
return ResponseEntity.ok(
29+
ApiResponse.success("SEARCH_OPTION_SUCCESS", "검색 옵션 조회 성공", searchOptions));
30+
}
31+
}

0 commit comments

Comments
 (0)