feat: add insurance claim HTTP tool scaffold
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled

This commit is contained in:
jade
2026-08-11 23:59:33 +09:00
parent 6662641903
commit 8cbdd2ecad
15 changed files with 234 additions and 4 deletions

View File

@@ -878,7 +878,7 @@ public class ToolScaffolder {
Files.writeString(generatedTestPath, useCaseTestContent(bizPackage, baseName));
log.append("[Unit Test] ").append(generatedTestPath).append("\\n");
log.append("[Test Command] .\\gradlew.bat :").append(moduleName.substring(moduleName.lastIndexOf(java.io.File.separator) + 1)).append(":test --tests \"*").append(baseName).append("UseCaseTest\"\\n");
log.append("\n Tip: HTTP Tool?€ WireMock???ㅽ뻾?????앹꽦??mapping URL濡??몄텧???뺤씤?섏꽭??\n");
log.append("\n Tip: HTTP Tool WireMock 실행 후 생성된 mapping URL로 호출을 확인하세요.\n");
return log.toString();
}
@@ -906,7 +906,7 @@ public class ToolScaffolder {
method: POST
content-type: application/json;charset=UTF-8
biz-pod: false
""".formatted(httpApiName, environmentKey, environmentKey, toolName);
""".formatted(httpApiName, environmentKey, environmentKey, toolName).stripTrailing() + "\n";
if (existing.isBlank()) {
existing = """
spring:
@@ -920,7 +920,7 @@ public class ToolScaffolder {
api-list:
""" + apiEntry;
} else if (existing.contains("\n mci:")) {
existing = existing.replace("\n mci:", apiEntry + " mci:");
existing = existing.replace("\n mci:", "\n" + apiEntry + " mci:");
} else if (existing.contains("\naxhub:")) {
existing = existing.replace("\naxhub:", apiEntry + "axhub:");
} else if (existing.contains("api-list:")) {

View File

@@ -19,6 +19,12 @@ glow:
method: POST
content-type: application/json;charset=UTF-8
biz-pod: false
- name: insurance
domain: ${AXHUB_INSURANCE_HTTP_DOMAIN:http://localhost:${server.port}}
url: ${AXHUB_INSURANCE_HTTP_URL:/api/mock/http/ins_insurance_processor}
method: POST
content-type: application/json;charset=UTF-8
biz-pod: false
mci:
host: ${GLOW_COMMUNICATION_MCI_HOST:http://localhost}
port: ${GLOW_COMMUNICATION_MCI_PORT:8080}

View File

@@ -150,7 +150,7 @@ class ToolScaffolderTest {
List<ToolScaffolder.FieldDefinition> inputFields = List.of(
new ToolScaffolder.FieldDefinition("employeeId", "String", "Employee identifier", "EMP10001", true));
ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "Employee search", "smp", "HTTP", moduleName,
String resultLog = ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "Employee search", "smp", "HTTP", moduleName,
"tester", "2026.08.11", false, null, null, null, inputFields, outputFields);
Path dtoRoot = root.resolve("dap-was-http/src/main/java/io/shinhanlife/dap/mcc/biz/smp/dto");
@@ -191,6 +191,7 @@ class ToolScaffolderTest {
assertTrue(Files.readString(localConfig).contains("url: ${AXHUB_EMPLOYEE_SEARCH_HTTP_URL:/api/mock/http/smp_employee_search}"));
Path podMockResponse = root.resolve("dap-was-http/src/main/resources/mock-responses/smp_employee_search.json");
assertTrue(Files.exists(podMockResponse), podMockResponse.toString());
assertTrue(resultLog.contains("Tip: HTTP Tool은 WireMock 실행 후 생성된 mapping URL로 호출을 확인하세요."), resultLog);
ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "Employee search", "smp", "HTTP", moduleName,
"tester", "2026.08.11", false, null, null, null, inputFields, outputFields);
@@ -229,4 +230,29 @@ class ToolScaffolderTest {
assertFalse(implementation.contains("AxhubHttpDomain"), implementation);
assertFalse(implementation.contains("\"/HR_EMPLOYEE_SEARCH\""), implementation);
}
@Test
void addsHttpApiEntryOnItsOwnYamlLineBeforeMciConfiguration() throws Exception {
String moduleName = root.resolve("dap-was-http-yaml").toString();
Path localConfig = root.resolve("dap-was-lib/src/main/resources/glow/application-glow-local.yml");
Files.createDirectories(localConfig.getParent());
Files.writeString(localConfig, """
glow:
communication:
http:
api-list:
- name: memo
biz-pod: false
mci:
host: localhost
""");
ToolScaffolder.scaffold("insurance claim processor", "CLAIM0000001", "Insurance claim", "Insurance claim", "ins", "HTTP", moduleName,
"tester", "2026.08.11", false, null, null, null, List.of(), List.of(), "insurance");
String yaml = Files.readString(localConfig);
assertFalse(yaml.contains("biz-pod: false - name"), yaml);
assertTrue(yaml.contains(" biz-pod: false\n mci:"), yaml);
assertTrue(yaml.contains(" - name: insurance"), yaml);
}
}

View File

@@ -0,0 +1,17 @@
package io.shinhanlife.dap.mcc.biz.ins.converter;
import io.shinhanlife.dap.mcc.biz.ins.dto.InsuranceClaimProcessorRequest;
import io.shinhanlife.dap.mcc.biz.ins.dto.InsuranceClaimProcessorResponse;
import io.shinhanlife.dap.mcc.infra.itrf.http.insurance.io.InsuranceClaimProcessorHttpRequest;
import io.shinhanlife.dap.mcc.infra.itrf.http.insurance.io.InsuranceClaimProcessorHttpResponse;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface InsuranceClaimProcessorConverter {
// Field names differ? Add mappings like this before the method.
// @Mapping(source = "sourceField", target = "targetField")
InsuranceClaimProcessorHttpRequest toHttpRequest(InsuranceClaimProcessorRequest request);
InsuranceClaimProcessorResponse toResponse(InsuranceClaimProcessorHttpResponse httpResponse);
}

View File

@@ -0,0 +1,19 @@
package io.shinhanlife.dap.mcc.biz.ins.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class InsuranceClaimProcessorRequest {
@Schema(description = "보험 청구 번호", example = "CLM20230001", requiredMode = Schema.RequiredMode.REQUIRED)
private String claimNumber;
@Schema(description = "청구 금액", example = "1500000.00", requiredMode = Schema.RequiredMode.REQUIRED)
private Double claimAmount;
@Schema(description = "청구 일자 (YYYY-MM-DD)", example = "2023-09-15", requiredMode = Schema.RequiredMode.REQUIRED)
private String claimDate;
}

View File

@@ -0,0 +1,16 @@
package io.shinhanlife.dap.mcc.biz.ins.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class InsuranceClaimProcessorResponse {
private String resultCode;
private String resultMessage;
@Schema(description = "청구 처리 고유 식별자", example = "CLM20230001", requiredMode = Schema.RequiredMode.REQUIRED)
private String claimId;
}

View File

@@ -0,0 +1,27 @@
package io.shinhanlife.dap.mcc.biz.ins.usecase;
import org.springaicommunity.mcp.annotation.McpTool;
import io.shinhanlife.dap.lib.annotation.ToolHint;
import io.shinhanlife.dap.mcc.biz.ins.dto.InsuranceClaimProcessorRequest;
import io.shinhanlife.dap.mcc.biz.ins.dto.InsuranceClaimProcessorResponse;
/**
* @package io.shinhanlife.dap.mcc.biz.ins.usecase
* @className InsuranceClaimProcessorUseCase
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @author Admin
* @create 2026.08.11
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- -------- ---------------------------
* 2026.08.11 Admin 理쒖큹?앹꽦
*
* </pre>
*/
public interface InsuranceClaimProcessorUseCase {
@McpTool(name = "ins_insurance_processor", title = "보험금 청구", description = "보험금 청구 요청을 처리하고 결과를 반환하는 LLM 도구 가이드")
@ToolHint(register = false, categoryKey = "ins", mappingId = "CLAIM0000001")
InsuranceClaimProcessorResponse execute(InsuranceClaimProcessorRequest req);
}

View File

@@ -0,0 +1,30 @@
package io.shinhanlife.dap.mcc.biz.ins.usecase.impl;
import io.shinhanlife.dap.mcc.biz.ins.converter.InsuranceClaimProcessorConverter;
import io.shinhanlife.dap.mcc.biz.ins.dto.InsuranceClaimProcessorRequest;
import io.shinhanlife.dap.mcc.biz.ins.dto.InsuranceClaimProcessorResponse;
import io.shinhanlife.dap.mcc.infra.itrf.http.insurance.InsuranceClient;
import io.shinhanlife.dap.mcc.infra.itrf.http.insurance.io.InsuranceClaimProcessorHttpRequest;
import io.shinhanlife.dap.mcc.infra.itrf.http.insurance.io.InsuranceClaimProcessorHttpResponse;
import io.shinhanlife.dap.mcc.biz.ins.usecase.InsuranceClaimProcessorUseCase;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class InsuranceClaimProcessorUseCaseImpl implements InsuranceClaimProcessorUseCase {
private final InsuranceClaimProcessorConverter converter;
private final InsuranceClient insuranceClient;
@Override
public InsuranceClaimProcessorResponse execute(InsuranceClaimProcessorRequest req) {
InsuranceClaimProcessorHttpRequest httpRequest = converter.toHttpRequest(req);
InsuranceClaimProcessorHttpResponse httpResponse = insuranceClient.call(httpRequest, InsuranceClaimProcessorHttpResponse.class);
InsuranceClaimProcessorResponse response = converter.toResponse(httpResponse);
response.setResultCode("SUCCESS");
response.setResultMessage("HTTP API call completed.");
return response;
}
}

View File

@@ -0,0 +1,17 @@
package io.shinhanlife.dap.mcc.infra.itrf.http.insurance;
import io.shinhanlife.dap.lib.integration.http.component.AxhubHttpComponent;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class InsuranceClient {
private static final String API_NAME = "insurance";
private final AxhubHttpComponent http;
public <I, O> O call(I request, Class<O> responseType) {
return http.call(API_NAME, request, responseType);
}
}

View File

@@ -0,0 +1,19 @@
package io.shinhanlife.dap.mcc.infra.itrf.http.insurance.io;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class InsuranceClaimProcessorHttpRequest {
@Schema(description = "보험 청구 번호", example = "CLM20230001", requiredMode = Schema.RequiredMode.REQUIRED)
private String claimNumber;
@Schema(description = "청구 금액", example = "1500000.00", requiredMode = Schema.RequiredMode.REQUIRED)
private Double claimAmount;
@Schema(description = "청구 일자 (YYYY-MM-DD)", example = "2023-09-15", requiredMode = Schema.RequiredMode.REQUIRED)
private String claimDate;
}

View File

@@ -0,0 +1,16 @@
package io.shinhanlife.dap.mcc.infra.itrf.http.insurance.io;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class InsuranceClaimProcessorHttpResponse {
private String resultCode;
private String resultMessage;
@Schema(description = "청구 처리 고유 식별자", example = "CLM20230001", requiredMode = Schema.RequiredMode.REQUIRED)
private String claimId;
}

View File

@@ -0,0 +1,4 @@
{
"resultCode" : "SUCCESS",
"claimId" : "CLM20230001"
}

View File

@@ -0,0 +1,16 @@
package io.shinhanlife.dap.mcc.biz.ins.usecase;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.shinhanlife.dap.mcc.biz.ins.dto.InsuranceClaimProcessorRequest;
import io.shinhanlife.dap.mcc.biz.ins.dto.InsuranceClaimProcessorResponse;
import org.junit.jupiter.api.Test;
class InsuranceClaimProcessorUseCaseTest {
@Test
void createsToolRequestAndResponseDtos() {
assertNotNull(new InsuranceClaimProcessorRequest());
assertNotNull(new InsuranceClaimProcessorResponse());
}
}

View File

@@ -0,0 +1,4 @@
{
"resultCode" : "SUCCESS",
"claimId" : "CLM20230001"
}

View File

@@ -0,0 +1,13 @@
{
"request" : {
"method" : "POST",
"urlPath" : "/CLAIM0000001"
},
"response" : {
"status" : 200,
"headers" : {
"Content-Type" : "application/json;charset=UTF-8"
},
"bodyFileName" : "ins_insurance_processor.json"
}
}