diff --git a/src/main/java/com/snp/batch/service/BypassCodeGenerator.java b/src/main/java/com/snp/batch/service/BypassCodeGenerator.java index 1880bf4..fb7c299 100644 --- a/src/main/java/com/snp/batch/service/BypassCodeGenerator.java +++ b/src/main/java/com/snp/batch/service/BypassCodeGenerator.java @@ -84,23 +84,27 @@ public class BypassCodeGenerator { StringBuilder fieldLines = new StringBuilder(); for (BypassApiField field : fields) { String jsonProp = field.getJsonProperty() != null ? field.getJsonProperty() : field.getFieldName(); - fieldLines.append(" @JsonProperty(\"%s\")\n".formatted(jsonProp)); - fieldLines.append(" private %s %s;\n\n".formatted(field.getFieldType(), field.getFieldName())); + fieldLines.append(" @JsonProperty(\"").append(jsonProp).append("\")\n"); + fieldLines.append(" private ").append(field.getFieldType()).append(" ").append(field.getFieldName()).append(";\n\n"); } return """ - package %s; + package {{PACKAGE}}; - %s + {{IMPORTS}} @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor - public class %sBypassDto { + public class {{CLASS_NAME}} { - %s} - """.formatted(packageName, imports, domainCap, fieldLines); + {{FIELDS}}} + """ + .replace("{{PACKAGE}}", packageName) + .replace("{{IMPORTS}}", imports.toString()) + .replace("{{CLASS_NAME}}", domainCap + "BypassDto") + .replace("{{FIELDS}}", fieldLines.toString()); } /** @@ -115,7 +119,7 @@ public class BypassCodeGenerator { boolean isList = "LIST".equalsIgnoreCase(config.getResponseType()); boolean isPost = "POST".equalsIgnoreCase(config.getHttpMethod()); - String returnType = isList ? "List<%s>".formatted(dtoClass) : dtoClass; + String returnType = isList ? "List<" + dtoClass + ">" : dtoClass; String methodName = "get" + domainCap + "Data"; String fetchMethod = buildFetchMethodCall(config, params, isList, isPost); String methodParams = buildMethodParams(params); @@ -123,52 +127,51 @@ public class BypassCodeGenerator { String listImport = isList ? "import java.util.List;\n" : ""; return """ - package %s; + package {{PACKAGE}}; - import %s.%s; + import {{DTO_IMPORT}}; import com.snp.batch.common.web.service.BaseBypassService; - %simport org.springframework.beans.factory.annotation.Qualifier; + {{LIST_IMPORT}}import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.ParameterizedTypeReference; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; /** - * %s bypass 서비스 + * {{DISPLAY_NAME}} bypass 서비스 * 외부 Maritime API에서 데이터를 실시간 조회하여 그대로 반환 */ @Service - public class %sBypassService extends BaseBypassService<%s> { + public class {{DOMAIN_CAP}}BypassService extends BaseBypassService<{{DTO_CLASS}}> { - public %sBypassService( - @Qualifier("%s") WebClient webClient) { - super(webClient, "%s", "%s", + public {{DOMAIN_CAP}}BypassService( + @Qualifier("{{WEBCLIENT_BEAN}}") WebClient webClient) { + super(webClient, "{{DISPLAY_NAME}}", "{{EXTERNAL_PATH}}", new ParameterizedTypeReference<>() {}, new ParameterizedTypeReference<>() {}); } /** - * %s 데이터를 조회합니다. + * {{DISPLAY_NAME}} 데이터를 조회합니다. * - * @return %s + * @return {{DISPLAY_NAME}} */ - public %s %s(%s) { - %s + public {{RETURN_TYPE}} {{METHOD_NAME}}({{METHOD_PARAMS}}) { + {{FETCH_METHOD}} } } - """.formatted( - packageName, - dtoPackage, dtoClass, - listImport, - config.getDisplayName(), - domainCap, dtoClass, - domainCap, - config.getWebclientBean(), - config.getExternalPath(), - config.getDisplayName(), - config.getDisplayName(), - returnType, methodName, methodParams, - fetchMethod - ); + """ + .replace("{{PACKAGE}}", packageName) + .replace("{{DTO_IMPORT}}", dtoPackage + "." + dtoClass) + .replace("{{LIST_IMPORT}}", listImport) + .replace("{{DISPLAY_NAME}}", config.getDisplayName()) + .replace("{{DOMAIN_CAP}}", domainCap) + .replace("{{DTO_CLASS}}", dtoClass) + .replace("{{WEBCLIENT_BEAN}}", config.getWebclientBean()) + .replace("{{EXTERNAL_PATH}}", config.getExternalPath()) + .replace("{{RETURN_TYPE}}", returnType) + .replace("{{METHOD_NAME}}", methodName) + .replace("{{METHOD_PARAMS}}", methodParams) + .replace("{{FETCH_METHOD}}", fetchMethod); } /** @@ -187,7 +190,7 @@ public class BypassCodeGenerator { boolean isList = "LIST".equalsIgnoreCase(config.getResponseType()); boolean isPost = "POST".equalsIgnoreCase(config.getHttpMethod()); - String responseGeneric = isList ? "List<%s>".formatted(dtoClass) : dtoClass; + String responseGeneric = isList ? "List<" + dtoClass + ">" : dtoClass; String mappingAnnotation = isPost ? "@PostMapping" : "@GetMapping"; String listImport = isList ? "import java.util.List;\n" : ""; @@ -205,59 +208,58 @@ public class BypassCodeGenerator { String requestMappingPath = "/api/" + domain; return """ - package %s; + package {{PACKAGE}}; - import %s.%s; - import %s.%s; + import {{DTO_IMPORT}}; + import {{SERVICE_IMPORT}}; import com.snp.batch.common.web.ApiResponse; import com.snp.batch.common.web.controller.BaseBypassController; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; - %simport lombok.RequiredArgsConstructor; + {{LIST_IMPORT}}import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; - %s%s%s%s + {{PATH_VARIABLE_IMPORT}}{{REQUEST_PARAM_IMPORT}}{{REQUEST_BODY_IMPORT}} /** - * %s bypass API + * {{DISPLAY_NAME}} bypass API * S&P Maritime API에서 데이터를 실시간 조회하여 그대로 반환 */ @RestController - @RequestMapping("%s") + @RequestMapping("{{REQUEST_MAPPING_PATH}}") @RequiredArgsConstructor - @Tag(name = "%s", description = "%s bypass API") - public class %sController extends BaseBypassController { + @Tag(name = "{{DOMAIN_CAP}}", description = "{{DISPLAY_NAME}} bypass API") + public class {{DOMAIN_CAP}}Controller extends BaseBypassController { - private final %s %s; + private final {{SERVICE_CLASS}} {{SERVICE_FIELD}}; @Operation( - summary = "%s 조회", - description = "S&P API에서 %s 데이터를 요청하고 응답을 그대로 반환합니다." + summary = "{{DISPLAY_NAME}} 조회", + description = "S&P API에서 {{DISPLAY_NAME}} 데이터를 요청하고 응답을 그대로 반환합니다." ) - %s - public ResponseEntity> get%sData(%s) { - return execute(() -> %s.get%sData(%s)); + {{MAPPING_ANNOTATION}} + public ResponseEntity> get{{DOMAIN_CAP}}Data({{PARAM_ANNOTATIONS}}) { + return execute(() -> {{SERVICE_FIELD}}.get{{DOMAIN_CAP}}Data({{SERVICE_CALL_ARGS}})); } } - """.formatted( - packageName, - dtoPackage, dtoClass, - servicePackage, serviceClass, - listImport, - pathVariableImport, requestParamImport, requestBodyImport, - mappingAnnotation.equals("@PostMapping") ? "" : "", - config.getDisplayName(), - requestMappingPath, - domainCap, config.getDisplayName(), - domainCap, - serviceClass, serviceField, - config.getDisplayName(), config.getDisplayName(), - mappingAnnotation + mappingPath, - responseGeneric, domainCap, - paramAnnotations.isEmpty() ? "" : paramAnnotations, - serviceField, domainCap, serviceCallArgs - ); + """ + .replace("{{PACKAGE}}", packageName) + .replace("{{DTO_IMPORT}}", dtoPackage + "." + dtoClass) + .replace("{{SERVICE_IMPORT}}", servicePackage + "." + serviceClass) + .replace("{{LIST_IMPORT}}", listImport) + .replace("{{PATH_VARIABLE_IMPORT}}", pathVariableImport) + .replace("{{REQUEST_PARAM_IMPORT}}", requestParamImport) + .replace("{{REQUEST_BODY_IMPORT}}", requestBodyImport) + .replace("{{DISPLAY_NAME}}", config.getDisplayName()) + .replace("{{DOMAIN_CAP}}", domainCap) + .replace("{{REQUEST_MAPPING_PATH}}", requestMappingPath) + .replace("{{SERVICE_CLASS}}", serviceClass) + .replace("{{SERVICE_FIELD}}", serviceField) + .replace("{{MAPPING_ANNOTATION}}", mappingAnnotation + mappingPath) + .replace("{{RESPONSE_GENERIC}}", responseGeneric) + .replace("{{PARAM_ANNOTATIONS}}", paramAnnotations.isEmpty() ? "" : paramAnnotations) + .replace("{{SERVICE_CALL_ARGS}}", serviceCallArgs); } /** @@ -265,10 +267,6 @@ public class BypassCodeGenerator { */ private String buildFetchMethodCall(BypassApiConfig config, List params, boolean isList, boolean isPost) { - List pathParams = params.stream() - .filter(p -> "PATH".equalsIgnoreCase(p.getParamIn())) - .sorted((a, b) -> Integer.compare(a.getSortOrder(), b.getSortOrder())) - .toList(); List queryParams = params.stream() .filter(p -> "QUERY".equalsIgnoreCase(p.getParamIn())) .sorted((a, b) -> Integer.compare(a.getSortOrder(), b.getSortOrder())) @@ -276,12 +274,9 @@ public class BypassCodeGenerator { StringBuilder uriBuilder = new StringBuilder(); uriBuilder.append("uri -> uri.path(getApiPath())"); - for (BypassApiParam p : pathParams) { - // PATH 파라미터는 path()에 직접 치환되므로 별도 처리 불필요 - } for (BypassApiParam p : queryParams) { - uriBuilder.append("\n .queryParam(\"%s\", %s)" - .formatted(p.getParamName(), p.getParamName())); + uriBuilder.append("\n .queryParam(\"") + .append(p.getParamName()).append("\", ").append(p.getParamName()).append(")"); } uriBuilder.append("\n .build()"); @@ -292,10 +287,10 @@ public class BypassCodeGenerator { .filter(p -> "BODY".equalsIgnoreCase(p.getParamIn())) .findFirst().orElse(null); String bodyArg = bodyParam != null ? bodyParam.getParamName() : "null"; - return "return %s(%s, %s);".formatted(fetchName, bodyArg, uriBuilder); + return "return " + fetchName + "(" + bodyArg + ", " + uriBuilder + ");"; } else { fetchName = isList ? "fetchGetList" : "fetchGetOne"; - return "return %s(%s);".formatted(fetchName, uriBuilder); + return "return " + fetchName + "(" + uriBuilder + ");"; } } @@ -329,24 +324,20 @@ public class BypassCodeGenerator { return params.stream() .sorted((a, b) -> Integer.compare(a.getSortOrder(), b.getSortOrder())) .map(p -> { - String annotation; String description = p.getDescription() != null ? p.getDescription() : p.getParamName(); - switch (p.getParamIn().toUpperCase()) { - case "PATH" -> annotation = """ - @Parameter(description = "%s") - @PathVariable %s %s""".formatted(description, toJavaType(p.getParamType()), p.getParamName()); - case "BODY" -> annotation = """ - @Parameter(description = "%s") - @RequestBody %s %s""".formatted(description, toJavaType(p.getParamType()), p.getParamName()); + String javaType = toJavaType(p.getParamType()); + String paramName = p.getParamName(); + return switch (p.getParamIn().toUpperCase()) { + case "PATH" -> "@Parameter(description = \"" + description + "\")\n" + + " @PathVariable " + javaType + " " + paramName; + case "BODY" -> "@Parameter(description = \"" + description + "\")\n" + + " @RequestBody " + javaType + " " + paramName; default -> { String required = Boolean.TRUE.equals(p.getRequired()) ? "true" : "false"; - annotation = """ - @Parameter(description = "%s") - @RequestParam(required = %s) %s %s""".formatted( - description, required, toJavaType(p.getParamType()), p.getParamName()); + yield "@Parameter(description = \"" + description + "\")\n" + + " @RequestParam(required = " + required + ") " + javaType + " " + paramName; } - } - return annotation; + }; }) .collect(Collectors.joining(",\n ")); } @@ -365,7 +356,7 @@ public class BypassCodeGenerator { String path = pathParams.stream() .map(p -> "{" + p.getParamName() + "}") .collect(Collectors.joining("/", "/", "")); - return "(\"%s\")".formatted(path); + return "(\"" + path + "\")"; } /**