feat: add MCI request DTO scaffold conversion
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 31s
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 31s
This commit is contained in:
@@ -13,7 +13,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Glow MCI 응답 전문({@code *_O.java})을 분석하여 LLM 노출용 Response DTO와
|
||||
* Glow MCI 전문({@code *_I.java}/{@code *_O.java})을 분석하여 LLM 노출용 Request/Response DTO와
|
||||
* MapStruct Converter 소스를 생성합니다. AI는 필드명 제안에만 사용하고 전문 구조와
|
||||
* {@code GlowTrgmField.description}은 이 클래스가 원문에서 직접 추출합니다.
|
||||
*/
|
||||
@@ -60,6 +60,9 @@ public final class MciResponseScaffolder {
|
||||
public record GeneratedSources(String responseSource, String converterSource) {
|
||||
}
|
||||
|
||||
public record GeneratedRequestSources(String requestSource, String converterSource) {
|
||||
}
|
||||
|
||||
public static ParsedSource parse(String source) {
|
||||
if (source == null || source.isBlank()) {
|
||||
throw new IllegalArgumentException("MCI 응답 Java 소스를 입력해주세요.");
|
||||
@@ -113,6 +116,28 @@ public final class MciResponseScaffolder {
|
||||
converterClassName, mappingBySource));
|
||||
}
|
||||
|
||||
/**
|
||||
* LLM Request DTO에서 Glow MCI 입력 전문으로 변환하는 MapStruct Converter를 생성합니다.
|
||||
*/
|
||||
public static GeneratedRequestSources generateRequest(ParsedSource parsed, String requestPackage,
|
||||
String requestClassName, String converterPackage,
|
||||
String converterClassName, List<FieldMapping> mappings) {
|
||||
if (parsed == null || parsed.types() == null || parsed.types().isEmpty()) {
|
||||
throw new IllegalArgumentException("분석된 MCI 요청 정보가 없습니다.");
|
||||
}
|
||||
validatePackage(requestPackage, "Request package");
|
||||
validatePackage(converterPackage, "Converter package");
|
||||
validateClassName(requestClassName, "Request class");
|
||||
validateClassName(converterClassName, "Converter class");
|
||||
|
||||
Map<String, FieldMapping> mappingBySource = normalizeMappings(parsed, mappings);
|
||||
validateTargetNames(parsed, mappingBySource);
|
||||
return new GeneratedRequestSources(
|
||||
responseSource(parsed, requestPackage, requestClassName, mappingBySource),
|
||||
requestConverterSource(parsed, requestPackage, requestClassName, converterPackage,
|
||||
converterClassName, mappingBySource));
|
||||
}
|
||||
|
||||
private static List<ClassRange> findClassRanges(String source) {
|
||||
List<ClassRangeDraft> drafts = new ArrayList<>();
|
||||
Matcher matcher = CLASS_PATTERN.matcher(source);
|
||||
@@ -356,6 +381,30 @@ public final class MciResponseScaffolder {
|
||||
return source.append("}\n").toString();
|
||||
}
|
||||
|
||||
private static String requestConverterSource(ParsedSource parsed, String requestPackage, String requestClassName,
|
||||
String converterPackage, String converterClassName,
|
||||
Map<String, FieldMapping> mappings) {
|
||||
StringBuilder source = new StringBuilder("package ").append(converterPackage).append(";\n\n")
|
||||
.append("import ").append(requestPackage).append('.').append(requestClassName).append(";\n")
|
||||
.append("import ").append(parsed.packageName()).append('.').append(parsed.rootClassName()).append(";\n")
|
||||
.append("import org.mapstruct.Mapper;\n")
|
||||
.append("import org.mapstruct.Mapping;\n")
|
||||
.append("import org.mapstruct.ReportingPolicy;\n\n")
|
||||
.append("@Mapper(componentModel = \"spring\", unmappedTargetPolicy = ReportingPolicy.IGNORE)\n")
|
||||
.append("public interface ").append(converterClassName).append(" {\n\n");
|
||||
|
||||
appendRequestMappingMethod(source, parsed.types().getFirst(), parsed.rootClassName(),
|
||||
requestClassName, "toLegacyRequest", mappings);
|
||||
Map<String, ParsedType> typeByName = new HashMap<>();
|
||||
parsed.types().forEach(type -> typeByName.put(type.name(), type));
|
||||
for (ParsedType type : parsed.types().stream().skip(1).toList()) {
|
||||
appendRequestMappingMethod(source, type,
|
||||
sourceTypePath(parsed.rootClassName(), type, typeByName),
|
||||
requestClassName + "." + type.name(), "toLegacy" + type.name(), mappings);
|
||||
}
|
||||
return source.append("}\n").toString();
|
||||
}
|
||||
|
||||
private static void appendMappingMethod(StringBuilder source, ParsedType type, String targetType,
|
||||
String sourceType, String methodName,
|
||||
Map<String, FieldMapping> mappings) {
|
||||
@@ -370,6 +419,20 @@ public final class MciResponseScaffolder {
|
||||
.append('(').append(sourceType).append(" source);\n\n");
|
||||
}
|
||||
|
||||
private static void appendRequestMappingMethod(StringBuilder source, ParsedType type, String legacyTargetType,
|
||||
String requestSourceType, String methodName,
|
||||
Map<String, FieldMapping> mappings) {
|
||||
for (ParsedField field : type.fields()) {
|
||||
FieldMapping mapping = mappings.get(key(type.name(), field.name()));
|
||||
if (mapping.include() && !field.name().equals(mapping.targetName())) {
|
||||
source.append(" @Mapping(source = \"").append(mapping.targetName())
|
||||
.append("\", target = \"").append(field.name()).append("\")\n");
|
||||
}
|
||||
}
|
||||
source.append(" ").append(legacyTargetType).append(' ').append(methodName)
|
||||
.append('(').append(requestSourceType).append(" source);\n\n");
|
||||
}
|
||||
|
||||
private static String sourceTypePath(String rootClassName, ParsedType type,
|
||||
Map<String, ParsedType> typeByName) {
|
||||
List<String> names = new ArrayList<>();
|
||||
|
||||
@@ -80,6 +80,35 @@ class MciResponseScaffolderTest {
|
||||
"IndividualCustomerDetailInquiryResponse.CmnnPrafIfinOutDto toCmnnPrafIfinOutDto(ONBSZ0460_O.CmnnPrafIfinOutDto source);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesLlmRequestAndMapsLlmFieldsBackToLegacyInput() {
|
||||
String requestSource = MCI_SOURCE.replace("ONBSZ0460_O", "ONBSZ0460_I");
|
||||
MciResponseScaffolder.ParsedSource parsed = MciResponseScaffolder.parse(requestSource);
|
||||
List<MciResponseScaffolder.FieldMapping> mappings = List.of(
|
||||
new MciResponseScaffolder.FieldMapping("ONBSZ0460_I", "employeeItems", "employees", true),
|
||||
new MciResponseScaffolder.FieldMapping("CmnnPrafIfinOutDto", "prafNo", "employeeNumber", true),
|
||||
new MciResponseScaffolder.FieldMapping("CmnnPrafIfinOutDto", "prafTypeCd", "employeeTypeCode", true),
|
||||
new MciResponseScaffolder.FieldMapping("CmnnPrafIfinOutDto", "prafNm", "employeeName", true),
|
||||
new MciResponseScaffolder.FieldMapping("CmnnPrafIfinOutDto", "rdreNo", "residentRegistrationNumber", true));
|
||||
|
||||
MciResponseScaffolder.GeneratedRequestSources generated = MciResponseScaffolder.generateRequest(
|
||||
parsed,
|
||||
"io.shinhanlife.dat.mcc.biz.pro.dto",
|
||||
"IndividualCustomerDetailInquiryRequest",
|
||||
"io.shinhanlife.dat.mcc.biz.pro.converter",
|
||||
"IndividualCustomerDetailInquiryRequestConverter",
|
||||
mappings);
|
||||
|
||||
assertTrue(generated.requestSource().contains("@Schema(description = \"인사번호\")"));
|
||||
assertTrue(generated.requestSource().contains("private String employeeNumber;"));
|
||||
assertTrue(generated.converterSource().contains(
|
||||
"@Mapping(source = \"employeeNumber\", target = \"prafNo\")"));
|
||||
assertTrue(generated.converterSource().contains(
|
||||
"ONBSZ0460_I toLegacyRequest(IndividualCustomerDetailInquiryRequest source);"));
|
||||
assertTrue(generated.converterSource().contains(
|
||||
"ONBSZ0460_I.CmnnPrafIfinOutDto toLegacyCmnnPrafIfinOutDto("));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsDuplicateAiTargetNamesWithinTheSameType() {
|
||||
MciResponseScaffolder.ParsedSource parsed = MciResponseScaffolder.parse(MCI_SOURCE);
|
||||
|
||||
Reference in New Issue
Block a user