Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,42 @@ private BooleanBuilder buildHistoryPredicate(
}
}

// 인챈트 검색 조건
if (c.enchantSearchRequest() != null) {
String enchantPrefix = c.enchantSearchRequest().enchantPrefix();
String enchantSuffix = c.enchantSearchRequest().enchantSuffix();

if (enchantPrefix != null && !enchantPrefix.isBlank()) {
QAuctionHistoryItemOption optPrefix =
new QAuctionHistoryItemOption("enchantPrefix");
var subPrefix =
JPAExpressions.select(optPrefix.auctionHistory.auctionBuyId)
.from(optPrefix)
.where(
optPrefix
.optionType
.eq("인챈트")
.and(optPrefix.optionSubType.eq("접두"))
.and(optPrefix.optionValue.eq(enchantPrefix)));
builder.and(ah.auctionBuyId.in(subPrefix));
}

if (enchantSuffix != null && !enchantSuffix.isBlank()) {
QAuctionHistoryItemOption optSuffix =
new QAuctionHistoryItemOption("enchantSuffix");
var subSuffix =
JPAExpressions.select(optSuffix.auctionHistory.auctionBuyId)
.from(optSuffix)
.where(
optSuffix
.optionType
.eq("인챈트")
.and(optSuffix.optionSubType.eq("접미"))
.and(optSuffix.optionValue.eq(enchantSuffix)));
builder.and(ah.auctionBuyId.in(subSuffix));
}
}

return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ public record AuctionHistorySearchRequest(
@Schema(description = "소분류 카테고리", example = "검") String itemSubCategory,
@Schema(description = "거래 일자 조건") DateAuctionBuyRequest dateAuctionBuyRequest,
@Schema(description = "가격 검색 조건") PriceSearchRequest priceSearchRequest,
@Schema(description = "아이템 옵션 검색 조건") ItemOptionSearchRequest itemOptionSearchRequest) {}
@Schema(description = "아이템 옵션 검색 조건") ItemOptionSearchRequest itemOptionSearchRequest,
@Schema(description = "인챈트 검색 조건") EnchantSearchRequest enchantSearchRequest) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package until.the.eternity.auctionhistory.interfaces.rest.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;

/** 인챈트 검색 조건 DTO */
@Schema(description = "인챈트 검색 조건")
public record EnchantSearchRequest(
@Schema(description = "접두 인챈트 fullname (예: 당당한 (6랭크))") String enchantPrefix,
@Schema(description = "접미 인챈트 fullname (예: 강한 (5랭크))") String enchantSuffix) {}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,42 @@ private BooleanBuilder buildItemPredicate(
}
}

// 인챈트 검색 조건
if (c.enchantSearchRequest() != null) {
String enchantPrefix = c.enchantSearchRequest().enchantPrefix();
String enchantSuffix = c.enchantSearchRequest().enchantSuffix();

if (enchantPrefix != null && !enchantPrefix.isBlank()) {
QAuctionRealtimeItemOption optPrefix =
new QAuctionRealtimeItemOption("enchantPrefix");
var subPrefix =
JPAExpressions.select(optPrefix.auctionRealtimeItem.id)
.from(optPrefix)
.where(
optPrefix
.optionType
.eq("인챈트")
.and(optPrefix.optionSubType.eq("접두"))
.and(optPrefix.optionValue.eq(enchantPrefix)));
builder.and(ar.id.in(subPrefix));
}

if (enchantSuffix != null && !enchantSuffix.isBlank()) {
QAuctionRealtimeItemOption optSuffix =
new QAuctionRealtimeItemOption("enchantSuffix");
var subSuffix =
JPAExpressions.select(optSuffix.auctionRealtimeItem.id)
.from(optSuffix)
.where(
optSuffix
.optionType
.eq("인챈트")
.and(optSuffix.optionSubType.eq("접미"))
.and(optSuffix.optionValue.eq(enchantSuffix)));
builder.and(ar.id.in(subSuffix));
}
}

return builder;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package until.the.eternity.auctionrealtime.interfaces.rest.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import until.the.eternity.auctionhistory.interfaces.rest.dto.request.EnchantSearchRequest;
import until.the.eternity.auctionhistory.interfaces.rest.dto.request.ItemOptionSearchRequest;
import until.the.eternity.auctionhistory.interfaces.rest.dto.request.PriceSearchRequest;

Expand All @@ -17,4 +18,5 @@ public record AuctionRealtimeSearchRequest(
@Schema(description = "대분류 카테고리", example = "근거리 장비") String itemTopCategory,
@Schema(description = "소분류 카테고리", example = "검") String itemSubCategory,
@Schema(description = "가격 검색 조건") PriceSearchRequest priceSearchRequest,
@Schema(description = "아이템 옵션 검색 조건") ItemOptionSearchRequest itemOptionSearchRequest) {}
@Schema(description = "아이템 옵션 검색 조건") ItemOptionSearchRequest itemOptionSearchRequest,
@Schema(description = "인챈트 검색 조건") EnchantSearchRequest enchantSearchRequest) {}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package until.the.eternity.enchantinfo.application.service;

