release: 2026-03-31 (40건 커밋) #118

병합
HYOJIN develop 에서 main 로 40 commits 를 머지했습니다 2026-03-31 11:09:31 +09:00
Showing only changes of commit d2932f4032 - Show all commits

파일 보기

@ -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<ApiResponse<%s>> get%sData(%s) {
return execute(() -> %s.get%sData(%s));
{{MAPPING_ANNOTATION}}
public ResponseEntity<ApiResponse<{{RESPONSE_GENERIC}}>> 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<BypassApiParam> params,
boolean isList, boolean isPost) {
List<BypassApiParam> pathParams = params.stream()
.filter(p -> "PATH".equalsIgnoreCase(p.getParamIn()))
.sorted((a, b) -> Integer.compare(a.getSortOrder(), b.getSortOrder()))
.toList();
List<BypassApiParam> 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 + "\")";
}
/**