Skip to content

feat: 세공 상세 테이블 생성 및 동기화 API 구현#97

Merged
dev-ant merged 11 commits into
devfrom
feat/metalware-info-sync-api
Feb 11, 2026
Merged

feat: 세공 상세 테이블 생성 및 동기화 API 구현#97
dev-ant merged 11 commits into
devfrom
feat/metalware-info-sync-api

Conversation

@dev-ant

@dev-ant dev-ant commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

📋 상세 설명

  • 세공(세공 옵션) 데이터를 더 잘 구조화하기 위해 metalware_attribute_info 테이블 생성
  • 경매 기록에서 세공 능력치 정보를 동기화/조회할 수 있는 API 및 metalware_info 업서트 동기화 API를 구현
  • 세공 옵션 문자열 파싱 로직을 공통 유틸로 분리하고, 경매 히스토리 중복 판정에서 KST 변환 기준을 보정

📊 체크리스트

  • PR 제목이 형식에 맞나요 e.g. feat: PR을 등록한다
  • 코드가 테스트 되었나요
  • 문서는 업데이트 되었나요
  • 불필요한 코드를 제거했나요
  • 이슈와 라벨이 등록되었나요 이슈 미등록

@dev-ant dev-ant requested a review from Copilot February 11, 2026 13:34
@dev-ant dev-ant self-assigned this Feb 11, 2026
@dev-ant dev-ant added the ✨feature 새로운 기능 추가 label Feb 11, 2026
@github-actions

Copy link
Copy Markdown

✅ 테스트 결과 for PR

Build: success

🧪 테스트 실행 with Gradle
📈 Coverage: -0.00%

📁 테스트 결과
📁 커버리지 보고서 (HTML)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

세공(세공 옵션) 데이터를 더 잘 구조화하기 위해 metalware_attribute_info 테이블을 추가하고, 경매 기록에서 세공 능력치 정보를 동기화/조회할 수 있는 API 및 metalware_info 업서트 동기화 API를 구현합니다. 또한 세공 옵션 문자열 파싱 로직을 공통 유틸로 분리하고, 경매 히스토리 중복 판정에서 KST 변환 기준을 보정합니다.

Changes:

  • metalware_attribute_info 테이블/엔티티/리포지토리/서비스/컨트롤러를 추가하고 동기화·검색 API를 구현
  • metalware_attribute_infometalware_info 업서트 동기화 API 및 응답 DTO 추가
  • 세공 옵션 파서(SegongOptionParser) 도입 및 기존 Mapper들의 파싱 로직 통합, KST 기반 중복 판정 테스트/로직 보강

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/test/java/until/the/eternity/metalwareinfo/application/service/MetalwareInfoServiceTest.java 세공 정보 동기화 서비스 로직에 대한 단위 테스트 추가
src/test/java/until/the/eternity/common/util/SegongOptionParserTest.java 세공 옵션 파서에 대한 패턴별 테스트 추가
src/test/java/until/the/eternity/auctionhistory/domain/service/AuctionHistoryDuplicateCheckerTest.java KST 변환 기준을 반영한 중복 판정 테스트 보강
src/main/resources/db/migration/V19__create_metalware_attribute_info.sql 세공 능력치 정보 테이블 신규 생성
src/main/resources/db/migration/V18__migrate_unparsed_segong_option.sql 기존 미파싱 세공 옵션 데이터 파싱을 위한 마이그레이션 추가
src/main/java/until/the/eternity/metalwareinfo/interfaces/rest/dto/response/MetalwareInfoSyncResponse.java 세공 정보 업서트 동기화 결과 응답 DTO 추가
src/main/java/until/the/eternity/metalwareinfo/interfaces/rest/dto/response/MetalwareAttributeInfoResponse.java 세공 능력치 정보 조회 응답 DTO 추가
src/main/java/until/the/eternity/metalwareinfo/interfaces/rest/dto/request/MetalwareAttributeInfoSearchRequest.java 세공 능력치 검색 요청 파라미터(+페이징/정렬) DTO 추가
src/main/java/until/the/eternity/metalwareinfo/interfaces/rest/controller/MetalwareInfoController.java metalware_info 동기화 API(/sync) 추가
src/main/java/until/the/eternity/metalwareinfo/interfaces/rest/controller/MetalwareAttributeInfoController.java 세공 능력치 동기화/검색 API 컨트롤러 추가
src/main/java/until/the/eternity/metalwareinfo/infrastructure/persistence/MetalwareInfoRepositoryPortImpl.java metalware_info 업서트 메서드 구현 추가
src/main/java/until/the/eternity/metalwareinfo/infrastructure/persistence/MetalwareInfoJpaRepository.java metalware_attribute_info 기반 업서트 네이티브 쿼리 추가
src/main/java/until/the/eternity/metalwareinfo/infrastructure/persistence/MetalwareAttributeInfoRepositoryPortImpl.java 세공 능력치 동기화/검색 리포지토리 어댑터 추가
src/main/java/until/the/eternity/metalwareinfo/infrastructure/persistence/MetalwareAttributeInfoJpaRepository.java 경매 히스토리 옵션에서 세공 능력치 정보 upsert 동기화 쿼리 추가
src/main/java/until/the/eternity/metalwareinfo/infrastructure/persistence/MetalwareAttributeInfoEntity.java metalware_attribute_info JPA 엔티티 추가
src/main/java/until/the/eternity/metalwareinfo/domain/repository/MetalwareInfoRepositoryPort.java metalware_info 업서트 포트 메서드 추가
src/main/java/until/the/eternity/metalwareinfo/domain/repository/MetalwareAttributeInfoRepositoryPort.java 세공 능력치 동기화/검색 포트 인터페이스 추가
src/main/java/until/the/eternity/metalwareinfo/application/service/MetalwareInfoService.java metalware_info 업서트 동기화 서비스 메서드 추가
src/main/java/until/the/eternity/metalwareinfo/application/service/MetalwareAttributeInfoService.java 세공 능력치 동기화/검색 서비스 추가
src/main/java/until/the/eternity/common/util/SegongOptionParser.java 세공 옵션 문자열 파싱 유틸리티 신규 추가
src/main/java/until/the/eternity/auctionrealtime/domain/mapper/OpenApiRealtimeItemOptionMapper.java 세공 옵션 파싱 로직을 공통 파서로 이관
src/main/java/until/the/eternity/auctionhistory/domain/service/AuctionHistoryDuplicateChecker.java 중복 판정 시 KST 변환 적용
src/main/java/until/the/eternity/auctionhistory/domain/mapper/OpenApiItemOptionMapper.java 세공 옵션 파싱 로직을 공통 파서로 이관
src/main/java/until/the/eternity/auctionhistory/domain/mapper/OpenApiAuctionHistoryMapper.java UTC→KST 변환 구현을 상수 기반으로 정리
local_oab_auction_history_item_option.csv 로컬 CSV 샘플(추가됨)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +11
import until.the.eternity.metalwareinfo.infrastructure.persistence.MetalwareAttributeInfoEntity;