import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import until.the.eternity.common.exception.CustomException;
import until.the.eternity.enchantinfo.domain.exception.EnchantInfoExceptionCode;
import until.the.eternity.enchantinfo.domain.repository.EnchantInfoRepositoryPort;
import until.the.eternity.enchantinfo.interfaces.rest.dto.response.EnchantInfoResponse;
import until.the.eternity.enchantinfo.interfaces.rest.dto.response.EnchantInfoSyncResponse;
Expand All @@ -15,13 +18,21 @@
@RequiredArgsConstructor
public class EnchantInfoService {

private static final Set<String> ALLOWED_AFFIX_POSITIONS = Set.of("접두", "접미");

private final EnchantInfoRepositoryPort enchantInfoRepository;

public Page<EnchantInfoResponse> findAll(Pageable pageable) {
return enchantInfoRepository.findAll(pageable).map(EnchantInfoResponse::from);
}

public List<String> findAllFullnames() {
public List<String> findAllFullnames(String affixPosition) {
if (affixPosition != null) {

Copilot AI Feb 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation should also check for blank strings. Following the codebase convention seen in AuctionHistoryQueryDslRepository and AuctionRealtimeQueryDslRepository, string parameters should be validated with both null and isBlank() checks. A blank string (e.g., empty or whitespace-only) should be treated the same as null and return all enchants rather than throwing an exception.

Suggested change
if (affixPosition != null) {
if (affixPosition != null && !affixPosition.isBlank()) {

Copilot uses AI. Check for mistakes.
if (!ALLOWED_AFFIX_POSITIONS.contains(affixPosition)) {
throw new CustomException(EnchantInfoExceptionCode.INVALID_AFFIX_POSITION);
}
return enchantInfoRepository.findAllFullnamesByAffixPosition(affixPosition);
}
return enchantInfoRepository.findAllFullnames();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package until.the.eternity.enchantinfo.domain.exception;

import static org.springframework.http.HttpStatus.BAD_REQUEST;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import until.the.eternity.common.exception.ExceptionCode;

@Getter
@RequiredArgsConstructor
public enum EnchantInfoExceptionCode implements ExceptionCode {
INVALID_AFFIX_POSITION(BAD_REQUEST, "affix_position은 '접두' 또는 '접미'만 허용됩니다."),
;

private final HttpStatus status;
private final String message;

@Override
public String getCode() {
return this.name();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ public interface EnchantInfoRepositoryPort {

List<String> findAllFullnames();

List<String> findAllFullnamesByAffixPosition(String affixPosition);

int upsertFromAuctionHistory();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ public interface EnchantInfoJpaRepository extends JpaRepository<EnchantInfoEntit
@Query("SELECT e.fullname FROM EnchantInfoEntity e ORDER BY e.id ASC")
List<String> findAllFullnames();

@Query(
"SELECT e.fullname FROM EnchantInfoEntity e WHERE e.affixPosition = :affixPosition ORDER BY e.id ASC")
List<String> findAllFullnamesByAffixPosition(
@org.springframework.data.repository.query.Param("affixPosition") String affixPosition);

@Modifying
@Query(
value =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public List<String> findAllFullnames() {
return jpaRepository.findAllFullnames();
}

@Override
public List<String> findAllFullnamesByAffixPosition(String affixPosition) {
return jpaRepository.findAllFullnamesByAffixPosition(affixPosition);
}

@Override
public int upsertFromAuctionHistory() {
return jpaRepository.upsertFromAuctionHistory();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package until.the.eternity.enchantinfo.interfaces.rest.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand All @@ -13,6 +14,7 @@
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import until.the.eternity.enchantinfo.application.service.EnchantInfoService;
import until.the.eternity.enchantinfo.interfaces.rest.dto.request.EnchantInfoPageRequestDto;
Expand Down Expand Up @@ -41,10 +43,16 @@ public ResponseEntity<Page<EnchantInfoResponse>> getEnchantInfos(

@Operation(
summary = "모든 인챈트 fullname 조회",
description = "페이지네이션 없이 저장된 모든 인챈트의 fullname(이름 및 랭크)을 한 번에 조회합니다.")
description =
"페이지네이션 없이 저장된 모든 인챈트의 fullname(이름 및 랭크)을 한 번에 조회합니다. "
+ "affix_position을 지정하면 해당 위치(접두/접미)의 인챈트만 필터링합니다.")
@ApiResponse(responseCode = "400", description = "잘못된 affix_position 값 (접두 또는 접미만 허용)")
@GetMapping("/fullnames")
public List<String> getAllEnchantFullnames() {
return enchantInfoService.findAllFullnames();
public List<String> getAllEnchantFullnames(
@Parameter(description = "접두/접미 구분 필터 (허용값: 접두, 접미)", example = "접두")
@RequestParam(name = "affix_position", required = false)
String affixPosition) {
return enchantInfoService.findAllFullnames(affixPosition);
}

@Operation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public Pageable toPageable() {
this.direction != null ? this.direction : DEFAULT_DIRECTION;

return PageRequest.of(
resolvedPage, resolvedSize, Sort.by(resolvedDirection.toSpringDirection(), SORT_FIELD));
resolvedPage,
resolvedSize,
Sort.by(resolvedDirection.toSpringDirection(), SORT_FIELD));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class AuctionHistoryServiceTest {
void search_should_return_paged_response() {
// given
AuctionHistorySearchRequest searchRequest =
new AuctionHistorySearchRequest(null, null, null, null, null, null, null);
new AuctionHistorySearchRequest(null, null, null, null, null, null, null, null);
PageRequestDto pageRequestDto = mock(PageRequestDto.class);
Pageable pageable = PageRequest.of(0, 10);
when(pageRequestDto.toPageable()).thenReturn(pageable);
Expand Down