feat: MCI Request DTO example 자동 생성 및 Tool Scaffold UseCaseImpl/Client 생성 템플릿 개선
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 31s

This commit is contained in:
jade
2026-09-08 09:49:00 +09:00
parent 88d94e8b83
commit 9a8f716ec7
2 changed files with 159 additions and 42 deletions

View File

@@ -133,7 +133,7 @@ public final class MciResponseScaffolder {
Map<String, FieldMapping> mappingBySource = normalizeMappings(parsed, mappings);
validateTargetNames(parsed, mappingBySource);
return new GeneratedRequestSources(
responseSource(parsed, requestPackage, requestClassName, mappingBySource),
responseSource(parsed, requestPackage, requestClassName, mappingBySource, true),
requestConverterSource(parsed, requestPackage, requestClassName, converterPackage,
converterClassName, mappingBySource));
}
@@ -321,6 +321,11 @@ public final class MciResponseScaffolder {
private static String responseSource(ParsedSource parsed, String responsePackage, String responseClassName,
Map<String, FieldMapping> mappings) {
return responseSource(parsed, responsePackage, responseClassName, mappings, false);
}
private static String responseSource(ParsedSource parsed, String responsePackage, String responseClassName,
Map<String, FieldMapping> mappings, boolean isRequest) {
boolean usesList = parsed.types().stream().flatMap(type -> type.fields().stream())
.anyMatch(field -> field.type().contains("List<"));
boolean usesBigDecimal = parsed.types().stream().flatMap(type -> type.fields().stream())
@@ -333,31 +338,78 @@ public final class MciResponseScaffolder {
if (usesList) source.append("import java.util.List;\n");
source.append("\n@Data\n@JsonInclude(JsonInclude.Include.NON_NULL)\n")
.append("public class ").append(responseClassName).append(" {\n\n")
.append(responseFields(parsed.types().getFirst(), mappings, " "));
.append(responseFields(parsed.types().getFirst(), mappings, " ", isRequest));
for (ParsedType type : parsed.types().stream().skip(1).toList()) {
source.append(" @Data\n")
.append(" @JsonInclude(JsonInclude.Include.NON_NULL)\n")
.append(" public static class ").append(type.name()).append(" {\n\n")
.append(responseFields(type, mappings, " "))
.append(responseFields(type, mappings, " ", isRequest))
.append(" }\n\n");
}
return source.append("}\n").toString();
}
private static String responseFields(ParsedType type, Map<String, FieldMapping> mappings, String indent) {
return responseFields(type, mappings, indent, false);
}
private static String responseFields(ParsedType type, Map<String, FieldMapping> mappings, String indent, boolean isRequest) {
StringBuilder source = new StringBuilder();
for (ParsedField field : type.fields()) {
FieldMapping mapping = mappings.get(key(type.name(), field.name()));
if (!mapping.include()) continue;
source.append(indent).append("@Schema(description = \"")
.append(escapeJava(field.description())).append("\")\n")
.append(indent).append("private ").append(field.type()).append(' ')
if (isRequest) {
String example = exampleValue(field);
source.append(indent).append("@Schema(description = \"")
.append(escapeJava(field.description()))
.append("\", example = \"").append(example).append("\")\n");
} else {
source.append(indent).append("@Schema(description = \"")
.append(escapeJava(field.description())).append("\")\n");
}
source.append(indent).append("private ").append(field.type()).append(' ')
.append(mapping.targetName().trim()).append(";\n\n");
}
return source.toString();
}
private static String exampleValue(ParsedField field) {
String type = field.type();
int length = field.length();
String desc = field.description() == null ? "" : field.description();
if (type.contains("List<") || type.contains("[]")) return "";
if (type.equals("BigDecimal") || type.equals("Long") || type.equals("Integer") || type.equals("int") || type.equals("long")) {
return "0";
}
// 날짜 패턴
if (length == 8 && (desc.contains("일자") || desc.contains("날짜") || desc.contains("생년") || desc.contains("Ymd") || field.name().toLowerCase().contains("ymd"))) {
return "20240101";
}
// 년월 패턴
if (length == 6 && (desc.contains("년월") || desc.contains("연월") || field.name().toLowerCase().contains("ym"))) {
return "202401";
}
// 코드 패턴
if (desc.contains("코드") || desc.contains("구분") || field.name().toLowerCase().contains("cd") || field.name().toLowerCase().contains("sc")) {
return length <= 4 ? "01" : "0001";
}
// 번호 패턴
if (desc.contains("번호") || field.name().toLowerCase().contains("no")) {
return length <= 10 ? "1234567890".substring(0, Math.min(length, 10)) : "12345678901234";
}
// 이름 패턴
if (desc.contains("이름") || desc.contains("") || field.name().toLowerCase().contains("nm")) {
return "홍길동";
}
// 기본 String
return "";
}
private static String converterSource(ParsedSource parsed, String responsePackage, String responseClassName,
String converterPackage, String converterClassName,
Map<String, FieldMapping> mappings) {

View File

@@ -228,8 +228,31 @@ public class ToolScaffolder {
String sysCode = tool.clientSystemCode();
if (sysCode != null && (sysCode.length() == 4 || sysCode.length() == 9)) {
String clientCap = toPascalCase(mciClientPrefix(sysCode));
String recSvcId = sysCode.toUpperCase();
String itrfId = tool.interfaceId();
writeUtf8(clientDir.resolve("Mci" + clientCap + "Client.java"),
"package " + ioPackage + ";\n\nimport io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent;\nimport io.shinhanlife.glow.communication.dto.Transfer;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RequiredArgsConstructor\npublic class Mci" + clientCap + "Client {\n private final AxhubMciComponent mci;\n\n public <O> Transfer<O> callTo(String interfaceId, String dummy, Object mciReq, Class<O> resType) throws Exception {\n return mci.callTo(interfaceId, dummy, mciReq, resType);\n }\n}\n");
"package " + ioPackage + ";\n\n"
+ "import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent;\n"
+ "import io.shinhanlife.glow.communication.dto.Transfer;\n"
+ "import " + ioPackage + ".io." + ioPrefix + "_I;\n"
+ "import " + ioPackage + ".io." + ioPrefix + "_O;\n"
+ "import lombok.RequiredArgsConstructor;\n"
+ "import org.springframework.stereotype.Component;\n\n"
+ "@Component\n"
+ "@RequiredArgsConstructor\n"
+ "public class Mci" + clientCap + "Client {\n\n"
+ " private static final String INTERFACE_ID = \"" + itrfId + "\";\n"
+ " private static final String RECEIVE_SERVICE_ID = \"" + recSvcId + "\";\n\n"
+ " private final AxhubMciComponent mciComponent;\n\n"
+ " public <O> Transfer<O> callTo(String interfaceId, String receiveServiceId, Object mciReq, Class<O> resType) {\n"
+ " return mciComponent.callTo(interfaceId, receiveServiceId, mciReq, resType);\n"
+ " }\n\n"
+ " public " + ioPrefix + "_O call" + toPascalCase(ioPrefix) + "(" + ioPrefix + "_I request) {\n"
+ " Transfer<" + ioPrefix + "_O> transfer = mciComponent.callTo(\n"
+ " INTERFACE_ID, RECEIVE_SERVICE_ID, request, " + ioPrefix + "_O.class);\n"
+ " return transfer == null ? null : transfer.getBody();\n"
+ " }\n"
+ "}\n");
} else {
writeUtf8(clientDir.resolve(baseName + "Client.java"), groupedMciClientContent(ioPackage, baseName, tool.interfaceId()));
}
@@ -321,12 +344,12 @@ public class ToolScaffolder {
String ioPrefix = (tool.clientSystemCode() != null && !tool.clientSystemCode().isBlank()) ? tool.clientSystemCode().toUpperCase() : tool.interfaceId();
if (mci) {
clientClassName = mciClientClassName(tool.clientSystemCode());
clientVariable = mciClientVariable(tool.clientSystemCode());
clientVariable = "mci";
} else {
clientClassName = baseName + "Client";
clientVariable = Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1) + "Client";
}
String converterVariable = Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1) + "Converter";
String converterVariable = "converter";
String integrationPackage = BASE_PACKAGE + (mci ? ".infra.itrf.mci." + formatClientSystemCode(tool.clientSystemCode(), ".")
: ".infra.itrf.http." + toPackageSegment(tool.httpApiName()));
imports.append("import ").append(bizPackage).append(".dto.").append(baseName).append("Request;\n")
@@ -335,17 +358,23 @@ public class ToolScaffolder {
.append("import ").append(integrationPackage).append(".").append(clientClassName).append(";\n")
.append("import ").append(integrationPackage).append(".io.").append(mci ? ioPrefix + "_I;\n" : baseName + "HttpRequest;\n")
.append("import ").append(integrationPackage).append(".io.").append(mci ? ioPrefix + "_O;\n" : baseName + "HttpResponse;\n");
if (mci) imports.append("import io.shinhanlife.glow.communication.dto.Transfer;\n");
if (mci) {
imports.append("import io.shinhanlife.glow.communication.dto.Transfer;\n");
imports.append("import lombok.extern.slf4j.Slf4j;\n");
}
if (!fields.toString().contains(" " + clientVariable + ";")) {
fields.append(" private final ").append(clientClassName).append(" ").append(clientVariable).append(";\n");
}
fields.append(" private final ").append(baseName).append("Converter ").append(converterVariable).append(";\n");
if (!fields.toString().contains(" " + converterVariable + ";")) {
fields.append(" private final ").append(baseName).append("Converter ").append(converterVariable).append(";\n");
}
methods.append(groupedToolMethodContent(tool, baseName, converterVariable, clientVariable, ioPrefix, mci));
}
String slf4jAnno = tools.stream().anyMatch(t -> "MCI".equalsIgnoreCase(t.routingType())) ? "@Slf4j\n" : "";
return "package " + bizPackage + ".usecase.impl;\n\n"
+ "import " + bizPackage + ".usecase." + useCaseBaseName + "UseCase;\n"
+ "import lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\n" + imports
+ "\n@Service\n@RequiredArgsConstructor\npublic class " + useCaseBaseName + "UseCaseImpl implements " + useCaseBaseName + "UseCase {\n\n"
+ "\n" + slf4jAnno + "@Service\n@RequiredArgsConstructor\npublic class " + useCaseBaseName + "UseCaseImpl implements " + useCaseBaseName + "UseCase {\n\n"
+ fields + "\n" + methods + "}\n";
}
@@ -353,35 +382,43 @@ public class ToolScaffolder {
String converterVariable, String clientVariable,
String ioPrefix, boolean mci) {
if (mci) {
String snakeToolName = (tool.group() != null && !tool.group().isBlank() ? tool.group().toLowerCase(Locale.ROOT) + "_" : "")
+ toKebabCase(baseName).toLowerCase(Locale.ROOT).replace("-", "_");
String receiveServiceId = tool.clientSystemCode() != null ? tool.clientSystemCode().toUpperCase() : "";
return """
@Override
public %sResponse %s(%sRequest req) {
%s_I request = %s.toRequest(req);
log.info("[MCI Tool] {} 요청 수신.", "%s");
try {
Transfer<%s_O> transfer = %s.callTo("%s", null, request, %s_O.class);
if (transfer == null || transfer.getBody() == null) {
%sResponse errorResponse = new %sResponse();
errorResponse.setResultCode("ERROR");
return errorResponse;
// MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO)
%s_I mciReq = %s.toRequest(req);
Transfer<%s_O> resTransfer = %s.callTo(
"%s",
"%s",
mciReq,
%s_O.class
);
%sResponse response = new %sResponse();
if (resTransfer != null && resTransfer.getBody() != null) {
response = %s.toResponse(resTransfer.getBody());
}
%sResponse toolResponse = %s.toResponse(transfer.getBody());
if (toolResponse == null) {
toolResponse = new %sResponse();
toolResponse.setResultCode("ERROR");
return toolResponse;
}
toolResponse.setResultCode("SUCCESS");
return toolResponse;
response.setResultCode("SUCCESS");
return response;
} catch (Exception e) {
log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e);
%sResponse errorResponse = new %sResponse();
errorResponse.setResultCode("ERROR");
errorResponse.setResultMessage("MCI call failed.");
errorResponse.setResultMessage("MCI call failed: " + e.getMessage());
return errorResponse;
}
}
""".formatted(baseName, tool.methodName(), baseName, ioPrefix, converterVariable, ioPrefix,
clientVariable, tool.interfaceId(), ioPrefix, baseName, baseName, baseName, converterVariable, baseName,
""".formatted(baseName, tool.methodName(), baseName, snakeToolName,
ioPrefix, converterVariable,
ioPrefix, clientVariable, tool.interfaceId(), receiveServiceId, ioPrefix,
baseName, baseName,
converterVariable,
baseName, baseName);
}
return """
@@ -507,12 +544,12 @@ public class ToolScaffolder {
String clientVariable;
if (mci) {
clientClassName = mciClientClassName(tool.clientSystemCode());
clientVariable = mciClientVariable(tool.clientSystemCode());
clientVariable = "mci";
} else {
clientClassName = baseName + "Client";
clientVariable = Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1) + "Client";
}
String converterVariable = Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1) + "Converter";
String converterVariable = "converter";
implementation = addImport(implementation, "import " + bizPackage + ".dto." + requestType + ";");
implementation = addImport(implementation, "import " + bizPackage + ".dto." + responseType + ";");
@@ -520,7 +557,13 @@ public class ToolScaffolder {
implementation = addImport(implementation, "import " + integrationPackage + "." + clientClassName + ";");
implementation = addImport(implementation, "import " + integrationPackage + ".io." + requestIo + ";");
implementation = addImport(implementation, "import " + integrationPackage + ".io." + responseIo + ";");
if (mci) implementation = addImport(implementation, "import io.shinhanlife.glow.communication.dto.Transfer;");
if (mci) {
implementation = addImport(implementation, "import io.shinhanlife.glow.communication.dto.Transfer;");
implementation = addImport(implementation, "import lombok.extern.slf4j.Slf4j;");
if (!implementation.contains("@Slf4j")) {
implementation = implementation.replaceFirst("public class ", "@Slf4j\npublic class ");
}
}
implementation = addImport(implementation, "import lombok.RequiredArgsConstructor;");
if (!implementation.contains("@RequiredArgsConstructor")) {
implementation = implementation.replaceFirst("public class ", "@RequiredArgsConstructor\npublic class ");
@@ -528,7 +571,9 @@ public class ToolScaffolder {
if (!implementation.contains(" " + clientVariable + ";")) {
implementation = insertConstructorField(implementation, " private final " + clientClassName + " " + clientVariable + ";");
}
implementation = insertConstructorField(implementation, " private final " + baseName + "Converter " + converterVariable + ";");
if (!implementation.contains(" " + converterVariable + ";")) {
implementation = insertConstructorField(implementation, " private final " + baseName + "Converter " + converterVariable + ";");
}
String method = groupedToolMethodContent(tool, baseName, converterVariable, clientVariable, ioPrefix, mci);
implementation = insertBeforeLastBrace(implementation, method);
}
@@ -979,7 +1024,7 @@ public class ToolScaffolder {
if (mciReq != null) {
resTransfer = mci.callTo(
"%s",
null,
"%s",
mciReq,
%s_O.class
);
@@ -1034,6 +1079,7 @@ public class ToolScaffolder {
ioPrefix,
ioPrefix,
interfaceId,
((clientSystemCode != null && !clientSystemCode.isBlank()) ? clientSystemCode.toUpperCase() : ""),
ioPrefix,
baseName,
baseName,
@@ -1246,14 +1292,17 @@ public class ToolScaffolder {
log.append("[MCI Converter] ").append(converterDir.resolve(baseName + "Converter.java")).append("\n");
if (!clientPrefixCap.isEmpty()) {
String receiveServiceId = (clientSystemCode != null && !clientSystemCode.isBlank()) ? clientSystemCode.toUpperCase() : "";
String mciClientContent = """
package %s.%s;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent;
import io.shinhanlife.glow.communication.dto.Transfer;
import %s.%s.io.%s_I;
import %s.%s.io.%s_O;
/**
* @package %s.%s
* @className Mci%sClient
@@ -1265,21 +1314,37 @@ public class ToolScaffolder {
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 최초생성
*
*
* </pre>
*/
@Component
@RequiredArgsConstructor
public class Mci%sClient {
private final AxhubMciComponent mci;
public <O> Transfer<O> callTo(String interfaceId, String dummy, Object mciReq, Class<O> resType) throws Exception {
return mci.callTo(interfaceId, dummy, mciReq, resType);
private static final String INTERFACE_ID = "%s";
private static final String RECEIVE_SERVICE_ID = "%s";
private final AxhubMciComponent mciComponent;
public <O> Transfer<O> callTo(String interfaceId, String receiveServiceId, Object mciReq, Class<O> resType) {
return mciComponent.callTo(interfaceId, receiveServiceId, mciReq, resType);
}
public %s_O call%s(%s_I request) {
Transfer<%s_O> transfer = mciComponent.callTo(
INTERFACE_ID, RECEIVE_SERVICE_ID, request, %s_O.class);
return transfer == null ? null : transfer.getBody();
}
}
""".formatted(
BASE_PACKAGE, mciGroupPath.replace("/", "."),
BASE_PACKAGE, mciGroupPath.replace("/", "."), clientPrefixCap, author, createDate, createDate, author, clientPrefixCap
BASE_PACKAGE, mciGroupPath.replace("/", "."), ioPrefix,
BASE_PACKAGE, mciGroupPath.replace("/", "."), ioPrefix,
BASE_PACKAGE, mciGroupPath.replace("/", "."), clientPrefixCap, author, createDate, createDate, author,
clientPrefixCap,
interfaceId, receiveServiceId,
ioPrefix, toPascalCase(ioPrefix), ioPrefix,
ioPrefix, ioPrefix
);
writeUtf8(mciClientDir.resolve("Mci" + clientPrefixCap + "Client.java"), mciClientContent);
log.append("[MCI Client] ").append(mciClientDir.resolve("Mci" + clientPrefixCap + "Client.java")).append("\n");