public interface MetalwareAttributeInfoRepositoryPort {

int syncFromAuctionHistory();

Page<MetalwareAttributeInfoEntity> searchByMetalware(String metalware, Pageable pageable);

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

도메인 레이어의 RepositoryPort가 infrastructure.persistence의 JPA Entity를 반환 타입으로 노출하고 있습니다. 포트는 도메인 모델(또는 별도 DTO/Projection)만 의존하도록 분리하고, JPA Entity는 infrastructure 계층 내부로 숨기는 구조가 유지보수/의존성 방향 측면에서 안전합니다.

Suggested change
import until.the.eternity.metalwareinfo.infrastructure.persistence.MetalwareAttributeInfoEntity;
public interface MetalwareAttributeInfoRepositoryPort {
int syncFromAuctionHistory();
Page<MetalwareAttributeInfoEntity> searchByMetalware(String metalware, Pageable pageable);
/**
* Domain-level view of metalware attribute information.
* <p>
* This type intentionally lives in the domain package and is decoupled from
* any JPA or infrastructure concerns. Implementations of the repository port
* are responsible for mapping from persistence entities to this type.
*/
class MetalwareAttributeInfoDomain {
// Add domain fields and behavior here as needed.
}
public interface MetalwareAttributeInfoRepositoryPort {
int syncFromAuctionHistory();
Page<MetalwareAttributeInfoDomain> searchByMetalware(String metalware, Pageable pageable);

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +7
import lombok.Builder;
import until.the.eternity.metalwareinfo.infrastructure.persistence.MetalwareAttributeInfoEntity;

@Builder

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

응답 DTO가 infrastructure.persistence의 JPA Entity 타입을 직접 import해서 사용하고 있습니다. 컨트롤러/DTO 레이어가 영속성 구현에 결합되지 않도록, 서비스에서 도메인 모델/Projection으로 변환한 값을 DTO로 매핑하거나 from(...)의 파라미터를 도메인 타입으로 바꾸는 방식을 권장합니다.

Copilot uses AI. Check for mistakes.
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
metalware VARCHAR(50) NOT NULL COMMENT '세공',
level TINYINT NULL COMMENT '레벨',

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

level 컬럼이 NULL 허용인데 (metalware, level)에 UNIQUE KEY가 걸려 있어 metalware가 같고 level이 NULL인 행이 여러 개 들어갈 수 있습니다(MySQL UNIQUE에서 NULL은 중복 허용). limit_break_level 집계(MAX(level))도 고려하면 level을 NOT NULL로 두는 것이 데이터 정합성에 더 안전합니다.

Suggested change
level TINYINT NULL COMMENT '레벨',
level TINYINT NOT NULL COMMENT '레벨',

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +30
int levelAttributeUpserted =
metalwareInfoRepository.upsertLevelAttributeFromAttributeInfo();
int limitBreakLevelUpserted =
metalwareInfoRepository.upsertLimitBreakLevelFromAttributeInfo();

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

MySQL의 INSERT ... ON DUPLICATE KEY UPDATE는 JDBC update count가 '업서트된 레코드 수'와 일치하지 않을 수 있습니다(업데이트 시 2로 집계, no-op은 0 등). 이 값을 응답의 ...UpsertedCount로 노출하면 오해 소지가 있으니, 의미를 affectedRows로 명확히 하거나 커넥터 useAffectedRows=true 등으로 카운트 의미를 통일하는 방안을 검토해 주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +27
@Operation(summary = "세공 능력치 정보 동기화", description = "경매 기록에서 세공 능력치 정보를 추출하여 동기화합니다.")
@PostMapping("/sync")
public ResponseEntity<Integer> sync() {
int syncedCount = metalwareAttributeInfoService.sync();
return ResponseEntity.ok(syncedCount);

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

이 API가 반환하는 syncedCount는 upsert 쿼리의 update count라서 '실제 동기화된 레코드 수'와 다르게 보일 수 있습니다(업데이트=2, no-op=0 등). 응답 타입/문서에서 의미를 affectedRows로 명확히 하거나, 별도 집계 방식으로 동기화 건수를 계산하는 쪽이 안전합니다.

Suggested change
@Operation(summary = "세공 능력치 정보 동기화", description = "경매 기록에서 세공 능력치 정보를 추출하여 동기화합니다.")
@PostMapping("/sync")
public ResponseEntity<Integer> sync() {
int syncedCount = metalwareAttributeInfoService.sync();
return ResponseEntity.ok(syncedCount);
/**
* 세공 능력치 정보 동기화 결과 응답 DTO.
* affectedRows는 upsert 쿼리로 인해 영향 받은 레코드 수를 의미합니다.
*/
public static class SyncResultResponse {
private final int affectedRows;
public SyncResultResponse(int affectedRows) {
this.affectedRows = affectedRows;
}
public int getAffectedRows() {
return affectedRows;
}
}
@Operation(summary = "세공 능력치 정보 동기화", description = "경매 기록에서 세공 능력치 정보를 추출하여 동기화합니다.")
@PostMapping("/sync")
public ResponseEntity<SyncResultResponse> sync() {
int affectedRows = metalwareAttributeInfoService.sync();
return ResponseEntity.ok(new SyncResultResponse(affectedRows));

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +53
SegongOptionParser.ParseResult result = SegongOptionParser.parse(SEGONG, input);

assertThat(result).isNotNull();
assertThat(result.optionValue2()).isNotNull();
assertThat(result.optionDesc()).isNotNull();

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

현재 단언은 null 여부 위주라서 잘못된 파싱(예: optionValue에 레벨이 남거나 optionValue2가 오파싱)도 통과할 수 있습니다. 대표 케이스 몇 개는 optionValue/optionValue2/optionDesc의 '정확한 값'까지 검증하도록 보강하면 회귀 방지에 더 효과적입니다.

Copilot uses AI. Check for mistakes.
ON DUPLICATE KEY UPDATE attribute = VALUES(attribute)
""",
nativeQuery = true)
int syncFromAuctionHistory();

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

이 upsert 쿼리의 반환 int는 MySQL에서 '영향받은 행 수'(업데이트 시 2로 집계, no-op은 0 등)로 내려올 수 있어 '동기화된 레코드 수'와 다를 수 있습니다. 상위 레이어에서 이 값을 그대로 노출한다면 의미를 affectedRows로 명확히 하거나 별도 집계 방식을 고려해 주세요.

Suggested change
int syncFromAuctionHistory();
int syncFromAuctionHistoryAffectedRows();

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
option_value,option_value2,option_desc
최대생명력(15레벨:37.50 증가),,
최대마나(19레벨:95 증가),,
보호(5레벨:5 증가),,
돌진,,인간 및 엘프일 때 방패 없이 사용 가능

Copilot AI Feb 11, 2026

Copy link

Choose a reason for hiding this comment

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

현재 CSV는 코드/테스트에서 참조되는 위치가 아니고 파일명도 local_... 형태라 커밋된 로컬 실험 데이터로 보입니다. 저장이 필요하다면 src/test/resources 또는 docs/로 이동하고 사용 목적을 문서화하고, 아니라면 PR에서 제거하는 것이 저장소 관리에 유리합니다.

Copilot uses AI. Check for mistakes.
@dev-ant dev-ant merged commit 4701182 into dev Feb 11, 2026
8 checks passed
@dev-ant dev-ant deleted the feat/metalware-info-sync-api branch February 11, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨feature 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants