From 8cbdd2ecad6e2e18dd775387729b699b9d83f0b5 Mon Sep 17 00:00:00 2001 From: jade Date: Tue, 11 Aug 2026 23:59:33 +0900 Subject: [PATCH] feat: add insurance claim HTTP tool scaffold --- .../dap/lib/util/ToolScaffolder.java | 6 ++-- .../resources/glow/application-glow-local.yml | 6 ++++ .../dap/lib/util/ToolScaffolderTest.java | 28 ++++++++++++++++- .../InsuranceClaimProcessorConverter.java | 17 +++++++++++ .../dto/InsuranceClaimProcessorRequest.java | 19 ++++++++++++ .../dto/InsuranceClaimProcessorResponse.java | 16 ++++++++++ .../InsuranceClaimProcessorUseCase.java | 27 +++++++++++++++++ .../InsuranceClaimProcessorUseCaseImpl.java | 30 +++++++++++++++++++ .../itrf/http/insurance/InsuranceClient.java | 17 +++++++++++ .../InsuranceClaimProcessorHttpRequest.java | 19 ++++++++++++ .../InsuranceClaimProcessorHttpResponse.java | 16 ++++++++++ .../ins_insurance_processor.json | 4 +++ .../InsuranceClaimProcessorUseCaseTest.java | 16 ++++++++++ mci-mock/__files/ins_insurance_processor.json | 4 +++ .../mappings/ins_insurance_processor.json | 13 ++++++++ 15 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/converter/InsuranceClaimProcessorConverter.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/dto/InsuranceClaimProcessorRequest.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/dto/InsuranceClaimProcessorResponse.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/usecase/InsuranceClaimProcessorUseCase.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/usecase/impl/InsuranceClaimProcessorUseCaseImpl.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/InsuranceClient.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/io/InsuranceClaimProcessorHttpRequest.java create mode 100644 dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/io/InsuranceClaimProcessorHttpResponse.java create mode 100644 dap-was-oth/src/main/resources/mock-responses/ins_insurance_processor.json create mode 100644 dap-was-oth/src/test/java/io/shinhanlife/dap/mcc/biz/ins/usecase/InsuranceClaimProcessorUseCaseTest.java create mode 100644 mci-mock/__files/ins_insurance_processor.json create mode 100644 mci-mock/mappings/ins_insurance_processor.json diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java index 83f065e1..d1658a26 100644 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java +++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java @@ -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:")) { diff --git a/dap-was-lib/src/main/resources/glow/application-glow-local.yml b/dap-was-lib/src/main/resources/glow/application-glow-local.yml index af1ac603..0a9b014e 100644 --- a/dap-was-lib/src/main/resources/glow/application-glow-local.yml +++ b/dap-was-lib/src/main/resources/glow/application-glow-local.yml @@ -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} diff --git a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolScaffolderTest.java b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolScaffolderTest.java index c03d1118..8f7073ad 100644 --- a/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolScaffolderTest.java +++ b/dap-was-lib/src/test/java/io/shinhanlife/dap/lib/util/ToolScaffolderTest.java @@ -150,7 +150,7 @@ class ToolScaffolderTest { List 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); + } } diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/converter/InsuranceClaimProcessorConverter.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/converter/InsuranceClaimProcessorConverter.java new file mode 100644 index 00000000..e6d7d139 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/converter/InsuranceClaimProcessorConverter.java @@ -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); +} diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/dto/InsuranceClaimProcessorRequest.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/dto/InsuranceClaimProcessorRequest.java new file mode 100644 index 00000000..ccc1338d --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/dto/InsuranceClaimProcessorRequest.java @@ -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; + +} diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/dto/InsuranceClaimProcessorResponse.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/dto/InsuranceClaimProcessorResponse.java new file mode 100644 index 00000000..a4830ea5 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/dto/InsuranceClaimProcessorResponse.java @@ -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; + +} diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/usecase/InsuranceClaimProcessorUseCase.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/usecase/InsuranceClaimProcessorUseCase.java new file mode 100644 index 00000000..ec3faaa9 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/usecase/InsuranceClaimProcessorUseCase.java @@ -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 + *
+ * ---------- 媛쒖젙?대젰 ----------
+ * ?섏젙??     ?섏젙??   ?섏젙?댁슜
+ * ---------- -------- ---------------------------
+ * 2026.08.11  Admin    理쒖큹?앹꽦
+ *
+ * 
+ */ +public interface InsuranceClaimProcessorUseCase { + + @McpTool(name = "ins_insurance_processor", title = "보험금 청구", description = "보험금 청구 요청을 처리하고 결과를 반환하는 LLM 도구 가이드") + @ToolHint(register = false, categoryKey = "ins", mappingId = "CLAIM0000001") + InsuranceClaimProcessorResponse execute(InsuranceClaimProcessorRequest req); +} diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/usecase/impl/InsuranceClaimProcessorUseCaseImpl.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/usecase/impl/InsuranceClaimProcessorUseCaseImpl.java new file mode 100644 index 00000000..e18e2cc2 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/biz/ins/usecase/impl/InsuranceClaimProcessorUseCaseImpl.java @@ -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; + } +} diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/InsuranceClient.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/InsuranceClient.java new file mode 100644 index 00000000..b67a95ed --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/InsuranceClient.java @@ -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 O call(I request, Class responseType) { + return http.call(API_NAME, request, responseType); + } +} diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/io/InsuranceClaimProcessorHttpRequest.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/io/InsuranceClaimProcessorHttpRequest.java new file mode 100644 index 00000000..96ee6ef2 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/io/InsuranceClaimProcessorHttpRequest.java @@ -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; + +} diff --git a/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/io/InsuranceClaimProcessorHttpResponse.java b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/io/InsuranceClaimProcessorHttpResponse.java new file mode 100644 index 00000000..a2b05e99 --- /dev/null +++ b/dap-was-oth/src/main/java/io/shinhanlife/dap/mcc/infra/itrf/http/insurance/io/InsuranceClaimProcessorHttpResponse.java @@ -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; + +} diff --git a/dap-was-oth/src/main/resources/mock-responses/ins_insurance_processor.json b/dap-was-oth/src/main/resources/mock-responses/ins_insurance_processor.json new file mode 100644 index 00000000..d856cc5b --- /dev/null +++ b/dap-was-oth/src/main/resources/mock-responses/ins_insurance_processor.json @@ -0,0 +1,4 @@ +{ + "resultCode" : "SUCCESS", + "claimId" : "CLM20230001" +} diff --git a/dap-was-oth/src/test/java/io/shinhanlife/dap/mcc/biz/ins/usecase/InsuranceClaimProcessorUseCaseTest.java b/dap-was-oth/src/test/java/io/shinhanlife/dap/mcc/biz/ins/usecase/InsuranceClaimProcessorUseCaseTest.java new file mode 100644 index 00000000..7508af8d --- /dev/null +++ b/dap-was-oth/src/test/java/io/shinhanlife/dap/mcc/biz/ins/usecase/InsuranceClaimProcessorUseCaseTest.java @@ -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()); + } +} diff --git a/mci-mock/__files/ins_insurance_processor.json b/mci-mock/__files/ins_insurance_processor.json new file mode 100644 index 00000000..d856cc5b --- /dev/null +++ b/mci-mock/__files/ins_insurance_processor.json @@ -0,0 +1,4 @@ +{ + "resultCode" : "SUCCESS", + "claimId" : "CLM20230001" +} diff --git a/mci-mock/mappings/ins_insurance_processor.json b/mci-mock/mappings/ins_insurance_processor.json new file mode 100644 index 00000000..7234f808 --- /dev/null +++ b/mci-mock/mappings/ins_insurance_processor.json @@ -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" + } +}