diff --git a/README.md b/README.md index b2dd64c05..9f4cea6ad 100644 --- a/README.md +++ b/README.md @@ -1,504 +1,72 @@ -# DATMT MCP Tool Pods +# 신한라이프 시스템 도메인 Tool Pod (dat-was-datsy) -신한라이프 업무 기능을 MCP(Model Context Protocol) Tool로 제공하는 Java 멀티 모듈 프로젝트입니다. 각 업무 모듈은 독립 실행 가능한 Spring Boot 애플리케이션이며, MCP Streamable HTTP와 REST 실행 API를 함께 제공합니다. +신한라이프 DX그룹 PJT **MCP & TOOL 파트**에서 개발 및 운영하는 시스템(SYS) 도메인 전용 DATMT Tool Service Pod(`dat-was-sys`)입니다. +IAM(계정·인증·인가 및 담당자 조회)과 PCT(점검 일정, 릴리즈 이력, 시스템 공지사항 등) 영역에서 AI Agent가 시스템 운영 현황을 파악하고 질의응답을 지원할 수 있는 MCP 도구를 제공합니다. -이 저장소에는 DATMS(Gateway) 애플리케이션이 포함되어 있지 않습니다. DATMT는 Gateway로 Tool을 push 등록하지 않으며, 각 Pod가 `GET /tool-manifest`를 제공하면 DATMS가 이 Manifest를 pull하여 Tool 목록을 구성합니다. Tool 조회와 직접 실행은 각 Pod에서도 자체적으로 처리합니다. +--- -## 기술 기준 +## 1. 서비스 사양 요약 -- Java 21 -- Gradle Wrapper 8.14.3 -- Spring Boot 3.5.11 -- MCP Java SDK 2.0.0 -- Spring AI Community MCP Annotations 0.9.0 -- Jackson 2.19.4 (Spring Boot BOM) -- MapStruct, MyBatis, Redis, Kafka, Resilience4j -- JUnit 5 +| 항목 | 내용 | +|---|---| +| **서비스명 (모듈)** | `dat-was-sys` (`dat-was-datsy`) | +| **서버 포트** | **8086** (기본) | +| **MCP 엔드포인트** | `http://localhost:8086/mcp`, `http://localhost:8086/mcp/message` | +| **Swagger UI** | `http://localhost:8086/swagger-ui.html` | +| **Bundle ID** | `was-sys` | +| **기반 기술** | Java 21, Spring Boot 3.5.11, MCP Java SDK 2.0.0 | +| **공통 의존성** | `io.shinhanlife:dat-lib-datmt:0.0.1-SNAPSHOT` (`dat-was-lib`) | -## 모듈 구성 +--- -| 모듈 | 역할 | 기본 포트 | Tool 수 | -|---|---|---:|---:| -| `dat-was-lib` | MCP 서버, Tool 스캔·실행, Schema, Manifest, 보안, MCI/EAI/HTTP 연동 공통 기능 | - | - | -| `dat-was-cus` | 고객·CRM·VOC·웹 콘텐츠 관리 Tool | 8084 | 51 | -| `dat-was-sal` | 영업·청구·인수·동의·현장지원 Tool | 8082 | 52 | -| `dat-was-pro` | 상품 영역 Tool(개인고객 상세조회·모집수수료 공시) | 8085 | 2 | -| `dat-was-sys` | IAM·시스템 상태·공지·점검·배포 Tool | 8086 | 51 | +## 2. 제공 MCP 도구 (Tools) 목록 -업무 모듈에는 총 156개의 `@McpTool` 선언이 있습니다. 현재 업무 모듈의 `src/main/resources`에는 별도 `tool-definitions` YAML이 없으며, Tool 메타데이터는 어노테이션과 `@GrowToolHint`를 기준으로 생성됩니다. +### ① IAM (Identity & Access Management / 조직) +- **`AccountInquiryUseCase`**: 사번 또는 계정 ID를 통한 사용자/임직원 기본 정보 조회 +- **`AuthenticationInquiryUseCase`**: 계정의 2차 인증 상태, 계정 잠김 여부 등 인증 상태 확인 +- **`AuthorizationInquiryUseCase`**: 사용자가 보유한 시스템 권한 그룹 및 리소스 접근 인가 현황 조회 +- **`SystemStatusUseCase`**: 신한라이프 주요 IT 서비스 및 연계 채널의 실시간 가동 상태 점검 +- **`TeamContactUseCase`**: 특정 업무/시스템별 담당 부서, 담당자 및 비상 연락처 조회 -현재 업무 구현은 개발·연동 검증 단계입니다. `dat-was-sal`의 `cmm_claim_search`와 `cmm_memo_retriever`는 각각 MCI와 HTTP Client 흐름을 사용하며, 나머지 Tool은 외부 시스템을 변경하지 않는 모의 응답을 중심으로 구현되어 있습니다. +### ② PCT (Process & Change Management / 운영) +- **`MaintenanceInquiryUseCase`**: 예정된 시스템 정기 점검, 서버 패치 및 서비스 중단 일정 조회 +- **`ReleaseInquiryUseCase`**: 최근 배포된 시스템 기능 개선 및 릴리즈 이력 정보 조회 +- **`SystemNoticeUseCase`**: 전사 시스템 공지사항 및 긴급 장애 공지 내역 조회 -## 처리 구조 +--- + +## 3. 공통 라이브러리 연동 (Composite Build) + +본 저장소는 상위 워크스페이스의 `../dat-lib-datmt` 공통 라이브러리를 로컬 복합 빌드(Composite Build)로 직접 참조합니다. ```text -MCP Client 또는 REST Client - ├─ MCP Streamable HTTP: /mcp - └─ REST: POST /mcp/{toolName} - │ - ▼ -Tool Pod - ├─ McpToolMethodRegistry - │ └─ Spring Bean의 @McpTool 메서드 탐색 및 실행 메서드 캐시 - ├─ ToolRegistryHeartbeatSender - │ ├─ Tool 메타데이터 생성 - │ └─ 선택적 tool-definitions YAML 병합 - ├─ ToolManifestService - │ └─ DATMS가 pull할 bundle 단위 Manifest 생성 - ├─ McpToolExecutionService - │ ├─ 입력 Schema 검증 - │ ├─ 요청 DTO 변환 및 Tool 호출 - │ └─ 출력 Schema 검증 - └─ UseCase → Converter → MCI/EAI/HTTP Client 또는 Mock 응답 +workspace/ +├── dat-lib-datmt/ # 공통 라이브러리 (:dat-was-lib) +└── dat-was-datsy/ # [현재 저장소] 시스템 도메인 Tool Pod (:dat-was-sys) ``` -애플리케이션 시작 시 다음 순서로 Tool이 준비됩니다. +--- -1. `ToolDefinitionRepository`는 `classpath*:tool-definitions/**/*.yml` 경로의 정의가 있을 경우 이를 읽고 V17 필수 항목을 검증합니다. -2. `ToolRegistryHeartbeatSender`가 `@McpTool` 메서드를 스캔하고, 존재하는 YAML 정의가 있으면 병합해 `ToolMetadata`를 생성합니다. -3. `McpToolMethodRegistry`가 실제 호출 가능한 Bean과 메서드를 Tool 이름으로 캐시합니다. -4. `ToolPodMcpToolSynchronizer`가 Tool을 MCP SDK 서버에 등록합니다. -5. REST와 MCP 요청은 공통 `McpToolExecutionService`를 통해 실행됩니다. - -DATMT 내부에는 `/registry/register`, `/registry/deregister` 호출이나 주기적인 Gateway heartbeat 전송이 없습니다. `ToolRegistryHeartbeatSender`라는 클래스명은 호환성을 위해 남아 있지만 현재 역할은 로컬 Tool 스캔과 메타데이터 생성뿐입니다. - -### DATMS 연동 방식 - -```text -DATMS - └─ GET {DATMT Pod URL}/tool-manifest - └─ bundleId + revision + tools[] - └─ 각 Tool endpoint: {Pod URL}/mcp/{toolName} -``` - -- `mcp.manifest.bundle-id`는 Manifest를 제공하는 Pod의 고유 식별자이며 필수입니다. -- 현재 값은 `was-sal`, `was-cus`, `was-pro`, `was-sys`입니다. -- DATMS에 설정한 bundle ID와 DATMT가 반환하는 `bundleId`가 일치해야 같은 Tool bundle로 관리됩니다. -- `mcp.manifest.name-prefix`가 비어 있지 않으면 모든 Tool 이름이 해당 prefix로 시작해야 합니다. -- Manifest 내용이 바뀌면 `revision`이 증가하며, `If-None-Match`가 일치하면 `304 Not Modified`를 반환합니다. - -## 제공 API - -각 업무 Pod가 동일한 API 구조를 제공합니다. - -| 목적 | 메서드 | 경로 | -|---|---|---| -| MCP Streamable HTTP | MCP 프로토콜 | `/mcp` | -| MCP 메시지 전송 | MCP 프로토콜 | `/mcp/message` | -| 로컬 Tool 메타데이터 조회 | `GET` | `/mcp/api/v1/tools/local` | -| Tool 직접 실행 | `POST` | `/mcp/{toolName}` | -| Tool Manifest 조회 | `GET` | `/tool-manifest` | -| 카테고리별 Tool Manifest 조회 | `GET` | `/tool-manifest/{categoryKey}` | - -`GET /tool-manifest`는 `If-None-Match` 요청 헤더를 지원합니다. Manifest가 변경되지 않았으면 `304 Not Modified`를 반환합니다. - -### REST 실행 예시 - -다음은 SYS Pod의 시스템 상태 Tool 호출 예시입니다. +## 4. 빌드 및 실행 가이드 +### 컴파일 및 빌드 ```powershell -$headers = @{ - 'X-Tool-Server-API-Key' = $env:TOOL_SERVER_API_KEY - 'X-Guid' = 'guid-local-001' - 'X-Praf-No' = '100001' - 'X-Request-Id' = 'request-local-001' - 'X-Request-Time' = '2026-08-25T12:34:56+09:00' - 'X-Vrtl-Praf-No' = 'V100001' - 'X-App-Code' = 'DATMT' - 'X-Project-Code' = 'AXHUB' - 'X-User-Ip' = '10.0.0.10' - 'X-Caller-Ip' = '10.0.0.20' - 'X-Caller-Host' = 'caller.example.internal' - 'X-Channel' = 'MCP' - 'X-Agent-Id' = 'agent-local-001' - 'mcp-session-id' = 'session-local-001' -} - -Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:8086/mcp/iam_system_status' ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body '{"environment":"개발"}' +.\gradlew.bat compileJava ``` -### 요청 헤더 계약 - -DATMS가 DATMT Tool Service를 호출할 때 사용하는 헤더는 다음과 같습니다. HTTP 헤더 이름은 대소문자를 구분하지 않지만, 문서와 구현에서는 아래 표기를 기준으로 사용합니다. - -| 헤더 | 필수 여부 | 용도 | 전달 동작 | -|---|---|---|---| -| `X-Tool-Server-API-Key` | 인증 설정 시 필수 | DATMS와 DATMT 사이의 Tool Server 인증 | `mcp.security.api-key` 또는 `api-keys`와 비교 | -| `X-Guid` | 선택 | 업무 호출 상관관계 식별자 | 실행 로그, 성공 응답, 하위 HTTP 호출로 전달 | -| `X-Praf-No` | 선택 | 실제 사용자 사번 | 세션 조회와 하위 HTTP 호출로 전달 | -| `X-Request-Id` | 선택 | 요청 추적 식별자 | 실행 로그, 성공 응답, 하위 HTTP 호출로 전달 | -| `X-Request-Time` | 선택 | 요청 발생 시각 | 하위 HTTP 호출로 전달 | -| `X-Vrtl-Praf-No` | 선택 | 가상 사용자 사번 | 하위 HTTP 호출로 전달 | -| `X-App-Code` | 선택 | 호출 애플리케이션 코드 | 하위 HTTP 호출로 전달 | -| `X-Project-Code` | 선택 | 호출 프로젝트 코드 | 하위 HTTP 호출로 전달 | -| `X-User-Ip` | 선택 | 사용자 IP 주소 | 하위 HTTP 호출로 전달 | -| `X-Caller-Ip` | 선택 | 호출 시스템 IP 주소 | 하위 HTTP 호출로 전달 | -| `X-Caller-Host` | 선택 | 호출 시스템 호스트명 | 하위 HTTP 호출로 전달 | -| `X-Channel` | 선택 | 호출 채널 | 하위 HTTP 호출로 전달 | -| `X-Agent-Id` | 선택 | 호출 Agent 식별자 | 하위 HTTP 호출로 전달 | -| `mcp-session-id` | 선택 | MCP 세션 식별자 | 성공 응답과 하위 HTTP 호출로 전달 | - -`McpRequestHeaderFilter`는 URI에 `/mcp`가 포함된 요청에서 위 헤더를 `McpRequestHeaderContext`에 저장하므로 REST `/mcp/{toolName}`과 MCP Streamable HTTP `/mcp`, `/mcp/message`에 모두 적용됩니다. REST 경로는 `BusinessToolController`도 동일한 헤더를 직접 읽어 실행 서비스에 전달합니다. 헤더가 없는 하위 HTTP 호출에는 `X-ANONYMOUS-REQ: AXHUB-TOOL`이 설정됩니다. - -기존 `guid`, `employee-no`, `virtual-employee-no` 헤더는 지원하지 않습니다. - -주요 실행 응답은 다음과 같습니다. - -| HTTP 상태 | 코드 | 의미 | -|---:|---|---| -| 200 | - | Tool 실행 성공 | -| 404 | `TOOL_NOT_FOUND` | 요청한 Tool 이름이 없음 | -| 422 | `INVALID_PARAM` | 요청이 입력 Schema와 일치하지 않음 | -| 500 | `INVALID_TOOL_RESPONSE` | 결과가 출력 Schema와 일치하지 않음 | -| 502 | `TOOL_ERROR` | Tool 실행 중 예외 발생 | - -## Tool 구현 방식 - -호출 가능한 메서드는 Spring AI Community의 `@McpTool`로 선언합니다. 프로젝트 고유 실행·표시 정보는 `@GrowToolHint`로 보완합니다. - -```java -@McpTool( - name = "iam_system_status", - title = "시스템 상태 조회", - description = "모의 시스템 상태 정보를 조회합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false) -) -@GrowToolHint( - categoryKey = "iam", - mappingId = "DIRECT_IAM_STATUS", - requiresApproval = false, - timeoutMillis = 5000L, - retryMaxAttempts = 3 -) -SystemStatusResponse getSystemStatus(SystemStatusRequest request); -``` - -`@GrowToolHint`의 실행 제어 기본값은 `timeoutMillis = 5000L`, `retryMaxAttempts = 3`입니다. 어노테이션에서 값을 지정하면 해당 Tool의 메타데이터에 반영됩니다. `retryEnabled`는 지원하지 않으며, 재시도 여부는 `retryMaxAttempts` 값으로 판단합니다. - -`@McpTool.name`은 다음 형식을 사용합니다. - -```text -^[a-z][a-z0-9_]{2,63}$ -``` - -예: `cmm_claim_search`, `crm_customer_detail`, `iam_system_status` - -이름 중복과 형식은 `validateMcpToolNames` Gradle 작업으로 검사합니다. 현재 루트 `build.gradle`에는 `validateToolSchemaV17` Gradle 작업이 등록되어 있지 않으며, 모든 `bootJar` 작업은 `validateMcpToolNames`에만 의존합니다. - -## Tool YAML 정의 - -공통 라이브러리는 Tool 메타데이터를 보강하기 위한 선택적 YAML 정의를 지원합니다. YAML 정의를 추가할 경우 업무 모듈의 다음 경로를 사용합니다. - -```text -dat-was-*/src/main/resources/tool-definitions/{category}/{tool-name}.yml -``` - -현재 업무 모듈에는 이 경로의 YAML 정의가 없습니다. 따라서 실행 시 메타데이터는 `@McpTool`, `@GrowToolHint`, DTO Schema를 기준으로 구성됩니다. YAML 정의를 도입하는 경우 V17 주요 필수 항목은 다음과 같습니다. - -- `name`, `display_name`, `version`, `category_key` -- `description.function`, `when_to_use`, `when_not_to_use`, `io_limits` -- 3~10개의 `example_queries` -- `read_only`, `destructive`, `idempotent` -- `parameters_schema.type: object` -- `parameters_schema.additionalProperties: false` -- 각 입력 property의 `description` - -입력·출력 Schema는 `ToolSchemaResolver`가 어노테이션의 Schema 리소스와 인라인 Schema, DTO에서 생성한 Schema를 해석합니다. YAML 정의가 있으면 `ToolRegistryHeartbeatSender`가 `parameters_schema`와 `output_schema`를 병합하여 최종 메타데이터를 만듭니다. - -### Tool 실행 제어 메타데이터 - -다음 메타데이터는 Tool 목록과 Manifest에 함께 제공됩니다. - -| 필드 | 기본값 | 설명 | -|---|---:|---| -| `timeoutMillis` | `5000` | Gateway가 Tool 응답을 기다리는 최대 시간(밀리초) | -| `retryMaxAttempts` | `3` | 최초 호출을 포함한 최대 시도 횟수 | - -`retryEnabled` 필드는 외부 메타데이터와 `ToolMetadata`에서 제거되었습니다. 따라서 클라이언트와 Gateway는 `retryMaxAttempts`만 사용해야 하며, 값이 없거나 1보다 작으면 기본값 3이 적용됩니다. Scaffold로 생성되는 Tool에도 위 두 값이 자동으로 삽입됩니다. - -표준 MCP `tools/list` 응답에서는 두 필드가 Tool의 `_meta`에 camelCase로 내려갑니다. - -```json -{ - "_meta": { - "timeoutMillis": 5000, - "retryMaxAttempts": 3 - } -} -``` - -`GET /tool-manifest` 응답의 각 Tool `_meta`에도 동일한 두 필드가 포함됩니다. `GET /mcp/api/v1/tools/local` 응답은 내부 `ToolMetadata` 표현을 사용하므로 동일한 실행 제어 값을 확인할 수 있습니다. - -실행 시 Schema 검증은 MCP Java SDK의 `DefaultJsonSchemaValidator`를 사용하며 JSON Schema 2020-12 기준으로 처리합니다. 입력 불일치는 `422 INVALID_PARAM`, 출력 불일치는 `500 INVALID_TOOL_RESPONSE`로 반환됩니다. 단, Schema 해석 또는 검증기 자체에서 예외가 발생하면 현재 구현은 오류를 로그에 기록하고 해당 검증을 건너뜁니다. - -## 로컬 실행 - -### 사전 조건 - -- JDK 21 -- 프로젝트에 포함된 Gradle Wrapper -- Redis 또는 외부 연동이 필요한 경우 Docker - -기본 활성 프로필은 `local`입니다. 로컬 프로필은 H2 메모리 DB와 P6Spy를 사용합니다. - +### 단위 테스트 실행 ```powershell -$env:SPRING_PROFILES_ACTIVE = 'local' -$env:TOOL_SERVER_API_KEY = 'tool-server-key' +.\gradlew.bat test ``` -`TOOL_SERVER_API_KEY`를 지정하지 않으면 현재 개발 기본값인 `tool-server-key`가 사용됩니다. 운영 환경에서는 기본값을 사용하지 말고 DATMS의 Tool Server API Key와 동일한 별도 Secret을 주입해야 합니다. - -각 Pod는 별도 터미널에서 실행합니다. - +### 로컬 애플리케이션 실행 ```powershell -# 영업 Tool Pod -$env:AXHUB_TOOL_URL = 'http://localhost:8082' -.\gradlew.bat :dat-was-sal:bootRun - -# 고객 Tool Pod -$env:AXHUB_TOOL_URL = 'http://localhost:8084' -.\gradlew.bat :dat-was-cus:bootRun - -# 상품 Tool Pod -$env:AXHUB_TOOL_URL = 'http://localhost:8085' -.\gradlew.bat :dat-was-pro:bootRun - -# 시스템 Tool Pod -$env:AXHUB_TOOL_URL = 'http://localhost:8086' .\gradlew.bat :dat-was-sys:bootRun ``` +*기본 활성화 프로파일: `local` (`application-local.yml` 참조)* -실행 후 SYS Pod 기준 확인 URL은 다음과 같습니다. - -```text -http://localhost:8086/mcp/api/v1/tools/local -http://localhost:8086/tool-manifest -http://localhost:8086/swagger-ui/index.html -``` - -### 주요 환경 변수 - -| 환경 변수 | 설명 | 기본값 | -|---|---|---| -| `SPRING_PROFILES_ACTIVE` | Spring 활성 프로필 | `local` | -| `PORT` | Pod 수신 포트 | 모듈별 기본 포트 | -| `TOOL_SERVER_API_KEY` | DATMS가 `X-Tool-Server-API-Key`로 전달할 공통 인증 Key | `tool-server-key` | -| `AXHUB_TOOL_URL` | Manifest의 Tool endpoint 생성에 사용할 Pod 외부 URL | `http://localhost:${server.port}` | -| `AXHUB_GATEWAY_URL` | 프로필 및 Compose 호환용 Gateway URL. 현재 DATMT의 push 등록에는 사용하지 않음 | `http://localhost:8081` | -| `SPRING_DATA_REDIS_HOST` | Redis 호스트 | `localhost` | -| `SPRING_DATA_REDIS_PORT` | Redis 포트 | `6379` | -| `GLOW_COMMUNICATION_MCI_HOST` | MCI 대상 호스트 | 프로필별 설정 | -| `GLOW_COMMUNICATION_MCI_PORT` | MCI 대상 포트 | 프로필별 설정 | - -`mcp.security.api-key`는 단일 DATMS 공통 Key를, `mcp.security.api-keys`는 `API Key → tenant ID` 형태의 다중 Key를 지원합니다. `ApiKeyInterceptor`는 Spring MVC가 처리하는 `/rpc/**`, `/mcp/**` 요청에 적용되므로 REST `/mcp/{toolName}`과 `/mcp/api/v1/tools/local`은 올바른 `X-Tool-Server-API-Key`가 없으면 `401 Unauthorized`가 됩니다. 두 설정이 모두 비어 있을 때는 해당 MVC 요청을 익명으로 허용합니다. 현재 네 업무 Pod의 기본 `application.yml`은 단일 Key를 설정합니다. - -반면 `/mcp`와 `/mcp/message`는 별도 Servlet으로 등록되어 Spring MVC `HandlerInterceptor`를 통과하지 않습니다. 현재 구현만으로는 이 두 MCP Streamable HTTP 경로에 `ApiKeyInterceptor` 인증이 적용되지 않으므로, 운영 배포 전 Servlet Filter 또는 전용 MCP 인증 계층을 추가해야 합니다. - -## Scaffold - -공통 라이브러리는 새 Tool과 새 Tool Pod를 만드는 두 개의 Java CLI를 제공합니다. 두 클래스의 `main` 메서드를 IDE에서 실행하거나 필요한 인자를 전달해 실행할 수 있습니다. - -| Scaffolder | 역할 | 주요 생성·수정 대상 | -|---|---|---| -| `ToolScaffolder` | 기존 Pod에 Tool 구현 추가 | UseCase, DTO, Converter, Mock 응답, V17 Tool YAML, 연동 설정 | -| `PodScaffolder` | 새 실행 Pod 모듈 추가 | 모듈 디렉터리, `build.gradle`, Dockerfile, Application 클래스, 프로필 설정, `settings.gradle`, `docker-compose.yml` | - -`PodScaffolder`의 인자 순서는 다음과 같습니다. - -```text -PodScaffolder [author] [yyyy.MM.dd] -``` - -예를 들어 `payment 8099`를 입력하면 `dat-was-payment` 모듈을 생성합니다. 생성되는 `application.yml`에는 다음 계약이 포함됩니다. - -```yaml -mcp: - manifest: - bundle-id: dat-was-payment - name-prefix: "" - security: - api-key: ${TOOL_SERVER_API_KEY:tool-server-key} -``` - -생성되는 Compose 서비스에도 `TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key}`가 추가됩니다. 생성 후에는 다음 항목을 반드시 확인해야 합니다. - -1. `bundle-id`를 DATMS에 등록할 bundle ID와 일치시킵니다. -2. 운영 환경의 `TOOL_SERVER_API_KEY`를 DATMS가 전달하는 Key와 동일한 Secret으로 설정합니다. -3. 실제 배포 주소에 맞게 `AXHUB_TOOL_URL`을 설정합니다. -4. MCI·EAI·HTTP 연동 대상과 timeout을 환경별 설정으로 교체합니다. -5. `validateMcpToolNames`와 모듈·전체 테스트를 실행합니다. YAML 정의를 추가했다면 `ToolSchemaV17ValidationRunner` 또는 관련 테스트로 V17 항목을 별도 검증합니다. - -## 테스트와 검증 - +### 도커 컨테이너 빌드 및 로컬 구동 ```powershell -# 운영 소스 전체 컴파일 -.\gradlew.bat classes - -# 전체 테스트 -.\gradlew.bat test - -# 모듈별 테스트 -.\gradlew.bat :dat-was-lib:test -.\gradlew.bat :dat-was-cus:test -.\gradlew.bat :dat-was-sal:test -.\gradlew.bat :dat-was-pro:test -.\gradlew.bat :dat-was-sys:test - -# Tool 이름·중복 검사 -.\gradlew.bat validateMcpToolNames - -# 검증, 테스트, 패키징 -.\gradlew.bat clean build +docker compose -f docker-compose.local.yml up -d --build ``` -### 실행 전 확인 - -테스트 수와 성공 여부는 소스 변경에 따라 달라지므로 고정된 수치를 문서화하지 않습니다. 배포 전 현재 작업 트리에서 다음 명령을 실행해 확인합니다. - -```powershell -.\gradlew.bat test validateMcpToolNames -``` - -## Docker Compose - -`docker-compose.yml`은 DATMT의 네 업무 Pod만 정의합니다. - -| 서비스 | 컨테이너 포트 | 호스트 포트 | -|---|---:|---:| -| `was-sal` | 8082 | 8282 | -| `was-cus` | 8084 | 8284 | -| `was-pro` | 8085 | 8285 | -| `was-sys` | 8086 | 8286 | - -모듈 Dockerfile은 사전에 생성된 Boot JAR를 이미지에 복사합니다. 먼저 JAR를 빌드한 뒤 Compose를 실행합니다. - -```powershell -.\gradlew.bat :dat-was-sal:bootJar :dat-was-cus:bootJar :dat-was-pro:bootJar :dat-was-sys:bootJar -docker compose up --build -``` - -Compose 파일의 용도와 현재 주의점은 다음과 같습니다. - -| 파일 | 용도 | 현재 소스 기준 주의점 | -|---|---|---| -| `docker-compose.yml` | DATMT 네 Pod 단독 실행 | `gateway` 서비스가 없지만 push 등록이 제거되어 DATMT 시작에는 필요하지 않음 | -| `docker-compose.local.yml` | DATMS와 DATMT의 로컬 통합 구성 | 두 저장소가 같은 상위 디렉터리에 있는 구조를 가정 | -| `docker-compose.prod.yml` | DATMS와 DATMT의 개발 프로필 기반 OCI 구성 | 저장소의 runner 등록 토큰을 운영 Secret으로 분리해야 함 | - -`docker-compose.local.yml`과 `docker-compose.prod.yml`의 build context는 각각 `./dat-was-DATMS`, `./dat-was-datmt`입니다. 현재 파일 위치에서 사용할 때는 context 기준을 두 저장소의 상위 디렉터리로 맞춰야 합니다. - -```powershell -# DATMT 저장소 디렉터리에서 실행 -docker compose --project-directory .. -f docker-compose.local.yml up --build -``` - -현재 기존 네 Pod의 Compose 정의에는 `TOOL_SERVER_API_KEY` 환경 변수 전달이 없습니다. 따라서 컨테이너는 애플리케이션 기본값 `tool-server-key`를 사용합니다. 운영 배포 전 각 서비스에 Secret 기반 `TOOL_SERVER_API_KEY` 전달 설정을 추가하고 DATMS의 Key와 일치시켜야 합니다. DATMS는 각 Pod의 `AXHUB_TOOL_URL` 또는 배포 URL에 접근해 `/tool-manifest`를 pull할 수 있어야 합니다. - -## OpenShift (Kubernetes) 배포 - -OpenShift 개발 환경용 Kustomize 매니페스트는 [`k8s/`](k8s/)에 있습니다. 대상은 DATMT의 네 Tool Pod뿐이며 DATMS(Gateway)의 Deployment·Service·Route는 이 저장소에서 만들지 않습니다. - -```text -k8s/ -├─ base/ # 네 Pod 공통 ConfigMap, Service, Deployment -└─ overlays/dev/ # 개발 namespace, Registry 이미지 경로와 tag -``` - -### 배포 구조 - -각 Tool Pod는 Deployment 1개와 외부에 노출되지 않는 `ClusterIP` Service 1개를 사용합니다. OpenShift Route와 LoadBalancer Service는 생성하지 않으며, Gateway가 클러스터 내부 DNS로 호출합니다. - -| Service | Pod 포트 | Gateway 호출 주소 | -|---|---:|---| -| `was-sal` | 8082 | `http://was-sal:8082` | -| `was-cus` | 8084 | `http://was-cus:8084` | -| `was-pro` | 8085 | `http://was-pro:8085` | -| `was-sys` | 8086 | `http://was-sys:8086` | - -Gateway가 다른 namespace에 있으면 `was-sal.axhub-datmt-dev.svc`와 같은 FQDN을 사용하고, NetworkPolicy에서 Gateway namespace의 ingress를 별도로 허용해야 합니다. - -### 반영 전 설정 - -다음 값은 실제 신한라이프 개발망 값으로 교체해야 합니다. - -1. [`k8s/overlays/dev/kustomization.yaml`](k8s/overlays/dev/kustomization.yaml)의 namespace, 내부 Container Registry 경로, 배포 image tag -2. [`k8s/base/configmap.yaml`](k8s/base/configmap.yaml)의 `CHANGE_ME` MCI·EXTMCI·EAI 호스트 -3. 실제 Secret 값 - -`datmt-runtime-secrets` Secret은 Git에 저장하지 않고 OpenShift namespace에서 별도로 생성합니다. 최소한 `TOOL_SERVER_API_KEY`는 DATMS가 전달하는 `X-Tool-Server-API-Key`와 같은 값이어야 합니다. DB 계정·비밀번호, API Key, 인증서 비밀번호 등도 이 Secret으로 관리합니다. - -```powershell -# runtime-secrets.env는 저장소 밖에 보관합니다. -oc -n axhub-datmt-dev create secret generic datmt-runtime-secrets ` - --from-env-file=runtime-secrets.env -``` - -Secret이 없으면 각 Deployment의 `envFrom.secretRef`를 해석할 수 없어 Pod가 시작하지 않을 수 있습니다. - -### 이미지 빌드와 배포 - -모듈 Dockerfile은 미리 생성된 Boot JAR를 복사하므로, 이미지를 만들기 전에 네 모듈의 JAR를 빌드합니다. 개발망에서는 JDK/JRE 베이스 이미지와 Gradle/Maven 의존성을 내부 Registry·Nexus에서 사용할 수 있어야 합니다. - -```powershell -.\gradlew.bat :dat-was-sal:bootJar :dat-was-cus:bootJar :dat-was-pro:bootJar :dat-was-sys:bootJar - -# OpenShift 로그인 및 project 선택 후 -oc kustomize k8s/overlays/dev -oc apply -k k8s/overlays/dev -oc get deployment,pod,svc -n axhub-datmt-dev -``` - -적용 전에는 `oc kustomize k8s/overlays/dev | oc apply --dry-run=client -f -`로 서버 측 스키마 검증을 수행합니다. 현재 매니페스트의 readiness/liveness probe는 TCP 포트 확인 방식입니다. Actuator health endpoint를 추가한 뒤에는 HTTP readiness/liveness probe로 변경하는 것을 권장합니다. - -상세한 명령과 Gateway 연결 확인 방법은 [`k8s/README.md`](k8s/README.md)를 참고합니다. - -## 보안 및 운영 주의사항 - -현재 구현을 운영 환경에 노출하기 전에 아래 항목을 반드시 점검해야 합니다. - -- API Key 인터셉터는 Spring MVC의 `/rpc/**`, `/mcp/**` Handler에 적용됩니다. REST `/mcp/{toolName}`과 `/mcp/api/v1/tools/local`은 인증 대상입니다. -- 별도 Servlet인 `/mcp`, `/mcp/message`는 MVC 인터셉터를 우회하므로 현재 API Key 인증 대상이 아닙니다. 운영 노출 전에 별도 인증을 추가해야 합니다. -- `/tool-manifest`와 `/tool-manifest/{categoryKey}`는 위 인터셉터 경로 밖에 있어 현재 API Key 인증 대상이 아닙니다. 내부망·Ingress 정책 또는 별도 인증이 필요한지 운영 기준을 확인해야 합니다. -- 네 업무 Pod의 기본 Key는 모두 `tool-server-key`입니다. 운영에서는 반드시 별도 Secret으로 교체하고 DATMS의 `X-Tool-Server-API-Key` 값과 일치시켜야 합니다. -- 설정된 단일 Key와 다중 Key가 모두 없을 때만 익명 요청이 허용됩니다. -- `mcp.security.tenant-domains`는 설정 객체에 바인딩되지만 Tool별 인가에 사용되지 않습니다. -- `requiresApproval`은 메타데이터에만 기록되며 실행 차단이나 승인 확인 로직은 없습니다. -- 입력·출력 Schema 처리 자체에서 예외가 발생하면 현재 실행 서비스는 로그를 남기고 검증을 건너뜁니다. -- CORS는 모든 Origin을 허용하면서 credential도 허용하도록 설정되어 있습니다. 운영 Origin을 명시적으로 제한해야 합니다. -- `docker-compose.prod.yml`에 runner 등록 토큰이 평문으로 포함되어 있습니다. 사용 중인 토큰은 폐기·재발급하고 배포 Secret으로 이전해야 합니다. -- Tool 요청과 연동 오류 로그에 개인정보나 인증정보가 포함되지 않도록 DTO와 로그 마스킹 정책을 검토해야 합니다. - -## 주요 소스 위치 - -| 주제 | 위치 | -|---|---| -| 공통 빌드 및 검증 작업 | `build.gradle` | -| REST Tool 실행 API | `dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/presentation/BusinessToolController.java` | -| 요청 헤더 캡처·전달 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaderFilter.java`, `McpRequestHeaderContext.java` | -| Tool 실행 서비스 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpToolExecutionService.java` | -| JSON Schema 2020-12 검증 설정 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/ToolSchemaConfiguration.java` | -| 실행 메서드 Registry | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpToolMethodRegistry.java` | -| MCP SDK 서버 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolMcpServerConfiguration.java` | -| MCP Tool 동기화 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolPodMcpToolSynchronizer.java` | -| Tool 스캔 및 메타데이터 생성 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolRegistryHeartbeatSender.java` | -| MCP `tools/list` 메타데이터 변환 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolMetadataMcpMapper.java` | -| YAML Tool 정의 로딩 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionRepository.java` | -| V17 정의 검증 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionValidator.java` | -| Manifest 생성 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestService.java` | -| API Key 인터셉터 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/security/ApiKeyInterceptor.java` | -| Tool·Pod 스캐폴딩 | `dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolScaffolder.java`, `PodScaffolder.java` | - -## 개발 시 권장 확인 순서 - -1. Tool 메서드와 요청·응답 DTO를 구현합니다. -2. 동일 이름의 V17 YAML 정의를 `tool-definitions` 아래에 추가합니다. -3. `validateMcpToolNames`와 모듈·전체 테스트를 실행합니다. Tool YAML 정의를 추가한 경우에는 V17 항목을 별도 검증합니다. -4. 모듈 테스트와 전체 테스트를 실행합니다. -5. 로컬 Pod에서 `/mcp/api/v1/tools/local`과 `/tool-manifest`를 확인합니다. -6. DATMS의 bundle ID, Pod Manifest URL, Tool Server API Key가 DATMT 설정과 일치하는지 확인합니다. -7. REST와 MCP 양쪽에서 동일한 Tool 결과·요청 헤더 전달·오류 계약을 확인합니다. diff --git a/build.gradle b/build.gradle index 832ce8673..40f843809 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,15 @@ subprojects { } repositories { + // [로컬 개발 순서] + // 1. ../dat-lib-datmt에서 .\gradlew.bat :dat-was-lib:publishToMavenLocal 실행 + // 2. 위 명령이 ~/.m2/repository/io/shinhanlife/dat-lib-datmt에 JAR/POM을 등록 + // 3. 아래 mavenLocal()이 등록된 JAR을 찾아 implementation 의존성을 해석 + // + // [신한라이프 이관] + // mavenLocal() 대신 사내 Nexus Maven repository를 추가합니다. + // implementation 좌표는 그대로 유지합니다. + mavenLocal() // -------------------------------------------------------------------- // 외부 개발 환경 @@ -167,80 +176,3 @@ subprojects { useJUnitPlatform() } } - - -// ============================================================================ -// Tool Validation -// ============================================================================ - - -// Tool Annotation / Tool Schema 검증 프로그램이 들어있는 -// 공통 라이브러리 프로젝트입니다. -def toolCoreProject = project(':dat-was-lib') - - -// ============================================================================ -// MCP Tool Name Validation -// ============================================================================ - -// 전체 Tool Pod 프로젝트에서 선언된 -// @McpTool(name = "...") 값의 중복 여부를 검사합니다. -// -// 동일한 Tool Name이 존재하면 배포 산출물 생성 전에 -// 빌드를 실패시켜 중복 Tool이 배포되는 것을 차단합니다. -tasks.register('validateMcpToolNames', JavaExec) { - - // Gradle Verification Task 그룹으로 분류합니다. - group = 'verification' - - // Gradle Tasks 조회 시 표시되는 설명입니다. - description = 'Checks duplicate @McpTool names across all Tool modules before packaging.' - - - // 검사 프로그램이 dat-was-lib에 존재하므로 - // JavaExec 실행 전에 dat-was-lib의 classes 작업을 수행합니다. - dependsOn toolCoreProject.tasks.named('classes') - - - // dat-was-lib의 Runtime Classpath를 - // Tool Validation Runner 실행 Classpath로 사용합니다. - classpath = toolCoreProject.sourceSets.main.runtimeClasspath - - - // Tool Name 중복 검증을 수행하는 Main Class입니다. - mainClass.set( - 'io.shinhanlife.dat.lib.validation.McpToolNameValidationRunner' - ) - - // 전체 멀티모듈 프로젝트를 검사할 수 있도록 - // Root Project 경로를 Runner의 Argument로 전달합니다. - args rootProject.projectDir.absolutePath -} - -// ============================================================================ -// BootJar Validation Hook -// ============================================================================ - -subprojects { - - // Spring Boot 실행 모듈에서 bootJar를 수행할 경우 - // 실제 배포 JAR이 만들어지기 전에 - // - // 1. MCP Tool Name 중복 검사 - // 2. Tool Schema V17 검사 - // - // 두 검증 작업을 반드시 수행하도록 합니다. - tasks.matching { it.name == 'bootJar' }.configureEach { - - // @McpTool Name 중복 검증 - dependsOn rootProject.tasks.named('validateMcpToolNames') - - } -} -bootJar { - enabled = false -} - -jar { - enabled = false -} diff --git a/dat-was-cus/Dockerfile b/dat-was-cus/Dockerfile deleted file mode 100644 index 294c99e55..000000000 --- a/dat-was-cus/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM eclipse-temurin:21-jre-alpine -WORKDIR /app -RUN apk add --no-cache tzdata -ENV TZ=Asia/Seoul -COPY dat-was-cus/build/libs/*-SNAPSHOT.jar app.jar -EXPOSE 8084 -ENTRYPOINT ["java", "-jar", "app.jar"] - diff --git a/dat-was-cus/build.gradle b/dat-was-cus/build.gradle deleted file mode 100644 index 6fbc74fb6..000000000 --- a/dat-was-cus/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - // OTH Tool Pod를 독립 실행 가능한 Spring Boot JAR로 생성합니다. - id 'org.springframework.boot' -} - -dependencies { - // MCP Server, Tool 공통 처리, MCI/EAI 연동 기반은 dat-was-lib에서 상속합니다. - implementation project(':dat-was-lib') - implementation 'org.apache.poi:poi-ooxml:5.3.0' -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/dto/CrmToolRequest.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/dto/CrmToolRequest.java deleted file mode 100644 index 90b4ec628..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/dto/CrmToolRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.crm.dto; - -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class CrmToolRequest { - @McpToolParam(description = "업무 대상 식별자", required = false) - private String subjectId; - - @McpToolParam(description = "고객 식별자", required = false) - private String customerId; - - @McpToolParam(description = "검색어 또는 처리 내용", required = false) - private String content; - - @McpToolParam(description = "상태, 유형, 등급 또는 처리 값", required = false) - private String value; - - @McpToolParam(description = "담당자 식별자", required = false) - private String assigneeId; - - @McpToolParam(description = "조회 시작일(YYYY-MM-DD)", required = false) - private String startDate; - - @McpToolParam(description = "조회 종료일 또는 처리 예정일(YYYY-MM-DD)", required = false) - private String endDate; - - @McpToolParam(description = "페이지 번호", required = false) - private Integer page; - - @McpToolParam(description = "페이지 크기", required = false) - private Integer size; -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/dto/CrmToolResponse.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/dto/CrmToolResponse.java deleted file mode 100644 index 9ff1692ca..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/dto/CrmToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.crm.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class CrmToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String processedAt; - private List details; -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/usecase/CrmToolUseCase.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/usecase/CrmToolUseCase.java deleted file mode 100644 index 1c9d00d2f..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/usecase/CrmToolUseCase.java +++ /dev/null @@ -1,298 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.crm.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.crm.dto.CrmToolRequest; -import io.shinhanlife.dat.mcc.biz.crm.dto.CrmToolResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface CrmToolUseCase { - @McpTool(name = "crm_customer_search", title = "고객 통합 조회", description = "고객 통합 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CUSTOMER_SEARCH", - functionDescription = "고객 통합 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 통합 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 통합 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 통합 조회 해줘", "고객 통합 조회 결과를 알려줘", "고객 통합 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse searchCustomers(CrmToolRequest request); - @McpTool(name = "crm_customer_detail", title = "고객 상세 정보 조회", description = "고객 상세 정보 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CUSTOMER_DETAIL", - functionDescription = "고객 상세 정보 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 상세 정보 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 상세 정보 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 상세 정보 조회 해줘", "고객 상세 정보 조회 결과를 알려줘", "고객 상세 정보 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse getCustomerDetail(CrmToolRequest request); - @McpTool(name = "crm_customer_create", title = "고객 정보 등록", description = "고객 정보 등록 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CUSTOMER_CREATE", - functionDescription = "고객 정보 등록 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 정보 등록 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 정보 등록 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 정보 등록 해줘", "고객 정보 등록 결과를 알려줘", "고객 정보 등록 기능을 실행해줘"}, - destructive = false, - idempotent = false, - tags = {"crm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse createCustomer(CrmToolRequest request); - @McpTool(name = "crm_customer_update", title = "고객 정보 수정", description = "고객 정보 수정 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CUSTOMER_UPDATE", - functionDescription = "고객 정보 수정 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 정보 수정 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 정보 수정 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 정보 수정 해줘", "고객 정보 수정 결과를 알려줘", "고객 정보 수정 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse updateCustomer(CrmToolRequest request); - @McpTool(name = "crm_customer_duplicate_check", title = "고객 중복 여부 확인", description = "고객 중복 여부 확인 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CUSTOMER_DUPLICATE_CHECK", - functionDescription = "고객 중복 여부 확인 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 중복 여부 확인 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 중복 여부 확인 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 중복 여부 확인 해줘", "고객 중복 여부 확인 결과를 알려줘", "고객 중복 여부 확인 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse checkDuplicateCustomer(CrmToolRequest request); - @McpTool(name = "crm_consultation_history", title = "고객 상담 이력 조회", description = "고객 상담 이력 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CONSULTATION_HISTORY", - functionDescription = "고객 상담 이력 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 상담 이력 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 상담 이력 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 상담 이력 조회 해줘", "고객 상담 이력 조회 결과를 알려줘", "고객 상담 이력 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse getConsultationHistory(CrmToolRequest request); - @McpTool(name = "crm_consultation_register", title = "고객 상담 이력 등록", description = "고객 상담 이력 등록 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CONSULTATION_REGISTER", - functionDescription = "고객 상담 이력 등록 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 상담 이력 등록 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 상담 이력 등록 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 상담 이력 등록 해줘", "고객 상담 이력 등록 결과를 알려줘", "고객 상담 이력 등록 기능을 실행해줘"}, - destructive = false, - idempotent = false, - tags = {"crm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse registerConsultation(CrmToolRequest request); - @McpTool(name = "crm_contact_history", title = "고객 접촉 이력 조회", description = "고객 접촉 이력 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CONTACT_HISTORY", - functionDescription = "고객 접촉 이력 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 접촉 이력 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 접촉 이력 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 접촉 이력 조회 해줘", "고객 접촉 이력 조회 결과를 알려줘", "고객 접촉 이력 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse getContactHistory(CrmToolRequest request); - @McpTool(name = "crm_contact_register", title = "고객 접촉 이력 등록", description = "고객 접촉 이력 등록 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CONTACT_REGISTER", - functionDescription = "고객 접촉 이력 등록 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 접촉 이력 등록 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 접촉 이력 등록 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 접촉 이력 등록 해줘", "고객 접촉 이력 등록 결과를 알려줘", "고객 접촉 이력 등록 기능을 실행해줘"}, - destructive = false, - idempotent = false, - tags = {"crm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse registerContact(CrmToolRequest request); - @McpTool(name = "crm_grade_detail", title = "고객 등급 조회", description = "고객 등급 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_GRADE_DETAIL", - functionDescription = "고객 등급 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 등급 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 등급 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 등급 조회 해줘", "고객 등급 조회 결과를 알려줘", "고객 등급 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse getCustomerGrade(CrmToolRequest request); - @McpTool(name = "crm_grade_change", title = "고객 등급 변경", description = "고객 등급 변경 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_GRADE_CHANGE", - functionDescription = "고객 등급 변경 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 등급 변경 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 등급 변경 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 등급 변경 해줘", "고객 등급 변경 결과를 알려줘", "고객 등급 변경 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse changeCustomerGrade(CrmToolRequest request); - @McpTool(name = "crm_segment_detail", title = "고객 세그먼트 조회", description = "고객 세그먼트 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_SEGMENT_DETAIL", - functionDescription = "고객 세그먼트 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 세그먼트 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 세그먼트 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 세그먼트 조회 해줘", "고객 세그먼트 조회 결과를 알려줘", "고객 세그먼트 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse getCustomerSegment(CrmToolRequest request); - @McpTool(name = "crm_tag_manage", title = "고객 태그 관리", description = "고객 태그 관리 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_TAG_MANAGE", - functionDescription = "고객 태그 관리 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 태그 관리 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 태그 관리 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 태그 관리 해줘", "고객 태그 관리 결과를 알려줘", "고객 태그 관리 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse manageCustomerTags(CrmToolRequest request); - @McpTool(name = "crm_consent_detail", title = "고객 동의 정보 조회", description = "고객 동의 정보 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CONSENT_DETAIL", - functionDescription = "고객 동의 정보 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 동의 정보 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 동의 정보 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 동의 정보 조회 해줘", "고객 동의 정보 조회 결과를 알려줘", "고객 동의 정보 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse getCustomerConsent(CrmToolRequest request); - @McpTool(name = "crm_consent_change", title = "고객 동의 정보 변경", description = "고객 동의 정보 변경 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_CONSENT_CHANGE", - functionDescription = "고객 동의 정보 변경 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 동의 정보 변경 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 동의 정보 변경 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 동의 정보 변경 해줘", "고객 동의 정보 변경 결과를 알려줘", "고객 동의 정보 변경 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse changeCustomerConsent(CrmToolRequest request); - @McpTool(name = "crm_owner_assign", title = "고객 담당자 배정", description = "고객 담당자 배정 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_OWNER_ASSIGN", - functionDescription = "고객 담당자 배정 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 담당자 배정 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 담당자 배정 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 담당자 배정 해줘", "고객 담당자 배정 결과를 알려줘", "고객 담당자 배정 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse assignCustomerOwner(CrmToolRequest request); - @McpTool(name = "crm_activity_status", title = "고객 활동 현황 조회", description = "고객 활동 현황 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "crm", - mappingId = "DIRECT_CRM_ACTIVITY_STATUS", - functionDescription = "고객 활동 현황 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객 활동 현황 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객 활동 현황 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 활동 현황 조회 해줘", "고객 활동 현황 조회 결과를 알려줘", "고객 활동 현황 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"crm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - CrmToolResponse getCustomerActivityStatus(CrmToolRequest request); -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/usecase/impl/CrmToolUseCaseImpl.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/usecase/impl/CrmToolUseCaseImpl.java deleted file mode 100644 index 259e71d0c..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/crm/usecase/impl/CrmToolUseCaseImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.crm.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.crm.dto.CrmToolRequest; -import io.shinhanlife.dat.mcc.biz.crm.dto.CrmToolResponse; -import io.shinhanlife.dat.mcc.biz.crm.usecase.CrmToolUseCase; -import java.time.LocalDateTime; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class CrmToolUseCaseImpl implements CrmToolUseCase { - @Override - public CrmToolResponse searchCustomers(CrmToolRequest request) { - return mockResponse(request, "crm_customer_search", "고객 통합 조회"); - } - @Override - public CrmToolResponse getCustomerDetail(CrmToolRequest request) { - return mockResponse(request, "crm_customer_detail", "고객 상세 정보 조회"); - } - @Override - public CrmToolResponse createCustomer(CrmToolRequest request) { - return mockResponse(request, "crm_customer_create", "고객 정보 등록"); - } - @Override - public CrmToolResponse updateCustomer(CrmToolRequest request) { - return mockResponse(request, "crm_customer_update", "고객 정보 수정"); - } - @Override - public CrmToolResponse checkDuplicateCustomer(CrmToolRequest request) { - return mockResponse(request, "crm_customer_duplicate_check", "고객 중복 여부 확인"); - } - @Override - public CrmToolResponse getConsultationHistory(CrmToolRequest request) { - return mockResponse(request, "crm_consultation_history", "고객 상담 이력 조회"); - } - @Override - public CrmToolResponse registerConsultation(CrmToolRequest request) { - return mockResponse(request, "crm_consultation_register", "고객 상담 이력 등록"); - } - @Override - public CrmToolResponse getContactHistory(CrmToolRequest request) { - return mockResponse(request, "crm_contact_history", "고객 접촉 이력 조회"); - } - @Override - public CrmToolResponse registerContact(CrmToolRequest request) { - return mockResponse(request, "crm_contact_register", "고객 접촉 이력 등록"); - } - @Override - public CrmToolResponse getCustomerGrade(CrmToolRequest request) { - return mockResponse(request, "crm_grade_detail", "고객 등급 조회"); - } - @Override - public CrmToolResponse changeCustomerGrade(CrmToolRequest request) { - return mockResponse(request, "crm_grade_change", "고객 등급 변경"); - } - @Override - public CrmToolResponse getCustomerSegment(CrmToolRequest request) { - return mockResponse(request, "crm_segment_detail", "고객 세그먼트 조회"); - } - @Override - public CrmToolResponse manageCustomerTags(CrmToolRequest request) { - return mockResponse(request, "crm_tag_manage", "고객 태그 관리"); - } - @Override - public CrmToolResponse getCustomerConsent(CrmToolRequest request) { - return mockResponse(request, "crm_consent_detail", "고객 동의 정보 조회"); - } - @Override - public CrmToolResponse changeCustomerConsent(CrmToolRequest request) { - return mockResponse(request, "crm_consent_change", "고객 동의 정보 변경"); - } - @Override - public CrmToolResponse assignCustomerOwner(CrmToolRequest request) { - return mockResponse(request, "crm_owner_assign", "고객 담당자 배정"); - } - @Override - public CrmToolResponse getCustomerActivityStatus(CrmToolRequest request) { - return mockResponse(request, "crm_activity_status", "고객 활동 현황 조회"); - } - private CrmToolResponse mockResponse(CrmToolRequest request, String toolName, String title) { - CrmToolResponse response = new CrmToolResponse(); - response.setToolName(toolName); - response.setCategory("CRM"); - response.setReferenceId(request != null && hasText(request.getSubjectId()) ? request.getSubjectId() : "CRM-000001"); - response.setStatus("정상"); - response.setSummary(title + " 처리 결과"); - response.setProcessedAt(LocalDateTime.now().toString()); - response.setDetails(List.of(title + " 모의 상세 정보", "외부 시스템을 변경하지 않는 샘플 응답")); - return response; - } - - private boolean hasText(String value) { - return value != null && !value.isBlank(); - } -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/dto/VocToolRequest.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/dto/VocToolRequest.java deleted file mode 100644 index b6d80982b..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/dto/VocToolRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.voc.dto; - -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class VocToolRequest { - @McpToolParam(description = "업무 대상 식별자", required = false) - private String subjectId; - - @McpToolParam(description = "고객 식별자", required = false) - private String customerId; - - @McpToolParam(description = "검색어 또는 처리 내용", required = false) - private String content; - - @McpToolParam(description = "상태, 유형, 등급 또는 처리 값", required = false) - private String value; - - @McpToolParam(description = "담당자 식별자", required = false) - private String assigneeId; - - @McpToolParam(description = "조회 시작일(YYYY-MM-DD)", required = false) - private String startDate; - - @McpToolParam(description = "조회 종료일 또는 처리 예정일(YYYY-MM-DD)", required = false) - private String endDate; - - @McpToolParam(description = "페이지 번호", required = false) - private Integer page; - - @McpToolParam(description = "페이지 크기", required = false) - private Integer size; -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/dto/VocToolResponse.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/dto/VocToolResponse.java deleted file mode 100644 index aa5c9f592..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/dto/VocToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.voc.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class VocToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String processedAt; - private List details; -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/usecase/VocToolUseCase.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/usecase/VocToolUseCase.java deleted file mode 100644 index b52c10dfb..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/usecase/VocToolUseCase.java +++ /dev/null @@ -1,298 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.voc.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.voc.dto.VocToolRequest; -import io.shinhanlife.dat.mcc.biz.voc.dto.VocToolResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface VocToolUseCase { - @McpTool(name = "voc_register", title = "VOC 접수 등록", description = "VOC 접수 등록 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_REGISTER", - functionDescription = "VOC 접수 등록 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 접수 등록 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 접수 등록 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 접수 등록 해줘", "VOC 접수 등록 결과를 알려줘", "VOC 접수 등록 기능을 실행해줘"}, - destructive = false, - idempotent = false, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse registerVoc(VocToolRequest request); - @McpTool(name = "voc_detail", title = "VOC 상세 조회", description = "VOC 상세 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_DETAIL", - functionDescription = "VOC 상세 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 상세 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 상세 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 상세 조회 해줘", "VOC 상세 조회 결과를 알려줘", "VOC 상세 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse getVocDetail(VocToolRequest request); - @McpTool(name = "voc_search", title = "VOC 목록 검색", description = "VOC 목록 검색 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_SEARCH", - functionDescription = "VOC 목록 검색 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 목록 검색 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 목록 검색 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 목록 검색 해줘", "VOC 목록 검색 결과를 알려줘", "VOC 목록 검색 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse searchVocs(VocToolRequest request); - @McpTool(name = "voc_update", title = "VOC 내용 수정", description = "VOC 내용 수정 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_UPDATE", - functionDescription = "VOC 내용 수정 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 내용 수정 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 내용 수정 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 내용 수정 해줘", "VOC 내용 수정 결과를 알려줘", "VOC 내용 수정 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse updateVoc(VocToolRequest request); - @McpTool(name = "voc_assign", title = "VOC 담당자 배정", description = "VOC 담당자 배정 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_ASSIGN", - functionDescription = "VOC 담당자 배정 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 담당자 배정 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 담당자 배정 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 담당자 배정 해줘", "VOC 담당자 배정 결과를 알려줘", "VOC 담당자 배정 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse assignVocOwner(VocToolRequest request); - @McpTool(name = "voc_change_status", title = "VOC 처리 상태 변경", description = "VOC 처리 상태 변경 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_CHANGE_STATUS", - functionDescription = "VOC 처리 상태 변경 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 처리 상태 변경 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 처리 상태 변경 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 처리 상태 변경 해줘", "VOC 처리 상태 변경 결과를 알려줘", "VOC 처리 상태 변경 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse changeVocStatus(VocToolRequest request); - @McpTool(name = "voc_register_result", title = "VOC 처리 결과 등록", description = "VOC 처리 결과 등록 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_REGISTER_RESULT", - functionDescription = "VOC 처리 결과 등록 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 처리 결과 등록 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 처리 결과 등록 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 처리 결과 등록 해줘", "VOC 처리 결과 등록 결과를 알려줘", "VOC 처리 결과 등록 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse registerVocResult(VocToolRequest request); - @McpTool(name = "voc_send_reply", title = "VOC 답변 발송", description = "VOC 답변 발송 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_SEND_REPLY", - functionDescription = "VOC 답변 발송 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 답변 발송 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 답변 발송 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 답변 발송 해줘", "VOC 답변 발송 결과를 알려줘", "VOC 답변 발송 기능을 실행해줘"}, - destructive = false, - idempotent = false, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse sendVocReply(VocToolRequest request); - @McpTool(name = "voc_transfer", title = "VOC 이관 처리", description = "VOC 이관 처리 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_TRANSFER", - functionDescription = "VOC 이관 처리 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 이관 처리 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 이관 처리 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 이관 처리 해줘", "VOC 이관 처리 결과를 알려줘", "VOC 이관 처리 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse transferVoc(VocToolRequest request); - @McpTool(name = "voc_set_priority", title = "VOC 우선순위 설정", description = "VOC 우선순위 설정 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_SET_PRIORITY", - functionDescription = "VOC 우선순위 설정 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 우선순위 설정 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 우선순위 설정 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 우선순위 설정 해줘", "VOC 우선순위 설정 결과를 알려줘", "VOC 우선순위 설정 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse setVocPriority(VocToolRequest request); - @McpTool(name = "voc_classify_type", title = "VOC 유형 분류", description = "VOC 유형 분류 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_CLASSIFY_TYPE", - functionDescription = "VOC 유형 분류 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 유형 분류 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 유형 분류 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 유형 분류 해줘", "VOC 유형 분류 결과를 알려줘", "VOC 유형 분류 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse classifyVocType(VocToolRequest request); - @McpTool(name = "voc_attachments", title = "VOC 첨부파일 조회", description = "VOC 첨부파일 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_ATTACHMENTS", - functionDescription = "VOC 첨부파일 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 첨부파일 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 첨부파일 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 첨부파일 조회 해줘", "VOC 첨부파일 조회 결과를 알려줘", "VOC 첨부파일 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse getVocAttachments(VocToolRequest request); - @McpTool(name = "voc_customer_history", title = "고객별 VOC 이력 조회", description = "고객별 VOC 이력 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_CUSTOMER_HISTORY", - functionDescription = "고객별 VOC 이력 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 고객별 VOC 이력 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "고객별 VOC 이력 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객별 VOC 이력 조회 해줘", "고객별 VOC 이력 조회 결과를 알려줘", "고객별 VOC 이력 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse getCustomerVocHistory(VocToolRequest request); - @McpTool(name = "voc_detect_duplicate", title = "중복 VOC 탐지", description = "중복 VOC 탐지 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_DETECT_DUPLICATE", - functionDescription = "중복 VOC 탐지 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 중복 VOC 탐지 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "중복 VOC 탐지 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"중복 VOC 탐지 해줘", "중복 VOC 탐지 결과를 알려줘", "중복 VOC 탐지 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse detectDuplicateVoc(VocToolRequest request); - @McpTool(name = "voc_urgent_list", title = "긴급 VOC 목록 조회", description = "긴급 VOC 목록 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_URGENT_LIST", - functionDescription = "긴급 VOC 목록 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 긴급 VOC 목록 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "긴급 VOC 목록 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"긴급 VOC 목록 조회 해줘", "긴급 VOC 목록 조회 결과를 알려줘", "긴급 VOC 목록 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse getUrgentVocs(VocToolRequest request); - @McpTool(name = "voc_extend_due_date", title = "VOC 처리 기한 연장", description = "VOC 처리 기한 연장 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_EXTEND_DUE_DATE", - functionDescription = "VOC 처리 기한 연장 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 처리 기한 연장 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 처리 기한 연장 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 처리 기한 연장 해줘", "VOC 처리 기한 연장 결과를 알려줘", "VOC 처리 기한 연장 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse extendVocDueDate(VocToolRequest request); - @McpTool(name = "voc_statistics", title = "VOC 통계 조회", description = "VOC 통계 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "voc", - mappingId = "DIRECT_VOC_STATISTICS", - functionDescription = "VOC 통계 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 VOC 통계 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "VOC 통계 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"VOC 통계 조회 해줘", "VOC 통계 조회 결과를 알려줘", "VOC 통계 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"voc", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - VocToolResponse getVocStatistics(VocToolRequest request); -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/usecase/impl/VocToolUseCaseImpl.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/usecase/impl/VocToolUseCaseImpl.java deleted file mode 100644 index e209531ab..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/voc/usecase/impl/VocToolUseCaseImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.voc.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.voc.dto.VocToolRequest; -import io.shinhanlife.dat.mcc.biz.voc.dto.VocToolResponse; -import io.shinhanlife.dat.mcc.biz.voc.usecase.VocToolUseCase; -import java.time.LocalDateTime; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class VocToolUseCaseImpl implements VocToolUseCase { - @Override - public VocToolResponse registerVoc(VocToolRequest request) { - return mockResponse(request, "voc_register", "VOC 접수 등록"); - } - @Override - public VocToolResponse getVocDetail(VocToolRequest request) { - return mockResponse(request, "voc_detail", "VOC 상세 조회"); - } - @Override - public VocToolResponse searchVocs(VocToolRequest request) { - return mockResponse(request, "voc_search", "VOC 목록 검색"); - } - @Override - public VocToolResponse updateVoc(VocToolRequest request) { - return mockResponse(request, "voc_update", "VOC 내용 수정"); - } - @Override - public VocToolResponse assignVocOwner(VocToolRequest request) { - return mockResponse(request, "voc_assign", "VOC 담당자 배정"); - } - @Override - public VocToolResponse changeVocStatus(VocToolRequest request) { - return mockResponse(request, "voc_change_status", "VOC 처리 상태 변경"); - } - @Override - public VocToolResponse registerVocResult(VocToolRequest request) { - return mockResponse(request, "voc_register_result", "VOC 처리 결과 등록"); - } - @Override - public VocToolResponse sendVocReply(VocToolRequest request) { - return mockResponse(request, "voc_send_reply", "VOC 답변 발송"); - } - @Override - public VocToolResponse transferVoc(VocToolRequest request) { - return mockResponse(request, "voc_transfer", "VOC 이관 처리"); - } - @Override - public VocToolResponse setVocPriority(VocToolRequest request) { - return mockResponse(request, "voc_set_priority", "VOC 우선순위 설정"); - } - @Override - public VocToolResponse classifyVocType(VocToolRequest request) { - return mockResponse(request, "voc_classify_type", "VOC 유형 분류"); - } - @Override - public VocToolResponse getVocAttachments(VocToolRequest request) { - return mockResponse(request, "voc_attachments", "VOC 첨부파일 조회"); - } - @Override - public VocToolResponse getCustomerVocHistory(VocToolRequest request) { - return mockResponse(request, "voc_customer_history", "고객별 VOC 이력 조회"); - } - @Override - public VocToolResponse detectDuplicateVoc(VocToolRequest request) { - return mockResponse(request, "voc_detect_duplicate", "중복 VOC 탐지"); - } - @Override - public VocToolResponse getUrgentVocs(VocToolRequest request) { - return mockResponse(request, "voc_urgent_list", "긴급 VOC 목록 조회"); - } - @Override - public VocToolResponse extendVocDueDate(VocToolRequest request) { - return mockResponse(request, "voc_extend_due_date", "VOC 처리 기한 연장"); - } - @Override - public VocToolResponse getVocStatistics(VocToolRequest request) { - return mockResponse(request, "voc_statistics", "VOC 통계 조회"); - } - private VocToolResponse mockResponse(VocToolRequest request, String toolName, String title) { - VocToolResponse response = new VocToolResponse(); - response.setToolName(toolName); - response.setCategory("VOC"); - response.setReferenceId(request != null && hasText(request.getSubjectId()) ? request.getSubjectId() : "VOC-000001"); - response.setStatus("정상"); - response.setSummary(title + " 처리 결과"); - response.setProcessedAt(LocalDateTime.now().toString()); - response.setDetails(List.of(title + " 모의 상세 정보", "외부 시스템을 변경하지 않는 샘플 응답")); - return response; - } - - private boolean hasText(String value) { - return value != null && !value.isBlank(); - } -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/dto/WcmToolRequest.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/dto/WcmToolRequest.java deleted file mode 100644 index d29d540e9..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/dto/WcmToolRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.wcm.dto; - -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class WcmToolRequest { - @McpToolParam(description = "업무 대상 식별자", required = false) - private String subjectId; - - @McpToolParam(description = "고객 식별자", required = false) - private String customerId; - - @McpToolParam(description = "검색어 또는 처리 내용", required = false) - private String content; - - @McpToolParam(description = "상태, 유형, 등급 또는 처리 값", required = false) - private String value; - - @McpToolParam(description = "담당자 식별자", required = false) - private String assigneeId; - - @McpToolParam(description = "조회 시작일(YYYY-MM-DD)", required = false) - private String startDate; - - @McpToolParam(description = "조회 종료일 또는 처리 예정일(YYYY-MM-DD)", required = false) - private String endDate; - - @McpToolParam(description = "페이지 번호", required = false) - private Integer page; - - @McpToolParam(description = "페이지 크기", required = false) - private Integer size; -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/dto/WcmToolResponse.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/dto/WcmToolResponse.java deleted file mode 100644 index 6fdbb8bf1..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/dto/WcmToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.wcm.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class WcmToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String processedAt; - private List details; -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/usecase/WcmToolUseCase.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/usecase/WcmToolUseCase.java deleted file mode 100644 index d1e463c36..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/usecase/WcmToolUseCase.java +++ /dev/null @@ -1,298 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.wcm.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.wcm.dto.WcmToolRequest; -import io.shinhanlife.dat.mcc.biz.wcm.dto.WcmToolResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface WcmToolUseCase { - @McpTool(name = "wcm_content_list", title = "웹 콘텐츠 목록 조회", description = "웹 콘텐츠 목록 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_LIST", - functionDescription = "웹 콘텐츠 목록 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 목록 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 목록 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 목록 조회 해줘", "웹 콘텐츠 목록 조회 결과를 알려줘", "웹 콘텐츠 목록 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse getContentList(WcmToolRequest request); - @McpTool(name = "wcm_content_detail", title = "웹 콘텐츠 상세 조회", description = "웹 콘텐츠 상세 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_DETAIL", - functionDescription = "웹 콘텐츠 상세 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 상세 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 상세 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 상세 조회 해줘", "웹 콘텐츠 상세 조회 결과를 알려줘", "웹 콘텐츠 상세 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse getContentDetail(WcmToolRequest request); - @McpTool(name = "wcm_content_create", title = "웹 콘텐츠 등록", description = "웹 콘텐츠 등록 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_CREATE", - functionDescription = "웹 콘텐츠 등록 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 등록 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 등록 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 등록 해줘", "웹 콘텐츠 등록 결과를 알려줘", "웹 콘텐츠 등록 기능을 실행해줘"}, - destructive = false, - idempotent = false, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse createContent(WcmToolRequest request); - @McpTool(name = "wcm_content_update", title = "웹 콘텐츠 수정", description = "웹 콘텐츠 수정 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_UPDATE", - functionDescription = "웹 콘텐츠 수정 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 수정 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 수정 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 수정 해줘", "웹 콘텐츠 수정 결과를 알려줘", "웹 콘텐츠 수정 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse updateContent(WcmToolRequest request); - @McpTool(name = "wcm_content_delete", title = "웹 콘텐츠 삭제", description = "웹 콘텐츠 삭제 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_DELETE", - functionDescription = "웹 콘텐츠 삭제 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 삭제 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 삭제 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 삭제 해줘", "웹 콘텐츠 삭제 결과를 알려줘", "웹 콘텐츠 삭제 기능을 실행해줘"}, - destructive = true, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse deleteContent(WcmToolRequest request); - @McpTool(name = "wcm_content_copy", title = "웹 콘텐츠 복사", description = "웹 콘텐츠 복사 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_COPY", - functionDescription = "웹 콘텐츠 복사 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 복사 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 복사 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 복사 해줘", "웹 콘텐츠 복사 결과를 알려줘", "웹 콘텐츠 복사 기능을 실행해줘"}, - destructive = false, - idempotent = false, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse copyContent(WcmToolRequest request); - @McpTool(name = "wcm_content_preview", title = "웹 콘텐츠 미리보기", description = "웹 콘텐츠 미리보기 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_PREVIEW", - functionDescription = "웹 콘텐츠 미리보기 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 미리보기 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 미리보기 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 미리보기 해줘", "웹 콘텐츠 미리보기 결과를 알려줘", "웹 콘텐츠 미리보기 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse previewContent(WcmToolRequest request); - @McpTool(name = "wcm_content_publish", title = "웹 콘텐츠 게시", description = "웹 콘텐츠 게시 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_PUBLISH", - functionDescription = "웹 콘텐츠 게시 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 게시 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 게시 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 게시 해줘", "웹 콘텐츠 게시 결과를 알려줘", "웹 콘텐츠 게시 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse publishContent(WcmToolRequest request); - @McpTool(name = "wcm_content_unpublish", title = "웹 콘텐츠 게시 중지", description = "웹 콘텐츠 게시 중지 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_UNPUBLISH", - functionDescription = "웹 콘텐츠 게시 중지 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 게시 중지 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 게시 중지 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 게시 중지 해줘", "웹 콘텐츠 게시 중지 결과를 알려줘", "웹 콘텐츠 게시 중지 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse unpublishContent(WcmToolRequest request); - @McpTool(name = "wcm_content_schedule_publish", title = "웹 콘텐츠 예약 게시", description = "웹 콘텐츠 예약 게시 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_SCHEDULE_PUBLISH", - functionDescription = "웹 콘텐츠 예약 게시 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 예약 게시 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 예약 게시 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 예약 게시 해줘", "웹 콘텐츠 예약 게시 결과를 알려줘", "웹 콘텐츠 예약 게시 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse scheduleContentPublish(WcmToolRequest request); - @McpTool(name = "wcm_content_request_approval", title = "웹 콘텐츠 승인 요청", description = "웹 콘텐츠 승인 요청 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_REQUEST_APPROVAL", - functionDescription = "웹 콘텐츠 승인 요청 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 승인 요청 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 승인 요청 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 승인 요청 해줘", "웹 콘텐츠 승인 요청 결과를 알려줘", "웹 콘텐츠 승인 요청 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse requestContentApproval(WcmToolRequest request); - @McpTool(name = "wcm_content_review_approval", title = "웹 콘텐츠 승인·반려", description = "웹 콘텐츠 승인·반려 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_REVIEW_APPROVAL", - functionDescription = "웹 콘텐츠 승인·반려 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 승인·반려 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 승인·반려 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 승인·반려 해줘", "웹 콘텐츠 승인·반려 결과를 알려줘", "웹 콘텐츠 승인·반려 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse reviewContentApproval(WcmToolRequest request); - @McpTool(name = "wcm_content_version_history", title = "웹 콘텐츠 버전 이력 조회", description = "웹 콘텐츠 버전 이력 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_VERSION_HISTORY", - functionDescription = "웹 콘텐츠 버전 이력 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 버전 이력 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 버전 이력 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 버전 이력 조회 해줘", "웹 콘텐츠 버전 이력 조회 결과를 알려줘", "웹 콘텐츠 버전 이력 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse getContentVersionHistory(WcmToolRequest request); - @McpTool(name = "wcm_content_restore_version", title = "웹 콘텐츠 이전 버전 복원", description = "웹 콘텐츠 이전 버전 복원 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_CONTENT_RESTORE_VERSION", - functionDescription = "웹 콘텐츠 이전 버전 복원 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 웹 콘텐츠 이전 버전 복원 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "웹 콘텐츠 이전 버전 복원 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"웹 콘텐츠 이전 버전 복원 해줘", "웹 콘텐츠 이전 버전 복원 결과를 알려줘", "웹 콘텐츠 이전 버전 복원 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse restoreContentVersion(WcmToolRequest request); - @McpTool(name = "wcm_metadata_detail", title = "콘텐츠 메타데이터 조회", description = "콘텐츠 메타데이터 조회 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_METADATA_DETAIL", - functionDescription = "콘텐츠 메타데이터 조회 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 콘텐츠 메타데이터 조회 기능이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 승인 처리가 필요한 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 조회 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "콘텐츠 메타데이터 조회 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"콘텐츠 메타데이터 조회 해줘", "콘텐츠 메타데이터 조회 결과를 알려줘", "콘텐츠 메타데이터 조회 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse getContentMetadata(WcmToolRequest request); - @McpTool(name = "wcm_metadata_upsert", title = "콘텐츠 메타데이터 등록·수정", description = "콘텐츠 메타데이터 등록·수정 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_METADATA_UPSERT", - functionDescription = "콘텐츠 메타데이터 등록·수정 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 콘텐츠 메타데이터 등록·수정 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "콘텐츠 메타데이터 등록·수정 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"콘텐츠 메타데이터 등록·수정 해줘", "콘텐츠 메타데이터 등록·수정 결과를 알려줘", "콘텐츠 메타데이터 등록·수정 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse upsertContentMetadata(WcmToolRequest request); - @McpTool(name = "wcm_taxonomy_manage", title = "콘텐츠 카테고리·태그 관리", description = "콘텐츠 카테고리·태그 관리 기능을 안전한 모의 데이터로 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "wcm", - mappingId = "DIRECT_WCM_TAXONOMY_MANAGE", - functionDescription = "콘텐츠 카테고리·태그 관리 기능을 수행합니다.", - whenToUse = "고객채널 업무에서 콘텐츠 카테고리·태그 관리 기능이 필요할 때 사용합니다.", - whenNotToUse = "필수 식별자와 변경 내용이 확인되지 않은 경우에는 사용하지 않습니다.", - ioLimits = "입력 범위에 대한 모의 처리 결과만 반환하며 외부 시스템을 직접 변경하지 않습니다.", - displayDescription = "콘텐츠 카테고리·태그 관리 기능을 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"콘텐츠 카테고리·태그 관리 해줘", "콘텐츠 카테고리·태그 관리 결과를 알려줘", "콘텐츠 카테고리·태그 관리 기능을 실행해줘"}, - destructive = false, - idempotent = true, - tags = {"wcm", "고객채널", "처리", "mock"}, - ownerOrg = "MCP_TOOL" - ) - WcmToolResponse manageContentTaxonomy(WcmToolRequest request); -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/usecase/impl/WcmToolUseCaseImpl.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/usecase/impl/WcmToolUseCaseImpl.java deleted file mode 100644 index 5b59adce9..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/biz/wcm/usecase/impl/WcmToolUseCaseImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.wcm.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.wcm.dto.WcmToolRequest; -import io.shinhanlife.dat.mcc.biz.wcm.dto.WcmToolResponse; -import io.shinhanlife.dat.mcc.biz.wcm.usecase.WcmToolUseCase; -import java.time.LocalDateTime; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class WcmToolUseCaseImpl implements WcmToolUseCase { - @Override - public WcmToolResponse getContentList(WcmToolRequest request) { - return mockResponse(request, "wcm_content_list", "웹 콘텐츠 목록 조회"); - } - @Override - public WcmToolResponse getContentDetail(WcmToolRequest request) { - return mockResponse(request, "wcm_content_detail", "웹 콘텐츠 상세 조회"); - } - @Override - public WcmToolResponse createContent(WcmToolRequest request) { - return mockResponse(request, "wcm_content_create", "웹 콘텐츠 등록"); - } - @Override - public WcmToolResponse updateContent(WcmToolRequest request) { - return mockResponse(request, "wcm_content_update", "웹 콘텐츠 수정"); - } - @Override - public WcmToolResponse deleteContent(WcmToolRequest request) { - return mockResponse(request, "wcm_content_delete", "웹 콘텐츠 삭제"); - } - @Override - public WcmToolResponse copyContent(WcmToolRequest request) { - return mockResponse(request, "wcm_content_copy", "웹 콘텐츠 복사"); - } - @Override - public WcmToolResponse previewContent(WcmToolRequest request) { - return mockResponse(request, "wcm_content_preview", "웹 콘텐츠 미리보기"); - } - @Override - public WcmToolResponse publishContent(WcmToolRequest request) { - return mockResponse(request, "wcm_content_publish", "웹 콘텐츠 게시"); - } - @Override - public WcmToolResponse unpublishContent(WcmToolRequest request) { - return mockResponse(request, "wcm_content_unpublish", "웹 콘텐츠 게시 중지"); - } - @Override - public WcmToolResponse scheduleContentPublish(WcmToolRequest request) { - return mockResponse(request, "wcm_content_schedule_publish", "웹 콘텐츠 예약 게시"); - } - @Override - public WcmToolResponse requestContentApproval(WcmToolRequest request) { - return mockResponse(request, "wcm_content_request_approval", "웹 콘텐츠 승인 요청"); - } - @Override - public WcmToolResponse reviewContentApproval(WcmToolRequest request) { - return mockResponse(request, "wcm_content_review_approval", "웹 콘텐츠 승인·반려"); - } - @Override - public WcmToolResponse getContentVersionHistory(WcmToolRequest request) { - return mockResponse(request, "wcm_content_version_history", "웹 콘텐츠 버전 이력 조회"); - } - @Override - public WcmToolResponse restoreContentVersion(WcmToolRequest request) { - return mockResponse(request, "wcm_content_restore_version", "웹 콘텐츠 이전 버전 복원"); - } - @Override - public WcmToolResponse getContentMetadata(WcmToolRequest request) { - return mockResponse(request, "wcm_metadata_detail", "콘텐츠 메타데이터 조회"); - } - @Override - public WcmToolResponse upsertContentMetadata(WcmToolRequest request) { - return mockResponse(request, "wcm_metadata_upsert", "콘텐츠 메타데이터 등록·수정"); - } - @Override - public WcmToolResponse manageContentTaxonomy(WcmToolRequest request) { - return mockResponse(request, "wcm_taxonomy_manage", "콘텐츠 카테고리·태그 관리"); - } - private WcmToolResponse mockResponse(WcmToolRequest request, String toolName, String title) { - WcmToolResponse response = new WcmToolResponse(); - response.setToolName(toolName); - response.setCategory("WCMS"); - response.setReferenceId(request != null && hasText(request.getSubjectId()) ? request.getSubjectId() : "WCMS-000001"); - response.setStatus("정상"); - response.setSummary(title + " 처리 결과"); - response.setProcessedAt(LocalDateTime.now().toString()); - response.setDetails(List.of(title + " 모의 상세 정보", "외부 시스템을 변경하지 않는 샘플 응답")); - return response; - } - - private boolean hasText(String value) { - return value != null && !value.isBlank(); - } -} diff --git a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/cus/DatWasCusApplication.java b/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/cus/DatWasCusApplication.java deleted file mode 100644 index 9bb320f8f..000000000 --- a/dat-was-cus/src/main/java/io/shinhanlife/dat/mcc/cus/DatWasCusApplication.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.shinhanlife.dat.mcc.cus; - - -/** - * @package io.shinhanlife.dat.mcc.cus - * @className DatWasCusApplication - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationPropertiesScan; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Import; -import io.shinhanlife.dat.lib.mcp.ToolMcpServerConfiguration; - -@SpringBootApplication(scanBasePackages = {"io.shinhanlife.dat.mcc", "io.shinhanlife.dat.lib"}) -@ConfigurationPropertiesScan(basePackages = {"io.shinhanlife.dat.mcc", "io.shinhanlife.dat.lib"}) -@EnableCaching -@Import(ToolMcpServerConfiguration.class) -public class DatWasCusApplication { - public static void main(String[] args) { - SpringApplication.run(DatWasCusApplication.class, args); - } -} diff --git a/dat-was-cus/src/main/resources/application-dev.yml b/dat-was-cus/src/main/resources/application-dev.yml deleted file mode 100644 index b3e646360..000000000 --- a/dat-was-cus/src/main/resources/application-dev.yml +++ /dev/null @@ -1,15 +0,0 @@ -# OCI 클라우드 개발 환경 전용 설정 -server: - port: ${PORT:8084} - -axhub: - gateway: - url: https://axhubmcp.devjun.net - -spring: - config: - activate: - on-profile: dev - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-dev.yml diff --git a/dat-was-cus/src/main/resources/application-local.yml b/dat-was-cus/src/main/resources/application-local.yml deleted file mode 100644 index 0699708fc..000000000 --- a/dat-was-cus/src/main/resources/application-local.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Local 환경 전용 설정 (H2 메모리 DB 등) -spring: - config: - activate: - on-profile: local - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-local.yml - datasource: - url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1; - username: sa - password: password - h2: - console: - enabled: true - -mcp: - security: - tenant-domains: - mcp-client-1: CUSTOMER,COMMON - mcp-client-2: ALL - -axhub: - gateway: - url: http://localhost:8081 - tool: - url: ${AXHUB_TOOL_URL:http://localhost:${server.port}} - -sol: - req-detail: - mock-enabled: true diff --git a/dat-was-cus/src/main/resources/application-prod.yml b/dat-was-cus/src/main/resources/application-prod.yml deleted file mode 100644 index b5f08f21b..000000000 --- a/dat-was-cus/src/main/resources/application-prod.yml +++ /dev/null @@ -1,16 +0,0 @@ -server: - port: ${PORT:8084} - -spring: - config: - activate: - on-profile: prod - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-prod.yml - -axhub: - gateway: - url: ${AXHUB_GATEWAY_URL} - tool: - url: ${AXHUB_TOOL_URL} diff --git a/dat-was-cus/src/main/resources/application-test.yml b/dat-was-cus/src/main/resources/application-test.yml deleted file mode 100644 index ab1c69084..000000000 --- a/dat-was-cus/src/main/resources/application-test.yml +++ /dev/null @@ -1,16 +0,0 @@ -server: - port: ${PORT:8084} - -spring: - config: - activate: - on-profile: test - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-test.yml - -axhub: - gateway: - url: ${AXHUB_GATEWAY_URL} - tool: - url: ${AXHUB_TOOL_URL} diff --git a/dat-was-cus/src/main/resources/application.yml b/dat-was-cus/src/main/resources/application.yml deleted file mode 100644 index 52b49f87d..000000000 --- a/dat-was-cus/src/main/resources/application.yml +++ /dev/null @@ -1,22 +0,0 @@ -server: - port: 8084 -spring: - application: - name: dat-was-cus - config: - import: optional:classpath:tool-service-manifest.yml - profiles: - active: local -logging: - level: - org.apache.kafka: ERROR -mcp: - namespace: "" - manifest: - bundle-id: was-cus - # Set the AA-assigned prefix before MCP pull activation (for example: cus.). - name-prefix: "" - security: - api-key: ${TOOL_SERVER_API_KEY:tool-server-key} - tenant-domains: - TESTER-DEV: ALL diff --git a/dat-was-cus/src/main/resources/logback-spring.xml b/dat-was-cus/src/main/resources/logback-spring.xml deleted file mode 100644 index f0fe7bdee..000000000 --- a/dat-was-cus/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - ${LOG_PATTERN} - - - - - - - - - /swlog/dat-was-cus/A01/${HOSTNAME}_A01.log - - - /swlog/dat-was-cus/A01/${HOSTNAME}_A01_%d{yyyyMMdd}.%i.log - - 100MB - 30 - 1GB - - - ${LOG_PATTERN} - - - - - - - - - - - - diff --git a/dat-was-cus/src/main/resources/mock-responses/cmm_customer_tool.json b/dat-was-cus/src/main/resources/mock-responses/cmm_customer_tool.json deleted file mode 100644 index 07ad1840d..000000000 --- a/dat-was-cus/src/main/resources/mock-responses/cmm_customer_tool.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode" : "SUCCESS", - "data" : "[{\"date\":\"2024-01-05\",\"message\":\"안내 내용\"}]" -} diff --git a/dat-was-cus/src/main/resources/mock-responses/ins_insurance_processor.json b/dat-was-cus/src/main/resources/mock-responses/ins_insurance_processor.json deleted file mode 100644 index d856cc5b5..000000000 --- a/dat-was-cus/src/main/resources/mock-responses/ins_insurance_processor.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode" : "SUCCESS", - "claimId" : "CLM20230001" -} diff --git a/dat-was-cus/src/main/resources/tool-service-manifest.yml b/dat-was-cus/src/main/resources/tool-service-manifest.yml deleted file mode 100644 index 21acb9e98..000000000 --- a/dat-was-cus/src/main/resources/tool-service-manifest.yml +++ /dev/null @@ -1,16 +0,0 @@ -mcp: - manifest: - routing-functions: - - name: route_to_dat-was-cus - description-serialization: 고객 업무 서버로 요청을 라우팅합니다. 고객 정보와 고객 상담 업무를 처리합니다. 요청의 주요 엔티티와 업무 영역을 기준으로 고객 서버를 선택합니다. - server-id: dat-was-cus - category-key: cus - product-boundary: insurance - business-domain: 고객 관리 - business-outcome: 고객 정보와 고객 상담 업무를 처리합니다. - primary-entities: [고객, 상담, 접촉이력] - capabilities: [고객 조회, 고객 정보 수정, 상담 이력 조회] - select-if: 고객 또는 상담 관련 요청인 경우 - reject-if: 계약, 상품, 시스템 운영 업무가 주된 요청인 경우 - confusable-servers: [dat-was-sal, dat-was-pro, dat-was-sys] - decision-policy: 요청의 주요 엔티티와 업무 영역을 기준으로 고객 서버를 선택합니다. diff --git a/dat-was-lib/build.gradle b/dat-was-lib/build.gradle deleted file mode 100644 index a7e23dcfc..000000000 --- a/dat-was-lib/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -plugins { - // Gateway와 모든 Tool Pod가 의존하는 공통 라이브러리 모듈입니다. - id 'java-library' -} - -dependencies { - // 공통 REST Controller, HTTP Client, 예외 처리 기반입니다. - api 'org.springframework.boot:spring-boot-starter-web' - - // Tool Request DTO의 Bean Validation을 지원합니다. - api 'org.springframework.boot:spring-boot-starter-validation' - - // Tool Manifest/Registry 캐시 및 Redis 기반 공통 기능을 제공합니다. - api 'org.springframework.boot:spring-boot-starter-data-redis' - - // Tool SLA, 로깅, 공통 Aspect를 적용합니다. - api 'org.springframework.boot:spring-boot-starter-aop' - - // MCI/DB 연동에 필요한 JDBC 공통 기능입니다. - api 'org.springframework.boot:spring-boot-starter-jdbc' - - // Tool 호출의 Circuit Breaker, Rate Limit, Retry 등 복원력 기능입니다. - api 'io.github.resilience4j:resilience4j-spring-boot3:2.2.0' - api 'io.github.resilience4j:resilience4j-core:2.2.0' - api 'io.github.resilience4j:resilience4j-circuitbreaker:2.2.0' - api 'io.github.resilience4j:resilience4j-ratelimiter' - api 'io.github.resilience4j:resilience4j-retry:2.2.0' - - // MCI/업무 데이터 접근을 위한 MyBatis 지원입니다. - api 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' - - // 현재 로컬 테스트 DB 및 SQL 로그 처리에 사용합니다. - api 'com.h2database:h2' - - // Spring Data Redis가 사용하는 Redis Client를 공통 모듈에서 직접 참조합니다. - api 'io.lettuce:lettuce-core:6.6.0.RELEASE' - - // MCI XML 전문, JSON Tool Schema/Manifest 처리에 사용합니다. - api 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml' - api 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml' - api 'com.fasterxml.jackson.core:jackson-databind' - // MCP Java SDK 2.0.0의 mcp-json-jackson2가 호출하는 Schema API와 반드시 맞춘다. - // 3.x는 Schema.validate(JsonNode) 반환 규격이 달라 런타임 NoSuchMethodError가 발생한다. - api 'com.networknt:json-schema-validator:2.0.0' - - // Boot 3.5.11 위에서 MCP Java SDK 2.0.0의 Server/Client와 Servlet 전송 계층을 사용합니다. - // Jackson 2 공급자를 명시해 Boot 3의 Jackson 계열과 맞춥니다. - api 'io.modelcontextprotocol.sdk:mcp-core:2.0.0' - api 'io.modelcontextprotocol.sdk:mcp-json-jackson2:2.0.0' - - // Boot 3.5 호환 Spring AI 어노테이션 API를 유지합니다. - api 'org.springaicommunity:mcp-annotations:0.9.0' - - // 이벤트 기반 확장이 필요한 Tool의 공통 Kafka 연동 기능입니다. - api 'org.springframework.kafka:spring-kafka:3.2.0' - - // Tool Pod REST API 문서 및 Swagger UI를 제공합니다. - api 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0' -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/ErrorDetail.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/ErrorDetail.java deleted file mode 100644 index 3824a1ffd..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/ErrorDetail.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.shinhanlife.dat.lib.adapter.dto; - -import lombok.Builder; -import lombok.NoArgsConstructor; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -/** - * @package io.shinhanlife.dat.lib.adapter.dto - * @className ErrorDetail - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Getter -@Setter -@AllArgsConstructor -@Builder -@NoArgsConstructor -public class ErrorDetail { - private int code; - private String message; -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/JsonRpcRequest.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/JsonRpcRequest.java deleted file mode 100644 index 375a90103..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/JsonRpcRequest.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.shinhanlife.dat.lib.adapter.dto; - -import lombok.Getter; -import lombok.Setter; - -/** - * @package io.shinhanlife.dat.lib.adapter.dto - * @className JsonRpcRequest - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Getter -@Setter -public class JsonRpcRequest { - private String jsonrpc; - private String method; - private Params params; - private String id; -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/JsonRpcResponse.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/JsonRpcResponse.java deleted file mode 100644 index 4f6a10c74..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/JsonRpcResponse.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.shinhanlife.dat.lib.adapter.dto; - -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import lombok.Getter; -import lombok.Setter; - -// 2. 응답 DTO -/** - * @package io.shinhanlife.dat.lib.adapter.dto - * @className JsonRpcResponse - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Getter -@Setter -@JsonPropertyOrder({"jsonrpc", "result", "error", "id"}) -public class JsonRpcResponse { - public String jsonrpc = "2.0"; - public Object result; - public Object error; - public String id; -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/Params.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/Params.java deleted file mode 100644 index 25a8e9eea..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/dto/Params.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.shinhanlife.dat.lib.adapter.dto; - -import lombok.Builder; -import lombok.NoArgsConstructor; -import lombok.AllArgsConstructor; - -import java.util.List; -import java.util.Map; - -import lombok.Getter; -import lombok.Setter; - -/** - * @package io.shinhanlife.dat.lib.adapter.dto - * @className Params - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Params { - private String routingType; - private String name; - private String interfaceId; - private Map data; - private List> spec; -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/test/MockEimsHttpServer.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/test/MockEimsHttpServer.java deleted file mode 100644 index 91a8d5325..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/test/MockEimsHttpServer.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.shinhanlife.dat.lib.adapter.test; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.core.io.ClassPathResource; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * Local HTTP mock server for scaffolded HTTP Tools. - * - *

Each Tool Pod returns the JSON generated under - * {@code src/main/resources/mock-responses/{toolName}.json}. It is enabled only - * when {@code axhub.mock.http.enabled=true}, which the HTTP Scaffold adds to local configuration.

- */ -@Slf4j -@RestController -@RequiredArgsConstructor -@ConditionalOnProperty(prefix = "axhub.mock.http", name = "enabled", havingValue = "true") -@RequestMapping("/api") -public class MockEimsHttpServer { - - private final ObjectMapper objectMapper; - - @PostMapping("/mock/http/{toolName:[a-z0-9_-]+}") - public ResponseEntity mockToolHttpResponse( - @PathVariable String toolName, - @RequestBody(required = false) JsonNode request) { - ClassPathResource resource = new ClassPathResource("mock-responses/" + toolName + ".json"); - if (!resource.exists()) { - return ResponseEntity.notFound().build(); - } - try (InputStream inputStream = resource.getInputStream()) { - log.info("[MockEimsHttpServer] HTTP mock request. toolName={}, body={}", toolName, request); - return ResponseEntity.ok(objectMapper.readTree(inputStream)); - } catch (JsonProcessingException e) { - log.warn("[MockEimsHttpServer] Invalid mock response JSON. toolName={}", toolName, e); - return ResponseEntity.internalServerError().build(); - } catch (IOException e) { - log.warn("[MockEimsHttpServer] Unable to read mock response. toolName={}", toolName, e); - return ResponseEntity.internalServerError().build(); - } - } - - @PostMapping("/gateway") - public ResponseEntity mockEimsReceiver( - @RequestHeader(value = "X-Trace-Id", required = false) String traceId, - @RequestBody Map request) { - String interfaceId = String.valueOf(request.getOrDefault("interfaceId", "")); - log.info("[MockEimsHttpServer] Legacy gateway mock request. traceId={}, interfaceId={}", traceId, interfaceId); - return ResponseEntity.ok(Map.of( - "status", "404", - "message", "MOCK data is not defined for interfaceId: " + interfaceId)); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/util/PiiMaskingLogbackConverter.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/util/PiiMaskingLogbackConverter.java deleted file mode 100644 index 8f8241109..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/util/PiiMaskingLogbackConverter.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.shinhanlife.dat.lib.adapter.util; - -import ch.qos.logback.classic.pattern.MessageConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; - -/** - * Logback 커스텀 컨버터 - * 모든 로그 메시지(%msg)가 파일이나 콘솔에 찍히기 직전에 이 클래스를 거쳐가게 됩니다. - * 여기서 PiiMaskingUtils.mask()를 호출하여 PII(주민번호, 계좌번호 등)를 안전하게 별표(*) 처리합니다. - */ -/** - * @package io.shinhanlife.dat.lib.adapter.util - * @className PiiMaskingLogbackConverter - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public class PiiMaskingLogbackConverter extends MessageConverter { - - @Override - public String convert(ILoggingEvent event) { - // 원본 로그 메시지를 가져옵니다. - String originalMessage = super.convert(event); - - // 정규식을 이용하여 개인정보가 포함되어 있으면 마스킹 처리하여 반환합니다. - return PiiMaskingUtils.mask(originalMessage); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/util/PiiMaskingUtils.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/util/PiiMaskingUtils.java deleted file mode 100644 index 63b445715..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/adapter/util/PiiMaskingUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.shinhanlife.dat.lib.adapter.util; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @package io.shinhanlife.dat.lib.adapter.util - * @className PiiMaskingUtils - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public class PiiMaskingUtils { - - // 1. 주민등록번호 패턴 (ex: 900101-1234567 또는 9001011234567) - private static final Pattern RRN_PATTERN = Pattern.compile("(\\d{6})[-]?([1-4]\\d{6})"); - - // 2. 휴대전화번호 패턴 (ex: 010-1234-5678) - private static final Pattern PHONE_PATTERN = Pattern.compile("(01[016789])[-]?(\\d{3,4})[-]?(\\d{4})"); - - // 3. 신한라이프 계좌/증권번호 패턴 (단순 예시용 계좌번호 11~14자리) - private static final Pattern ACCOUNT_PATTERN = Pattern.compile("(\\d{3})-?(\\d{3})-?(\\d{5,8})"); - - public static String mask(String input) { - if (input == null || input.isEmpty()) { - return input; - } - - String masked = input; - - // [1] 주민번호 뒷자리 마스킹 (첫자리 성별 식별자는 남기고 마스킹: 900101-1******) - Matcher rrnMatcher = RRN_PATTERN.matcher(masked); - StringBuffer rrnBuffer = new StringBuffer(); - while (rrnMatcher.find()) { - String firstPart = rrnMatcher.group(1); - String secondPart = rrnMatcher.group(2); - rrnMatcher.appendReplacement(rrnBuffer, firstPart + "-" + secondPart.charAt(0) + "******"); - } - rrnMatcher.appendTail(rrnBuffer); - masked = rrnBuffer.toString(); - - // [2] 전화번호 중간자리 마스킹 (010-****-5678) - Matcher phoneMatcher = PHONE_PATTERN.matcher(masked); - StringBuffer phoneBuffer = new StringBuffer(); - while (phoneMatcher.find()) { - String p1 = phoneMatcher.group(1); - String p2 = phoneMatcher.group(2); - String p3 = phoneMatcher.group(3); - String maskedP2 = p2.replaceAll(".", "*"); - phoneMatcher.appendReplacement(phoneBuffer, p1 + "-" + maskedP2 + "-" + p3); - } - phoneMatcher.appendTail(phoneBuffer); - masked = phoneBuffer.toString(); - - // [3] 계좌번호 뒷자리 마스킹 (110-123-********) - Matcher accMatcher = ACCOUNT_PATTERN.matcher(masked); - StringBuffer accBuffer = new StringBuffer(); - while (accMatcher.find()) { - String a1 = accMatcher.group(1); - String a2 = accMatcher.group(2); - String a3 = accMatcher.group(3); - String maskedA3 = a3.replaceAll(".", "*"); - accMatcher.appendReplacement(accBuffer, a1 + "-" + a2 + "-" + maskedA3); - } - accMatcher.appendTail(accBuffer); - masked = accBuffer.toString(); - - return masked; - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/annotation/GrowToolHint.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/annotation/GrowToolHint.java deleted file mode 100644 index 8a26b17d0..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/annotation/GrowToolHint.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.shinhanlife.dat.lib.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Spring AI @Tool 어노테이션을 보완하여 MCP 시스템 메타데이터를 추가 제공하는 힌트 어노테이션 - * @package io.shinhanlife.dat.lib.annotation - * @className GrowToolHint - * @description 비즈니스 로직(Tool)과 시스템 제어 메타데이터 분리 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface GrowToolHint { - boolean register() default true; - boolean requiresApproval() default false; - String categoryKey() default "com"; - String mappingId() default ""; - String inputSchemaResource() default ""; - String outputSchemaResource() default ""; - long timeoutMillis() default 5000L; - int retryMaxAttempts() default 3; - - // Meta 정보 추가 (보고용 샘플) - String displayDescription() default ""; - String functionDescription() default ""; - String whenToUse() default ""; - String whenNotToUse() default ""; - String ioLimits() default ""; - String[] exampleQueries() default {}; - boolean destructive() default false; - boolean idempotent() default false; - String[] tags() default {}; - String[] requiredEnvKeys() default {}; - String ownerOrg() default ""; -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/annotation/McpOutputSchema.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/annotation/McpOutputSchema.java deleted file mode 100644 index d203287fd..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/annotation/McpOutputSchema.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.shinhanlife.dat.lib.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Marks a response DTO whose generated JSON Schema must be exposed and validated for a Tool response. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface McpOutputSchema { -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/aop/ToolSlaMonitoringAspect.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/aop/ToolSlaMonitoringAspect.java deleted file mode 100644 index 59328dc5c..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/aop/ToolSlaMonitoringAspect.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.shinhanlife.dat.lib.aop; - - -/** - * @package io.shinhanlife.dat.lib.aop - * @className ToolSlaMonitoringAspect - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -import io.shinhanlife.dat.lib.config.McpProperties; -import java.lang.reflect.Method; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.reflect.MethodSignature; -import org.springaicommunity.mcp.annotation.McpTool; -import org.springframework.stereotype.Component; -import org.springframework.util.StopWatch; - -@Slf4j -@Aspect -@Component -@RequiredArgsConstructor -public class ToolSlaMonitoringAspect { - - private final McpProperties mcpProperties; - - // @McpTool 어노테이션이 붙은 모든 비즈니스 툴 메서드 실행을 가로챕니다. - @Around("@annotation(org.springaicommunity.mcp.annotation.McpTool)") - public Object monitorToolSla(ProceedingJoinPoint joinPoint) throws Throwable { - MethodSignature signature = (MethodSignature) joinPoint.getSignature(); - Method method = signature.getMethod(); - McpTool functionAnnotation = method.getAnnotation(McpTool.class); - - // 네임스페이스 자동 주입 로직을 반영하여 최종 툴 이름을 산출합니다. - String baseName = functionAnnotation.name(); - String finalName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty() - ? mcpProperties.getNamespace() + "_" + baseName - : baseName; - - StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - - try { - // 실제 비즈니스 로직(툴) 실행 - Object result = joinPoint.proceed(); - - stopWatch.stop(); - long timeMillis = stopWatch.getTotalTimeMillis(); - - // SLA 기준을 초과하면 (예: 2초 이상) 경고 로깅 처리 가능 - if (timeMillis > 2000) { - log.warn(" [SLA 경고] Tool: {} | 소요시간: {}ms | 상태: SLOW_RESPONSE", finalName, timeMillis); - } else { - log.info(" [SLA 추적] Tool: {} | 소요시간: {}ms | 상태: SUCCESS", finalName, timeMillis); - } - - return result; - - } catch (RuntimeException e) { - if (stopWatch.isRunning()) { - stopWatch.stop(); - } - long timeMillis = stopWatch.getTotalTimeMillis(); - - // 에러 발생 시 명확하게 실패 로그 기록 - log.error(" [SLA 장애] Tool: {} | 소요시간: {}ms | 상태: FAILED | 사유: {}", finalName, timeMillis, e.getMessage()); - - // 원래 흐름대로 예외를 던져서 게이트웨이나 상위 로직이 에러를 처리하게 함 - throw e; - } - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/AxhubHttpConfiguration.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/AxhubHttpConfiguration.java deleted file mode 100644 index c113d3086..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/AxhubHttpConfiguration.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.shinhanlife.dat.lib.config; - -import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent; -import java.net.http.HttpClient; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.JdkClientHttpRequestFactory; -import org.springframework.web.client.RestClient; - -/** - * Registers the temporary Glow HTTP compatibility component from the DAP library scan scope. - * The bean is only created when an official GlowHttpComponent has not already been supplied. - */ -@Configuration(proxyBeanMethods = false) -public class AxhubHttpConfiguration { - - @Bean - @ConditionalOnMissingBean(GlowHttpComponent.class) - public GlowHttpComponent glowHttpComponent(RestClient.Builder restClientBuilder) { - // WireMock and legacy internal endpoints can only support HTTP/1.1. - // Avoid JDK HTTP/2 negotiation that may cause RST_STREAM responses. - HttpClient http11Client = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .build(); - JdkClientHttpRequestFactory requestFactory = new JdkClientHttpRequestFactory(http11Client); - return new GlowHttpComponent(restClientBuilder.requestFactory(requestFactory)); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/CorsConfig.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/CorsConfig.java deleted file mode 100644 index 7bd14df35..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/CorsConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.shinhanlife.dat.lib.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - * @package io.shinhanlife.dat.lib.config - * @className CorsConfig - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Configuration -public class CorsConfig implements WebMvcConfigurer { - - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") // 모든 엔드포인트에 대해 CORS 허용 - .allowedOriginPatterns("*") // 외부 Agent Builder 등 모든 오리진 허용 - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH") // 허용할 HTTP 메서드 - .allowedHeaders("*") // 모든 헤더 허용 - .exposedHeaders("Mcp-Session-Id") // MCP-HTTP 세션 아이디 노출 허용 - .allowCredentials(true) // 쿠키/인증 정보 허용 - .maxAge(3600); // preflight 요청 캐시 시간 (초 단위) - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/GlowCommunicationProperties.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/GlowCommunicationProperties.java deleted file mode 100644 index 08feff705..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/GlowCommunicationProperties.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.shinhanlife.dat.lib.config; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -/** - * [Glow Framework 통신 환경 설정 클래스] - * application-glow-local.yml 의 'glow.communication' 하위 설정값들을 - * 자바 객체(Bean)로 매핑하여 제공합니다. - */ -/** - * @package io.shinhanlife.dat.lib.config - * @className GlowCommunicationProperties - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Component -@Getter -@Setter -@ConfigurationProperties(prefix = "glow.communication") -public class GlowCommunicationProperties { - - private Common common = new Common(); - private Http http = new Http(); - private Mci mci = new Mci(); - private ExtMci extmci = new ExtMci(); - private Eai eai = new Eai(); - private Websocket websocket = new Websocket(); - - @Getter @Setter - public static class Common { - private String envType; // 대내표준 헤더의 환경 타입정보 (D, T, P) - } - - @Getter @Setter - public static class Http { - private int connectionTimeout; // 연결 타임아웃 시간 (초 단위) - private int readTimeout; // 읽기 타임아웃 시간 (초 단위) - } - - @Getter @Setter - public static class Mci { - private String host; - private int port; - private String uri; - private String receiveUri; - private int connectionTimeout; - private int readTimeout; - private String encoding; - } - - @Getter @Setter - public static class ExtMci { - private String host; - private int port; - private String uri; - private String jsonUri; - private String receiveUri; - private int connectionTimeout; - private int readTimeout; - private String encoding; - } - - @Getter @Setter - public static class Eai { - private String host; - private int port; - } - - @Getter @Setter - public static class Websocket { - private String endpoint; - private String allowedOrigins; - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/McpProperties.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/McpProperties.java deleted file mode 100644 index ff53822ec..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/McpProperties.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.shinhanlife.dat.lib.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -import java.util.Map; -import java.util.List; - -/** - * @package io.shinhanlife.dat.lib.config - * @className McpProperties - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Data -@Configuration -@ConfigurationProperties(prefix = "mcp") -public class McpProperties { - - private String namespace; - private Manifest manifest = new Manifest(); - - @Data - public static class Manifest { - private String bundleId; - private String namePrefix; - private List routingFunctions = List.of(); - } - - @Data - public static class RoutingFunction { - private String name; - private String descriptionSerialization; - private String serverId; - private String categoryKey; - private String productBoundary; - private String businessDomain; - private String businessOutcome; - private List primaryEntities = List.of(); - private List capabilities = List.of(); - private String selectIf; - private String rejectIf; - private List confusableServers = List.of(); - private String decisionPolicy; - } - -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/MybatisConfig.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/MybatisConfig.java deleted file mode 100644 index 61ff46a74..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/MybatisConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.shinhanlife.dat.lib.config; - -import javax.sql.DataSource; - -import org.apache.ibatis.session.SqlSessionFactory; -import org.mybatis.spring.SqlSessionFactoryBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @package io.shinhanlife.dat.lib.config - * @className MybatisConfig - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -import org.mybatis.spring.annotation.MapperScan; -import io.shinhanlife.glow.GlowMybatisMapper; - -@Configuration -@MapperScan(basePackages = "io.shinhanlife.dat", annotationClass = GlowMybatisMapper.class) -public class MybatisConfig { - - @Bean - public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { - SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); - sessionFactory.setDataSource(dataSource); - return sessionFactory.getObject(); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/ToolSchemaConfiguration.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/ToolSchemaConfiguration.java deleted file mode 100644 index dc1f6e483..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/config/ToolSchemaConfiguration.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.shinhanlife.dat.lib.config; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.json.schema.JsonSchemaValidator; -import io.modelcontextprotocol.json.schema.jackson2.DefaultJsonSchemaValidator; -import io.shinhanlife.dat.lib.util.ToolSchemaResolver; -import io.shinhanlife.dat.lib.validation.ToolArgumentSchemaValidator; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * Common MCP Tool Schema Bean configuration. - */ -@Configuration -public class ToolSchemaConfiguration { - - @Bean - public ToolSchemaResolver toolSchemaResolver(ObjectMapper objectMapper) { - return new ToolSchemaResolver(objectMapper); - } - - @Bean - public JsonSchemaValidator mcpJsonSchemaValidator() { - return new DefaultJsonSchemaValidator(); - } - - @Bean - public ToolArgumentSchemaValidator toolArgumentSchemaValidator(ObjectMapper objectMapper, - JsonSchemaValidator jsonSchemaValidator) { - return new ToolArgumentSchemaValidator(objectMapper, jsonSchemaValidator); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/dto/OperationType.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/dto/OperationType.java deleted file mode 100644 index 2c74bc42f..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/dto/OperationType.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.dat.lib.dto; - -/** - * @package io.shinhanlife.dat.mcg.dto - * @className OperationType - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public enum OperationType { - READ, - WRITE -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/exception/BizException.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/exception/BizException.java deleted file mode 100644 index ee7fe5a66..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/exception/BizException.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.shinhanlife.dat.lib.exception; - -// import io.shinhanlife.glow.core.exception.abstracts.BusinessException; (이 패키지는 현재 버전에 존재하지 않아 주석 처리함) -import lombok.Getter; -import lombok.Setter; - -/** - * @package io.shinhanlife.dat.lib.exception - * @className BizException - * @description AX HUB 시스템 처리 Exception - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01 0986406    최초생성
- * 
- * 
- */ -@Setter -@Getter -public class BizException extends RuntimeException { - - private Object[] replaceStrings; - private String code; - private String msg; - - public BizException(String code) { - super(""); - this.code = code; - this.replaceStrings = new Object[]{""}; - } - - public BizException(String code, Object[] replaceStrings) { - super(""); - this.code = code; - this.replaceStrings = replaceStrings; - } - - public BizException(String code, String msg) { - super(""); - this.code = code; - this.msg = msg; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/GlowIntegrationCall.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/GlowIntegrationCall.java deleted file mode 100644 index e97be495e..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/GlowIntegrationCall.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.shinhanlife.dat.lib.integration; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -// TODO: 실제 Glow Framework 의존성이 추가되면 아래 주석들을 풀고 사용하세요! -// import io.shinhanlife.glow.communication.dto.CommonHeader; -// import io.shinhanlife.glow.communication.dto.Transfer; -// import io.shinhanlife.glow.communication.module.eai.component.GlowEaiComponent; -// import io.shinhanlife.glow.communication.module.mci.component.GlowExtMciComponent; -// import io.shinhanlife.glow.communication.module.mci.component.GlowMciComponent; -// import io.shinhanlife.glow.communication.util.CommonHeaderFactory; - -/** - * [MCI / EAI 공통 연동 래퍼(Wrapper) 템플릿] - * 신한라이프 Glow Framework 개발표준정의서를 바탕으로 대내/대외망/EAI 통신을 - * MCP 툴에서 손쉽게 호출할 수 있도록 일원화한 컴포넌트입니다. - */ -/** - * @package io.shinhanlife.dat.lib.integration - * @className GlowIntegrationCall - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Component -@RequiredArgsConstructor -public class GlowIntegrationCall { - - // 1. 실제 의존성이 주입될 프레임워크 컴포넌트들 (임시 주석 처리) - /* - private final GlowMciComponent mci; - private final GlowEaiComponent eai; - private final GlowExtMciComponent extMci; - */ - - /** - * 1. 대외 MCI 호출 (타행, 금융결제원 등 외부 기관) - * 가이드 2.2.2에 명시된 필수 파라미터(기관코드, 종별코드, 업무코드, 거래코드)를 모두 포함합니다. - */ - /* - public Transfer callExtMci(String itrfId, String frbuCd, String cmouDutjCd, String cmouCssfCd, String cmouTraCd, S body, Class resBody) { - - // 1. 공통 헤더 생성 - CommonHeader header = CommonHeaderFactory.createRequestHeader(itrfId); - - // 2. 대외 전용 필수 코드 세팅 로직 (프레임워크 내부 스펙에 맞게 가공) - // (예: 헤더에 해당 속성들을 주입하거나 Transfer 객체에 싣는 과정 추가) - - // 3. Transfer 객체 빌드 - Transfer req = Transfer.builder() - .header(header) - .body(body) - .build(); - - // 4. 대외 MCI 컴포넌트를 통해 최종 전송 - return extMci.call(req, resBody); - } - */ - - /** - * 2. EAI 호출 (대내망 중계기) - * 가이드 2.3에 명시된 대로 수신서비스 ID 없이 인터페이스 ID(itrfId)만 필수로 받습니다. - */ - /* - public Transfer callEai(String itrfId, S body, Class resBody) { - - // 1. EAI는 인터페이스 ID만으로 심플하게 헤더 생성 - CommonHeader header = CommonHeaderFactory.createRequestHeader(itrfId); - - // 2. Transfer 객체 빌드 - Transfer req = Transfer.builder() - .header(header) - .body(body) - .build(); - - // 3. EAI 컴포넌트를 통해 최종 전송 - return eai.call(req, resBody); - } - */ - - /** - * 3. 대내 MCI 호출 (사내 시스템 간 통신) - */ - /* - public Transfer callMci(String itrfId, S body, Class resBody) { - - CommonHeader header = CommonHeaderFactory.createRequestHeader(itrfId); - - Transfer req = Transfer.builder() - .header(header) - .body(body) - .build(); - - return mci.call(req, resBody); - } - */ -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/dto/SampleGlowMessage.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/dto/SampleGlowMessage.java deleted file mode 100644 index 84439e91c..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/dto/SampleGlowMessage.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.shinhanlife.dat.lib.integration.dto; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import java.util.List; - -// TODO: 실제 Glow Framework 의존성이 추가되면 아래 주석을 풀고 사용하세요! -// import io.shinhanlife.glow.communication.annotation.GlowMciFieldInfo; - -/** - * [대외 MCI 연동용 DTO 표준 템플릿] - * Glow Framework 개발표준정의서(2.2.1 IO 작성) 규칙을 100% 준수한 샘플입니다. - * 새로운 대외 통신 전문을 만들 때 이 파일을 복사해서 필드명과 길이만 수정하여 사용하세요. - */ -/** - * @package io.shinhanlife.dat.lib.integration.dto - * @className SampleGlowMessage - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Getter -@Builder -@AllArgsConstructor -@NoArgsConstructor(access = AccessLevel.PUBLIC) // [규칙 1] Reflection을 위한 기본 생성자 필수 (public 유지) -public class SampleGlowMessage { - - // [규칙 2] @GlowMciFieldInfo 선언 필수 (order: 순서) - // @GlowMciFieldInfo(order = 1) - private MessageHeader header; - - // [규칙 2] @GlowMciFieldInfo 선언 필수 (order: 순서) - // @GlowMciFieldInfo(order = 2) - private List msgDtdvValu; // 다건(List) 본문 데이터 - - - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor(access = AccessLevel.PUBLIC) - public static class MessageHeader { - - // [규칙 2] 단건 필드의 경우 length 필수 입력 (EIMS 길이와 일치해야 함) - // @GlowMciFieldInfo(order = 1, length = 1) - private String msgTnsmTypeCd; - - // @GlowMciFieldInfo(order = 2, length = 8) - private int msdvLen; - - // [규칙 3] 다건(List) 건수 필드의 경우, target 속성에 대상 변수명("msgDtdvValu") 필수 기입! - // @GlowMciFieldInfo(order = 3, length = 2, target = "msgDtdvValu") - private int msgRpttCc; - } - - - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor(access = AccessLevel.PUBLIC) - public static class MessageBody { - - // @GlowMciFieldInfo(order = 1, length = 8) - private String msgCd; - - // @GlowMciFieldInfo(order = 2, length = 1) - private String msgPrnAttrCd; - - // @GlowMciFieldInfo(order = 3, length = 200) - private String msgCt; - - // @GlowMciFieldInfo(order = 4, length = 200) - private String anxMsgCt; - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/eai/component/AxhubEaiComponent.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/eai/component/AxhubEaiComponent.java deleted file mode 100644 index 6b489bd86..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/eai/component/AxhubEaiComponent.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.shinhanlife.dat.lib.integration.eai.component; - -import io.shinhanlife.dat.lib.config.GlowCommunicationProperties; -import io.shinhanlife.glow.communication.dto.CommonHeader; -import io.shinhanlife.glow.communication.dto.Transfer; -import io.shinhanlife.glow.communication.module.eai.component.GlowEaiComponent; -import io.shinhanlife.glow.communication.util.CommonHeaderFactory; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 신한라이프 내부 Glow 표준 EAI 컴포넌트 어댑터 (AXHUB) - */ -@Slf4j -@Component -@RequiredArgsConstructor - -/** - * @package io.shinhanlife.dat.lib.integration.eai.component - * @className AxhubEaiComponent - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public class AxhubEaiComponent { - - @SuppressWarnings("rawtypes") - private final GlowEaiComponent eai; - private final GlowCommunicationProperties communicationProperties; - - @SuppressWarnings("unchecked") - private Transfer syncEai(Transfer request) { - // LOG 저장 (AXHUB 방식 로깅) - CommonHeader reqHeader = (CommonHeader) request.getHeader(); - log.info("[AxhubEaiComponent] {} EAI 호출시작 (수신서비스: {})", reqHeader.getItrfId(), reqHeader.getRcvSvcId()); - - Transfer response = (Transfer) eai.sync(request); - - log.info("[AxhubEaiComponent] {} EAI 호출종료 (수신서비스: {})", reqHeader.getItrfId(), reqHeader.getRcvSvcId()); - - return response; - } - - /** - * EAI 호출 - */ - public Transfer call(String itrfId, String rcvSvcId, I inputDto) throws Exception { - CommonHeader header = CommonHeaderFactory.createRequestHeader(itrfId, rcvSvcId); - - Transfer request = Transfer.builder() - .header(header) - .body(inputDto) - .build(); - - return syncEai(request); - } - - /** - * EAI 호출 (응답 타입 명시) - */ - @SuppressWarnings("unchecked") - public Transfer call(String itrfId, String rcvSvcId, I inputDto, Class resBodyClass) throws Exception { - CommonHeader header = CommonHeaderFactory.createRequestHeader(itrfId, rcvSvcId); - - Transfer request = Transfer.builder() - .header(header) - .body(inputDto) - .resBodyClass((Class) (Class) resBodyClass) - .build(); - - return syncEai(request); - } - - /** - * EAI 호출 (rcvSvcId 없는 경우) - */ - public Transfer call(String itrfId, I inputDTO, Class resBodyClass) throws Exception { - String className = inputDTO.getClass().getSimpleName(); - String rcvSvcId = className.replace("_I", ""); - return call(itrfId, rcvSvcId, inputDTO, resBodyClass); - } - - /** - * EAI 호출 (Response body class와 rcvSvcId 없는 경우) - */ - public Transfer call(String itrfId, I inputDTO) throws Exception { - String className = inputDTO.getClass().getSimpleName(); - String rcvSvcId = className.replace("_I", ""); - return call(itrfId, rcvSvcId, inputDTO); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpComponent.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpComponent.java deleted file mode 100644 index 1f7cb328c..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpComponent.java +++ /dev/null @@ -1,150 +0,0 @@ -package io.shinhanlife.dat.lib.integration.http.component; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dat.lib.config.GlowCommunicationProperties; -import io.shinhanlife.dat.lib.mcp.McpRequestHeaderContext; -import io.shinhanlife.dat.lib.mcp.McpRequestHeaders; -import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent; -import io.shinhanlife.glow.communication.module.http.dto.HttpBody; -import io.shinhanlife.glow.communication.module.http.dto.HttpHeader; -import io.shinhanlife.glow.communication.module.http.dto.HttpTransfer; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -/** - * Tool Pod outbound HTTP component using the Glow HTTP client. - * Target URL, HTTP method, content type and Pod-to-Pod behaviour are resolved from - * {@code glow.communication.http.api-list}; business source code does not own endpoint values. - */ -@Slf4j -@Component -@RequiredArgsConstructor -public class AxhubHttpComponent { - - private static final String ANONYMOUS_REQUEST = "AXHUB-TOOL"; - - private final GlowHttpComponent http; - private final ObjectMapper json; - private final GlowCommunicationProperties communicationProperties; - private final AxhubHttpProperties properties; - - /** Calls the exact URL configured for the API name. */ - public R call(String apiName, T inputDto, Class responseBodyClass) { - return call(apiName, "", inputDto, responseBodyClass, 0); - } - - /** Calls the configured URL with an optional resource suffix. */ - public R call(String apiName, String uri, T inputDto, Class responseBodyClass) { - return call(apiName, uri, inputDto, responseBodyClass, 0); - } - - public R call(String apiName, String uri, T inputDto, Class responseBodyClass, int timeout) { - AxhubHttpProperties.ApiDefinition api = resolveApi(apiName); - return execute(api, uri, inputDto, responseBodyClass, timeout); - } - - /** Calls the configured URL only when the target is marked as a business Pod. */ - public R callBizPod(String apiName, T inputDto, Class responseBodyClass) { - AxhubHttpProperties.ApiDefinition api = resolveApi(apiName); - if (!api.bizPod()) { - throw new IllegalArgumentException("Configured API is not a business Pod: " + apiName); - } - return execute(api, "", inputDto, responseBodyClass, 0); - } - - private R execute(AxhubHttpProperties.ApiDefinition api, String uri, T inputDto, - Class responseBodyClass, int timeout) { - HttpHeader header = createHeader(api, timeout); - String requestUri = joinPath(api.url(), uri); - HttpTransfer request = HttpTransfer.http() - .header(header) - .domain(api.domain()) - .uri(requestUri) - .method(api.method()) - .contentType(contentType(api)) - .responseEntity(responseBodyClass) - .body(inputDto) - .build(); - - log.info("[AxhubHttpComponent] Glow HTTP call. apiName={}, method={}, uri={}", - api.name(), api.method(), requestUri); - ResponseEntity response = http.sync(request); - return convertResponse(response.getBody(), responseBodyClass); - } - - private AxhubHttpProperties.ApiDefinition resolveApi(String apiName) { - return properties.getApiList().stream() - .filter(api -> apiName != null && apiName.equals(api.name())) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("No HTTP API configuration for name: " + apiName)); - } - - private HttpHeader createHeader(AxhubHttpProperties.ApiDefinition api, int timeout) { - HttpHeader header = new HttpHeader(); - if (api.bizPod()) { - header.set("X-POD-TO-POD", "true"); - } - McpRequestHeaders inbound = McpRequestHeaderContext.current(); - if (inbound == null) { - header.set("X-ANONYMOUS-REQ", ANONYMOUS_REQUEST); - } else { - putIfPresent(header, "X-Guid", inbound.guid()); - putIfPresent(header, "X-Praf-No", inbound.prafNo()); - putIfPresent(header, "X-Request-Id", inbound.requestId()); - putIfPresent(header, "X-Request-Time", inbound.requestTime()); - putIfPresent(header, "X-Vrtl-Praf-No", inbound.vrtlPrafNo()); - putIfPresent(header, "X-App-Code", inbound.appCode()); - putIfPresent(header, "X-Project-Code", inbound.projectCode()); - putIfPresent(header, "X-User-Ip", inbound.userIp()); - putIfPresent(header, "X-Caller-Ip", inbound.callerIp()); - putIfPresent(header, "X-Caller-Host", inbound.callerHost()); - putIfPresent(header, "X-Channel", inbound.channel()); - putIfPresent(header, "X-Agent-Id", inbound.agentId()); - putIfPresent(header, "mcp-session-id", inbound.mcpSessionId()); - } - header.setReadTimeout(timeout == 0 ? defaultReadTimeout() : timeout); - return header; - } - - private int defaultReadTimeout() { - return communicationProperties == null || communicationProperties.getHttp() == null - ? 0 : communicationProperties.getHttp().getReadTimeout(); - } - - private MediaType contentType(AxhubHttpProperties.ApiDefinition api) { - return api.contentType() == null || api.contentType().isBlank() - ? MediaType.APPLICATION_JSON - : MediaType.parseMediaType(api.contentType()); - } - - private R convertResponse(HttpBody responseBody, Class responseBodyClass) { - String content = responseBody == null ? null : responseBody.content(); - if (responseBodyClass == String.class) { - return responseBodyClass.cast(content); - } - try { - return json.readValue(content, responseBodyClass); - } catch (JsonProcessingException e) { - throw new IllegalStateException("Failed to convert HTTP response to " + responseBodyClass.getSimpleName(), e); - } - } - - private void putIfPresent(HttpHeader header, String name, String value) { - if (value != null && !value.isBlank()) { - header.set(name, value); - } - } - - private String joinPath(String configuredUrl, String suffix) { - String left = configuredUrl == null ? "" : configuredUrl.replaceAll("/+$", ""); - if (suffix == null || suffix.isBlank()) { - return left.isEmpty() ? "/" : left; - } - String right = suffix.startsWith("/") ? suffix : "/" + suffix; - return left + right; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpProperties.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpProperties.java deleted file mode 100644 index 1b7bd29a4..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpProperties.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.shinhanlife.dat.lib.integration.http.component; - -import java.util.ArrayList; -import java.util.List; -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Component; - -/** - * Glow HTTP target catalog. - * - *

Each target follows the ShinhanLife standard: name, domain, url, method, - * content-type, and biz-pod. Target-specific values belong in application-glow*.yml.

- */ -@Getter -@Setter -@Component -@ConfigurationProperties(prefix = "glow.communication.http") -public class AxhubHttpProperties { - - private List apiList = new ArrayList<>(); - - public record ApiDefinition( - String name, - String domain, - String url, - HttpMethod method, - String contentType, - boolean bizPod - ) { - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/component/AxhubMciComponent.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/component/AxhubMciComponent.java deleted file mode 100644 index 13f82ca96..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/component/AxhubMciComponent.java +++ /dev/null @@ -1,170 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.component; - -import io.shinhanlife.dat.lib.session.dto.SessionDto; -import io.shinhanlife.dat.lib.util.SessionUtil; -import io.shinhanlife.dat.lib.config.GlowCommunicationProperties; -import io.shinhanlife.glow.communication.dto.CommonHeader; -import io.shinhanlife.glow.communication.dto.HeaderDefaults; -import io.shinhanlife.glow.communication.dto.Transfer; -import io.shinhanlife.glow.communication.module.mci.component.GlowMciComponent; -import io.shinhanlife.glow.communication.util.CommonHeaderFactory; -import io.shinhanlife.dat.lib.integration.mci.enums.IndvCtinRoleTyp; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.Map; - -/** - * 신한라이프 내부 Glow 표준 컴포넌트 어댑터 (AXHUB) - */ -@Slf4j -@Component -@RequiredArgsConstructor - -/** - * @package io.shinhanlife.dat.lib.integration.mci.component - * @className AxhubMciComponent - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public class AxhubMciComponent { - - @SuppressWarnings("rawtypes") - private final GlowMciComponent mci; - private final GlowCommunicationProperties communicationProperties; - - /** 전문생성채널유형코드 : 1 (채널계) */ - private final static String TGRM_CREA_CHNN_TYPE_CD_1 = "1"; - private static final String SUCO_UNBL_CODE = "NNB00147"; // 청약불가 - - /** - * 전문 Common Header 생성 - * @param itrfName 인터페이스Id - * @param rcvSvcId 수신서비스Id - * @return Map - */ - private Map createCommonHeaderMap(String itrfName, String rcvSvcId) { - SessionDto sessionDto = SessionUtil.getSession(); - Map commonHeaderMap = new HashMap<>(); - - commonHeaderMap.put(HeaderDefaults.ITRF_ID, itrfName); - commonHeaderMap.put(HeaderDefaults.RCV_SVC_ID, rcvSvcId); - - if (sessionDto != null) { - commonHeaderMap.put(HeaderDefaults.STR_YMD, sessionDto.getStrYmd()); - commonHeaderMap.put(HeaderDefaults.ACNT_OGNZ_NO, sessionDto.getBrafNo()); - commonHeaderMap.put(HeaderDefaults.PSMR_ASRT_CD, sessionDto.getPsmrAsrtCd()); - commonHeaderMap.put(HeaderDefaults.SBSN_RULP_ASRT_CD, sessionDto.getSbsnRulpAsrtCd()); - commonHeaderMap.put(HeaderDefaults.BSDU_CD, sessionDto.getBsduCd()); - commonHeaderMap.put(HeaderDefaults.BSQU_CD, sessionDto.getBsquCd()); - commonHeaderMap.put(HeaderDefaults.OGNZ_ASRT_CD, sessionDto.getOgnzAsrtCd()); - commonHeaderMap.put(HeaderDefaults.OGNZ_LEVE_CD, sessionDto.getOgnzLeveCd()); - commonHeaderMap.put(HeaderDefaults.SCRN_ID, sessionDto.getPrgrId()); - commonHeaderMap.put(HeaderDefaults.DRTM_CD, sessionDto.getPrafDutyCd()); - commonHeaderMap.put(HeaderDefaults.USER_ID, sessionDto.getPrafNo()); - } - - commonHeaderMap.put(HeaderDefaults.INDV_CTIN_ROLE_CD, IndvCtinRoleTyp.CD_Z99.getCode()); - commonHeaderMap.put(HeaderDefaults.TGRM_CREA_CHNN_TYPE_CD, TGRM_CREA_CHNN_TYPE_CD_1); - - if (communicationProperties != null && communicationProperties.getCommon() != null) { - commonHeaderMap.put(HeaderDefaults.ENVR_TYPE_CD, communicationProperties.getCommon().getEnvType()); - } - - return commonHeaderMap; - } - - @SuppressWarnings("unchecked") - private Transfer syncMci(Transfer request) { - // LOG 저장 (AXHUB 방식 로깅) - CommonHeader reqHeader = (CommonHeader) request.getHeader(); - log.info("[AxhubMciComponent] {} MCI 호출시작 (수신서비스: {})", reqHeader.getItrfId(), reqHeader.getRcvSvcId()); - - Transfer response = (Transfer) mci.sync(request); - - log.info("[AxhubMciComponent] {} MCI 호출종료 (수신서비스: {})", reqHeader.getItrfId(), reqHeader.getRcvSvcId()); - - if (response != null && response.getHeader() != null) { - CommonHeader resHeader = (CommonHeader) response.getHeader(); - String tgrmDalRsltCd = resHeader.getTgrmDalRsltCd(); - // TODO 추가 메시지 처리 및 오류 코드 제어 로직 - } - - return response; - } - - public Transfer callTo(String itrfName, String rcvSvcId, I inputDto) { - Map commonHeaderMap = createCommonHeaderMap(itrfName, rcvSvcId); - CommonHeader header = CommonHeaderFactory.createRequestHeader(commonHeaderMap); - - Transfer request = Transfer.builder() - .header(header) - .body(inputDto) - .build(); - - return syncMci(request); - } - - /** - * 대내 mci 호출 - * @param itrfName 인터페이스Id - * @param rcvSvcId 수신서비스Id - * @param inputDto inputDto - * @param resBodyClass resBodyClass - * @return Transfer - * @param resBodyClass 제너릭 - * @param inputDto 제너릭 - */ - @SuppressWarnings("unchecked") - public Transfer callTo(String itrfName, String rcvSvcId, I inputDto, Class resBodyClass) { - Map commonHeaderMap = createCommonHeaderMap(itrfName, rcvSvcId); - CommonHeader header = CommonHeaderFactory.createRequestHeader(commonHeaderMap); - - Transfer request = Transfer.builder() - .header(header) - .body(inputDto) - .resBodyClass((Class) (Class) resBodyClass) - .build(); - - return syncMci(request); - } - - /** - * 대내 mci 호출 (rcvSvcId 없는 경우) - * @param itrfName 인터페이스Id - * @param inputDTO 수신서비스Id (클래스명 대체) - * @param resBodyClass resBodyClass - * @return Transfer - * @param resBodyClass 제너릭 - * @param inputDto 제너릭 - */ - public Transfer callTo(String itrfName, I inputDTO, Class resBodyClass) { - String className = inputDTO.getClass().getSimpleName(); - String rcvSvcId = className.replace("_I", ""); - return callTo(itrfName, rcvSvcId, inputDTO, resBodyClass); - } - - /** - * 대내 mci 호출 (Response body class와 rcvSvcId 없는 경우) - * @param itrfName 인터페이스Id - * @param inputDTO 수신서비스Id (클래스명 대체) - * @return Transfer - * @param resBodyClass 제너릭 - * @param inputDto 제너릭 - */ - public Transfer callTo(String itrfName, I inputDTO) { - String className = inputDTO.getClass().getSimpleName(); - String rcvSvcId = className.replace("_I", ""); - return callTo(itrfName, rcvSvcId, inputDTO); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/config/GlowMockConfig.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/config/GlowMockConfig.java deleted file mode 100644 index 42d598d26..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/config/GlowMockConfig.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.config; - -import io.shinhanlife.glow.communication.module.mci.component.GlowMciComponent; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * TODO: 실제 Glow Framework 의존성이 추가되어 io.shinhanlife.glow 패키지가 - * ComponentScan에 잡히게 되면 이 설정 클래스는 삭제하세요. - */ -@Configuration - -/** - * @package io.shinhanlife.dat.lib.integration.mci.config - * @className GlowMockConfig - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public class GlowMockConfig { - - @Bean - @SuppressWarnings("rawtypes") - public GlowMciComponent glowMciComponent() { - return new GlowMciComponent(); - } - - @Bean - @SuppressWarnings("rawtypes") - public io.shinhanlife.glow.communication.module.eai.component.GlowEaiComponent glowEaiComponent() { - return new io.shinhanlife.glow.communication.module.eai.component.GlowEaiComponent(); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/config/ShinhanIntegrationProperties.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/config/ShinhanIntegrationProperties.java deleted file mode 100644 index 3e2d982c3..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/config/ShinhanIntegrationProperties.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.config; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -/** - * @package io.shinhanlife.dat.lib.integration.mci.config - * @className ShinhanIntegrationProperties - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Getter -@Setter -@ConfigurationProperties(prefix = "shinhan.integration") -public class ShinhanIntegrationProperties { - - /** - * 환경유형코드: 운영(R), 테스트(T), 개발(D) - */ - private String envrTypeCd = "D"; - - private ServerInfo eai = new ServerInfo(); - private ServerInfo internalMci = new ServerInfo(); - - @Getter - @Setter - public static class ServerInfo { - private String url; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/MciRequestWrapper.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/MciRequestWrapper.java deleted file mode 100644 index f1393a3bd..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/MciRequestWrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -/** - * @package io.shinhanlife.dat.lib.integration.mci.dto - * @className MciRequestWrapper - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class MciRequestWrapper { - private ShinhanCommonHeaderDto tgrmCmnnhddValu; - - @JsonUnwrapped - private T body; -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/MciResponseWrapper.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/MciResponseWrapper.java deleted file mode 100644 index e584c00e3..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/MciResponseWrapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.dto; - -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @package io.shinhanlife.dat.lib.integration.mci.dto - * @className MciResponseWrapper - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class MciResponseWrapper { - private ShinhanCommonHeaderDto tgrmCmnnhddValu; - - @JsonUnwrapped - private T body; -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/OlCommonHeaderDto.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/OlCommonHeaderDto.java deleted file mode 100644 index 5845b5c53..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/OlCommonHeaderDto.java +++ /dev/null @@ -1,90 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import java.util.List; - -/** - * @package io.shinhanlife.dat.lib.integration.mci.dto - * @className OlCommonHeaderDto - * @description AX HUB 시스템 처리 클래스 - OL(구 오렌지라이프) 공통 헤더 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class OlCommonHeaderDto { - private String custNm; // 고객명 - private String custRrn; // 고객 주민등록번호 - private String custNo; // 고객번호 - private String rcevNo; // 접수번호 - private String pono; // 증권번호 - private String scrNm; // 화면명 - private String scrId; // 화면ID - private String lginDttm; // 사용자가 로그인한 접속일시 - private String lginIpAddr; // 사용자가 접속한 IP 주소 - private String userNm; // 사용자 이름(한글) - private String userEngNm; // 사용자 영문이름 - private String userId; // 사용자 ID(AD ID) - private String userNo; // 사용자번호 - private String deptCd; // 사용자조직 코드 - private String salsDvCd; // 영업본부코드 - private String salsBoCd; // 영업지점코드 - private String uppDeptCd; // 상위조직코드 - private String prcsrUserId; // 처리자 ID(AD ID) - private String prcsrUserNo; // 처리지번호 - private String prcsrDeptCd; // 처리지조직 코드 - private String prcsrDvCd; // 처리지 영업본부코드 - private String prcsrBoCd; // 처리지 영업지점코드 - private String prcsrUppDeptCd; // 부서코드 - private String sysCd; // 요청이 들어온 시스템을 표시 - private String reqtSvcNm; // 요청하는 서비스 모듈명 - private String reqtMthdNm; // 요청하는 메소드명 - private String reqtVoNm; // 요청메소드에 전달할 값을 담는 VO명 - private String scrButnFuncClssCd; // 화면에서 버튼 별 이벤트 구분을 위한 구분코드 - private String scrGriCnt; // 화면 그리드 개수 - private List pageList; // 페이징 리스트 (L2 반복) - private String reqtDttm; // 요청일시 - private String crdtInfoIcluFlg; // 신용정보포함여부(Y,N) - private String crdtInfoDataChgTypCd; // 업무내역별 식별코드 부여 - private String crdtInfoIdfInEngAbbrNm; // 신용정보식별영문약어명 - private String crdtInfoIdfnSysCd; // 신용정보식별시스템코드 - private String scrButnNm; // 화면버튼명 - private String msgCnt; // 메시지 개수 - private List msgList; // 메시지 리스트 (L2 반복) - private String respDttm; // 응답일시 - private String svcRunNm; // 거래별로 유일한 ServiceExecutionID - private String stdate; // 기준일자 - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class OlPageDto { - private String pageSrno; // 페이지 인덱스값 (L3) - private String pageInqCnt; // 한페이지에 조회될 건수 (L3) - private String nxtButnNm; // 다음버튼ID (L3) - private String nxtButnEnbFlg; // 다음버튼 활성여부 (L3) - } - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class OlMsgDto { - private String msgNo; // 서버 측에서 세팅한 정상/에러 메시지코드 (L3) - private String msgTypCd; // 메시지유형코드 (L3) - private String msgNm; // 메시지코드의 내용 (L3) - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanCommonHeaderDto.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanCommonHeaderDto.java deleted file mode 100644 index aa77dd1bb..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanCommonHeaderDto.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @package io.shinhanlife.dat.lib.integration.mci.dto - * @className ShinhanCommonHeaderDto - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ShinhanCommonHeaderDto { - - private String tgrmLencn; // 전문길이 - private String glbId; // 글로벌ID (전사공통키) - private String pgrsSriaNo; // 진행일련번호 - private String tgrmVrsnInfoValu; // 전문버전정보값 - private String tgrmEncrYn; // 전문암호화여부 - private String gpcpCd; // 그룹사코드 - private String appliDutjCd; // 어플리케이션업무코드 - private String appliDtptDutjCd; // 어플리케이션상세업무코드 - private String frbuCd; // 대외기관코드 - private String cmouDutjCd; // 대외업무코드 - private String cmouCssfCd; // 대외종별코드 - private String cmouTraCd; // 대외거래코드 - private String rcvSvcId; // 수신서비스ID - private String rsltRcvSvcId; // 결과수신서비스ID - private String tgrmCreaChnnTypeCd; // 전문생성채널유형코드 - - private String lnggDvsnCd; // 언어구분코드 - private String simulTraYn; // 시뮬레이션거래여부 - private String itrIfId; // 인터페이스ID - private String reqRspnScCd; // 요청응답구분코드 - private String tnsmTypeCd; // 전송유형코드 - private String envrTypeCd; // 환경유형코드 - private String inqrTraTypeCd; // 조회거래유형코드 - private String reqTgrmTnsmDtptDt; // 요청전문전송상세일시 - private String strYmd; // 기준일자 - private String scrnId; // 화면ID - private String scrnBtnId; // 화면버튼ID - - private String userIpAddr; // 사용자IP주소 - private String drtmCd; // 부서코드 - private String userId; // 사용자ID - private String indvCtinRoleCd; // 개인신용정보역할코드 - private String acntOgnzNo; // 경리조직번호 - private String rspnTgrmTnsmDtptDt; // 응답전문전송상세일시 - private String tgrmDalRsltCd; // 전문처리결과코드 - private String ognzAsrtCd; // 조직분류코드 - private String ognzLeveCd; // 조직레벨코드 - private String psmrAsrtCd; // 인사조직분류코드 - private String sbsnRulpAsrtCd; // 영업규정분류코드 - private String bsduCd; // 영업지국코드 - private String bsquCd; // 영업자격코드 - private String linkPrafDutyCd; // 연계인사직책코드 - private String indvInfoLogWritYn; // 개인정보로그작성여부 - private String prepImhdNm; // 예비항목명 -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanMessageDto.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanMessageDto.java deleted file mode 100644 index b022a391f..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanMessageDto.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @package io.shinhanlife.dat.lib.integration.mci.dto - * @className ShinhanMessageDto - * @description AX HUB 시스템 처리 클래스 - MCI 전문 메시지부 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ShinhanMessageDto { - - private MsgHddvValu msgHddvValu; // 메시지헤더부값 - private MsgDtdvValu msgDtdvValu; // 메시지데이터부값 - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class MsgHddvValu { - private String msgTnsmTypeCd; // 메시지전송유형코드 - private Integer msdvLencn; // 메시지부길이 - private Integer msgRpttCc; // 메시지반복건수 - } - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class MsgDtdvValu { - private String msgCd; // 메시지코드 - private String msgPrnAttrCd; // 메시지출력속성코드 - private String msgCt; // 메시지내용 - private String anxMsgCt; // 부가메시지내용 - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanTelegramWrapper.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanTelegramWrapper.java deleted file mode 100644 index 3ec9598b8..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/ShinhanTelegramWrapper.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -/** - * @package io.shinhanlife.dat.lib.integration.mci.dto - * @className ShinhanTelegramWrapper - * @description AX HUB 시스템 처리 클래스 - MCI 전문 전체 래퍼 (공통헤더부 + 메시지부 + 데이터부) - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ShinhanTelegramWrapper { - - // 1. 공통 헤더부 - private ShinhanCommonHeaderDto tgrmCmnnhddValu; - - // 2. 메시지부 - private ShinhanMessageDto tgrmMsdvValu; - - // 3. 데이터부 (비즈니스마다 다름, JsonUnwrapped로 평탄화하거나 객체 자체로 유지 가능. 여기서는 객체 유지) - private T tgrmDtdvValu; -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/SlCommonHeaderDto.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/SlCommonHeaderDto.java deleted file mode 100644 index be9ebc126..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/dto/SlCommonHeaderDto.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * @package io.shinhanlife.dat.lib.integration.mci.dto - * @className SlCommonHeaderDto - * @description AX HUB 시스템 처리 클래스 - SL(신한라이프) 표준 헤더 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SlCommonHeaderDto { - private String length; // 전문길이 - private SlGlobalId globalId; // 글로벌ID - private String headerVer; // 전문헤더버전 - private String encodeFlag; // 전문암호화여부 - - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class SlGlobalId { - private String writeDate; // 전문작성일 (8) - private String sysCd; // 생성시스템명 (8) - private String typeCd; // 구분코드 (2) - private String detailCd; // 세부업무코드 (4) - private String seqNo; // 채번번호 (8) - private String step; // 진행상황 (2) - } - - private String groupCoCd; // 그룹사코드 - private String instCd; // 기관코드 - private String applCd; // 업무코드 - private String kindCd; // 종별코드 - private String txCd; // 거래코드 - private String pfmAppName; // 어플리케이션 명 - private String pfmSvcName; // 서비스 명 - private String pfmFnName; // 오퍼레이션 명 - private String systemCd; // 생성시스템구분 - private String trFlag; // 요청응답구분 - private String syncFlag; // 동기구분 - private String envrFlag; // 환경구분 - private String crudFlag; // 조회거래구분 - private String sendTime; // 전문전송일시 - private String screenId; // 화면ID - private String clntIp; // Client IP - private String orgCd; // 부서(지점)코드 - private String userId; // 사용자 사번(아이디) - private String indvCrdtInfo; // 개인신용정보역할코드 - private String acntOgnzNo; // 경리조직번호 - private String ttiFlag; // TimeOut사용 - private String ttiStartTm; // 최초시작시간 - private String ttiKeepTm; // 유지시간초수 - private String outMsgTm; // 응답전문작성일시 - private String resType; // 처리결과 - private String resCode; // 응답코드 - private String resBascMsg; // 응답기본내역 - private String msgType; // 메시지 유형 - private String rcvSvcCd; // 수신 서비스 Code - private String rsltRcvSvcCd; // 결과수신 서비스 Code - private String realSvcCd; // Real 서비스 Code - private String ognzAsrtCd; // 조직분류코드 - private String ognzLeveCd; // 조직레벨구분코드 - private String psmrAsrtCd; // 인사조직분류코드 - private String sbsnRulpAsrtCd; // 영업규정분류코드 - private String bsduCd; // 영업지국코드 - private String bsquCd; // 영업자격코드 - private String linkPrafDutyCd; // 직책코드 - private String temp; // 예비 필드 -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/enums/IndvCtinRoleTyp.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/enums/IndvCtinRoleTyp.java deleted file mode 100644 index 7beb9b4e0..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/integration/mci/enums/IndvCtinRoleTyp.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.enums; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor - -/** - * @package io.shinhanlife.dat.lib.integration.mci.enums - * @className IndvCtinRoleTyp - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public enum IndvCtinRoleTyp { - CD_Z99("Z99"); - - private final String code; -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestAnnotations.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestAnnotations.java deleted file mode 100644 index 0a1ca54c9..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestAnnotations.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.shinhanlife.dat.lib.manifest; - -/** Behaviour hints exposed by the Tool Service manifest. */ -public record ToolManifestAnnotations( - String title, - boolean readOnlyHint, - boolean destructiveHint, - boolean idempotentHint, - boolean openWorldHint) { -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestItem.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestItem.java deleted file mode 100644 index f7ea069e0..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestItem.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.lib.manifest; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Map; - -/** One MCP Tool declaration published by a Tool Service. */ -public record ToolManifestItem( - String name, - String endpoint, - String title, - String description, - Map inputSchema, - Map outputSchema, - ToolManifestAnnotations annotations, - @JsonProperty("_meta") ToolManifestMeta meta) { -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestMeta.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestMeta.java deleted file mode 100644 index abec6fd17..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestMeta.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.dat.lib.manifest; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; - -/** Operational metadata exposed by the Tool Service manifest. */ -public record ToolManifestMeta( - String version, - long timeoutMillis, - int retryMaxAttempts, - boolean enabled, - List exampleQueries, - List tags, - String legacyInterfaceId, - List requiredEnvKeys, - String ownerOrg, - @JsonProperty("when_to_use") String whenToUse, - @JsonProperty("when_not_to_use") String whenNotToUse, - @JsonProperty("io_limits") String ioLimits) { -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestResponse.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestResponse.java deleted file mode 100644 index bda03bab9..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.shinhanlife.dat.lib.manifest; - -import java.util.List; - -/** Top-level response for GET /tool-manifest. */ -public record ToolManifestResponse(String bundleId, String revision, List tools) { -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestService.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestService.java deleted file mode 100644 index f7d586320..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolManifestService.java +++ /dev/null @@ -1,239 +0,0 @@ -package io.shinhanlife.dat.lib.manifest; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dat.lib.config.McpProperties; -import io.shinhanlife.dat.mcc.dto.ToolMetadata; -import io.shinhanlife.dat.lib.mcp.ToolRegistryHeartbeatSender; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Supplier; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** Builds the Tool Service owned manifest consumed by the MCP server. */ -@Service -public class ToolManifestService { - - private static final long DEFAULT_TIMEOUT_MILLIS = 5000L; - private static final int DEFAULT_RETRY_MAX_ATTEMPTS = 3; - private static final AtomicLong LAST_ISSUED_REVISION = new AtomicLong(); - private final Supplier> toolSupplier; - private final ObjectMapper objectMapper; - private final McpProperties properties; - private String lastFingerprint; - private String lastRevision; - - @Autowired - public ToolManifestService(ToolRegistryHeartbeatSender heartbeatSender, ObjectMapper objectMapper, - McpProperties properties) { - this(heartbeatSender::getAllScannedTools, objectMapper, properties); - } - - ToolManifestService(Supplier> toolSupplier, ObjectMapper objectMapper, - McpProperties properties) { - this.toolSupplier = toolSupplier; - this.objectMapper = objectMapper; - this.properties = properties; - } - - public ToolManifestResponse currentManifest() { - return currentManifest(null); - } - - public ToolManifestResponse currentManifest(String categoryKey) { - String bundleId = properties.getManifest() == null ? null : properties.getManifest().getBundleId(); - if (bundleId == null || bundleId.isBlank()) { - throw new IllegalStateException("mcp.manifest.bundle-id must be configured"); - } - - List tools = toolSupplier.get().stream() - .filter(tool -> isMatchCategory(categoryKey, tool.getCategoryKey(), bundleId)) - .map(this::toManifestItem) - .sorted(Comparator.comparing(ToolManifestItem::name)) - .toList(); - validate(tools); - return new ToolManifestResponse(bundleId, revision(bundleId, tools), tools); - } - - public ToolServiceManifestResponse currentToolServiceManifest() { - McpProperties.Manifest manifest = properties.getManifest(); - String serviceId = manifest == null ? null : manifest.getBundleId(); - List configured = manifest == null - ? List.of() : defaultRoutingList(manifest.getRoutingFunctions()); - List> routingFunctions = configured.stream() - .map(this::toRoutingFunctionEnvelope) - .toList(); - try { - String source = objectMapper.writeValueAsString(Map.of("serviceId", serviceId, - "routingFunctions", routingFunctions)); - return new ToolServiceManifestResponse(serviceId, sha256(source), routingFunctions); - } catch (Exception exception) { - throw new IllegalStateException("Failed to build Tool Service manifest", exception); - } - } - - private boolean isMatchCategory(String requestedCategory, String toolCategory, String bundleId) { - if (requestedCategory == null) { - return true; - } - if (requestedCategory.equalsIgnoreCase(toolCategory)) { - return true; - } - - // Return all tools in this module if the requested category matches the module's bundle ID - if (bundleId != null && - (bundleId.equalsIgnoreCase(requestedCategory) || - bundleId.equalsIgnoreCase("was-" + requestedCategory))) { - return true; - } - - return false; - } - - private ToolManifestItem toManifestItem(ToolMetadata tool) { - String title = tool.getDisplayName() == null || tool.getDisplayName().isBlank() - ? tool.getName() : tool.getDisplayName(); - Map schema = tool.getParametersSchema() == null ? emptySchema() : tool.getParametersSchema(); - return new ToolManifestItem( - tool.getName(), endpoint(tool), title, tool.getDescription(), schema, tool.getOutputSchema(), - new ToolManifestAnnotations(title, isTrue(tool.getReadOnlyHint()), isTrue(tool.getDestructiveHint()), - isTrue(tool.getIdempotentHint()), isTrue(tool.getOpenWorldHint())), - new ToolManifestMeta(defaultString(tool.getSemver(), "1.0.0"), - positiveOrDefault(tool.getTimeoutMillis(), DEFAULT_TIMEOUT_MILLIS), - positiveOrDefault(tool.getRetryMaxAttempts(), DEFAULT_RETRY_MAX_ATTEMPTS), - tool.getEnabled() == null || tool.getEnabled(), - defaultList(tool.getExampleQueries()), defaultList(tool.getTags()), - tool.getMciServiceId(), defaultList(tool.getRequiredEnvKeys()), tool.getOwnerOrg(), - tool.getWhenToUse(), tool.getWhenNotToUse(), tool.getIoLimits())); - } - - private void validate(List tools) { - String namePrefix = properties.getManifest() == null ? null : properties.getManifest().getNamePrefix(); - Set names = new LinkedHashSet<>(); - for (ToolManifestItem tool : tools) { - if (tool.name() == null || tool.name().isBlank()) { - throw new IllegalStateException("Tool manifest contains a blank tool name"); - } - if (!names.add(tool.name())) { - throw new IllegalStateException("Tool manifest contains duplicate tool name: " + tool.name()); - } - if (namePrefix != null && !namePrefix.isBlank() && !tool.name().startsWith(namePrefix)) { - throw new IllegalStateException("Tool name does not match mcp.manifest.name-prefix: " + tool.name()); - } - if (!"object".equals(tool.inputSchema().get("type"))) { - throw new IllegalStateException("Tool inputSchema root type must be object: " + tool.name()); - } - } - } - - private synchronized String revision(String bundleId, List tools) { - String fingerprint = fingerprint(bundleId, tools); - if (!fingerprint.equals(lastFingerprint)) { - long localMinimum = lastRevision == null ? Long.MIN_VALUE : Long.parseLong(lastRevision) + 1; - long nextTimestamp = LAST_ISSUED_REVISION.updateAndGet(previous -> - Math.max(Math.max(System.currentTimeMillis(), localMinimum), previous + 1)); - lastFingerprint = fingerprint; - lastRevision = Long.toString(nextTimestamp); - } - return lastRevision; - } - - private long positiveOrDefault(Long value, long defaultValue) { - return value != null && value > 0 ? value : defaultValue; - } - - private int positiveOrDefault(Integer value, int defaultValue) { - return value != null && value > 0 ? value : defaultValue; - } - - private String fingerprint(String bundleId, List tools) { - try { - return objectMapper.writeValueAsString(Map.of("bundleId", bundleId, "tools", tools)); - } catch (Exception exception) { - throw new IllegalStateException("Failed to build Tool manifest revision source", exception); - } - } - - private String endpoint(ToolMetadata tool) { - if (tool.getEndpoint() != null && !tool.getEndpoint().isBlank()) { - return tool.getEndpoint(); - } - if (tool.getPodUrl() == null || tool.getPodUrl().isBlank()) { - return "/mcp/" + tool.getName(); - } - return tool.getPodUrl().replaceAll("/+$", "") + "/mcp/" + tool.getName(); - } - private Map emptySchema() { - return Map.of("type", "object", "properties", Map.of(), "additionalProperties", false); - } - - private boolean isTrue(Boolean value) { - return Boolean.TRUE.equals(value); - } - - private String defaultString(String value, String fallback) { - return value == null || value.isBlank() ? fallback : value; - } - - private List defaultList(List value) { - return value == null ? List.of() : List.copyOf(value); - } - - private List defaultRoutingList(List value) { - return value == null ? List.of() : List.copyOf(value); - } - - private Map toRoutingFunctionEnvelope(McpProperties.RoutingFunction route) { - Map contract = new LinkedHashMap<>(); - contract.put("schema_version", "3.0"); - contract.put("server_id", route.getServerId()); - contract.put("category_key", route.getCategoryKey()); - contract.put("product_boundary", route.getProductBoundary()); - contract.put("business_domain", route.getBusinessDomain()); - contract.put("business_outcome", route.getBusinessOutcome()); - contract.put("primary_entities", defaultList(route.getPrimaryEntities())); - contract.put("select_if", route.getSelectIf()); - contract.put("reject_if", route.getRejectIf()); - contract.put("capability_index", defaultList(route.getCapabilities())); - contract.put("confusable_servers", defaultList(route.getConfusableServers())); - contract.put("decision_policy", route.getDecisionPolicy()); - - Map function = new LinkedHashMap<>(); - function.put("name", route.getName()); - function.put("description_serialization", routingDescription(route, contract)); - function.put("routing_contract", contract); - function.put("parameters", emptySchema()); - - Map envelope = new LinkedHashMap<>(); - envelope.put("type", "function"); - envelope.put("function", function); - return envelope; - } - - private String routingDescription(McpProperties.RoutingFunction route, Map contract) { - try { - return defaultString(route.getDescriptionSerialization(), String.join(" ", - nonBlank(route.getBusinessOutcome()), nonBlank(route.getDecisionPolicy())).trim()); - } catch (Exception exception) { - throw new IllegalStateException("Failed to serialize routing contract", exception); - } - } - - private String sha256(String value) throws Exception { - byte[] digest = MessageDigest.getInstance("SHA-256").digest(value.getBytes(StandardCharsets.UTF_8)); - StringBuilder result = new StringBuilder(); - for (byte item : digest) result.append(String.format("%02x", item)); - return result.toString(); - } - - private String nonBlank(String value) { - return value == null ? "" : value.trim(); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolServiceManifestResponse.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolServiceManifestResponse.java deleted file mode 100644 index 85f631dde..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/manifest/ToolServiceManifestResponse.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.shinhanlife.dat.lib.manifest; - -import java.util.List; -import java.util.Map; - -/** Server-level routing metadata loaded from tool-service-manifest.yml. */ -public record ToolServiceManifestResponse(String serviceId, String revision, - List> routingFunctions) { -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaderContext.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaderContext.java deleted file mode 100644 index afd4c52da..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaderContext.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -/** Holds optional MCP headers for the lifetime of one HTTP request thread. */ -public final class McpRequestHeaderContext { - private static final ThreadLocal CURRENT_HEADERS = new ThreadLocal<>(); - - private McpRequestHeaderContext() { - } - - public static McpRequestHeaders current() { - return CURRENT_HEADERS.get(); - } - - static void set(McpRequestHeaders headers) { - CURRENT_HEADERS.set(headers); - } - - static void clear() { - CURRENT_HEADERS.remove(); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaderFilter.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaderFilter.java deleted file mode 100644 index 903a0cafe..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaderFilter.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import java.io.IOException; - -import jakarta.servlet.*; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.stereotype.Component; -import io.shinhanlife.dat.lib.session.mci.AxhubSessionService; -import io.shinhanlife.dat.lib.session.dto.SessionDto; -import org.springframework.beans.factory.annotation.Autowired; - -/** Captures optional Tool request headers for an MCP HTTP call and creates the user session. */ -@Component -public class McpRequestHeaderFilter implements Filter { - - @Autowired(required = false) - private AxhubSessionService sessionService; - - @Override - public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) - throws IOException, ServletException { - - HttpServletRequest request = (HttpServletRequest) req; - HttpServletResponse response = (HttpServletResponse) res; - - if (!request.getRequestURI().contains("/mcp")) { - filterChain.doFilter(request, response); - return; - } - - String prafNo = request.getHeader("X-Praf-No"); - - McpRequestHeaderContext.set(new McpRequestHeaders( - request.getHeader("X-Guid"), - prafNo, - request.getHeader("X-Request-Id"), - request.getHeader("X-Request-Time"), - request.getHeader("X-Vrtl-Praf-No"), - request.getHeader("X-App-Code"), - request.getHeader("X-Project-Code"), - request.getHeader("X-User-Ip"), - request.getHeader("X-Caller-Ip"), - request.getHeader("X-Caller-Host"), - request.getHeader("X-Channel"), - request.getHeader("X-Agent-Id"), - request.getHeader("mcp-session-id"))); - - // Fetch session info via MCI using X-Praf-No (only when the service is available in a Tool Pod). - if (sessionService != null && prafNo != null && !prafNo.isEmpty()) { - SessionDto sessionDto = sessionService.fetchUserSession(prafNo); - if (sessionDto != null) { - // Set to request attribute so SessionUtil can find it - request.setAttribute("userInfo", sessionDto); - } - } - - try { - filterChain.doFilter(request, response); - } finally { - McpRequestHeaderContext.clear(); - } - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaders.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaders.java deleted file mode 100644 index 693976916..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpRequestHeaders.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -/** Optional request headers propagated from an MCP HTTP request to a Tool invocation. */ -public record McpRequestHeaders( - String guid, - String prafNo, - String requestId, - String requestTime, - String vrtlPrafNo, - String appCode, - String projectCode, - String userIp, - String callerIp, - String callerHost, - String channel, - String agentId, - String mcpSessionId) { -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpToolExecutionService.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpToolExecutionService.java deleted file mode 100644 index af83630f4..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpToolExecutionService.java +++ /dev/null @@ -1,122 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.json.schema.JsonSchemaValidator.ValidationResponse; -import io.shinhanlife.dat.lib.util.ToolSchemaResolver; -import io.shinhanlife.dat.lib.util.ToolSchemaResolver.ToolSchemaResourceException; -import io.shinhanlife.dat.lib.validation.ToolArgumentSchemaValidator; -import io.shinhanlife.glow.BizException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -/** Executes a cached Tool independently from its HTTP or MCP transport. */ -@Slf4j -@Service -@RequiredArgsConstructor -public class McpToolExecutionService { - - private final McpToolMethodRegistry toolMethodRegistry; - private final ObjectMapper objectMapper; - private final ToolArgumentSchemaValidator toolArgumentSchemaValidator; - private final ToolSchemaResolver toolSchemaResolver; - - public ToolExecutionResult execute(String functionName, McpRequestHeaders requestHeaders, - Map arguments) { - String requestId = requestHeaders == null ? null : requestHeaders.requestId(); - String guid = requestHeaders == null ? null : requestHeaders.guid(); - String mcpSessionId = requestHeaders == null ? null : requestHeaders.mcpSessionId(); - log.info("[Tool] IN - X-Guid: {}, X-Request-Id: {}, tool: {}", guid, requestId, functionName); - - McpToolMethodRegistry.RegisteredTool resolvedTool = toolMethodRegistry.find(functionName); - if (resolvedTool == null) { - return error(404, "TOOL_NOT_FOUND", "Tool not found: " + functionName, requestId); - } - ToolExecutionResult validationFailure = validateInput(resolvedTool, arguments, requestId); - if (validationFailure != null) { - return validationFailure; - } - try { - Object methodResult = invoke(resolvedTool, convertArgument(resolvedTool.method(), arguments)); - ToolExecutionResult outputFailure = validateOutput(resolvedTool, methodResult, requestId); - if (outputFailure != null) { - return outputFailure; - } - Map headers = new HashMap<>(); - if (requestId != null) headers.put("X-Request-Id", requestId); - if (guid != null) headers.put("X-Guid", guid); - if (mcpSessionId != null) headers.put("mcp-session-id", mcpSessionId); - log.info("[Tool] OUT - X-Guid: {}, X-Request-Id: {}, tool: {}", guid, requestId, functionName); - return new ToolExecutionResult(200, methodResult, headers); - } catch (IllegalArgumentException error) { - log.error("[Tool] Tool argument conversion failed. tool={}", functionName, error); - return error(502, "TOOL_ERROR", "Tool execution failed", requestId); - } catch (IllegalAccessException error) { - log.error("[Tool] Tool method is not accessible. tool={}", functionName, error); - return error(502, "TOOL_ERROR", "Tool execution failed", requestId); - } catch (InvocationTargetException error) { - Throwable cause = error.getCause(); - if (cause instanceof BizException businessError) { - log.error("[Tool] Tool business execution failed. tool={}", functionName, businessError); - return error(502, "TOOL_ERROR", "Tool execution failed", requestId); - } - if (cause instanceof RuntimeException runtimeError) { - throw runtimeError; - } - if (cause instanceof Error jvmError) { - throw jvmError; - } - log.error("[Tool] Tool execution failed. tool={}", functionName, error); - return error(502, "TOOL_ERROR", "Tool execution failed", requestId); - } - } - - private ToolExecutionResult validateInput(McpToolMethodRegistry.RegisteredTool tool, - Map arguments, String requestId) { - if (tool.method().getParameterCount() == 0 || Map.class.isAssignableFrom(tool.method().getParameterTypes()[0])) return null; - try { - Map schema = toolSchemaResolver.resolve(tool.annotation(), tool.hint(), tool.method().getParameterTypes()[0]); - ValidationResponse result = toolArgumentSchemaValidator.validate(schema, arguments); - return result.valid() ? null : error(422, "INVALID_PARAM", "Tool arguments do not match the input schema", requestId); - } catch (ToolSchemaResourceException error) { - log.error("[Tool] Input schema resource loading failed. tool={}", tool.annotation().name(), error); - return null; - } - } - - private Object convertArgument(Method method, Map arguments) { - if (method.getParameterCount() == 0 || arguments == null || Map.class.isAssignableFrom(method.getParameterTypes()[0])) return arguments; - return objectMapper.convertValue(arguments, method.getParameterTypes()[0]); - } - - private Object invoke(McpToolMethodRegistry.RegisteredTool tool, Object argument) - throws IllegalAccessException, InvocationTargetException { - return tool.method().getParameterCount() == 0 ? tool.method().invoke(tool.bean()) : tool.method().invoke(tool.bean(), argument); - } - - private ToolExecutionResult validateOutput(McpToolMethodRegistry.RegisteredTool tool, - Object methodResult, String requestId) { - try { - Map outputSchema = toolSchemaResolver.resolveOutput(tool.annotation(), tool.method().getReturnType(), tool.hint()); - if (!outputSchema.isEmpty() && !toolArgumentSchemaValidator.validateValue(outputSchema, methodResult).valid()) { - return error(500, "INVALID_TOOL_RESPONSE", "Tool response does not match its output schema", requestId); - } - } catch (ToolSchemaResourceException error) { - log.error("[Tool] Output schema resource loading failed. tool={}", tool.annotation().name(), error); - } - return null; - } - - private ToolExecutionResult error(int statusCode, String code, String message, String requestId) { - Map body = new HashMap<>(); - body.put("code", code); - body.put("message", message); - body.put("details", Map.of("status", Integer.toString(statusCode))); - if (requestId != null) body.put("request_id", requestId); - return new ToolExecutionResult(statusCode, body, Map.of()); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpToolMethodRegistry.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpToolMethodRegistry.java deleted file mode 100644 index 62c1fef59..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/McpToolMethodRegistry.java +++ /dev/null @@ -1,209 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.lib.config.McpProperties; - -import java.lang.reflect.Method; -import java.util.LinkedHashMap; -import java.util.Map; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springaicommunity.mcp.annotation.McpTool; -import org.springframework.aop.support.AopUtils; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationContext; -import org.springframework.context.event.EventListener; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.stereotype.Component; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; - -/** - * Caches executable {@link McpTool} methods once when a Tool Pod starts. - */ -@Slf4j -@Component -@RequiredArgsConstructor -public class McpToolMethodRegistry { - - private final ApplicationContext applicationContext; - private final McpProperties mcpProperties; - - private volatile Map tools = Map.of(); - - @EventListener(ApplicationReadyEvent.class) - public void initialize() { - - Map discovered = new LinkedHashMap<>(); - for (Object bean : applicationContext.getBeansOfType(Object.class).values()) { - Class targetClass = AopUtils.getTargetClass(bean); - for (Method declaredMethod : targetClass.getDeclaredMethods()) { - - /* - * Tool Annotation 검색 - * - * 1순위 : Interface - * 2순위 : 구현체 - */ - ToolAnnotationMetadata metadata = findToolAnnotationMetadata(targetClass, declaredMethod); - - if (metadata == null) { - continue; - } - - McpTool annotation = metadata.mcpTool(); - GrowToolHint hint = metadata.growToolHint(); - - /* - * Annotation은 Interface에서 가져올 수 있지만 - * 실제 호출 Method는 구현체 Method를 사용한다. - */ - RegisteredTool tool = new RegisteredTool(bean, findInvocableMethod(bean, declaredMethod), annotation, hint); - - /* - * 원래 Tool Name 등록 - */ - register(discovered, annotation.name(), tool); - - /* - * namespace alias 등록 - */ - registerNamespaceAlias(discovered, annotation.name(), tool); - - log.info("[Tool Registry] Registered Tool: {} -> {}#{}", annotation.name(), targetClass.getSimpleName(), declaredMethod.getName()); - } - } - tools = Map.copyOf(discovered); - log.info("[Tool Registry] {} executable tool names cached", tools.size()); - } - - public RegisteredTool find(String toolName) { - return tools.get(toolName); - } - - /** - * @McpTool / @GrowToolHint 검색 - *

- * 우선순위 - *

- * 1. Interface Method - * 2. 구현체 Method - */ - private ToolAnnotationMetadata findToolAnnotationMetadata(Class targetClass, Method declaredMethod) { - - /* - * ========================================== - * STEP 1. - * Interface부터 검색 - * ========================================== - */ - for (Class interfaceClass : ClassUtils.getAllInterfacesForClassAsSet(targetClass)) { - - /* - * 구현체 Method와 동일한 Signature를 가진 - * Interface Method를 찾는다. - */ - Method interfaceMethod = ReflectionUtils.findMethod(interfaceClass, declaredMethod.getName(), declaredMethod.getParameterTypes()); - - if (interfaceMethod == null) { - continue; - } - - /* - * Interface의 @McpTool 검색 - */ - McpTool mcpTool = AnnotationUtils.findAnnotation(interfaceMethod, McpTool.class); - if (mcpTool == null) { - continue; - } - - /* - * @McpTool을 Interface에서 발견했다면 - * @GrowToolHint 역시 같은 Interface Method에서 가져온다. - */ - GrowToolHint growToolHint = AnnotationUtils.findAnnotation(interfaceMethod, GrowToolHint.class); - log.debug("[Tool Registry] Interface @McpTool found: " + "{}#{} -> {}", interfaceClass.getSimpleName(), interfaceMethod.getName(), mcpTool.name()); - return new ToolAnnotationMetadata(mcpTool, growToolHint); - } - - /* - * ========================================== - * STEP 2. - * Interface에서 발견되지 않은 경우 - * 구현체 Method 검색 - * ========================================== - */ - McpTool mcpTool = AnnotationUtils.findAnnotation(declaredMethod, McpTool.class); - - if (mcpTool == null) { - return null; - } - - GrowToolHint growToolHint = AnnotationUtils.findAnnotation(declaredMethod, GrowToolHint.class); - log.debug("[Tool Registry] Implementation @McpTool found: " + "{}#{} -> {}", targetClass.getSimpleName(), declaredMethod.getName(), mcpTool.name()); - return new ToolAnnotationMetadata(mcpTool, growToolHint); - } - - /** - * Namespace가 존재하는 경우 - *

- * ex) - *

- * pct_notice_list - *

- * namespace = sys - *

- * sys_pct_notice_list - *

- * 두 이름으로 동일 Tool을 조회할 수 있도록 등록 - */ - private void registerNamespaceAlias(Map discovered, String toolName, RegisteredTool tool) { - - String namespace = mcpProperties.getNamespace(); - - if (StringUtils.hasText(namespace)) { - register(discovered, namespace + "_" + toolName, tool); - } - } - - /** - * Tool Registry 등록 - */ - private void register(Map discovered, String toolName, RegisteredTool tool) { - - RegisteredTool existing = discovered.putIfAbsent(toolName, tool); - - if (existing != null && existing != tool) { - throw new IllegalStateException("Duplicate @McpTool name: " + toolName); - } - } - - /** - * 실제 호출 가능한 Method 확보 - *

- * Annotation 검색은 Interface에서 하더라도 - * Tool 실행은 구현체 Bean을 대상으로 수행해야 한다. - */ - private Method findInvocableMethod(Object bean, Method declaredMethod) { - - try { - return bean.getClass().getMethod(declaredMethod.getName(), declaredMethod.getParameterTypes()); - } catch (NoSuchMethodException ignored) { - return declaredMethod; - } - } - - /** - * Annotation 검색 결과 - */ - private record ToolAnnotationMetadata(McpTool mcpTool, GrowToolHint growToolHint) { - } - - /** - * 실제 실행 Registry 정보 - */ - public record RegisteredTool(Object bean, Method method, McpTool annotation, GrowToolHint hint) { - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolExecutionResult.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolExecutionResult.java deleted file mode 100644 index fee8459f3..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolExecutionResult.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import java.util.Map; - -/** Protocol-neutral result of invoking one Tool Pod business tool. */ -public record ToolExecutionResult(int statusCode, Object body, Map headers) { - - public ToolExecutionResult { - headers = headers == null ? Map.of() : Map.copyOf(headers); - } - - public boolean isSuccess() { - return statusCode >= 200 && statusCode < 300; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolMcpServerConfiguration.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolMcpServerConfiguration.java deleted file mode 100644 index 88ef705f4..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolMcpServerConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import io.modelcontextprotocol.server.McpServer; -import io.modelcontextprotocol.server.McpSyncServer; -import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; -import io.modelcontextprotocol.spec.McpSchema; -import org.springframework.boot.web.servlet.ServletRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import jakarta.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; - -/** Exposes every Tool Pod through the MCP Streamable HTTP transport. */ -@Slf4j -@Configuration -public class ToolMcpServerConfiguration { - - @PostConstruct - public void init() { - log.warn("================================================="); - log.warn("ToolMcpServerConfiguration IS LOADED BY SPRING!!!"); - log.warn("================================================="); - } - - @Bean - @Primary - public HttpServletStreamableServerTransportProvider toolMcpTransportProvider() { - return HttpServletStreamableServerTransportProvider.builder() - .mcpEndpoint("/mcp") - .build(); - } - - /** Creates the Tool Pod MCP server directly with MCP Java SDK 2.0.0. */ - @Bean(destroyMethod = "close") - public McpSyncServer toolMcpServer(HttpServletStreamableServerTransportProvider transportProvider) { - return McpServer.sync(transportProvider) - .serverInfo("dap-tool-pod", "2.0.0") - .capabilities(McpSchema.ServerCapabilities.builder().tools(true).build()) - .build(); - } - - @Bean - public ServletRegistrationBean toolMcpServlet( - HttpServletStreamableServerTransportProvider transportProvider) { - // "/mcp/*"로 매핑하면 BusinessToolController의 "/mcp/api/v1/tools/local" 까지 가로채게 되므로, - // 정확히 MCP 통신에 사용되는 "/mcp" 와 "/mcp/message" 두 개만 매핑합니다. - return new ServletRegistrationBean<>(transportProvider, "/mcp", "/mcp/message"); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolMetadataMcpMapper.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolMetadataMcpMapper.java deleted file mode 100644 index 99a5a1aad..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolMetadataMcpMapper.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import io.modelcontextprotocol.spec.McpSchema; -import io.shinhanlife.dat.mcc.dto.ToolMetadata; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** Registry 메타데이터를 MCP SDK Tool 명세로 일관되게 변환합니다. */ -public final class ToolMetadataMcpMapper { - private ToolMetadataMcpMapper() { - } - - public static McpSchema.Tool toTool(ToolMetadata metadata) { - McpSchema.Tool.Builder builder = McpSchema.Tool.builder() - .name(metadata.getName()) - .title(defaultText(metadata.getDisplayName(), metadata.getName())) - .description(defaultText(metadata.getDescription(), metadata.getName() + " Tool")) - .inputSchema(toJsonSchema(metadata.getParametersSchema())) - .annotations(new McpSchema.ToolAnnotations( - metadata.getDisplayName(), metadata.getReadOnlyHint(), metadata.getDestructiveHint(), - metadata.getIdempotentHint(), metadata.getOpenWorldHint(), null)) - .meta(meta(metadata)); - if (metadata.getOutputSchema() != null && !metadata.getOutputSchema().isEmpty()) { - builder.outputSchema(metadata.getOutputSchema()); - } - return builder.build(); - } - - public static Map meta(ToolMetadata metadata) { - Map meta = new LinkedHashMap<>(); - put(meta, "version", metadata.getSemver()); - put(meta, "category_key", metadata.getCategoryKey()); - put(meta, "display_description", metadata.getDisplayDescription()); - put(meta, "when_to_use", metadata.getWhenToUse()); - put(meta, "when_not_to_use", metadata.getWhenNotToUse()); - put(meta, "io_limits", metadata.getIoLimits()); - put(meta, "example_queries", metadata.getExampleQueries()); - put(meta, "tags", metadata.getTags()); - put(meta, "legacy_interface_id", metadata.getMciServiceId()); - put(meta, "required_env_keys", metadata.getRequiredEnvKeys()); - put(meta, "owner_org", metadata.getOwnerOrg()); - meta.put("timeoutMillis", positiveOrDefault(metadata.getTimeoutMillis(), 5000L)); - meta.put("retryMaxAttempts", positiveOrDefault(metadata.getRetryMaxAttempts(), 3)); - return Map.copyOf(meta); - } - - private static long positiveOrDefault(Long value, long defaultValue) { - return value != null && value > 0 ? value : defaultValue; - } - - private static int positiveOrDefault(Integer value, int defaultValue) { - return value != null && value > 0 ? value : defaultValue; - } - - private static void put(Map target, String key, Object value) { - if (value instanceof String text && !text.isBlank()) { - target.put(key, text); - } else if (value instanceof List list && !list.isEmpty()) { - target.put(key, List.copyOf(list)); - } - } - - private static String defaultText(String value, String fallback) { - return value == null || value.isBlank() ? fallback : value; - } - - @SuppressWarnings("unchecked") - private static McpSchema.JsonSchema toJsonSchema(Map source) { - Map schema = source == null ? emptySchema() : source; - return new McpSchema.JsonSchema( - String.valueOf(schema.getOrDefault("type", "object")), - schema.get("properties") instanceof Map properties - ? (Map) properties : Map.of(), - schema.get("required") instanceof List required ? (List) required : List.of(), - schema.get("additionalProperties") instanceof Boolean additionalProperties - ? additionalProperties : Boolean.TRUE, - schema.get("$defs") instanceof Map defs ? (Map) defs : Map.of(), - schema.get("definitions") instanceof Map definitions - ? (Map) definitions : Map.of()); - } - - private static Map emptySchema() { - return Map.of("type", "object", "properties", Map.of(), "additionalProperties", false); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolPodMcpToolSynchronizer.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolPodMcpToolSynchronizer.java deleted file mode 100644 index 3d174a9e7..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolPodMcpToolSynchronizer.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.server.McpServerFeatures; -import io.modelcontextprotocol.server.McpSyncServer; -import io.modelcontextprotocol.spec.McpSchema; -import io.shinhanlife.dat.mcc.dto.ToolMetadata; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; - -/** Registers the Tool Pod's existing annotated tools with its MCP SDK server. */ -@Component -@ConditionalOnBean(McpToolExecutionService.class) -public class ToolPodMcpToolSynchronizer { - private final McpSyncServer mcpServer; - private final ToolRegistryHeartbeatSender heartbeatSender; - private final McpToolExecutionService toolExecutionService; - private final ObjectMapper objectMapper; - - public ToolPodMcpToolSynchronizer(McpSyncServer mcpServer, ToolRegistryHeartbeatSender heartbeatSender, - McpToolExecutionService toolExecutionService, ObjectMapper objectMapper) { - this.mcpServer = mcpServer; - this.heartbeatSender = heartbeatSender; - this.toolExecutionService = toolExecutionService; - this.objectMapper = objectMapper; - } - - @EventListener(ApplicationReadyEvent.class) - public void registerLocalTools() { - heartbeatSender.getAllScannedTools().stream() - .filter(tool -> Boolean.TRUE.equals(tool.getVisible())) - .forEach(tool -> mcpServer.addTool(specification(tool))); - } - - private McpServerFeatures.SyncToolSpecification specification(ToolMetadata tool) { - McpSchema.Tool mcpTool = ToolMetadataMcpMapper.toTool(tool); - return McpServerFeatures.SyncToolSpecification.builder().tool(mcpTool) - .callHandler((context, request) -> invoke(tool.getName(), McpRequestHeaderContext.current(), request.arguments())).build(); - } - - private McpSchema.CallToolResult invoke(String toolName, McpRequestHeaders requestHeaders, - Map arguments) { - ToolExecutionResult result = toolExecutionService.execute(toolName, requestHeaders, arguments); - boolean failed = !result.isSuccess(); - Object body = result.body(); - try { - return McpSchema.CallToolResult.builder().addTextContent(objectMapper.writeValueAsString(body)) - .structuredContent(body).isError(failed).build(); - } catch (Exception error) { - return McpSchema.CallToolResult.builder().addTextContent(String.valueOf(body)).isError(failed).build(); - } - } - - private Map emptySchema() { - Map schema = new LinkedHashMap<>(); - schema.put("type", "object"); - schema.put("properties", Map.of()); - schema.put("additionalProperties", false); - return schema; - } - @SuppressWarnings("unchecked") - private McpSchema.JsonSchema toJsonSchema(Map source) { - Map schema = source == null ? emptySchema() : source; - return new McpSchema.JsonSchema( - String.valueOf(schema.getOrDefault("type", "object")), - schema.get("properties") instanceof Map properties - ? (Map) properties : Map.of(), - schema.get("required") instanceof List required - ? (List) required : List.of(), - schema.get("additionalProperties") instanceof Boolean additionalProperties - ? additionalProperties : Boolean.TRUE, - schema.get("$defs") instanceof Map defs ? (Map) defs : Map.of(), - schema.get("definitions") instanceof Map definitions - ? (Map) definitions : Map.of()); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolRegistryHeartbeatSender.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolRegistryHeartbeatSender.java deleted file mode 100644 index 658ce2f08..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/ToolRegistryHeartbeatSender.java +++ /dev/null @@ -1,329 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - - -/** - * @package io.shinhanlife.dat.mcc.service - * @className ToolRegistryHeartbeatSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *

- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- *
- * 
- */ - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.lib.config.McpProperties; -import io.shinhanlife.dat.lib.metadata.ToolDefinition; -import io.shinhanlife.dat.lib.metadata.ToolDefinitionRepository; -import io.shinhanlife.dat.lib.util.ToolSchemaResolver; -import io.shinhanlife.dat.mcc.dto.ToolMetadata; -import jakarta.annotation.PostConstruct; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.springaicommunity.mcp.annotation.McpTool; -import org.springframework.aop.support.AopUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.lang.Nullable; -import org.springframework.stereotype.Component; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -@Slf4j -@Component -@ConditionalOnBean(McpToolExecutionService.class) -public class ToolRegistryHeartbeatSender { - - private final ApplicationContext applicationContext; - private final ObjectMapper objectMapper; - private final McpProperties mcpProperties; - private final ToolSchemaResolver toolSchemaResolver; - private final ToolDefinitionRepository toolDefinitionRepository; - - @Autowired - public ToolRegistryHeartbeatSender(ApplicationContext applicationContext, ObjectMapper objectMapper, McpProperties mcpProperties, ToolSchemaResolver toolSchemaResolver, @Nullable ToolDefinitionRepository toolDefinitionRepository) { - - this.applicationContext = applicationContext; - this.objectMapper = objectMapper; - this.mcpProperties = mcpProperties; - this.toolSchemaResolver = toolSchemaResolver; - this.toolDefinitionRepository = toolDefinitionRepository; - } - - public ToolRegistryHeartbeatSender(ApplicationContext applicationContext, ObjectMapper objectMapper, McpProperties mcpProperties, ToolSchemaResolver toolSchemaResolver) { - - this(applicationContext, objectMapper, mcpProperties, toolSchemaResolver, null); - } - - @Value("${axhub.tool.url:http://localhost:8080}") - private String podUrl; - - @Value("${spring.application.name:}") - private String applicationName; - - @Getter - private List allScannedTools = new ArrayList<>(); - - @PostConstruct - public void init() { - log.info(" [ToolScanner] 초기화 시작. Pod URL: {}", podUrl); - scanAndBuildMetadata(); - } - - private void scanAndBuildMetadata() { - - /* - * 재호출될 가능성을 고려해서 - * 기존 Metadata 제거 - */ - allScannedTools.clear(); - Map allBeans = applicationContext.getBeansOfType(Object.class); - for (Object bean : allBeans.values()) { - Class targetClass = AopUtils.getTargetClass(bean); - for (Method method : targetClass.getDeclaredMethods()) { - /* - * Annotation 검색 - * - * 1순위 : Interface - * 2순위 : 구현체 - */ - ToolAnnotationMetadata annotationMetadata = findToolAnnotationMetadata(targetClass, method); - - if (annotationMetadata == null) { - continue; - } - - McpTool functionAnnotation = annotationMetadata.mcpTool(); - GrowToolHint hintAnnotation = annotationMetadata.growToolHint(); - - String rawSubToolName = functionAnnotation.name(); - /* - * Namespace 적용 - */ - String subToolName = mcpProperties.getNamespace() != null && !mcpProperties.getNamespace().isEmpty() ? mcpProperties.getNamespace() + "_" + rawSubToolName : rawSubToolName; - - /* - * register 값은 외부 Gateway 전송이 아니라 - * Tool 메타데이터 호환 필드로 유지 - */ - boolean isRegister = hintAnnotation == null || hintAnnotation.register(); - ToolMetadata meta = new ToolMetadata(); - meta.setUid(UUID.nameUUIDFromBytes(subToolName.getBytes()).toString()); - String displayName = functionAnnotation.title().isEmpty() ? functionAnnotation.name() : functionAnnotation.title(); - meta.setDisplayName(displayName); - meta.setName(subToolName); - meta.setSemver("1.0.0"); - meta.setTimeoutMillis(hintAnnotation == null ? 5000L : positiveOrDefault(hintAnnotation.timeoutMillis(), 5000L)); - meta.setRetryMaxAttempts(hintAnnotation == null ? 3 : positiveOrDefault(hintAnnotation.retryMaxAttempts(), 3)); - meta.setEnabled(true); - meta.setDescription(functionAnnotation.description()); - - meta.setCategoryKey(hintAnnotation == null || hintAnnotation.categoryKey().isBlank() ? "common" : hintAnnotation.categoryKey()); - meta.setIntegrationType("REST"); - meta.setMciServiceId(hintAnnotation != null ? hintAnnotation.mappingId() : ""); - - meta.setPodUrl(podUrl); - meta.setModuleName(applicationName); - - meta.setEndpoint("/mcp/" + subToolName); - - meta.setVisible(true); - meta.setIsRegistered(isRegister); - meta.setRequiresApproval(hintAnnotation != null && hintAnnotation.requiresApproval()); - - /* - * MCP standard hint - */ - McpTool.McpAnnotations ann = functionAnnotation.annotations(); - - if (ann != null) { - meta.setReadOnlyHint(ann.readOnlyHint()); - meta.setDestructiveHint(ann.destructiveHint()); - meta.setIdempotentHint(ann.idempotentHint()); - meta.setOpenWorldHint(ann.openWorldHint()); - } else { - meta.setReadOnlyHint(false); - meta.setDestructiveHint(true); - meta.setIdempotentHint(false); - meta.setOpenWorldHint(true); - } - - if (hintAnnotation != null) { - if (!hintAnnotation.displayDescription().isBlank()) meta.setDisplayDescription(hintAnnotation.displayDescription()); - if (!hintAnnotation.functionDescription().isBlank()) meta.setFunctionDescription(hintAnnotation.functionDescription()); - if (!hintAnnotation.whenToUse().isBlank()) meta.setWhenToUse(hintAnnotation.whenToUse()); - if (!hintAnnotation.whenNotToUse().isBlank()) meta.setWhenNotToUse(hintAnnotation.whenNotToUse()); - if (!hintAnnotation.ioLimits().isBlank()) meta.setIoLimits(hintAnnotation.ioLimits()); - if (hintAnnotation.exampleQueries().length > 0) meta.setExampleQueries(java.util.List.of(hintAnnotation.exampleQueries())); - if (hintAnnotation.tags().length > 0) meta.setTags(java.util.List.of(hintAnnotation.tags())); - if (hintAnnotation.requiredEnvKeys().length > 0) meta.setRequiredEnvKeys(java.util.List.of(hintAnnotation.requiredEnvKeys())); - if (!hintAnnotation.ownerOrg().isBlank()) meta.setOwnerOrg(hintAnnotation.ownerOrg()); - } - - Map prompts = new HashMap<>(); - - meta.setActionPrompts(prompts); - - /* - * Request / Response Schema 생성 - * - * Annotation은 Interface에서 읽어오지만 - * 실제 Method의 Parameter/Return Type은 - * 구현체 Method를 기준으로 사용 - */ - if (method.getParameterCount() > 0) { - - try { - - Class paramType = method.getParameterTypes()[0]; - Map finalSchema = toolSchemaResolver.resolve(functionAnnotation, hintAnnotation, paramType); - meta.setParametersSchema(finalSchema); - Map outputSchema = toolSchemaResolver.resolveOutput(functionAnnotation, method.getReturnType(), hintAnnotation); - if (!outputSchema.isEmpty()) { - meta.setOutputSchema(outputSchema); - } - - } catch (Exception e) { - - log.error("Failed to generate schema for {}", subToolName, e); - } - } - - /* - * YML Tool Definition 병합 - */ - enrichWithDefinition(meta, rawSubToolName, hintAnnotation); - allScannedTools.add(meta); - log.info(" [ToolScanner] 도구 메타데이터 생성: " + "name={}, class={}#{}, " + "isRegistered={}", subToolName, targetClass.getSimpleName(), method.getName(), isRegister); - } - } - - log.info(" [ToolScanner] 총 {}개 Tool 메타데이터 생성 완료", allScannedTools.size()); - } - - private long positiveOrDefault(long value, long defaultValue) { - return value > 0 ? value : defaultValue; - } - - private int positiveOrDefault(int value, int defaultValue) { - return value > 0 ? value : defaultValue; - } - - /** - * @McpTool / @GrowToolHint 검색 - *

- * 우선순위 - *

- * 1. Interface - * 2. Implementation - */ - private ToolAnnotationMetadata findToolAnnotationMetadata(Class targetClass, Method method) { - - /* - * ======================================== - * STEP 1. - * Interface부터 검색 - * ======================================== - */ - for (Class interfaceClass : ClassUtils.getAllInterfacesForClassAsSet(targetClass)) { - - Method interfaceMethod = ReflectionUtils.findMethod(interfaceClass, method.getName(), method.getParameterTypes()); - if (interfaceMethod == null) { - continue; - } - - McpTool mcpTool = AnnotationUtils.findAnnotation(interfaceMethod, McpTool.class); - if (mcpTool == null) { - continue; - } - GrowToolHint growToolHint = AnnotationUtils.findAnnotation(interfaceMethod, GrowToolHint.class); - log.debug(" [ToolScanner] Interface @McpTool 발견: " + "{}#{} -> {}", interfaceClass.getSimpleName(), interfaceMethod.getName(), mcpTool.name()); - - return new ToolAnnotationMetadata(mcpTool, growToolHint); - } - - /* - * ======================================== - * STEP 2. - * Interface에서 찾지 못한 경우에만 - * 구현체 Method 검색 - * ======================================== - */ - McpTool mcpTool = AnnotationUtils.findAnnotation(method, McpTool.class); - - if (mcpTool == null) { - return null; - } - - GrowToolHint growToolHint = AnnotationUtils.findAnnotation(method, GrowToolHint.class); - - log.debug(" [ToolScanner] Implementation @McpTool 발견: " + "{}#{} -> {}", targetClass.getSimpleName(), method.getName(), mcpTool.name()); - - return new ToolAnnotationMetadata(mcpTool, growToolHint); - } - - private void enrichWithDefinition(ToolMetadata meta, String rawToolName, GrowToolHint hintAnnotation) { - - if (toolDefinitionRepository == null) { - return; - } - - toolDefinitionRepository.findByName(rawToolName).ifPresent(definition -> applyDefinition(meta, definition, hintAnnotation)); - } - - private void applyDefinition(ToolMetadata meta, ToolDefinition definition, GrowToolHint hintAnnotation) { - - meta.setDisplayName(definition.displayName()); - meta.setSemver(definition.version()); - meta.setCategoryKey(definition.categoryKey()); - meta.setFunctionDescription(definition.description().function()); - meta.setWhenToUse(definition.description().whenToUse()); - meta.setWhenNotToUse(definition.description().whenNotToUse()); - meta.setIoLimits(definition.description().ioLimits()); - meta.setDescription(definition.description().function()); - meta.setDisplayDescription(definition.displayDescription()); - meta.setExampleQueries(definition.exampleQueries()); - meta.setReadOnlyHint(definition.readOnly()); - meta.setDestructiveHint(definition.destructive()); - meta.setIdempotentHint(definition.idempotent()); - boolean explicitInputResource = hintAnnotation != null && !hintAnnotation.inputSchemaResource().isBlank(); - - boolean explicitOutputResource = hintAnnotation != null && !hintAnnotation.outputSchemaResource().isBlank(); - - if (!explicitInputResource) { - meta.setParametersSchema(definition.parametersSchema()); - } - - if (!explicitOutputResource && definition.outputSchema() != null && !definition.outputSchema().isEmpty()) { - meta.setOutputSchema(definition.outputSchema()); - } - - meta.setTags(definition.tags()); - meta.setMciServiceId(definition.legacyInterfaceId()); - meta.setRequiredEnvKeys(definition.requiredEnvKeys()); - meta.setOwnerOrg(definition.ownerOrg()); - } - - /** - * Annotation 검색 결과 - */ - private record ToolAnnotationMetadata(McpTool mcpTool, GrowToolHint growToolHint) { - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/CacheConfig.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/CacheConfig.java deleted file mode 100644 index f206e1110..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/CacheConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.config; - -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @package io.shinhanlife.dat.lib.mcp.config - * @className CacheConfig - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *

- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Configuration -@EnableCaching -public class CacheConfig { - - // 스프링이 캐시를 관리할 기본 저장소를 빈(Bean)으로 등록합니다. - @Bean - public CacheManager cacheManager() { - return new ConcurrentMapCacheManager("eimsData"); // 아까 설정한 캐시 이름 등록 - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/JacksonConfig.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/JacksonConfig.java deleted file mode 100644 index 21cdebad3..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/JacksonConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.config; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; - -/** - * @package io.shinhanlife.dat.lib.mcp.config - * @className JacksonConfig - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Configuration -public class JacksonConfig { - - // 1. JSON 변환기(ObjectMapper)를 스프링 Bean으로 등록 - @Bean - @Primary - public ObjectMapper jsonMapper() { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - return mapper; - } - - // 2. XML 변환기(XmlMapper)를 스프링 Bean으로 등록 - @Bean - public XmlMapper xmlMapper() { - return new XmlMapper(); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/KafkaLocalConfig.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/KafkaLocalConfig.java deleted file mode 100644 index de4486316..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/KafkaLocalConfig.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.config; - -import org.apache.kafka.clients.producer.ProducerConfig; -import org.apache.kafka.common.serialization.StringSerializer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.kafka.core.DefaultKafkaProducerFactory; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.kafka.core.ProducerFactory; - -import org.springframework.beans.factory.annotation.Value; -import java.util.HashMap; -import java.util.Map; - -/** - * @package io.shinhanlife.dat.lib.mcp.config - * @className KafkaLocalConfig - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Configuration -public class KafkaLocalConfig { - - @Value("${spring.kafka.bootstrap-servers:localhost:9092}") - private String bootstrapServers; - - // 1. 카프카 전송 공장(Factory) 세팅 - @Bean - public ProducerFactory producerFactory() { - Map configProps = new HashMap<>(); - // 가짜 로컬 주소 혹은 환경변수 세팅 - configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); - // 데이터를 카프카로 보낼 때 문자열(String) 형태로 변환하겠다는 규칙 - configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - - // 3초 만에 빠른 실패 처리 (로컬 무한 대기 방지) - configProps.put(ProducerConfig.MAX_BLOCK_MS_CONFIG, 3000); - // 재접속 주기를 10초로 설정 (콘솔 로그 도배 방지) - configProps.put(ProducerConfig.RECONNECT_BACKOFF_MAX_MS_CONFIG, 10000); - - return new DefaultKafkaProducerFactory<>(configProps); - } - - // 2. EaiEimsSender가 애타게 찾던 KafkaTemplate을 스프링 Bean으로 등록! - @Bean - public KafkaTemplate kafkaTemplate() { - return new KafkaTemplate<>(producerFactory()); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/SwaggerConfig.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/SwaggerConfig.java deleted file mode 100644 index f561be290..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/SwaggerConfig.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.config; - -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.security.SecurityRequirement; -import io.swagger.v3.oas.models.security.SecurityScheme; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @package io.shinhanlife.dat.lib.mcp.config - * @className SwaggerConfig - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Configuration -public class SwaggerConfig { - - @Bean - public OpenAPI customOpenAPI() { - return new OpenAPI() - .info(new Info() - .title("Shinhan MCP Gateway API 명세서") - .version("v1.0") - .description("AI Agent와 신한라이프 내부망(EIMS/EAI)을 연결하는 Adapter Gateway API 문서입니다.")) - .addServersItem(new io.swagger.v3.oas.models.servers.Server().url("http://localhost:8080").description("Adapter Pod (8080)")) - .addServersItem(new io.swagger.v3.oas.models.servers.Server().url("http://localhost:8081").description("Gateway Pod (8081)")) - // DAPMS가 Tool Service 호출에 사용하는 API Key 헤더를 Swagger UI에도 추가합니다. - .addSecurityItem(new SecurityRequirement().addList("X-Tool-Server-API-Key")) - .components(new Components() - .addSecuritySchemes("X-Tool-Server-API-Key", - new SecurityScheme() - .name("X-Tool-Server-API-Key") - .type(SecurityScheme.Type.APIKEY) - .in(SecurityScheme.In.HEADER) - .description("헤더에 API Key를 입력해주세요. "))); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/WebConfig.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/WebConfig.java deleted file mode 100644 index 44ddbf5d7..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/config/WebConfig.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.config; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.config.annotation.CorsRegistry; - -import io.shinhanlife.dat.lib.mcp.security.ApiKeyInterceptor; - -/** - * @package io.shinhanlife.dat.lib.mcp.config - * @className WebConfig - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Configuration -@RequiredArgsConstructor -public class WebConfig implements WebMvcConfigurer { - - // 1. 우리가 만든 인터셉터를 주입받습니다. - private final ApiKeyInterceptor apiKeyInterceptor; - - @Override - public void addInterceptors(InterceptorRegistry registry) { - // 2. 인터셉터 등록 및 검사할 URL 패턴 지정 - registry.addInterceptor(apiKeyInterceptor) - .addPathPatterns("/rpc/**", "/mcp/**") - .excludePathPatterns( - "/test/**", "/health", "/error", "/mcp/api/v1/admin/**", - "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**", // Swagger UI 경로는 인증 제외 - "/mcp/api/v1/tools/docs/markdown", "/favicon.ico", "/mcp/api/v1/tools/list" - ); - } - - @Override - public void addCorsMappings(CorsRegistry registry) { - // Swagger UI(8080)에서 Gateway(8081)로 API 호출 시 발생하는 CORS 에러 해결 - registry.addMapping("/**") - .allowedOriginPatterns("*") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") - .exposedHeaders("Mcp-Session-Id") - .allowCredentials(true); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/exception/GlobalExceptionHandler.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/exception/GlobalExceptionHandler.java deleted file mode 100644 index 3f63ceb6d..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.exception; - - -/** - * @package io.shinhanlife.dat.lib.mcp.exception - * @className GlobalExceptionHandler - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -import io.shinhanlife.dat.lib.adapter.dto.ErrorDetail; -import io.shinhanlife.dat.lib.adapter.dto.JsonRpcResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.servlet.resource.NoResourceFoundException; - -@Slf4j -@RestControllerAdvice // 이 어노테이션이 전역 적용의 핵심입니다! -public class GlobalExceptionHandler { - - @ExceptionHandler(NoResourceFoundException.class) - public ResponseEntity handleNoResourceFound(NoResourceFoundException e) { - log.warn(" [Gateway Not Found] 요청하신 리소스를 찾을 수 없습니다: {}", e.getResourcePath()); - return ResponseEntity.notFound().build(); - } - - @ExceptionHandler(IllegalArgumentException.class) - public ResponseEntity handleIllegalArgument(IllegalArgumentException e) { - log.warn(" [Gateway Bad Request] 잘못된 요청: {}", e.getMessage()); - return buildErrorResponse(-32602, "Invalid params: " + e.getMessage()); - } - - @ExceptionHandler(org.springframework.web.context.request.async.AsyncRequestNotUsableException.class) - public ResponseEntity handleAsyncRequestNotUsable(org.springframework.web.context.request.async.AsyncRequestNotUsableException e) { - log.debug(" [Gateway Info] 클라이언트가 비동기 연결을 종료했습니다 (정상): {}", e.getMessage()); - return ResponseEntity.ok().build(); - } - - @ExceptionHandler(RuntimeException.class) - public ResponseEntity handleRuntime(RuntimeException e) { - log.error(" [Gateway Internal Error] 시스템 장애: {}", e.getMessage(), e); - return buildErrorResponse(-32603, "Internal error: " + e.getMessage()); - } - - @ExceptionHandler(Exception.class) - public ResponseEntity handleAllException(Exception e) { - log.error(" [Gateway Fatal Error] 치명적 오류 발생", e); - return buildErrorResponse(-32000, "Server error: 시스템 관리자에게 문의하세요."); - } - - private ResponseEntity buildErrorResponse(int code, String message) { - JsonRpcResponse response = new JsonRpcResponse(); - response.setError(new ErrorDetail(code, message)); - - return ResponseEntity.ok(response); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/filter/MdcLoggingFilter.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/filter/MdcLoggingFilter.java deleted file mode 100644 index 5d26339ed..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/filter/MdcLoggingFilter.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.filter; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.slf4j.MDC; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.util.UUID; - -/** - * @package io.shinhanlife.dat.lib.mcp.filter - * @className MdcLoggingFilter - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Component -public class MdcLoggingFilter extends OncePerRequestFilter { - - private static final String TRACE_ID_HEADER = "X-Trace-Id"; - private static final String MDC_KEY = "traceId"; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - - // 클라이언트가 보낸 Trace ID가 있으면 쓰고, 없으면 새로 생성 - String traceId = request.getHeader(TRACE_ID_HEADER); - if (traceId == null || traceId.isEmpty()) { - // 간결하게 8자리 UUID만 사용 - traceId = UUID.randomUUID().toString().substring(0, 8); - } - - // 로깅 컨텍스트에 고유 ID 저장 - MDC.put(MDC_KEY, traceId); - - try { - // 이 요청이 처리되는 동안 찍히는 모든 log.info, log.error에 traceId가 자동으로 붙습니다. - filterChain.doFilter(request, response); - } finally { - // 메모리 누수 방지를 위해 요청이 끝나면 반드시 비워줍니다. - MDC.clear(); - } - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/security/ApiKeyInterceptor.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/security/ApiKeyInterceptor.java deleted file mode 100644 index dc8620ed7..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/security/ApiKeyInterceptor.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.security; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.slf4j.MDC; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - -import java.util.Map; - -/** - * @package io.shinhanlife.dat.lib.mcp.security - * @className ApiKeyInterceptor - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Component -@RequiredArgsConstructor -public class ApiKeyInterceptor implements HandlerInterceptor { - - // 1. 다중 테넌트 API Key 목록이 담긴 프로퍼티 객체를 주입받습니다. - private final SecurityProperties securityProperties; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - - if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { - return true; - } - - String apiKey = request.getHeader("X-Tool-Server-API-Key"); - String configuredApiKey = securityProperties.getApiKey(); - Map validApiKeys = securityProperties.getApiKeys(); - boolean singleApiKeyConfigured = configuredApiKey != null && !configuredApiKey.isBlank(); - boolean multipleApiKeysConfigured = validApiKeys != null && !validApiKeys.isEmpty(); - - // 2. 만약 프로퍼티에 API Key가 하나도 설정되어 있지 않다면 (개발/로컬 환경 등) 인증 없이 통과시킵니다. - if (!singleApiKeyConfigured && !multipleApiKeysConfigured) { - MDC.put("tenantId", "anonymous"); - request.setAttribute("tenantId", "anonymous"); - log.debug(" [보안 패스] 등록된 API Key 없음 - 익명 사용자(anonymous)로 통과"); - return true; - } - - // 3. 헤더로 들어온 API Key가 단일 공통 Key 또는 다중 테넌트 Key 목록에 존재하는지 확인합니다. - boolean matchesSingleApiKey = singleApiKeyConfigured && configuredApiKey.equals(apiKey); - boolean matchesMultipleApiKeys = apiKey != null && multipleApiKeysConfigured && validApiKeys.containsKey(apiKey); - if (!matchesSingleApiKey && !matchesMultipleApiKeys) { - log.warn(" [보안 차단] 유효하지 않은 API Key 접근 시도 - IP: {}", request.getRemoteAddr()); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid API Key"); - return false; // 컨트롤러로 넘어가지 않음 - } - - // 4. 유효하다면 해당 키에 맵핑된 Tenant ID(식별자)를 가져옵니다. (ex. mcp-client-1) - String tenantId = matchesSingleApiKey ? "dapms" : validApiKeys.get(apiKey); - - // 4. 추출한 Tenant ID를 현재 스레드의 로깅 컨텍스트(MDC)에 저장합니다. - // 이렇게 하면 이 요청이 끝날 때까지 찍히는 모든 로그에 어떤 테넌트가 호출했는지 자동으로 기록됩니다. - MDC.put("tenantId", tenantId); - - // 5. 필요시 컨트롤러 로직에서 사용할 수 있도록 Request 속성에도 담아줍니다. - request.setAttribute("tenantId", tenantId); - - log.debug(" [보안 통과] API Key 인증 성공 - 접속 테넌트: {}", tenantId); - - return true; // 인증 통과! 컨트롤러로 진행 - } - - @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { - // 6. 메모리 누수를 방지하기 위해 요청 처리가 완전히 끝나면 MDC에서 테넌트 정보를 지워줍니다. - MDC.remove("tenantId"); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/security/SecurityProperties.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/security/SecurityProperties.java deleted file mode 100644 index 17cd6f46c..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/mcp/security/SecurityProperties.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.security; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * [다중 테넌트 설정 매핑 클래스] - * application-local.properties 파일에 정의된 mcp.security.api-keys.* 설정들을 - * Map 자료구조로 자동 바인딩(주입) 받기 위한 설정 클래스입니다. - */ -/** - * @package io.shinhanlife.dat.lib.mcp.security - * @className SecurityProperties - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Data -@Component -@ConfigurationProperties(prefix = "mcp.security") -public class SecurityProperties { - // DAPMS가 X-Tool-Server-API-Key 헤더로 전달하는 단일 공통 Key - private String apiKey; - - // API Key를 Key로, Tenant ID를 Value로 가지는 맵 - private Map apiKeys = new HashMap<>(); - - // Tenant ID를 Key로, 허용된 도메인 그룹 목록을 Value로 가지는 맵 (ex. mcp-client-1 -> [CUSTOMER, COMMON]) - // 만약 "ALL" 이 포함되어 있다면 모든 도메인에 접근 허용 - private Map> tenantDomains = new HashMap<>(); -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinition.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinition.java deleted file mode 100644 index f6f49a52b..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinition.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.shinhanlife.dat.lib.metadata; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; -import java.util.Map; - -/** BC-DAB-STD-003 V17 Tool 정의 파일의 불변 모델입니다. */ -public record ToolDefinition( - String name, - @JsonProperty("display_name") String displayName, - String version, - @JsonProperty("category_key") String categoryKey, - ToolDescription description, - @JsonProperty("display_description") String displayDescription, - @JsonProperty("example_queries") List exampleQueries, - @JsonProperty("read_only") Boolean readOnly, - Boolean destructive, - Boolean idempotent, - @JsonProperty("parameters_schema") Map parametersSchema, - @JsonProperty("output_schema") Map outputSchema, - List tags, - @JsonProperty("legacy_interface_id") String legacyInterfaceId, - @JsonProperty("required_env_keys") List requiredEnvKeys, - @JsonProperty("owner_org") String ownerOrg) { - - public ToolDefinition withExampleQueries(List queries) { - return new ToolDefinition(name, displayName, version, categoryKey, description, displayDescription, - queries, readOnly, destructive, idempotent, parametersSchema, outputSchema, tags, legacyInterfaceId, - requiredEnvKeys, ownerOrg); - } - - public ToolDefinition withName(String value) { - return new ToolDefinition(value, displayName, version, categoryKey, description, displayDescription, - exampleQueries, readOnly, destructive, idempotent, parametersSchema, outputSchema, tags, legacyInterfaceId, - requiredEnvKeys, ownerOrg); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionRepository.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionRepository.java deleted file mode 100644 index 967ff5961..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionRepository.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.shinhanlife.dat.lib.metadata; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.stereotype.Component; - -/** classpath의 Tool 정의를 기동 시 한 번 읽어 name 기준으로 캐시합니다. */ -@Component -public class ToolDefinitionRepository { - public static final String DEFAULT_LOCATION = "classpath*:tool-definitions/**/*.yml"; - - private final Map definitions; - - @Autowired - public ToolDefinitionRepository(ResourceLoader resourceLoader) { - this(new ObjectMapper(new YAMLFactory()), resourceLoader, DEFAULT_LOCATION); - } - - public ToolDefinitionRepository(ObjectMapper yamlMapper, ResourceLoader resourceLoader, String location) { - this(yamlMapper, new PathMatchingResourcePatternResolver(resourceLoader), location); - } - - private ToolDefinitionRepository(ObjectMapper yamlMapper, ResourcePatternResolver resolver, String location) { - this.definitions = Collections.unmodifiableMap(load(yamlMapper, resolver, location)); - } - - public Optional findByName(String name) { - return Optional.ofNullable(definitions.get(name)); - } - - public Map findAll() { - return definitions; - } - - private Map load(ObjectMapper mapper, ResourcePatternResolver resolver, String location) { - Map loaded = new LinkedHashMap<>(); - try { - for (Resource resource : resolver.getResources(location)) { - ToolDefinition definition = mapper.readValue(resource.getInputStream(), ToolDefinition.class); - String source = resource.getDescription(); - ToolDefinitionValidator.validate(definition, source); - ToolDefinition previous = loaded.putIfAbsent(definition.name(), definition); - if (previous != null) { - throw new IllegalStateException("Duplicate Tool definition name: " + definition.name()); - } - } - return loaded; - } catch (IOException exception) { - throw new IllegalStateException("Failed to load Tool definitions from " + location, exception); - } - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionValidator.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionValidator.java deleted file mode 100644 index e3c05d797..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionValidator.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.shinhanlife.dat.lib.metadata; - -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -/** Tool 정의가 BC-DAB-STD-003 V17 필수 규칙을 만족하는지 검증합니다. */ -public final class ToolDefinitionValidator { - private static final Pattern TOOL_NAME = Pattern.compile("^[a-z][a-z0-9_]{2,63}$"); - - private ToolDefinitionValidator() { - } - - public static void validate(ToolDefinition definition, String source) { - if (definition == null) { - fail(source, "definition", "문서가 비어 있습니다"); - } - if (isBlank(definition.name()) || !TOOL_NAME.matcher(definition.name()).matches()) { - fail(source, "name", "^[a-z][a-z0-9_]{2,63}$ 형식이어야 합니다"); - } - requireText(source, "display_name", definition.displayName()); - requireText(source, "version", definition.version()); - requireText(source, "category_key", definition.categoryKey()); - requireText(source, "display_description", definition.displayDescription()); - if (definition.description() == null) { - fail(source, "description", "필수입니다"); - } - requireText(source, "description.function", definition.description().function()); - requireText(source, "description.when_to_use", definition.description().whenToUse()); - requireText(source, "description.when_not_to_use", definition.description().whenNotToUse()); - requireText(source, "description.io_limits", definition.description().ioLimits()); - List examples = definition.exampleQueries(); - if (examples == null || examples.size() < 3 || examples.size() > 10 - || examples.stream().anyMatch(ToolDefinitionValidator::isBlank)) { - fail(source, "example_queries", "비어 있지 않은 자연어 질의가 3~10개 필요합니다"); - } - if (examples.stream().anyMatch(query -> query.contains(definition.name()))) { - fail(source, "example_queries", "Tool name을 직접 포함할 수 없습니다"); - } - if (definition.readOnly() == null || definition.destructive() == null || definition.idempotent() == null) { - fail(source, "annotations", "read_only, destructive, idempotent는 필수입니다"); - } - validateSchema(definition.parametersSchema(), source); - if (definition.outputSchema() != null && !definition.outputSchema().isEmpty()) { - validateSchema(definition.outputSchema(), source + " output_schema"); - } - } - - @SuppressWarnings("unchecked") - private static void validateSchema(Map schema, String source) { - if (schema == null || !"object".equals(schema.get("type"))) { - fail(source, "parameters_schema.type", "object여야 합니다"); - } - if (!Boolean.FALSE.equals(schema.get("additionalProperties"))) { - fail(source, "parameters_schema.additionalProperties", "false여야 합니다"); - } - Object propertiesValue = schema.get("properties"); - if (!(propertiesValue instanceof Map)) { - fail(source, "parameters_schema.properties", "object여야 합니다"); - } - Map properties = (Map) propertiesValue; - for (Map.Entry entry : properties.entrySet()) { - if (!(entry.getValue() instanceof Map property) - || isBlank(String.valueOf(property.containsKey("description") - ? property.get("description") : ""))) { - fail(source, "parameters_schema.properties." + entry.getKey() + ".description", "필수입니다"); - } - } - } - - private static void requireText(String source, String field, String value) { - if (isBlank(value)) { - fail(source, field, "필수입니다"); - } - } - - private static boolean isBlank(String value) { - return value == null || value.isBlank(); - } - - private static void fail(String source, String field, String message) { - throw new IllegalStateException("Invalid Tool definition [" + source + "] " + field + ": " + message); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDescription.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDescription.java deleted file mode 100644 index 9eb8e6789..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/metadata/ToolDescription.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.shinhanlife.dat.lib.metadata; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** LLM이 Tool 선택 여부를 판단할 때 사용하는 V17 목적 설명입니다. */ -public record ToolDescription( - String function, - @JsonProperty("when_to_use") String whenToUse, - @JsonProperty("when_not_to_use") String whenNotToUse, - @JsonProperty("io_limits") String ioLimits) { -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/dto/SessionDto.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/dto/SessionDto.java deleted file mode 100644 index e4050c65c..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/dto/SessionDto.java +++ /dev/null @@ -1,90 +0,0 @@ -package io.shinhanlife.dat.lib.session.dto; - -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; - -/** - * @package io.shinhanlife.dat.lib.session.dto - * @className SessionDto - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Getter -@Setter -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class SessionDto { - - /* 인사번호 */ - private String prafNo; - /* 인사명 */ - private String prafNm; - /* 조직번호 */ - private String ognzNo; - /* 조직번호 */ - private String ognzNm; - /* 이메일주소 */ - private String addre; - /* 인사직무코드 */ - private String prafOfduCd; - /* 인사직무명 */ - private String prafOfduNm; - /* 인사직급코드 */ - private String prafOfleCd; - /* 인사직급명 */ - private String prafOfleNm; - /* 인사직책코드 */ - private String prafDutyCd; - /* 인사직책명 */ - private String prafDutyNm; - - private List roleNoList; - private List roleNmList; - private List tgtrPrafNoList; - private List tgtrOgnzNoList; - - // 추가된 LICO 연동 공통 헤더 필수 필드들 - private String strYmd; - private String brafNo; - private String psmrAsrtCd; - private String sbsnRulpAsrtCd; - private String bsduCd; - private String bsquCd; - private String ognzAsrtCd; - private String ognzLeveCd; - private String prgrId; - - - // 유틸성 - private String loginDtm; // 로그인일시 - private String isManager; // 관리자여부 - - public void setLoginDtm() { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); - this.loginDtm = LocalDateTime.now().format(formatter); - } - - public void setIsManager(String isManager) { - // TODO 역할 필터링 후 관리자 여부 체크 - this.isManager = "Y"; - } - - - -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/AxhubSessionService.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/AxhubSessionService.java deleted file mode 100644 index 68c39776f..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/AxhubSessionService.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.shinhanlife.dat.lib.session.mci; - -import io.shinhanlife.dat.lib.session.dto.SessionDto; -import io.shinhanlife.glow.BizException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.List; - -@Slf4j -@Service -@RequiredArgsConstructor -public class AxhubSessionService { - - private final MciOnbszClient mciOnbszClient; - - public SessionDto fetchUserSession(String employeeNo) { - if (employeeNo == null || employeeNo.trim().isEmpty()) { - return null; - } - - try { - ONBSZ0460_I.CmmnPrafIfinInDto input = ONBSZ0460_I.CmmnPrafIfinInDto.builder() - .prafNo(employeeNo) - .build(); - ONBSZ0460_I request = ONBSZ0460_I.builder() - .cmmnPrafIfinInDto(List.of(input)) - .build(); - - ONBSZ0460_O response = mciOnbszClient.callOnbsz0460(request); - - if (response != null && response.getCmmnPrafIfinOutDto() != null - && !response.getCmmnPrafIfinOutDto().isEmpty()) { - ONBSZ0460_O.CmmnPrafIfinOutDto info = response.getCmmnPrafIfinOutDto().get(0); - - SessionDto sessionDto = new SessionDto(); - sessionDto.setOgnzNo(info.getOgnzAsrtCd()); - sessionDto.setPrafNo(info.getPrafNo()); - sessionDto.setStrYmd(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))); - sessionDto.setBrafNo(info.getBrafNo()); - sessionDto.setPsmrAsrtCd(info.getPsmrAsrtCd()); - sessionDto.setSbsnRulpAsrtCd(info.getSbsnRulpAsrtCd()); - sessionDto.setBsduCd(info.getBsduCd()); - sessionDto.setBsquCd(info.getBsquCd()); - sessionDto.setOgnzAsrtCd(info.getOgnzAsrtCd()); - sessionDto.setOgnzLeveCd(info.getOgnzLeveCd()); - - log.info("[AxhubSessionService] MCI ONBSZ0460 조회 성공: 사번={}, 조직코드={}", employeeNo, info.getOgnzAsrtCd()); - return sessionDto; - } - } catch (BizException e) { - log.error("[AxhubSessionService] MCI ONBSZ0460 사원 정보 조회 실패 (사번: {})", employeeNo, e); - } - return null; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/MciOnbszClient.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/MciOnbszClient.java deleted file mode 100644 index 4041c5850..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/MciOnbszClient.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.shinhanlife.dat.lib.session.mci; - -import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent; -import io.shinhanlife.glow.communication.dto.Transfer; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class MciOnbszClient { - - private static final String INTERFACE_ID = "ONBSZ0460"; - private static final String RECEIVE_SERVICE_ID = "ONBSZ"; - - private final AxhubMciComponent mciComponent; - - public ONBSZ0460_O callOnbsz0460(ONBSZ0460_I request) { - Transfer transfer = mciComponent.callTo( - INTERFACE_ID, RECEIVE_SERVICE_ID, request, ONBSZ0460_O.class); - return transfer == null ? null : transfer.getBody(); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/ONBSZ0460_I.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/ONBSZ0460_I.java deleted file mode 100644 index b0db35334..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/ONBSZ0460_I.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.shinhanlife.dat.lib.session.mci; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import io.shinhanlife.glow.communication.annotation.GlowTrgmField; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ONBSZ0460_I { - - @GlowTrgmField(order = 1, description = "공통인사정보조회InDto", type = "gm") - private List cmmnPrafIfinInDto; - - @Getter - @Setter - @Builder - @NoArgsConstructor - @AllArgsConstructor - @JsonIgnoreProperties(ignoreUnknown = true) - public static class CmmnPrafIfinInDto { - - @GlowTrgmField(order = 1, length = 6, description = "마감년월") - private String closYm; - - @GlowTrgmField(order = 2, length = 7, description = "조직번호") - private String ognzNo; - - @GlowTrgmField(order = 3, length = 8, description = "인사번호") - private String prafNo; - - @GlowTrgmField(order = 4, length = 3, description = "인사조직분류코드") - private String psmrAsrtCd; - - @GlowTrgmField(order = 5, length = 1, description = "트레이너구분코드") - private String trnrSccd; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/ONBSZ0460_O.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/ONBSZ0460_O.java deleted file mode 100644 index 58b0b2366..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/session/mci/ONBSZ0460_O.java +++ /dev/null @@ -1,187 +0,0 @@ -package io.shinhanlife.dat.lib.session.mci; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import io.shinhanlife.glow.communication.annotation.GlowTrgmField; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ONBSZ0460_O { - - @GlowTrgmField(order = 1, description = "공통인사정보조회OutDto", type = "gm") - private List cmmnPrafIfinOutDto; - - @Getter - @Setter - @Builder - @NoArgsConstructor - @AllArgsConstructor - @JsonIgnoreProperties(ignoreUnknown = true) - public static class CmmnPrafIfinOutDto { - - @GlowTrgmField(order = 1, length = 8, description = "인사번호") - private String prafNo; - @GlowTrgmField(order = 2, length = 2, description = "인사유형코드") - private String prafTypeCd; - @GlowTrgmField(order = 3, length = 200, description = "인사명") - private String prafNm; - @GlowTrgmField(order = 4, length = 100, description = "인사영문명") - private String prafEngNm; - @GlowTrgmField(order = 5, length = 20, description = "생년월일") - private String birymd; - @GlowTrgmField(order = 6, length = 1, description = "성별코드") - private String gndrCd; - @GlowTrgmField(order = 7, length = 50, description = "주민등록번호") - private String rdreNo; - @GlowTrgmField(order = 8, length = 2, description = "조직분류코드") - private String ognzAsrtCd; - @GlowTrgmField(order = 9, length = 0, description = "TODO 전문 명세 매핑") - private String reserved09; - @GlowTrgmField(order = 10, length = 0, description = "TODO 전문 명세 매핑") - private String reserved10; - @GlowTrgmField(order = 11, length = 0, description = "TODO 전문 명세 매핑") - private String reserved11; - @GlowTrgmField(order = 12, length = 0, description = "TODO 전문 명세 매핑") - private String reserved12; - @GlowTrgmField(order = 13, length = 0, description = "TODO 전문 명세 매핑") - private String reserved13; - @GlowTrgmField(order = 14, length = 0, description = "위촉구분코드 - 길이 확인 필요") - private String appnSccd; - @GlowTrgmField(order = 15, length = 2, description = "위촉경력구분코드") - private String appnCareSccd; - @GlowTrgmField(order = 16, length = 2, description = "위해촉최종상태코드") - private String aprdLsstCd; - @GlowTrgmField(order = 17, length = 20, description = "위촉일자") - private String appnYmd; - @GlowTrgmField(order = 18, length = 20, description = "부임일자") - private String acsoYmd; - @GlowTrgmField(order = 19, length = 20, description = "해임일자") - private String dmssYmd; - @GlowTrgmField(order = 20, length = 20, description = "해촉일자") - private String dsapYmd; - @GlowTrgmField(order = 21, length = 20, description = "재위촉일자") - private String reapYmd; - @GlowTrgmField(order = 22, length = 3, description = "영업직책코드") - private String bsduCd; - @GlowTrgmField(order = 23, length = 10, description = "생명보험협회등록번호") - private String klirNo; - @GlowTrgmField(order = 24, length = 2, description = "신인등급코드") - private String nwfaGrdCd; - @GlowTrgmField(order = 25, length = 2, description = "영업자격코드") - private String bsquCd; - @GlowTrgmField(order = 26, length = 6, description = "자격심사년월") - private String qlfcIspaYm; - @GlowTrgmField(order = 27, length = 2, description = "인사등급코드") - private String prafGrdCd; - @GlowTrgmField(order = 28, length = 0, description = "인사조직분류코드 - 길이 확인 필요") - private String psmrAsrtCd; - @GlowTrgmField(order = 29, length = 0, description = "영업규정분류코드 - 길이 확인 필요") - private String sbsnRulpAsrtCd; - @GlowTrgmField(order = 30, length = 0, description = "조직레벨코드 - 길이 확인 필요") - private String ognzLeveCd; - @GlowTrgmField(order = 31, length = 0, description = "지점번호 - 길이 확인 필요") - private String brafNo; - @GlowTrgmField(order = 32, length = 0, description = "TODO 전문 명세 매핑") - private String reserved32; - @GlowTrgmField(order = 33, length = 0, description = "TODO 전문 명세 매핑") - private String reserved33; - @GlowTrgmField(order = 34, length = 0, description = "TODO 전문 명세 매핑") - private String reserved34; - @GlowTrgmField(order = 35, length = 0, description = "TODO 전문 명세 매핑") - private String reserved35; - @GlowTrgmField(order = 36, length = 0, description = "TODO 전문 명세 매핑") - private String reserved36; - @GlowTrgmField(order = 37, length = 0, description = "TODO 전문 명세 매핑") - private String reserved37; - @GlowTrgmField(order = 38, length = 0, description = "TODO 전문 명세 매핑") - private String reserved38; - @GlowTrgmField(order = 39, length = 0, description = "TODO 전문 명세 매핑") - private String reserved39; - @GlowTrgmField(order = 40, length = 0, description = "TODO 전문 명세 매핑") - private String reserved40; - @GlowTrgmField(order = 41, length = 0, description = "TODO 전문 명세 매핑") - private String reserved41; - @GlowTrgmField(order = 42, length = 0, description = "TODO 전문 명세 매핑") - private String reserved42; - @GlowTrgmField(order = 43, length = 0, description = "TODO 전문 명세 매핑") - private String reserved43; - @GlowTrgmField(order = 44, length = 0, description = "TODO 전문 명세 매핑") - private String reserved44; - @GlowTrgmField(order = 45, length = 0, description = "TODO 전문 명세 매핑") - private String reserved45; - @GlowTrgmField(order = 46, length = 0, description = "TODO 전문 명세 매핑") - private String reserved46; - @GlowTrgmField(order = 47, length = 0, description = "TODO 전문 명세 매핑") - private String reserved47; - @GlowTrgmField(order = 48, length = 0, description = "TODO 전문 명세 매핑") - private String reserved48; - @GlowTrgmField(order = 49, length = 0, description = "TODO 전문 명세 매핑") - private String reserved49; - @GlowTrgmField(order = 50, length = 0, description = "TODO 전문 명세 매핑") - private String reserved50; - @GlowTrgmField(order = 51, length = 0, description = "TODO 전문 명세 매핑") - private String reserved51; - @GlowTrgmField(order = 52, length = 0, description = "TODO 전문 명세 매핑") - private String reserved52; - @GlowTrgmField(order = 53, length = 0, description = "TODO 전문 명세 매핑") - private String reserved53; - @GlowTrgmField(order = 54, length = 0, description = "TODO 전문 명세 매핑") - private String reserved54; - @GlowTrgmField(order = 55, length = 0, description = "TODO 전문 명세 매핑") - private String reserved55; - @GlowTrgmField(order = 56, length = 0, description = "TODO 전문 명세 매핑") - private String reserved56; - @GlowTrgmField(order = 57, length = 0, description = "TODO 전문 명세 매핑") - private String reserved57; - @GlowTrgmField(order = 58, length = 0, description = "TODO 전문 명세 매핑") - private String reserved58; - @GlowTrgmField(order = 59, length = 0, description = "TODO 전문 명세 매핑") - private String reserved59; - @GlowTrgmField(order = 60, length = 0, description = "TODO 전문 명세 매핑") - private String reserved60; - @GlowTrgmField(order = 61, length = 0, description = "TODO 전문 명세 매핑") - private String reserved61; - @GlowTrgmField(order = 62, length = 0, description = "TODO 전문 명세 매핑") - private String reserved62; - @GlowTrgmField(order = 63, length = 0, description = "TODO 전문 명세 매핑") - private String reserved63; - @GlowTrgmField(order = 64, length = 0, description = "TODO 전문 명세 매핑") - private String reserved64; - @GlowTrgmField(order = 65, length = 0, description = "TODO 전문 명세 매핑") - private String reserved65; - @GlowTrgmField(order = 66, length = 0, description = "TODO 전문 명세 매핑") - private String reserved66; - @GlowTrgmField(order = 67, length = 0, description = "TODO 전문 명세 매핑") - private String reserved67; - @GlowTrgmField(order = 68, length = 0, description = "TODO 전문 명세 매핑") - private String reserved68; - @GlowTrgmField(order = 69, length = 0, description = "TODO 전문 명세 매핑") - private String reserved69; - @GlowTrgmField(order = 70, length = 0, description = "TODO 전문 명세 매핑") - private String reserved70; - @GlowTrgmField(order = 71, length = 0, description = "TODO 전문 명세 매핑") - private String reserved71; - @GlowTrgmField(order = 72, length = 0, description = "TODO 전문 명세 매핑") - private String reserved72; - @GlowTrgmField(order = 73, length = 0, description = "TODO 전문 명세 매핑") - private String reserved73; - @GlowTrgmField(order = 74, length = 0, description = "TODO 전문 명세 매핑") - private String reserved74; - @GlowTrgmField(order = 75, length = 0, description = "TODO 전문 명세 매핑") - private String reserved75; - @GlowTrgmField(order = 76, length = 0, description = "TODO 전문 명세 매핑") - private String reserved76; - @GlowTrgmField(order = 77, length = 0, description = "TODO 전문 명세 매핑") - private String reserved77; - @GlowTrgmField(order = 78, length = 0, description = "TODO 전문 명세 매핑") - private String reserved78; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/JsonSchemaGenerator.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/JsonSchemaGenerator.java deleted file mode 100644 index d992b05cb..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/JsonSchemaGenerator.java +++ /dev/null @@ -1,211 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import org.springaicommunity.mcp.annotation.McpToolParam; -import io.swagger.v3.oas.annotations.media.Schema; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import jakarta.validation.constraints.Pattern; - - -/** - * @package io.shinhanlife.dat.lib.util - * @className JsonSchemaGenerator - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public class JsonSchemaGenerator { - - /** - * Java DTO 클래스를 분석하여 MCP 규격의 완전한 JSON Schema를 생성합니다. - */ - public static Map generateSchema(Class clazz) { - return generateSchema(clazz, new HashSet<>()); - } - - private static Map generateSchema(Class clazz, Set> visiting) { - Map schema = new HashMap<>(); - schema.put("type", "object"); - schema.put("additionalProperties", false); - if (!visiting.add(clazz)) { - return schema; - } - - Map properties = new HashMap<>(); - List requiredList = new ArrayList<>(); - - for (Field field : clazz.getDeclaredFields()) { - Map fieldSchema = createFieldSchema(field, visiting); - - // 1. 타입 매핑 - - // 2. 어노테이션 기반 설명 추출 - McpToolParam paramAnnotation = field.getAnnotation(McpToolParam.class); - JsonPropertyDescription descAnnotation = field.getAnnotation(JsonPropertyDescription.class); - if (paramAnnotation != null && !paramAnnotation.description().isEmpty()) { - fieldSchema.put("description", paramAnnotation.description()); - } else if (descAnnotation != null && !descAnnotation.value().isEmpty()) { - fieldSchema.put("description", descAnnotation.value()); - } else { - fieldSchema.put("description", field.getName()); // 기본값 - } - - // 3. 필수 여부 판단 - JsonProperty jsonProp = field.getAnnotation(JsonProperty.class); - if ((jsonProp != null && jsonProp.required()) || (paramAnnotation != null && paramAnnotation.required())) { - requiredList.add(field.getName()); - } - - Pattern patternAnnotation = field.getAnnotation(Pattern.class); - if (patternAnnotation != null && !patternAnnotation.regexp().isEmpty()) { - fieldSchema.put("pattern", patternAnnotation.regexp()); - } - - Schema schemaAnnotation = field.getAnnotation(Schema.class); - if (schemaAnnotation != null) { - if (!schemaAnnotation.description().isEmpty() && !fieldSchema.containsKey("description")) { - fieldSchema.put("description", schemaAnnotation.description()); - } - if ((schemaAnnotation.required() || schemaAnnotation.requiredMode() == Schema.RequiredMode.REQUIRED) - && !requiredList.contains(field.getName())) { - requiredList.add(field.getName()); - } - if (!schemaAnnotation.pattern().isEmpty()) { - fieldSchema.put("pattern", schemaAnnotation.pattern()); - } - if (!schemaAnnotation.minimum().isEmpty()) { - try { - fieldSchema.put("minimum", Long.valueOf(schemaAnnotation.minimum())); - } catch (NumberFormatException ignored) {} - } - if (!schemaAnnotation.maximum().isEmpty()) { - try { - fieldSchema.put("maximum", Long.valueOf(schemaAnnotation.maximum())); - } catch (NumberFormatException ignored) {} - } - if (schemaAnnotation.minLength() > 0) { - fieldSchema.put("minLength", schemaAnnotation.minLength()); - } - if (schemaAnnotation.maxLength() > 0 && schemaAnnotation.maxLength() != Integer.MAX_VALUE) { - fieldSchema.put("maxLength", schemaAnnotation.maxLength()); - } - if (schemaAnnotation.allowableValues().length > 0 && !schemaAnnotation.allowableValues()[0].isEmpty()) { - fieldSchema.put("enum", List.of(schemaAnnotation.allowableValues())); - } - if (!schemaAnnotation.format().isEmpty()) { - fieldSchema.put("format", schemaAnnotation.format()); - } - if (!schemaAnnotation.defaultValue().isEmpty()) { - fieldSchema.put("default", coerceDefaultValue(schemaAnnotation.defaultValue(), field.getType())); - } - if (!schemaAnnotation.example().isEmpty()) { - fieldSchema.put("examples", List.of(schemaAnnotation.example())); - } - if (schemaAnnotation.nullable()) { - Map nonNullSchema = new HashMap<>(fieldSchema); - fieldSchema = new HashMap<>(); - fieldSchema.put("anyOf", List.of( - nonNullSchema, - Map.of("type", "null") - )); - } - } - properties.put(field.getName(), fieldSchema); - } - - schema.put("properties", properties); - if (!requiredList.isEmpty()) { - schema.put("required", requiredList); - } - - // anyOf removed - - visiting.remove(clazz); - return schema; - } - - - private static Object coerceDefaultValue(String value, Class fieldType) { - try { - if (fieldType == Integer.class || fieldType == int.class - || fieldType == Long.class || fieldType == long.class - || fieldType == Short.class || fieldType == short.class - || fieldType == Byte.class || fieldType == byte.class) { - return Long.valueOf(value); - } - if (fieldType == Double.class || fieldType == double.class - || fieldType == Float.class || fieldType == float.class) { - return Double.valueOf(value); - } - if (fieldType == Boolean.class || fieldType == boolean.class) { - return Boolean.valueOf(value); - } - return value; - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Invalid MCP default value: " + value, e); - } - } - private static Map createFieldSchema(Field field, Set> visiting) { - Class fieldType = field.getType(); - if (isSimpleType(fieldType)) { - return new HashMap<>(Map.of("type", mapJavaTypeToJsonType(fieldType))); - } - if (List.class.isAssignableFrom(fieldType)) { - Map fieldSchema = new HashMap<>(); - fieldSchema.put("type", "array"); - fieldSchema.put("items", generateItemsSchema(field, visiting)); - return fieldSchema; - } - return generateSchema(fieldType, visiting); - } - - private static Map generateItemsSchema(Field field, Set> visiting) { - Type genericType = field.getGenericType(); - if (genericType instanceof ParameterizedType parameterizedType) { - Type itemType = parameterizedType.getActualTypeArguments()[0]; - if (itemType instanceof Class itemClass) { - if (isSimpleType(itemClass)) { - return new HashMap<>(Map.of("type", mapJavaTypeToJsonType(itemClass))); - } - return generateSchema(itemClass, visiting); - } - } - return new HashMap<>(Map.of("type", "object")); - } - - private static boolean isSimpleType(Class clazz) { - return clazz == String.class - || clazz == Integer.class || clazz == int.class - || clazz == Long.class || clazz == long.class - || clazz == Double.class || clazz == double.class - || clazz == Float.class || clazz == float.class - || clazz == Boolean.class || clazz == boolean.class; - } - - private static String mapJavaTypeToJsonType(Class clazz) { - if (clazz == String.class) return "string"; - if (clazz == Integer.class || clazz == int.class) return "integer"; - if (clazz == Long.class || clazz == long.class) return "integer"; - if (clazz == Double.class || clazz == double.class) return "number"; - if (clazz == Float.class || clazz == float.class) return "number"; - if (clazz == Boolean.class || clazz == boolean.class) return "boolean"; - if (List.class.isAssignableFrom(clazz)) return "array"; - return "object"; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/PodScaffolder.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/PodScaffolder.java deleted file mode 100644 index ef3fc38b1..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/PodScaffolder.java +++ /dev/null @@ -1,488 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.charset.StandardCharsets; -import java.nio.file.StandardOpenOption; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class PodScaffolder { - - private static final ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory()); - - public static void main(String[] args) throws IOException { - Scanner scanner = new Scanner(System.in); - - String rawModuleName = getOrAsk(args, 0, scanner, "1. 생성할 모듈(Pod) 이름 (예: payment 또는 dat-was-payment): "); - String moduleName = rawModuleName.startsWith("dat-was-") ? rawModuleName : "dat-was-" + rawModuleName; - String portStr = getOrAsk(args, 1, scanner, "2. 사용할 포트 번호 (예: 8085): "); - String shortName = moduleName.replace("dat-was-", "").replace("-", ""); - - String defaultAuthor = System.getProperty("user.name"); - String defaultDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd")); - - String author = getOrAsk(args, 2, scanner, "3. 작성자 (엔터 입력 시 '" + defaultAuthor + "'): "); - if (author.trim().isEmpty()) author = defaultAuthor; - String createDate = getOrAsk(args, 3, scanner, "4. 작성일 (엔터 입력 시 '" + defaultDate + "'): "); - if (createDate.trim().isEmpty()) createDate = defaultDate; - - String result = scaffoldPod(moduleName, portStr, shortName, author, createDate); - } - - private static String getOrAsk(String[] args, int index, Scanner scanner, String prompt) { - if (args.length > index) { - return args[index]; - } - return scanner.nextLine().trim(); - } - - public static String scaffoldPod(String moduleName, String portStr, String shortName, String author, String createDate) throws IOException { - String envSourceDir = System.getProperty("AXHUB_SOURCE_DIR"); - if (envSourceDir == null || envSourceDir.isBlank()) { - envSourceDir = System.getenv("AXHUB_SOURCE_DIR"); - } - Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get("."); - return scaffoldPod(rootDir, moduleName, portStr, shortName, author, createDate, null); - } - - public static String scaffoldPod(String moduleName, String portStr, String shortName, String author, - String createDate, String toolServiceManifest) throws IOException { - String envSourceDir = System.getProperty("AXHUB_SOURCE_DIR"); - if (envSourceDir == null || envSourceDir.isBlank()) envSourceDir = System.getenv("AXHUB_SOURCE_DIR"); - Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get("."); - return scaffoldPod(rootDir, moduleName, portStr, shortName, author, createDate, toolServiceManifest); - } - - static String scaffoldPod(Path rootDir, String moduleName, String portStr, String shortName, - String author, String createDate) throws IOException { - return scaffoldPod(rootDir, moduleName, portStr, shortName, author, createDate, null); - } - - static String scaffoldPod(Path rootDir, String moduleName, String portStr, String shortName, - String author, String createDate, String toolServiceManifest) throws IOException { - Path modulePath = rootDir.resolve(Paths.get(moduleName)); - if (Files.exists(modulePath)) { - return "[오류] 이미 존재하는 모듈입니다: " + moduleName; - } - - StringBuilder log = new StringBuilder(); - log.append("[1/6] 모듈 디렉터리 생성 중...\n"); - Files.createDirectories(modulePath); - - log.append("[2/6] build.gradle 생성 중...\n"); - String buildGradle = """ - plugins { - // Spring Boot 3.5.11 version is managed by the root build.gradle. - id 'org.springframework.boot' - } - - dependencies { - // Shared MCP, Glow integration, validation, logging, Lombok and MapStruct configuration. - implementation project(':dat-was-lib') - } - """; - writeUtf8(modulePath.resolve("build.gradle"), buildGradle); - - log.append("[3/6] Dockerfile 생성 중...\n"); - String dockerfile = """ - FROM eclipse-temurin:21-jre-alpine - WORKDIR /app - RUN apk add --no-cache tzdata - ENV TZ=Asia/Seoul - COPY %s/build/libs/*-SNAPSHOT.jar app.jar - EXPOSE %s - ENTRYPOINT ["java", "-jar", "app.jar"] - """.formatted(moduleName, portStr); - writeUtf8(modulePath.resolve("Dockerfile"), dockerfile); - - log.append("[4/6] Application 클래스 및 설정 파일 생성 중...\n"); - Path srcPath = modulePath.resolve("src/main/java/io/shinhanlife/dat/mcc/" + shortName); - Files.createDirectories(srcPath); - - String appClass = """ - package io.shinhanlife.dat.mcc.%s; - - import org.springframework.boot.SpringApplication; - import org.springframework.boot.autoconfigure.SpringBootApplication; - import org.springframework.boot.context.properties.ConfigurationPropertiesScan; - import org.springframework.cache.annotation.EnableCaching; - - /** - * @package io.shinhanlife.dat.mcc.%s - * @className DatWas%sApplication - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-             * ---------- 개정이력 ----------
-             * 수정일      수정자    수정내용
-             * ---------- -------- ---------------------------
-             * %s  %s    최초생성
-             * 
-             * 
- */ - @SpringBootApplication(scanBasePackages = {"io.shinhanlife.dat.mcc", "io.shinhanlife.dat.lib"}) - @ConfigurationPropertiesScan(basePackages = {"io.shinhanlife.dat.mcc", "io.shinhanlife.dat.lib"}) - @EnableCaching - public class DatWas%sApplication { - public static void main(String[] args) { - SpringApplication.run(DatWas%sApplication.class, args); - } - } - """.formatted(shortName, shortName, capitalize(shortName), author, createDate, createDate, author, capitalize(shortName), capitalize(shortName)); - writeUtf8(srcPath.resolve("DatWas" + capitalize(shortName) + "Application.java"), appClass); - - Path resPath = modulePath.resolve("src/main/resources"); - Files.createDirectories(resPath); - String applicationYml = """ - server: - port: %s - spring: - application: - name: %s - config: - import: optional:classpath:tool-service-manifest.yml - profiles: - active: local - logging: - level: - org.apache.kafka: ERROR - mcp: - namespace: "" - manifest: - bundle-id: %s - name-prefix: "" - security: - api-key: ${TOOL_SERVER_API_KEY:tool-server-key} - tenant-domains: - TESTER-DEV: ALL - axhub: - tool: - url: ${AXHUB_TOOL_URL:http://localhost:${server.port}} - """.formatted(portStr, moduleName, moduleName.replace("dap-", "")); - writeUtf8(resPath.resolve("application.yml"), applicationYml); - - String manifest = toolServiceManifest == null || toolServiceManifest.isBlank() - ? defaultToolServiceManifest(moduleName) : toolServiceManifest.trim() + System.lineSeparator(); - writeUtf8(resPath.resolve("tool-service-manifest.yml"), manifest); - - String applicationLocalYml = """ - # Local 환경 전용 설정 (H2 메모리 DB 등) - spring: - config: - activate: - on-profile: local - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-local.yml - datasource: - url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1; - username: sa - password: password - h2: - console: - enabled: true - - eims: - http: - url: http://localhost:${server.port}/api/gateway - tcp: - host: 127.0.0.1 - port: 8090 - timeout: 5000 - jsp: - form: - url: http://localhost:${server.port}/mock/jsp-form - json: - url: http://localhost:${server.port}/mock/jsp-json - mci: - url: http://localhost:${server.port}/api/mock/esb/api - mcistring: - url: http://localhost:${server.port}/api/mock/esb/string - - mcp: - security: - tenant-domains: - mcp-client-1: CUSTOMER,COMMON - mcp-client-2: ALL - - axhub: - gateway: - url: http://localhost:8081 - tool: - url: ${AXHUB_TOOL_URL:http://localhost:${server.port}} - """; - writeUtf8(resPath.resolve("application-local.yml"), applicationLocalYml); - - String applicationDevYml = """ - # OCI 클라우드 환경 전용 설정 - server: - port: ${PORT:%s} - - spring: - config: - activate: - on-profile: dev - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-dev.yml - - axhub: - gateway: - url: https://axhubmcp.devjun.net - tool: - url: http://144.24.70.100:%s - - eims: - http: - url: http://localhost:${server.port}/api/gateway - tcp: - host: 127.0.0.1 - port: 8090 - timeout: 5000 - jsp: - form: - url: http://localhost:${server.port}/mock/jsp-form - json: - url: http://localhost:${server.port}/mock/jsp-json - mci: - url: http://localhost:${server.port}/api/mock/esb/api - mcistring: - url: http://localhost:${server.port}/api/mock/esb/string - - shinhan: - integration: - envrTypeCd: D - eai: - url: http://10.176.32.181 - internalMci: - url: http://10.176.32.173 - bancaMci: - url: http://10.176.32.117 - externalMci: - url: http://10.176.32.176 - """.formatted(portStr, portStr); - writeUtf8(resPath.resolve("application-dev.yml"), applicationDevYml); - - String applicationTestYml = """ - server: - port: ${PORT:%s} - - spring: - config: - activate: - on-profile: test - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-test.yml - - axhub: - gateway: - url: ${AXHUB_GATEWAY_URL} - tool: - url: ${AXHUB_TOOL_URL} - """.formatted(portStr); - writeUtf8(resPath.resolve("application-test.yml"), applicationTestYml); - - String applicationProdYml = """ - server: - port: ${PORT:%s} - - spring: - config: - activate: - on-profile: prod - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-prod.yml - - axhub: - gateway: - url: ${AXHUB_GATEWAY_URL} - tool: - url: ${AXHUB_TOOL_URL} - """.formatted(portStr); - writeUtf8(resPath.resolve("application-prod.yml"), applicationProdYml); - - String logbackXml = """ - - - - - - ${LOG_PATTERN} - - - - logs/%s.log - - logs/%s-%%d{yyyy-MM-dd}.log - 30 - - - ${LOG_PATTERN} - - - - - - - - - """.formatted(moduleName, moduleName); - writeUtf8(resPath.resolve("logback-spring.xml"), logbackXml); - - log.append("[5/6] settings.gradle 에 모듈 등록 중...\n"); - Path settingsPath = rootDir.resolve(Paths.get("settings.gradle")); - if (Files.exists(settingsPath)) { - String settings = Files.readString(settingsPath, StandardCharsets.UTF_8); - if (!settings.contains("include '" + moduleName + "'")) { - Files.writeString(settingsPath, System.lineSeparator() + "include '" + moduleName + "'" + System.lineSeparator(), StandardCharsets.UTF_8, StandardOpenOption.APPEND); - } - } - - log.append("[6/6] docker-compose.yml 에 서비스 추가 중...\n"); - Path dockerComposePath = rootDir.resolve(Paths.get("docker-compose.yml")); - if (Files.exists(dockerComposePath)) { - String compose = Files.readString(dockerComposePath, StandardCharsets.UTF_8); - String serviceName = moduleName.replace("dap-", ""); // e.g. tool-payment - if (!compose.contains(" " + serviceName + ":")) { - String newService = """ - %s: - build: - context: . - dockerfile: %s/Dockerfile - ports: - - "%s:%s" - environment: - - TZ=Asia/Seoul - - TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key} - - AXHUB_GATEWAY_URL=http://gateway:8081 - - AXHUB_TOOL_URL=http://%s:%s - - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - - GLOW_COMMUNICATION_MCI_PORT=8080 - - GLOW_COMMUNICATION_EXTMCI_HOST=http://mci-mock - - GLOW_COMMUNICATION_EXTMCI_PORT=8080 - - GLOW_COMMUNICATION_EAI_HOST=http://mci-mock - - GLOW_COMMUNICATION_EAI_PORT=8080 - """.formatted(serviceName, moduleName, portStr, portStr, serviceName, portStr); - Files.writeString(dockerComposePath, System.lineSeparator() + newService, StandardCharsets.UTF_8, StandardOpenOption.APPEND); - } - } - - List updatedManifests = updateReciprocalConfusableServers(rootDir, moduleName, manifest); - if (!updatedManifests.isEmpty()) { - log.append("[추가] 기존 Pod confusable-servers 갱신: ") - .append(String.join(", ", updatedManifests)).append("\n"); - } - - log.append("\n=========================================\n"); - log.append(" Pod Scaffolding Complete! \n"); - log.append("=========================================\n"); - log.append("1. [새로운 모듈] ").append(moduleName).append(" 폴더가 생성되었습니다.\n"); - log.append("2. [ToolScaffolder]를 사용해 이 모듈 안에 툴을 추가하세요.\n"); - log.append("3. 실행 전 Gradle 동기화(Sync)를 한 번 진행해 주세요.\n"); - return log.toString(); - } - - private static void writeUtf8(Path path, String content) throws IOException { - Files.writeString(path, content, StandardCharsets.UTF_8); - } - - private static String defaultToolServiceManifest(String moduleName) { - String key = moduleName.replace("dat-was-", ""); - return """ - mcp: - manifest: - routing-functions: - - name: route_to_%s - description-serialization: %s 업무 서버로 요청을 라우팅합니다. %s 관련 업무를 처리합니다. 요청의 주요 업무 영역을 기준으로 서버를 선택합니다. - server-id: %s - category-key: %s - product-boundary: insurance - business-domain: %s 업무 - business-outcome: %s 관련 업무를 처리합니다. - primary-entities: [] - capabilities: [] - select-if: %s 관련 요청인 경우 - reject-if: 다른 업무 영역이 주된 요청인 경우 - confusable-servers: [] - decision-policy: 요청의 주요 업무 영역을 기준으로 서버를 선택합니다. - """.formatted(moduleName, key, key, moduleName, key, key, key, key); - } - - private static List updateReciprocalConfusableServers(Path rootDir, String moduleName, - String newManifest) throws IOException { - List updated = new ArrayList<>(); - for (String target : extractConfusableServers(newManifest)) { - if (target.equals(moduleName) || !target.matches("^dat-was-[a-z0-9-]+$")) continue; - Path path = rootDir.resolve(target).resolve("src/main/resources/tool-service-manifest.yml"); - if (!Files.isRegularFile(path)) continue; - String before = Files.readString(path, StandardCharsets.UTF_8); - String after = addConfusableServer(before, moduleName); - if (!before.equals(after)) { - writeUtf8(path, after); - updated.add(rootDir.relativize(path).toString().replace('\\', '/')); - } - } - return updated; - } - - @SuppressWarnings("unchecked") - private static List extractConfusableServers(String manifest) throws IOException { - Map root = YAML_MAPPER.readValue(manifest, Map.class); - Object mcpValue = root.get("mcp"); - if (!(mcpValue instanceof Map mcp)) return List.of(); - Object manifestValue = mcp.get("manifest"); - if (!(manifestValue instanceof Map manifestMap)) return List.of(); - Object functionsValue = manifestMap.get("routing-functions"); - if (!(functionsValue instanceof List functions) || functions.isEmpty() - || !(functions.getFirst() instanceof Map function)) return List.of(); - Object serversValue = function.get("confusable-servers"); - if (!(serversValue instanceof Collection servers)) return List.of(); - LinkedHashSet values = new LinkedHashSet<>(); - for (Object value : servers) { - String normalized = value == null ? "" : value.toString().trim(); - if (!normalized.isBlank()) values.add(normalized); - } - return List.copyOf(values); - } - - private static String addConfusableServer(String manifest, String moduleName) { - Pattern pattern = Pattern.compile("(?m)^(\\s*)confusable-servers:\\s*\\[([^]]*)]\\s*$"); - Matcher matcher = pattern.matcher(manifest); - if (matcher.find()) { - LinkedHashSet values = new LinkedHashSet<>(); - for (String value : matcher.group(2).split(",")) { - String normalized = value.trim(); - if (!normalized.isBlank()) values.add(normalized); - } - if (!values.add(moduleName)) return manifest; - String replacement = matcher.group(1) + "confusable-servers: [" + String.join(", ", values) + "]"; - return matcher.replaceFirst(Matcher.quoteReplacement(replacement)); - } - Matcher category = Pattern.compile("(?m)^(\\s*)category-key:[^\\r\\n]*$").matcher(manifest); - if (!category.find()) return manifest; - String replacement = category.group() + System.lineSeparator() + category.group(1) - + "confusable-servers: [" + moduleName + "]"; - return category.replaceFirst(Matcher.quoteReplacement(replacement)); - } - - private static String capitalize(String str) { - if (str == null || str.isEmpty()) return str; - return str.substring(0, 1).toUpperCase() + str.substring(1); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/SessionUtil.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/SessionUtil.java deleted file mode 100644 index d89eb7191..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/SessionUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import io.shinhanlife.dat.lib.session.dto.SessionDto; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -/** - * @package io.shinhanlife.dat.lib.util - * @className SessionUtil - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public class SessionUtil { - - private static final String SESSION_KEY = "userInfo"; - - private SessionUtil() {} - - public static SessionDto getSession() { - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - if (attributes == null) return null; - - // 1. Check HTTP Request Attribute first (populated by McpRequestHeaderFilter) - HttpServletRequest request = attributes.getRequest(); - SessionDto requestSession = (SessionDto) request.getAttribute(SESSION_KEY); - if (requestSession != null) { - return requestSession; - } - - // 2. Fallback to standard HttpSession - HttpSession session = request.getSession(false); - if (session == null) return null; - return (SessionDto) session.getAttribute(SESSION_KEY); - } - - public static String getPrafNo() { - SessionDto session = getSession(); - return session != null ? session.getPrafNo() : null; - } - - public static String getOgnzNo() { - SessionDto session = getSession(); - return session != null ? session.getOgnzNo() : null; - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolAnnotationSyncRunner.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolAnnotationSyncRunner.java deleted file mode 100644 index 755b74864..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolAnnotationSyncRunner.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import io.shinhanlife.dat.lib.metadata.ToolDefinition; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.stream.Stream; -import java.util.List; - -/** - * CI/CD 및 로컬 개발 환경에서 V17 Tool YAML 정의 파일을 읽어 Java @McpTool 어노테이션을 동기화합니다. - */ -public final class ToolAnnotationSyncRunner { - - private ToolAnnotationSyncRunner() { - } - - public static void main(String[] args) { - if (args.length != 1) { - throw new IllegalArgumentException("Usage: ToolAnnotationSyncRunner "); - } - sync(Path.of(args[0])); - } - - static void sync(Path projectRoot) { - ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); - int syncCount = 0; - try (Stream files = Files.walk(projectRoot)) { - List yamlFiles = files.filter(Files::isRegularFile) - .filter(path -> path.toString().replace('\\', '/').contains("/src/main/resources/tool-definitions/")) - .filter(path -> path.toString().endsWith(".yml") || path.toString().endsWith(".yaml")) - .toList(); - - for (Path file : yamlFiles) { - ToolDefinition definition = yamlMapper.readValue(file.toFile(), ToolDefinition.class); - - String toolName = definition.name(); - String title = definition.displayName(); - String description = definition.displayDescription(); - boolean readOnlyHint = Boolean.TRUE.equals(definition.readOnly()); - boolean destructiveHint = Boolean.TRUE.equals(definition.destructive()); - boolean idempotentHint = Boolean.TRUE.equals(definition.idempotent()); - String categoryKey = definition.categoryKey(); - - try { - boolean updated = ToolSourceUpdater.syncToolSource(projectRoot, toolName, title, description, - readOnlyHint, destructiveHint, idempotentHint, categoryKey); - if (updated) { - syncCount++; - System.out.println("Synced (Updated): " + toolName); - } else { - System.out.println("Synced (Unchanged): " + toolName); - } - } catch (IllegalArgumentException e) { - System.err.println("Skipped (Not Found): " + toolName); - } - } - } catch (IOException exception) { - throw new IllegalStateException("Failed to run tool annotation sync", exception); - } - System.out.println("Tool annotation sync completed: " + syncCount + " tools updated."); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolScaffolder.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolScaffolder.java deleted file mode 100644 index 94ad3ee57..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolScaffolder.java +++ /dev/null @@ -1,2196 +0,0 @@ -package io.shinhanlife.dat.lib.util; - - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.charset.StandardCharsets; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.Scanner; -import java.util.stream.Collectors; - -/** - * MCP Tool 코드를 자동 생성(Scaffolding)하는 유틸리티 클래스입니다. - * - * [실행 방법] - * 방법 1. IDE(IntelliJ 등)에서 직접 실행 - * - ToolScaffolder.java의 main 메서드를 실행합니다. - * - 콘솔 질문에 차례대로 값을 입력하면 파일이 생성됩니다. - * - * 방법 2. 명령줄에서 실행 - * - 컴파일: javac -encoding UTF-8 dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolScaffolder.java - * - 실행: java -cp dat-was-lib/src/main/java io.shinhanlife.dat.lib.util.ToolScaffolder [이름] [ID] "[설명]" "[그룹]" "[통신방식]" "[모듈명]" - */ -/** - * @package io.shinhanlife.dat.lib.util - * @className ToolScaffolder - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일       수정자     수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- *
- * 
- */ -public class ToolScaffolder { - - private static final String BASE_PACKAGE = "io.shinhanlife.dat.mcc"; - private static final String BASE_PACKAGE_PATH = "src/main/java/io/shinhanlife/dat/mcc"; - - public record FieldDefinition(String name, String type, String description, List examples, String pattern, boolean required, - List enumValues, String itemType, List itemFields) { - public FieldDefinition(String name, String type, String description, List examples, String pattern, boolean required) { - this(name, type, description, examples, pattern, required, List.of(), null, List.of()); - } - } - - public record ToolMethodDefinition(String baseName, String methodName, String interfaceId, - String title, String description, String group, String routingType, - boolean register, String clientSystemCode, String httpApiName, - List inputFields, List outputFields, - ToolDefinitionOptions definitionOptions) { - } - - public record ToolDefinitionOptions( - String functionDescription, - String whenToUse, - String whenNotToUse, - String ioLimits, - String displayDescription, - List exampleQueries, - List tags, - String ownerOrg) { - } - - public static String scaffoldUseCase(String useCaseName, String moduleName, String author, - String createDate, List tools) throws IOException { - if (tools == null || tools.isEmpty()) { - throw new IllegalArgumentException("At least one Tool method is required."); - } - String useCaseBaseName = toPascalCase(useCaseName); - String group = tools.getFirst().group().toLowerCase(Locale.ROOT); - validateToolMethods(tools, group); - - String sourceDir = System.getProperty("AXHUB_SOURCE_DIR"); - if (sourceDir == null) { - sourceDir = System.getenv("AXHUB_SOURCE_DIR"); - } - Path rootDir = sourceDir != null ? Paths.get(sourceDir) : Paths.get("."); - Path configuredModule = Paths.get(moduleName); - Path moduleRoot = configuredModule.isAbsolute() ? configuredModule : rootDir.resolve(configuredModule); - Path sourceRoot = moduleRoot.resolve(BASE_PACKAGE_PATH); - Path useCaseDir = sourceRoot.resolve(Paths.get("biz", group, "usecase")); - Path implDir = useCaseDir.resolve("impl"); - Path dtoDir = sourceRoot.resolve(Paths.get("biz", group, "dto")); - Path converterDir = sourceRoot.resolve(Paths.get("biz", group, "converter")); - Path definitionDir = moduleRoot.resolve(Paths.get("src", "main", "resources", "tool-definitions", group)); - Path mockDir = moduleRoot.resolve(Paths.get("src", "main", "resources", "mock-responses")); - Files.createDirectories(useCaseDir); - Files.createDirectories(implDir); - Files.createDirectories(dtoDir); - Files.createDirectories(converterDir); - Files.createDirectories(definitionDir); - Files.createDirectories(mockDir); - - String bizPackage = BASE_PACKAGE + ".biz." + group; - Path useCaseFile = useCaseDir.resolve(useCaseBaseName + "UseCase.java"); - Path useCaseImplFile = implDir.resolve(useCaseBaseName + "UseCaseImpl.java"); - Path converterFile = converterDir.resolve(useCaseBaseName + "Converter.java"); - boolean existingUseCase = Files.exists(useCaseFile); - if (existingUseCase) { - appendGroupedUseCaseSources(useCaseFile, useCaseImplFile, converterFile, bizPackage, useCaseBaseName, - moduleName, tools); - } else { - writeUtf8(useCaseFile, groupedUseCaseContent(bizPackage, useCaseBaseName, moduleName, tools)); - writeUtf8(useCaseImplFile, groupedUseCaseImplContent(bizPackage, useCaseBaseName, tools)); - writeUtf8(converterFile, groupedConverterContent(bizPackage, useCaseBaseName, tools)); - } - - StringBuilder log = new StringBuilder("\n=========================================\n") - .append(" Multi Tool Scaffolding Complete\n") - .append("=========================================\n") - .append(existingUseCase ? " Existing UseCase Extended\n" : " New UseCase Created\n") - .append("[Usecase Interface] ").append(useCaseFile).append("\n") - .append("[Usecase Impl] ").append(useCaseImplFile).append("\n"); - for (ToolMethodDefinition tool : tools) { - writeGroupedToolFiles(moduleRoot, sourceRoot, dtoDir, definitionDir, mockDir, bizPackage, tool, moduleName, log); - if ("HTTP".equalsIgnoreCase(tool.routingType())) { - ensureLocalHttpApiConfiguration(moduleRoot, tool.httpApiName(), - toToolName(moduleName, tool.group(), toPascalCase(tool.baseName()))); - } - } - log.append("[Converter] ").append(converterFile).append("\n"); - return log.toString(); - } - - private static void validateToolMethods(List tools, String expectedGroup) { - Set methods = new LinkedHashSet<>(); - Set toolNames = new LinkedHashSet<>(); - for (ToolMethodDefinition tool : tools) { - if (tool == null || tool.baseName() == null || tool.baseName().isBlank() - || tool.methodName() == null || !tool.methodName().matches("^[a-zA-Z_$][a-zA-Z0-9_$]*$")) { - throw new IllegalArgumentException("Every Tool needs a valid base name and Java method name."); - } - if (!expectedGroup.equalsIgnoreCase(tool.group())) { - throw new IllegalArgumentException("All Tool methods in one UseCase must use the same category."); - } - boolean mci = "MCI".equalsIgnoreCase(tool.routingType()); - boolean http = "HTTP".equalsIgnoreCase(tool.routingType()); - if (!mci && !http) { - throw new IllegalArgumentException("Grouped Tool supports only MCI or HTTP routing."); - } - if (mci && (tool.interfaceId() == null || tool.interfaceId().isBlank() - || tool.clientSystemCode() == null || tool.clientSystemCode().isBlank())) { - throw new IllegalArgumentException("MCI Tool needs an interface ID and Client system code."); - } - if (http && (tool.httpApiName() == null || tool.httpApiName().isBlank())) { - throw new IllegalArgumentException("HTTP Tool needs an HTTP API name."); - } - String toolName = toToolName("", tool.group(), toPascalCase(tool.baseName())); - if (!methods.add(tool.methodName()) || !toolNames.add(toolName)) { - throw new IllegalArgumentException("Tool method names and MCP Tool names must be unique."); - } - } - } - - private static void writeGroupedToolFiles(Path moduleRoot, Path sourceRoot, Path dtoDir, Path definitionDir, - Path mockDir, String bizPackage, ToolMethodDefinition tool, - String moduleName, StringBuilder log) throws IOException { - String baseName = toPascalCase(tool.baseName()); - boolean mci = "MCI".equalsIgnoreCase(tool.routingType()); - String code = mci ? (tool.clientSystemCode().length() == 4 ? tool.clientSystemCode().substring(0,3).toLowerCase(Locale.ROOT) + "/" + tool.clientSystemCode().substring(3).toLowerCase(Locale.ROOT) : tool.clientSystemCode().toLowerCase(Locale.ROOT)) : toPackageSegment(tool.httpApiName()); - String ioPackage = BASE_PACKAGE + (mci ? ".infra.itrf.mci." : ".infra.itrf.http.") + code.replace("/", "."); - Path clientDir = sourceRoot.resolve(Paths.get("infra", "itrf", mci ? "mci" : "http", code)); - Path ioDir = clientDir.resolve("io"); - Files.createDirectories(ioDir); - writeUtf8(dtoDir.resolve(baseName + "Request.java"), - dtoContent(bizPackage + ".dto", baseName + "Request", tool.inputFields(), "", "", true)); - writeUtf8(dtoDir.resolve(baseName + "Response.java"), - dtoContent(bizPackage + ".dto", baseName + "Response", tool.outputFields(), "", "", false)); - writeStructuredFieldTypes(dtoDir, bizPackage + ".dto", baseName + "Request", tool.inputFields()); - writeStructuredFieldTypes(dtoDir, bizPackage + ".dto", baseName + "Response", tool.outputFields()); - if (mci) { - writeUtf8(ioDir.resolve(tool.interfaceId() + "_I.java"), - mciIoContent("infra.itrf.mci." + code.replace("/", "."), tool.interfaceId() + "_I", tool.inputFields(), "", "")); - writeUtf8(ioDir.resolve(tool.interfaceId() + "_O.java"), - mciIoContent("infra.itrf.mci." + code.replace("/", "."), tool.interfaceId() + "_O", tool.outputFields(), "", "")); - writeStructuredFieldTypes(ioDir, ioPackage + ".io", tool.interfaceId() + "_I", tool.inputFields()); - writeStructuredFieldTypes(ioDir, ioPackage + ".io", tool.interfaceId() + "_O", tool.outputFields()); - String sysCode = tool.clientSystemCode(); - if (sysCode != null && sysCode.length() == 4) { - String normalizedSysCode = sysCode.toLowerCase(Locale.ROOT); - String clientCap = normalizedSysCode.substring(0, 1).toUpperCase(Locale.ROOT) - + normalizedSysCode.substring(1); - 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 Transfer callTo(String interfaceId, String dummy, Object mciReq, Class resType) throws Exception {\n return mci.callTo(interfaceId, dummy, mciReq, resType);\n }\n}\n"); - } else { - writeUtf8(clientDir.resolve(baseName + "Client.java"), groupedMciClientContent(ioPackage, baseName, tool.interfaceId())); - } - writeUtf8(sourceRoot.resolve(Paths.get("biz", tool.group().toLowerCase(Locale.ROOT), "converter", baseName + "Converter.java")), - groupedMciConverterContent(bizPackage, baseName, ioPackage, tool.interfaceId())); - } else { - writeUtf8(ioDir.resolve(baseName + "HttpRequest.java"), - dtoContent(ioPackage + ".io", baseName + "HttpRequest", tool.inputFields(), "", "", true)); - writeUtf8(ioDir.resolve(baseName + "HttpResponse.java"), - dtoContent(ioPackage + ".io", baseName + "HttpResponse", tool.outputFields(), "", "", false)); - writeStructuredFieldTypes(ioDir, ioPackage + ".io", baseName + "HttpRequest", tool.inputFields()); - writeStructuredFieldTypes(ioDir, ioPackage + ".io", baseName + "HttpResponse", tool.outputFields()); - writeUtf8(clientDir.resolve(baseName + "Client.java"), - httpClientContent(ioPackage, baseName + "Client", tool.httpApiName())); - writeUtf8(sourceRoot.resolve(Paths.get("biz", tool.group().toLowerCase(Locale.ROOT), "converter", baseName + "Converter.java")), - groupedHttpConverterContent(bizPackage, baseName, ioPackage)); - } - - String toolName = toToolName(moduleName, tool.group(), baseName); - writeUtf8(mockDir.resolve(toolName + ".json"), mockResponseContent(tool.outputFields())); - log.append("[Tool] ").append(toolName).append(" -> ").append(clientDir.resolve(baseName + "Client.java")).append("\n"); - } - - private static String groupedUseCaseContent(String bizPackage, String useCaseBaseName, String moduleName, - List tools) { - StringBuilder imports = new StringBuilder(); - StringBuilder methods = new StringBuilder(); - for (ToolMethodDefinition tool : tools) { - String baseName = toPascalCase(tool.baseName()); - imports.append("import ").append(bizPackage).append(".dto.").append(baseName).append("Request;\n") - .append("import ").append(bizPackage).append(".dto.").append(baseName).append("Response;\n"); - boolean isMutation = isMutationTool(baseName); - ToolDefinitionOptions opts = tool.definitionOptions() == null ? new ToolDefinitionOptions(null, null, null, null, null, null, null, null) : tool.definitionOptions(); - - methods.append(" @McpTool(name = \"").append(toToolName(moduleName, tool.group(), baseName)) - .append("\", title = \"").append(javaText(option(tool.title(), baseName))) - .append("\", description = \"").append(javaText(option(tool.description(), ""))).append("\")\n") - .append(" @GrowToolHint(\n") - .append(" requiresApproval = ").append(isMutation).append(",\n") - .append(" categoryKey = \"").append(tool.group().toLowerCase(Locale.ROOT)).append("\",\n") - .append(" timeoutMillis = 5000L,\n") - .append(" retryMaxAttempts = 3,\n"); - if (tool.interfaceId() != null && !tool.interfaceId().isBlank()) { - methods.append(" mappingId = \"").append(javaText(tool.interfaceId())).append("\",\n"); - } - methods.append(" functionDescription = \"").append(javaText(opts.functionDescription() != null && !opts.functionDescription().isBlank() ? opts.functionDescription() : option(tool.title(), baseName) + " 기능을 수행합니다.")).append("\",\n") - .append(" whenToUse = \"").append(javaText(opts.whenToUse() != null && !opts.whenToUse().isBlank() ? opts.whenToUse() : "사용자가 이 업무 기능의 실행 또는 조회를 요청할 때 사용합니다.")).append("\",\n") - .append(" whenNotToUse = \"").append(javaText(opts.whenNotToUse() != null && !opts.whenNotToUse().isBlank() ? opts.whenNotToUse() : "정보 변경이나 실행 작업에는 사용하지 않습니다.")).append("\",\n") - .append(" ioLimits = \"").append(javaText(opts.ioLimits() != null && !opts.ioLimits().isBlank() ? opts.ioLimits() : "정의된 입력 항목만 허용하며 업무 결과만 반환합니다.")).append("\",\n") - .append(" displayDescription = \"").append(javaText(opts.displayDescription() != null && !opts.displayDescription().isBlank() ? opts.displayDescription() : option(tool.title(), baseName) + " 정보를 처리합니다.")).append("\",\n"); - - List examples = opts.exampleQueries(); - if (examples == null || examples.isEmpty()) { - examples = List.of(option(tool.title(), baseName) + " 정보를 보여줘", option(tool.title(), baseName) + " 확인해줘", "현재 " + option(tool.title(), baseName) + " 알려줘"); - } - methods.append(" exampleQueries = {") - .append(examples.stream().map(q -> "\"" + javaText(q) + "\"").collect(Collectors.joining(", "))) - .append("},\n") - .append(" destructive = ").append(isMutation).append(",\n") - .append(" idempotent = ").append(!isMutation).append(",\n"); - - List tags = opts.tags(); - if (tags == null || tags.isEmpty()) { - tags = List.of(tool.group().toLowerCase(Locale.ROOT), isMutation ? "처리" : "조회"); - } - methods.append(" tags = {") - .append(tags.stream().map(t -> "\"" + javaText(t) + "\"").collect(Collectors.joining(", "))) - .append("},\n") - .append(" ownerOrg = \"").append(javaText(opts.ownerOrg() != null && !opts.ownerOrg().isBlank() ? opts.ownerOrg() : "MCP_TOOL")).append("\"\n") - .append(" )\n") - .append(" ").append(baseName).append("Response ").append(tool.methodName()).append("(") - .append(baseName).append("Request req);\n\n"); - } - return "package " + bizPackage + ".usecase;\n\n" - + "import org.springaicommunity.mcp.annotation.McpTool;\n" - + "import io.shinhanlife.dat.lib.annotation.GrowToolHint;\n" - + imports + "\npublic interface " + useCaseBaseName + "UseCase {\n\n" + methods + "}\n"; - } - - private static String groupedUseCaseImplContent(String bizPackage, String useCaseBaseName, - List tools) { - StringBuilder imports = new StringBuilder(); - StringBuilder fields = new StringBuilder(); - StringBuilder methods = new StringBuilder(); - for (ToolMethodDefinition tool : tools) { - String baseName = toPascalCase(tool.baseName()); - String clientClassName; - String clientVariable; - boolean mci = "MCI".equalsIgnoreCase(tool.routingType()); - if (mci) { - String sysCode = tool.clientSystemCode().toLowerCase(Locale.ROOT); - clientClassName = "Mci" + sysCode.substring(0, 1).toUpperCase(Locale.ROOT) + sysCode.substring(1) + "Client"; - clientVariable = "mci" + sysCode.substring(0, 1).toUpperCase(Locale.ROOT) + sysCode.substring(1) + "Client"; - } 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 integrationPackage = BASE_PACKAGE + (mci ? ".infra.itrf.mci." + (tool.clientSystemCode().length() == 4 ? tool.clientSystemCode().substring(0,3).toLowerCase(Locale.ROOT) + "." + tool.clientSystemCode().substring(3).toLowerCase(Locale.ROOT) : tool.clientSystemCode().toLowerCase(Locale.ROOT)) - : ".infra.itrf.http." + toPackageSegment(tool.httpApiName())); - imports.append("import ").append(bizPackage).append(".dto.").append(baseName).append("Request;\n") - .append("import ").append(bizPackage).append(".dto.").append(baseName).append("Response;\n") - .append("import ").append(bizPackage).append(".converter.").append(baseName).append("Converter;\n") - .append("import ").append(integrationPackage).append(".").append(clientClassName).append(";\n") - .append("import ").append(integrationPackage).append(".io.").append(mci ? tool.interfaceId() + "_I;\n" : baseName + "HttpRequest;\n") - .append("import ").append(integrationPackage).append(".io.").append(mci ? tool.interfaceId() + "_O;\n" : baseName + "HttpResponse;\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"); - methods.append(" @Override\n public ").append(baseName).append("Response ").append(tool.methodName()) - .append("(").append(baseName).append("Request req) {\n") - .append(" ").append(mci ? tool.interfaceId() + "_I" : baseName + "HttpRequest").append(" request = ").append(converterVariable).append(".toRequest(req);\n") - .append(" ").append(mci ? tool.interfaceId() + "_O" : baseName + "HttpResponse").append(" response = ").append(clientVariable) - .append(mci ? ".callTo(\"" + tool.interfaceId() + "\", null, request, " + tool.interfaceId() + "_O.class).getBody();\n" : ".call(request, " + baseName + "HttpResponse.class);\n") - .append(" ").append(baseName).append("Response toolResponse = ").append(converterVariable).append(".toResponse(response);\n") - .append(" if (toolResponse == null) toolResponse = new ").append(baseName).append("Response();\n") - .append(" toolResponse.setResultCode(\"SUCCESS\");\n") - .append(" return toolResponse;\n }\n\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" - + fields + "\n" + methods + "}\n"; - } - - private static String groupedConverterContent(String bizPackage, String useCaseBaseName, - List tools) { - return "package " + bizPackage + ".converter;\n\n/** Per-Tool converters are generated beside this compatibility marker. */\n" - + "public interface " + useCaseBaseName + "Converter {\n}\n"; - } - - private static String groupedMciConverterContent(String bizPackage, String baseName, String ioPackage, String interfaceId) { - return "package " + bizPackage + ".converter;\n\n" - + "import " + bizPackage + ".dto." + baseName + "Request;\n" - + "import " + bizPackage + ".dto." + baseName + "Response;\n" - + "import " + ioPackage + ".io." + interfaceId + "_I;\n" - + "import " + ioPackage + ".io." + interfaceId + "_O;\n" - + "import org.mapstruct.Mapper;\nimport org.mapstruct.ReportingPolicy;\n\n" - + "@Mapper(componentModel = \"spring\", unmappedTargetPolicy = ReportingPolicy.IGNORE)\n" - + "public interface " + baseName + "Converter {\n" - + " " + interfaceId + "_I toRequest(" + baseName + "Request request);\n" - + " " + baseName + "Response toResponse(" + interfaceId + "_O response);\n}\n"; - } - - private static String groupedHttpConverterContent(String bizPackage, String baseName, String ioPackage) { - return "package " + bizPackage + ".converter;\n\n" - + "import " + bizPackage + ".dto." + baseName + "Request;\n" - + "import " + bizPackage + ".dto." + baseName + "Response;\n" - + "import " + ioPackage + ".io." + baseName + "HttpRequest;\n" - + "import " + ioPackage + ".io." + baseName + "HttpResponse;\n" - + "import org.mapstruct.Mapper;\nimport org.mapstruct.ReportingPolicy;\n\n" - + "@Mapper(componentModel = \"spring\", unmappedTargetPolicy = ReportingPolicy.IGNORE)\n" - + "public interface " + baseName + "Converter {\n" - + " " + baseName + "HttpRequest toRequest(" + baseName + "Request request);\n" - + " " + baseName + "Response toResponse(" + baseName + "HttpResponse response);\n}\n"; - } - - private static void appendGroupedUseCaseSources(Path useCaseFile, Path useCaseImplFile, Path converterFile, - String bizPackage, String useCaseBaseName, String moduleName, - List tools) throws IOException { - if (!Files.exists(useCaseImplFile)) { - throw new IllegalArgumentException("UseCase implementation not found: " + useCaseImplFile); - } - String useCase = Files.readString(useCaseFile, StandardCharsets.UTF_8); - String implementation = Files.readString(useCaseImplFile, StandardCharsets.UTF_8); - for (ToolMethodDefinition tool : tools) { - String baseName = toPascalCase(tool.baseName()); - String methodName = tool.methodName(); - String toolName = toToolName(moduleName, tool.group(), baseName); - if (useCase.matches("(?s).*\\b" + java.util.regex.Pattern.quote(methodName) + "\\s*\\(.*") - || useCase.contains("name = \"" + toolName + "\"")) { - throw new IllegalArgumentException("Tool method or MCP Tool name already exists: " + methodName); - } - boolean mci = "MCI".equalsIgnoreCase(tool.routingType()); - String integrationPackage = BASE_PACKAGE + (mci ? ".infra.itrf.mci." + (tool.clientSystemCode().length() == 4 ? tool.clientSystemCode().substring(0,3).toLowerCase(Locale.ROOT) + "." + tool.clientSystemCode().substring(3).toLowerCase(Locale.ROOT) : tool.clientSystemCode().toLowerCase(Locale.ROOT)) - : ".infra.itrf.http." + toPackageSegment(tool.httpApiName())); - String requestType = baseName + "Request"; - String responseType = baseName + "Response"; - String requestIo = mci ? tool.interfaceId() + "_I" : baseName + "HttpRequest"; - String responseIo = mci ? tool.interfaceId() + "_O" : baseName + "HttpResponse"; - - useCase = addImport(useCase, "import " + bizPackage + ".dto." + requestType + ";") ; - useCase = addImport(useCase, "import " + bizPackage + ".dto." + responseType + ";") ; - boolean isMutation = isMutationTool(baseName); - ToolDefinitionOptions opts = tool.definitionOptions() == null ? new ToolDefinitionOptions(null, null, null, null, null, null, null, null) : tool.definitionOptions(); - - StringBuilder declBuilder = new StringBuilder("\n @McpTool(name = \"").append(toolName) - .append("\", title = \"").append(javaText(option(tool.title(), baseName))) - .append("\", description = \"").append(javaText(option(tool.description(), ""))).append("\")\n") - .append(" @GrowToolHint(\n") - .append(" requiresApproval = ").append(isMutation).append(",\n") - .append(" categoryKey = \"").append(tool.group().toLowerCase(Locale.ROOT)).append("\",\n") - .append(" timeoutMillis = 5000L,\n") - .append(" retryMaxAttempts = 3,\n"); - String mappingId = option(tool.interfaceId(), tool.httpApiName()); - if (mappingId != null && !mappingId.isBlank()) { - declBuilder.append(" mappingId = \"").append(javaText(mappingId)).append("\",\n"); - } - declBuilder.append(" functionDescription = \"").append(javaText(opts.functionDescription() != null && !opts.functionDescription().isBlank() ? opts.functionDescription() : option(tool.title(), baseName) + " 기능을 수행합니다.")).append("\",\n") - .append(" whenToUse = \"").append(javaText(opts.whenToUse() != null && !opts.whenToUse().isBlank() ? opts.whenToUse() : "사용자가 이 업무 기능의 실행 또는 조회를 요청할 때 사용합니다.")).append("\",\n") - .append(" whenNotToUse = \"").append(javaText(opts.whenNotToUse() != null && !opts.whenNotToUse().isBlank() ? opts.whenNotToUse() : "정보 변경이나 실행 작업에는 사용하지 않습니다.")).append("\",\n") - .append(" ioLimits = \"").append(javaText(opts.ioLimits() != null && !opts.ioLimits().isBlank() ? opts.ioLimits() : "정의된 입력 항목만 허용하며 업무 결과만 반환합니다.")).append("\",\n") - .append(" displayDescription = \"").append(javaText(opts.displayDescription() != null && !opts.displayDescription().isBlank() ? opts.displayDescription() : option(tool.title(), baseName) + " 정보를 처리합니다.")).append("\",\n"); - - List examples = opts.exampleQueries(); - if (examples == null || examples.isEmpty()) { - examples = List.of(option(tool.title(), baseName) + " 정보를 보여줘", option(tool.title(), baseName) + " 확인해줘", "현재 " + option(tool.title(), baseName) + " 알려줘"); - } - declBuilder.append(" exampleQueries = {") - .append(examples.stream().map(q -> "\"" + javaText(q) + "\"").collect(Collectors.joining(", "))) - .append("},\n") - .append(" destructive = ").append(isMutation).append(",\n") - .append(" idempotent = ").append(!isMutation).append(",\n"); - - List tags = opts.tags(); - if (tags == null || tags.isEmpty()) { - tags = List.of(tool.group().toLowerCase(Locale.ROOT), isMutation ? "처리" : "조회"); - } - declBuilder.append(" tags = {") - .append(tags.stream().map(t -> "\"" + javaText(t) + "\"").collect(Collectors.joining(", "))) - .append("},\n") - .append(" ownerOrg = \"").append(javaText(opts.ownerOrg() != null && !opts.ownerOrg().isBlank() ? opts.ownerOrg() : "MCP_TOOL")).append("\"\n") - .append(" )\n") - .append(" ").append(responseType).append(" ").append(methodName).append("(").append(requestType).append(" req);\n"); - - String declaration = declBuilder.toString(); - useCase = insertBeforeLastBrace(useCase, declaration); - - String clientClassName; - String clientVariable; - if (mci) { - String sysCode = tool.clientSystemCode().toLowerCase(Locale.ROOT); - clientClassName = "Mci" + sysCode.substring(0, 1).toUpperCase(Locale.ROOT) + sysCode.substring(1) + "Client"; - clientVariable = "mci" + sysCode.substring(0, 1).toUpperCase(Locale.ROOT) + sysCode.substring(1) + "Client"; - } 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"; - - implementation = addImport(implementation, "import " + bizPackage + ".dto." + requestType + ";"); - implementation = addImport(implementation, "import " + bizPackage + ".dto." + responseType + ";"); - implementation = addImport(implementation, "import " + bizPackage + ".converter." + baseName + "Converter;"); - implementation = addImport(implementation, "import " + integrationPackage + "." + clientClassName + ";"); - implementation = addImport(implementation, "import " + integrationPackage + ".io." + requestIo + ";"); - implementation = addImport(implementation, "import " + integrationPackage + ".io." + responseIo + ";"); - implementation = addImport(implementation, "import lombok.RequiredArgsConstructor;"); - if (!implementation.contains("@RequiredArgsConstructor")) { - implementation = implementation.replaceFirst("public class ", "@RequiredArgsConstructor\npublic class "); - } - if (!implementation.contains(" " + clientVariable + ";")) { - implementation = insertConstructorField(implementation, " private final " + clientClassName + " " + clientVariable + ";"); - } - implementation = insertConstructorField(implementation, " private final " + baseName + "Converter " + converterVariable + ";"); - String call = mci - ? clientVariable + ".callTo(\"" + tool.interfaceId() + "\", null, request, " + responseIo + ".class).getBody()" - : clientVariable + ".call(request, " + responseIo + ".class)"; - String method = "\n @Override\n public " + responseType + " " + methodName + "(" + requestType + " req) {\n" - + " " + requestIo + " request = " + converterVariable + ".toRequest(req);\n" - + " " + responseIo + " response = " + call + ";\n" - + " " + responseType + " toolResponse = " + converterVariable + ".toResponse(response);\n" - + " if (toolResponse == null) toolResponse = new " + responseType + "();\n" - + " toolResponse.setResultCode(\"SUCCESS\");\n" - + " return toolResponse;\n }\n"; - implementation = insertBeforeLastBrace(implementation, method); - } - writeUtf8(useCaseFile, useCase); - writeUtf8(useCaseImplFile, implementation); - if (!Files.exists(converterFile)) { - writeUtf8(converterFile, groupedConverterContent(bizPackage, useCaseBaseName, tools)); - } - } - - private static String addImport(String content, String importLine) { - if (content.contains(importLine)) return content; - int lastImport = content.lastIndexOf("import "); - if (lastImport < 0) { - int packageEnd = content.indexOf(';'); - return content.substring(0, packageEnd + 1) + "\n\n" + importLine + content.substring(packageEnd + 1); - } - int lineEnd = content.indexOf('\n', lastImport); - return content.substring(0, lineEnd + 1) + importLine + "\n" + content.substring(lineEnd + 1); - } - - private static String insertConstructorField(String content, String field) { - if (content.contains(field)) return content; - int constructorField = content.lastIndexOf("private final "); - if (constructorField < 0) { - int classDecl = content.indexOf("public class "); - if (classDecl >= 0) { - int brace = content.indexOf('{', classDecl); - if (brace >= 0) return content.substring(0, brace + 1) + "\n" + field + content.substring(brace + 1); - } - return insertBeforeLastBrace(content, "\n" + field + "\n"); - } - int lineEnd = content.indexOf('\n', constructorField); - if (lineEnd < 0) return content + "\n" + field; - return content.substring(0, lineEnd + 1) + field + "\n" + content.substring(lineEnd + 1); - } - - private static String insertBeforeLastBrace(String content, String addition) { - int brace = content.lastIndexOf('}'); - if (brace < 0) throw new IllegalArgumentException("Java source closing brace not found."); - return content.substring(0, brace) + addition + content.substring(brace); - } - - private static String groupedMciClientContent(String ioPackage, String baseName, String interfaceId) { - return "package " + ioPackage + ";\n\n" - + "import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent;\n" - + "import io.shinhanlife.glow.communication.dto.Transfer;\n" - + "import lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Component;\n" - + "import " + ioPackage + ".io." + baseName + "_I;\n" - + "import " + ioPackage + ".io." + baseName + "_O;\n\n" - + "@Component\n@RequiredArgsConstructor\npublic class " + baseName + "Client {\n" - + " private final AxhubMciComponent mci;\n\n" - + " public " + baseName + "_O call" + baseName + "(" + baseName + "_I request) {\n" - + " try {\n" - + " Transfer<" + baseName + "_O> transfer = mci.callTo(\"" + javaText(interfaceId) + "\", null, request, " + baseName + "_O.class);\n" - + " return transfer.getBody();\n" - + " } catch (Exception e) {\n" - + " throw new IllegalStateException(\"MCI call failed: " + javaText(interfaceId) + "\", e);\n" - + " }\n }\n}\n"; - } - - private static String javaText(String value) { - return value == null ? "" : value.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", " ").replace("\n", " "); - } - - /** - * 콘솔 Scaffold에서 LLM 설명 대신 업무 단위의 제안서를 입력할 수 있도록 안내한다. - */ - public static String businessProposalGuide() { - return """ - \n[업무 제안서 작성 가이드] - Tool 이름만 적지 말고, 아래 항목을 한두 문장씩 포함해 작성하세요. - 1. 조회 대상·식별 기준: 무엇을 어떤 값으로 찾는지 - 2. 조회 목적·사용자: 누가 어떤 업무 상황에서 쓰는지 - 3. 조회 범위·결과 단위: 목록인지 상세인지, 반드시 필요한 정보는 무엇인지 - 4. 조건·예외: 기준일, 포함/제외 조건, 식별값 부족·다건 조회 시 처리 - 5. 권한·마스킹 및 제외 범위: 노출하면 안 되는 정보와 별도 Tool로 분리할 업무 - - [통합계약원부 조회 제안서 예시] - 상담사가 고객의 현재 유효 계약을 확인한다. 계약번호가 있으면 상세 원부를 조회하고, - 없으면 고객 식별값으로 계약 목록을 먼저 조회한다. 계약 기본정보, 계약자·피보험자, - 주계약·특약, 납입 상태와 실효 여부를 반환한다. 계약이 여러 건이면 목록을 먼저 반환하고, - 주민번호와 계좌번호는 마스킹한다. 해지·대출·보험금 상세는 별도 Tool로 분리한다. - """; - } - - public static void main(String[] args) throws IOException { - Scanner scanner = new Scanner(System.in); - - String baseName = getOrAsk(args, 0, scanner, "1. 생성할 Tool의 기본 이름 (예: ExchangeRate) [영문 PascalCase]: "); - String interfaceId = getOrAsk(args, 1, scanner, "2. 레거시 API 인터페이스 ID (예: EXCH_001): "); - String title = getOrAsk(args, 2, scanner, "3. Tool title: "); - System.out.println(businessProposalGuide()); - String description = getOrAsk(args, 3, scanner, "4. 업무 제안서 (생성 Tool의 LLM 설명으로 사용): "); - String group = getOrAsk(args, 4, scanner, "5. Tool category: "); - if (group.isEmpty()) group = "COMMON"; - String routingType = getOrAsk(args, 5, scanner, "6. Routing type (HTTP, TCP, MCI, EAI): "); - if (routingType.trim().isEmpty()) { - routingType = "HTTP"; - } - String moduleName = getOrAsk(args, 6, scanner, "7. Target module (default dat-was-cus): "); - if (moduleName.trim().isEmpty()) { - moduleName = "dat-was-cus"; - } - - String defaultAuthor = System.getProperty("user.name"); - String defaultDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd")); - - String author = getOrAsk(args, 7, scanner, "8. 작성자(Enter 입력 시 '" + defaultAuthor + "'): "); - if (author.trim().isEmpty()) author = defaultAuthor; - String createDate = getOrAsk(args, 8, scanner, "9. 작성일(Enter 입력 시 '" + defaultDate + "'): "); - if (createDate.trim().isEmpty()) createDate = defaultDate; - - String useSchemaResourceStr = getOrAsk(args, 9, scanner, "10. input/output JSON Schema 파일 자동 생성 여부 (y/N): "); - boolean useSchemaResource = "y".equalsIgnoreCase(useSchemaResourceStr.trim()); - - String schemaResourceDirectory = "classpath:tool-schemas/" + group.toLowerCase() + "/"; - String inputSchemaResource = useSchemaResource ? schemaResourceDirectory + toKebabCase(baseName) + "-resource-input-schema.json" : null; - String outputSchemaResource = useSchemaResource ? schemaResourceDirectory + toKebabCase(baseName) + "-resource-output-schema.json" : null; - String defaultWorkspace = "c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dat-was-datmt"; - String workspace = getOrAsk(args, 10, scanner, "11. 대상 프로젝트 워크스페이스 경로 (default: " + defaultWorkspace + "): "); - if (workspace.trim().isEmpty()) { - workspace = defaultWorkspace; - } - System.setProperty("AXHUB_SOURCE_DIR", workspace); - - String result = scaffold(baseName, interfaceId, title, description, group, routingType, moduleName, author, createDate, false, null, inputSchemaResource, outputSchemaResource, List.of(new FieldDefinition("query", "String", "Search query", List.of("example"), "", false)), List.of()); - } - - private static String getOrAsk(String[] args, int index, Scanner scanner, String prompt) { - if (args.length > index) { - return args[index]; - } - return scanner.nextLine().trim(); - } - - public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode) throws IOException { - return scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, createDate, register, clientSystemCode, null, null); - } - - public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode, String inputSchemaResource, String outputSchemaResource) throws IOException { - return scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, createDate, - register, clientSystemCode, inputSchemaResource, outputSchemaResource, - List.of(new FieldDefinition("query", "String", "Search query", List.of("example"), "", false)), List.of()); - } - - public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode, String inputSchemaResource, String outputSchemaResource, List inputFields, List outputFields) throws IOException { - return scaffold(baseName, interfaceId, description, description, group, routingType, moduleName, author, createDate, - register, clientSystemCode, inputSchemaResource, outputSchemaResource, inputFields, outputFields); - } - - /** - * Generates a Tool with a human-facing title and an LLM-facing description. - * Existing overloads keep their previous behavior by using the description as the title. - */ - public static String scaffold(String baseName, String interfaceId, String title, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode, String inputSchemaResource, String outputSchemaResource, List inputFields, List outputFields) throws IOException { - return scaffold(baseName, interfaceId, title, description, group, routingType, moduleName, author, createDate, - register, clientSystemCode, inputSchemaResource, outputSchemaResource, inputFields, outputFields, - toKebabCase(toPascalCase(baseName))); - } - - /** - * Generates a Tool using an HTTP API name that is resolved from glow.communication.http.api-list. - */ - public static String scaffold(String baseName, String interfaceId, String title, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode, String inputSchemaResource, String outputSchemaResource, List inputFields, List outputFields, String httpApiName) throws IOException { - return scaffold(baseName, interfaceId, title, description, group, routingType, moduleName, author, createDate, - register, clientSystemCode, inputSchemaResource, outputSchemaResource, inputFields, outputFields, - httpApiName, null); - } - - public static String scaffold(String baseName, String interfaceId, String title, String description, String group, - String routingType, String moduleName, String author, String createDate, - boolean register, String clientSystemCode, String inputSchemaResource, - String outputSchemaResource, List inputFields, - List outputFields, String httpApiName, - ToolDefinitionOptions definitionOptions) throws IOException { - baseName = toPascalCase(baseName); - title = title == null || title.isBlank() ? baseName : title.trim(); - description = description == null ? "" : description.trim(); - httpApiName = httpApiName == null || httpApiName.isBlank() ? toKebabCase(baseName) : httpApiName.trim(); - if (definitionOptions == null) { - definitionOptions = new ToolDefinitionOptions(null, null, null, null, null, null, null, null); - } - String envSourceDir = System.getProperty("AXHUB_SOURCE_DIR"); - if (envSourceDir == null) { - envSourceDir = System.getenv("AXHUB_SOURCE_DIR"); - } - Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get("."); - - Path usecaseDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "usecase")); - Path usecaseImplDir = usecaseDir.resolve("impl"); - Path dtoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "dto")); - - Path legacyDtoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "legacy")); - Path converterDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "converter")); - - // Schema Resource 파일 경로(useSchemaResource=true일 때만 생성) - boolean useSchemaResource = (inputSchemaResource != null && !inputSchemaResource.trim().isEmpty()) || (outputSchemaResource != null && !outputSchemaResource.trim().isEmpty()); - String schemaBaseName = toKebabCase(baseName); - String inputSchemaFileName = schemaBaseName + "-resource-input-schema.json"; - String outputSchemaFileName = schemaBaseName + "-resource-output-schema.json"; - Path schemaDir = rootDir.resolve(Paths.get(moduleName, "src/main/resources/tool-schemas", group.toLowerCase())); - Path definitionDir = rootDir.resolve(Paths.get(moduleName, "src/main/resources/tool-definitions", group.toLowerCase())); - String inputSchemaClasspath = "classpath:tool-schemas/" + group.toLowerCase() + "/" + inputSchemaFileName; - String outputSchemaClasspath = "classpath:tool-schemas/" + group.toLowerCase() + "/" + outputSchemaFileName; - - String bizPackage = BASE_PACKAGE + ".biz." + group.toLowerCase(); - boolean isMci = "MCI".equalsIgnoreCase(routingType); - boolean isHttp = "HTTP".equalsIgnoreCase(routingType); - if (!isMci && !isHttp) { - throw new IllegalArgumentException("Unsupported routing type: " + routingType + ". Only MCI and HTTP are supported."); - } - String mciGroupPath = "infra/itrf/mci/" + group.toLowerCase(); - String clientPrefixCap = ""; - Path mciClientDir = null; - - if (isMci && clientSystemCode != null && clientSystemCode.length() == 4) { - String clientPrefix = clientSystemCode.toLowerCase(); - clientPrefixCap = toPascalCase(clientSystemCode); - mciGroupPath = "infra/itrf/mci/" + clientPrefix.substring(0, 3) + "/" + clientPrefix.substring(3); - mciClientDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, mciGroupPath)); - } - - Path mciIoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, mciGroupPath, "io")); - String httpApiPackage = toPackageSegment(httpApiName); - String httpApiClass = toPascalCase(httpApiName); - String httpGroupPath = "infra/itrf/http/" + httpApiPackage; - Path httpClientDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, httpGroupPath)); - Path httpIoDir = httpClientDir.resolve("io"); - - Files.createDirectories(usecaseDir); - Files.createDirectories(usecaseImplDir); - Files.createDirectories(dtoDir); - if (isMci) { - Files.createDirectories(mciIoDir); - if (mciClientDir != null) { - Files.createDirectories(mciClientDir); - } - } else if (isHttp) { - Files.createDirectories(httpIoDir); - } else { - Files.createDirectories(legacyDtoDir); - } - Files.createDirectories(converterDir); - - StringBuilder log = new StringBuilder(); - - // Generate Request DTO - String reqContent = """ - package %s.dto; - - import com.fasterxml.jackson.annotation.JsonInclude; - import lombok.Data; - - /** - * @package %s.dto - * @className %sRequest - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-             * ---------- 개정이력 ----------
-             * 수정일       수정자     수정내용
-             * ---------- -------- ---------------------------
-             * %s  %s    최초생성
-             * 
-             * 
- */ - @Data - @JsonInclude(JsonInclude.Include.NON_NULL) - public class %sRequest { - @McpToolParam(description = "수신자 전화번호", required = true) - -(?:\\\\d{3}|\\\\d{4})-\\\\d{4}$", examples = {"010-1234-5678"}) - private String phoneNumber; - - @McpToolParam(description = "전송할 메시지 내용", required = true) - private String message; - } - """.formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName); - reqContent = reqContent - .replace("import com.fasterxml.jackson.annotation.JsonInclude;", - "import com.fasterxml.jackson.annotation.JsonInclude;\nimport io.swagger.v3.oas.annotations.media.Schema;") - .replaceAll("(?m)^\\s*@McpToolParam\\([^\\r\\n]*\\)\\R", "") - .replaceAll("(?m)^\\s*-\\(\\?:[^\\r\\n]*\\R", "") - .replace("private String phoneNumber;", "@Schema(example = \"01012345678\")\n private String phoneNumber;") - .replace("private String message;", "@Schema(example = \"테스트 메시지입니다.\")\n private String message;"); - reqContent = dtoContent(bizPackage + ".dto", baseName + "Request", inputFields, author, createDate, true); - writeUtf8(dtoDir.resolve(baseName + "Request.java"), reqContent); - writeStructuredFieldTypes(dtoDir, bizPackage + ".dto", baseName + "Request", inputFields); - - // Generate Response DTO - String resContent = """ - package %s.dto; - - import com.fasterxml.jackson.annotation.JsonInclude; - import lombok.Data; - - /** - * @package %s.dto - * @className %sResponse - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-             * ---------- 개정이력 ----------
-             * 수정일       수정자     수정내용
-             * ---------- -------- ---------------------------
-             * %s  %s    최초생성
-             * 
-             * 
- */ - @Data - @JsonInclude(JsonInclude.Include.NON_NULL) - public class %sResponse { - private String resultCode; - - private String resultMessage; - - // TODO: Add response fields here. Do not include PII in the Tool response. - } - """.formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName); - resContent = dtoContent(bizPackage + ".dto", baseName + "Response", outputFields, author, createDate, false); - writeUtf8(dtoDir.resolve(baseName + "Response.java"), resContent); - writeStructuredFieldTypes(dtoDir, bizPackage + ".dto", baseName + "Response", outputFields); - - String toolName = toToolName(moduleName, group, baseName); - - boolean isMutation = isMutationTool(baseName); - StringBuilder sb = new StringBuilder(" @GrowToolHint(\n"); - if (useSchemaResource) { - sb.append(" inputSchemaResource = \"").append(inputSchemaClasspath).append("\",\n"); - sb.append(" outputSchemaResource = \"").append(outputSchemaClasspath).append("\",\n"); - } - sb.append(" requiresApproval = ").append(isMutation).append(",\n"); - sb.append(" categoryKey = \"").append(group.toLowerCase(Locale.ROOT)).append("\",\n"); - sb.append(" timeoutMillis = 5000L,\n"); - sb.append(" retryMaxAttempts = 3,\n"); - if (interfaceId != null && !interfaceId.isBlank()) { - sb.append(" mappingId = \"").append(interfaceId).append("\",\n"); - } - sb.append(" functionDescription = \"").append(javaText(definitionOptions.functionDescription() != null && !definitionOptions.functionDescription().isBlank() ? definitionOptions.functionDescription() : title + " 기능을 수행합니다.")).append("\",\n"); - sb.append(" whenToUse = \"").append(javaText(definitionOptions.whenToUse() != null && !definitionOptions.whenToUse().isBlank() ? definitionOptions.whenToUse() : "사용자가 이 업무 기능의 실행 또는 조회를 요청할 때 사용합니다.")).append("\",\n"); - sb.append(" whenNotToUse = \"").append(javaText(definitionOptions.whenNotToUse() != null && !definitionOptions.whenNotToUse().isBlank() ? definitionOptions.whenNotToUse() : "정보 변경이나 실행 작업에는 사용하지 않습니다.")).append("\",\n"); - sb.append(" ioLimits = \"").append(javaText(definitionOptions.ioLimits() != null && !definitionOptions.ioLimits().isBlank() ? definitionOptions.ioLimits() : "정의된 입력 항목만 허용하며 업무 결과만 반환합니다.")).append("\",\n"); - sb.append(" displayDescription = \"").append(javaText(definitionOptions.displayDescription() != null && !definitionOptions.displayDescription().isBlank() ? definitionOptions.displayDescription() : title + " 정보를 처리합니다.")).append("\",\n"); - - List examples = definitionOptions.exampleQueries(); - if (examples == null || examples.isEmpty()) { - examples = List.of(title + " 정보를 보여줘", title + " 확인해줘", "현재 " + title + " 알려줘"); - } - sb.append(" exampleQueries = {") - .append(examples.stream().map(q -> "\"" + javaText(q) + "\"").collect(Collectors.joining(", "))) - .append("},\n"); - - sb.append(" destructive = ").append(isMutation).append(",\n"); - sb.append(" idempotent = ").append(!isMutation).append(",\n"); - - List tags = definitionOptions.tags(); - if (tags == null || tags.isEmpty()) { - tags = List.of(group.toLowerCase(Locale.ROOT), isMutation ? "처리" : "조회"); - } - sb.append(" tags = {") - .append(tags.stream().map(t -> "\"" + javaText(t) + "\"").collect(Collectors.joining(", "))) - .append("},\n"); - - sb.append(" ownerOrg = \"").append(javaText(definitionOptions.ownerOrg() != null && !definitionOptions.ownerOrg().isBlank() ? definitionOptions.ownerOrg() : "MCP_TOOL")).append("\"\n"); - sb.append(" )"); - String toolHintLine = sb.toString(); - - String serviceInterfaceContent = """ - package %s.usecase; - - import org.springaicommunity.mcp.annotation.McpTool; - import io.shinhanlife.dat.lib.annotation.GrowToolHint; - import %s.dto.%sRequest; - import %s.dto.%sResponse; - - /** - * @package %s.usecase - * @className %sUseCase - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-             * ---------- 개정이력 ----------
-             * 수정일       수정자     수정내용
-             * ---------- -------- ---------------------------
-             * %s  %s    최초생성
-             *
-             * 
- */ - public interface %sUseCase { - - @McpTool(name = "%s", title = "%s", description = "%s") - %s - %sResponse execute(%sRequest req); - } - """.formatted( - bizPackage, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, author, createDate, createDate, author, - baseName, - toolName, title, description, - toolHintLine, - baseName, baseName - ); - - writeUtf8(usecaseDir.resolve(baseName + "UseCase.java"), serviceInterfaceContent); - - String serviceImplContent; - - if (isMci) { - serviceImplContent = """ - package %s.usecase.impl; - - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import %s.usecase.%sUseCase; - import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent; - import io.shinhanlife.glow.communication.dto.Transfer; - import org.springframework.stereotype.Service; - import lombok.RequiredArgsConstructor; - import lombok.extern.slf4j.Slf4j; - import %s.converter.%sConverter; - import %s.%s.io.%s_I; - %s - - /** - * @package %s.usecase.impl - * @className %sUseCaseImpl - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                 * ---------- 개정이력 ----------
-                 * 수정일       수정자     수정내용
-                 * ---------- -------- ---------------------------
-                 * %s  %s    최초생성
-                 * 
-                 * 
- */ - @Slf4j - @Service - @RequiredArgsConstructor - public class %sUseCaseImpl implements %sUseCase { - - %s - private final %sConverter converter; - - @Override - public %sResponse execute(%sRequest req) { - log.info("[MCI Tool] {} 요청 수신.", "%s"); - try { - // MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO) - %s_I mciReq = converter.toLegacyRequest(req); - - Transfer resTransfer = mci.callTo( - "%s", - null, - mciReq, - Object.class - ); - %sResponse response = new %sResponse(); - response.setResultCode("SUCCESS"); - response.setResultMessage(resTransfer.getBody() != null - ? "MCI call completed." - : "MCI call completed without a response body."); - return response; - } catch (Exception e) { - log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e); - %sResponse response = new %sResponse(); - response.setResultCode("ERROR"); - response.setResultMessage(e.getMessage() != null ? e.getMessage() : "Unknown error"); - return response; - } - } - } - """.formatted( - bizPackage, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, - BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, - (clientPrefixCap.isEmpty() ? "" : "import " + BASE_PACKAGE + "." + mciGroupPath.replace("/", ".") + ".Mci" + clientPrefixCap + "Client;\n"), - bizPackage, - baseName, - author, - createDate, - createDate, author, - baseName, - baseName, - (clientPrefixCap.isEmpty() ? "private final AxhubMciComponent mci;" : "private final Mci" + clientPrefixCap + "Client mci;"), - baseName, - baseName, - baseName, - toolName, - interfaceId, - interfaceId, - baseName, - baseName, - baseName, - baseName - ); - String mciIoPackage = BASE_PACKAGE + "." + mciGroupPath.replace("/", "."); - serviceImplContent = serviceImplContent - .replace("import " + mciIoPackage + ".io." + interfaceId + "_I;", - "import " + mciIoPackage + ".io." + interfaceId + "_I;\nimport " + mciIoPackage + ".io." + interfaceId + "_O;") - .replace("Transfer resTransfer", "Transfer<" + interfaceId + "_O> resTransfer") - .replace("Object.class", interfaceId + "_O.class") - .replace("response.setResultCode(\"SUCCESS\");", - "if (resTransfer.getBody() != null) {\n response = converter.toResponse(resTransfer.getBody());\n }\n response.setResultCode(\"SUCCESS\");"); - } else { - serviceImplContent = isHttp - ? httpUseCaseImplContent(bizPackage, baseName, httpGroupPath.replace("/", "."), httpApiClass, author, createDate) - : """ - package %s.usecase.impl; - - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import %s.usecase.%sUseCase; - import %s.converter.%sConverter; - import org.springframework.stereotype.Service; - import lombok.RequiredArgsConstructor; - import lombok.extern.slf4j.Slf4j; - - /** - * @package %s.usecase.impl - * @className %sUseCaseImpl - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                 * ---------- 개정이력 ----------
-                 * 수정일       수정자     수정내용
-                 * ---------- -------- ---------------------------
-                 * %s  %s    최초생성
-                 * 
-                 * 
- */ - @Slf4j - @Service - @RequiredArgsConstructor - public class %sUseCaseImpl implements %sUseCase { - - private final %sConverter converter; - - @Override - public %sResponse execute(%sRequest req) { - // %sLegacyRequest legacyRequest = converter.toLegacyRequest(req); - Object legacyResponse = null; - if (legacyResponse instanceof %sResponse response) { - return response; - } - %sResponse response = new %sResponse(); - response.setResultCode("SUCCESS"); - response.setResultMessage("Legacy call completed."); - return response; - } - } - """.formatted( - bizPackage, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, - author, - createDate, - createDate, author, - baseName, baseName, - baseName, - baseName, - baseName, - baseName, - baseName, - baseName, - baseName, - routingType, interfaceId - ); - } - - writeUtf8(usecaseImplDir.resolve(baseName + "UseCaseImpl.java"), serviceImplContent); - - if (isMci) { - String mciReqContent = """ - package %s.%s.io; - - import lombok.Data; - - /** - * @package %s.%s.io - * @className %s_I - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                 * ---------- 개정이력 ----------
-                 * 수정일       수정자     수정내용
-                 * ---------- -------- ---------------------------
-                 * %s  %s    최초생성
-                 * 
-                 * 
- */ - @Data - public class %s_I { - /** - * EAI 시스템이 요구하는 수신자 번호 파라미터명 - */ - private String phone; - - /** - * EAI 시스템이 요구하는 메시지 내용 파라미터명 - */ - private String content; - } - """.formatted(BASE_PACKAGE, mciGroupPath.replace("/", "."), BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, author, createDate, createDate, author, interfaceId); - mciReqContent = mciIoContent(mciGroupPath.replace("/", "."), interfaceId + "_I", inputFields, author, createDate); - writeUtf8(mciIoDir.resolve(interfaceId + "_I.java"), mciReqContent); - writeStructuredFieldTypes(mciIoDir, BASE_PACKAGE + "." + mciGroupPath.replace("/", ".") + ".io", interfaceId + "_I", inputFields); - - String mciResContent = """ - package %s.%s.io; - - import lombok.Data; - - /** - * @package %s.%s.io - * @className %s_O - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                 * ---------- 개정이력 ----------
-                 * 수정일       수정자     수정내용
-                 * ---------- -------- ---------------------------
-                 * %s  %s    최초생성
-                 * 
-                 * 
- */ - @Data - public class %s_O { - // TODO: Add response fields here - } - """.formatted(BASE_PACKAGE, mciGroupPath.replace("/", "."), BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, author, createDate, createDate, author, interfaceId); - mciResContent = mciIoContent(mciGroupPath.replace("/", "."), interfaceId + "_O", outputFields, author, createDate); - writeUtf8(mciIoDir.resolve(interfaceId + "_O.java"), mciResContent); - writeStructuredFieldTypes(mciIoDir, BASE_PACKAGE + "." + mciGroupPath.replace("/", ".") + ".io", interfaceId + "_O", outputFields); - - String converterContent = """ - package %s.converter; - - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import %s.%s.io.%s_I; - import %s.%s.io.%s_O; - import org.mapstruct.Mapper; - import org.mapstruct.Mapping; - import org.mapstruct.factory.Mappers; - - /** - * @package %s.converter - * @className %sConverter - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                 * ---------- 개정이력 ----------
-                 * 수정일       수정자     수정내용
-                 * ---------- -------- ---------------------------
-                 * %s  %s    최초생성
-                 * 
-                 * 
- */ - @Mapper(componentModel = "spring") - public interface %sConverter { - - @Mapping(source = "phoneNumber", target = "phone") - @Mapping(source = "message", target = "content") - %s_I toLegacyRequest(%sRequest req); - - @Mapping(source = "phone", target = "phoneNumber") - @Mapping(source = "content", target = "message") - %sRequest toRequest(%s_I mciReq); - - // %sResponse toResponse(%s_O mciRes); - } - """.formatted( - bizPackage, - bizPackage, baseName, - bizPackage, baseName, - BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, - BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, - bizPackage, baseName, author, createDate, createDate, author, - baseName, interfaceId, baseName, - baseName, interfaceId, - baseName, interfaceId - ); - converterContent = mciConverterContent(bizPackage, baseName, mciGroupPath.replace("/", "."), interfaceId); - writeUtf8(converterDir.resolve(baseName + "Converter.java"), converterContent); - - log.append("\n=========================================\n"); - log.append(" Scaffolding Complete! (Routing: " + routingType + ")\n"); - log.append("=========================================\n"); - log.append("[Usecase Interface] ").append(usecaseDir.resolve(baseName + "UseCase.java")).append("\n"); - log.append("[Usecase Impl] ").append(usecaseImplDir.resolve(baseName + "UseCaseImpl.java")).append("\n"); - log.append("[Request DTO] ").append(dtoDir.resolve(baseName + "Request.java")).append("\n"); - log.append("[Response DTO] ").append(dtoDir.resolve(baseName + "Response.java")).append("\n"); - log.append("[MCI Request IO] ").append(mciIoDir.resolve(interfaceId + "_I.java")).append("\n"); - log.append("[MCI Response IO] ").append(mciIoDir.resolve(interfaceId + "_O.java")).append("\n"); - log.append("[MCI Converter] ").append(converterDir.resolve(baseName + "Converter.java")).append("\n"); - - if (!clientPrefixCap.isEmpty()) { - 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; - - /** - * @package %s.%s - * @className Mci%sClient - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                     * ---------- 개정이력 ----------
-                     * 수정일       수정자     수정내용
-                     * ---------- -------- ---------------------------
-                     * %s  %s    최초생성
-                     * 
-                     * 
- */ - @Component - @RequiredArgsConstructor - public class Mci%sClient { - private final AxhubMciComponent mci; - - public Transfer callTo(String interfaceId, String dummy, Object mciReq, Class resType) throws Exception { - return mci.callTo(interfaceId, dummy, mciReq, resType); - } - } - """.formatted( - BASE_PACKAGE, mciGroupPath.replace("/", "."), - BASE_PACKAGE, mciGroupPath.replace("/", "."), clientPrefixCap, author, createDate, createDate, author, clientPrefixCap - ); - writeUtf8(mciClientDir.resolve("Mci" + clientPrefixCap + "Client.java"), mciClientContent); - log.append("[MCI Client] ").append(mciClientDir.resolve("Mci" + clientPrefixCap + "Client.java")).append("\n"); - } - - } else if (isHttp) { - String httpPackage = BASE_PACKAGE + "." + httpGroupPath.replace("/", "."); - String httpRequestClass = baseName + "HttpRequest"; - String httpResponseClass = baseName + "HttpResponse"; - String httpClientClass = httpApiClass + "Client"; - - writeUtf8(httpIoDir.resolve(httpRequestClass + ".java"), - dtoContent(httpPackage + ".io", httpRequestClass, inputFields, author, createDate, true)); - writeUtf8(httpIoDir.resolve(httpResponseClass + ".java"), - dtoContent(httpPackage + ".io", httpResponseClass, outputFields, author, createDate, false)); - writeStructuredFieldTypes(httpIoDir, httpPackage + ".io", httpRequestClass, inputFields); - writeStructuredFieldTypes(httpIoDir, httpPackage + ".io", httpResponseClass, outputFields); - writeUtf8(httpClientDir.resolve(httpClientClass + ".java"), - httpClientContent(httpPackage, httpClientClass, httpApiName)); - writeUtf8(converterDir.resolve(baseName + "Converter.java"), - httpConverterContent(bizPackage, baseName, httpPackage)); - - log.append("\n=========================================\n"); - log.append(" Scaffolding Complete! (Routing: HTTP)\n"); - log.append("=========================================\n"); - log.append("[Usecase Interface] ").append(usecaseDir.resolve(baseName + "UseCase.java")).append("\n"); - log.append("[Usecase Impl] ").append(usecaseImplDir.resolve(baseName + "UseCaseImpl.java")).append("\n"); - log.append("[Request DTO] ").append(dtoDir.resolve(baseName + "Request.java")).append("\n"); - log.append("[Response DTO] ").append(dtoDir.resolve(baseName + "Response.java")).append("\n"); - log.append("[HTTP Request IO] ").append(httpIoDir.resolve(httpRequestClass + ".java")).append("\n"); - log.append("[HTTP Response IO] ").append(httpIoDir.resolve(httpResponseClass + ".java")).append("\n"); - log.append("[HTTP Client] ").append(httpClientDir.resolve(httpClientClass + ".java")).append("\n"); - log.append("[HTTP Converter] ").append(converterDir.resolve(baseName + "Converter.java")).append("\n"); - } else { - String legacyReqContent = """ - package %s.legacy; - - import lombok.Data; - - /** - * @package %s.legacy - * @className %sLegacyRequest - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                 * ---------- 개정이력 ----------
-                 * 수정일       수정자     수정내용
-                 * ---------- -------- ---------------------------
-                 * %s  %s    최초생성
-                 * 
-                 * 
- */ - @Data - public class %sLegacyRequest { - /** - * EAI 시스템이 요구하는 수신자 번호 파라미터명 - */ - private String phone; - - /** - * EAI 시스템이 요구하는 메시지 내용 파라미터명 - */ - private String content; - } - """.formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName); - legacyReqContent = dtoContent(bizPackage + ".legacy", baseName + "LegacyRequest", inputFields, author, createDate, true); - writeUtf8(legacyDtoDir.resolve(baseName + "LegacyRequest.java"), legacyReqContent); - - String legacyResContent = """ - package %s.legacy; - - import lombok.Data; - - /** - * @package %s.legacy - * @className %sLegacyResponse - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                 * ---------- 개정이력 ----------
-                 * 수정일       수정자     수정내용
-                 * ---------- -------- ---------------------------
-                 * %s  %s    최초생성
-                 * 
-                 * 
- */ - @Data - public class %sLegacyResponse { - // TODO: Add legacy response fields here - } - """.formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName); - legacyResContent = dtoContent(bizPackage + ".legacy", baseName + "LegacyResponse", outputFields, author, createDate, true); - writeUtf8(legacyDtoDir.resolve(baseName + "LegacyResponse.java"), legacyResContent); - - String converterContent = """ - package %s.converter; - - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import %s.legacy.%sLegacyRequest; - import %s.legacy.%sLegacyResponse; - import org.mapstruct.Mapper; - import org.mapstruct.Mapping; - import org.mapstruct.factory.Mappers; - - /** - * @package %s.converter - * @className %sConverter - * @description AX HUB 시스템 처리 클래스 - * @author %s - * @create %s - *
-                 * ---------- 개정이력 ----------
-                 * 수정일       수정자     수정내용
-                 * ---------- -------- ---------------------------
-                 * %s  %s    최초생성
-                 * 
-                 * 
- */ - @Mapper(componentModel = "spring") - public interface %sConverter { - - @Mapping(source = "phoneNumber", target = "phone") - @Mapping(source = "message", target = "content") - %sLegacyRequest toLegacyRequest(%sRequest req); - - @Mapping(source = "phone", target = "phoneNumber") - @Mapping(source = "content", target = "message") - %sRequest toRequest(%sLegacyRequest legacyRequest); - - // %sResponse toResponse(%sLegacyResponse legacyResponse); - } - """.formatted( - bizPackage, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, author, createDate, createDate, author, - baseName, baseName, baseName, baseName, baseName, baseName, baseName - ); - converterContent = legacyConverterContent(bizPackage, baseName); - writeUtf8(converterDir.resolve(baseName + "Converter.java"), converterContent); - - log.append("\n=========================================\n"); - log.append(" Scaffolding Complete! (Routing: " + routingType + ")\n"); - log.append("=========================================\n"); - log.append("[Usecase Interface] ").append(usecaseDir.resolve(baseName + "UseCase.java")).append("\n"); - log.append("[Usecase Impl] ").append(usecaseImplDir.resolve(baseName + "UseCaseImpl.java")).append("\n"); - log.append("[Request DTO] ").append(dtoDir.resolve(baseName + "Request.java")).append("\n"); - log.append("[Response DTO] ").append(dtoDir.resolve(baseName + "Response.java")).append("\n"); - log.append("[Legacy Request DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyRequest.java")).append("\n"); - log.append("[Legacy Response DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyResponse.java")).append("\n"); - log.append("[Legacy Converter] ").append(converterDir.resolve(baseName + "Converter.java")).append("\n"); - } - // Schema Resource 파일 생성(useSchemaResource=true일 때) - if (useSchemaResource) { - Files.createDirectories(schemaDir); - String inputSchema = """ - { - "type": "object", - "additionalProperties": false, - "properties": { - "TODO_FIELD": { - "type": "string", - "description": "TODO: 파라미터 설명을 입력하세요." - } - }, - "required": [] - } - """; - String outputSchema = """ - { - "type": "object", - "additionalProperties": false, - "properties": { - "status": { - "type": "string", - "description": "처리 결과 상태 (SUCCESS / FAILURE)", - "enum": ["SUCCESS", "FAILURE"] - }, - "message": { - "type": "string", - "description": "처리 결과 메시지" - } - }, - "required": ["status"] - } - """; - writeUtf8(schemaDir.resolve(inputSchemaFileName), inputSchema); - writeUtf8(schemaDir.resolve(outputSchemaFileName), outputSchema); - log.append("[Input Schema] ").append(schemaDir.resolve(inputSchemaFileName)).append("\n"); - log.append("[Output Schema] ").append(schemaDir.resolve(outputSchemaFileName)).append("\n"); - } - - String mockResponse = mockResponseContent(outputFields); - if (isHttp) { - Path moduleRoot = rootDir.resolve(moduleName).toAbsolutePath().normalize(); - Path projectRoot = moduleRoot.getParent(); - Path wireMockBodyPath = projectRoot.resolve(Paths.get("mci-mock", "__files", toolName + ".json")); - Path wireMockMappingPath = projectRoot.resolve(Paths.get("mci-mock", "mappings", toolName + ".json")); - Path podMockResponsePath = moduleRoot.resolve(Paths.get("src/main/resources/mock-responses", toolName + ".json")); - Files.createDirectories(wireMockBodyPath.getParent()); - Files.createDirectories(wireMockMappingPath.getParent()); - Files.createDirectories(podMockResponsePath.getParent()); - writeUtf8(wireMockBodyPath, mockResponse); - writeUtf8(wireMockMappingPath, wireMockMappingContent(interfaceId, wireMockBodyPath.getFileName().toString())); - writeUtf8(podMockResponsePath, mockResponse); - ensureLocalHttpApiConfiguration(projectRoot, httpApiName, toolName); - log.append("[WireMock Response] ").append(wireMockBodyPath).append("\n"); - log.append("[WireMock Mapping] ").append(wireMockMappingPath).append("\n"); - log.append("[Pod Mock Response] ").append(podMockResponsePath).append("\n"); - } else { - Path mockResponsePath = rootDir.resolve(Paths.get(moduleName, "src/main/resources/mock-responses", toolName + ".json")); - Files.createDirectories(mockResponsePath.getParent()); - writeUtf8(mockResponsePath, mockResponse); - log.append("[Mock Response] ").append(mockResponsePath).append("\n"); - } - - Path generatedTestDir = rootDir.resolve(Paths.get(moduleName, "src/test/java/io/shinhanlife/dat/mcc/biz", group.toLowerCase(), "usecase")); - Files.createDirectories(generatedTestDir); - Path generatedTestPath = generatedTestDir.resolve(baseName + "UseCaseTest.java"); - writeUtf8(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"); - // Tool Definition YML is no longer generated. We use @GrowToolHint instead. - // log.append("[V17 Tool Definition] ...\n"); - log.append("\n Tip: HTTP Tool은 WireMock 실행 후 생성된 mapping URL로 호출을 확인하세요.\n"); - - return log.toString(); - } - - private static String toolDefinitionContent(String toolName, String title, String description, - String categoryKey, String interfaceId, - List inputFields, boolean mutation) { - String safeDescription = description == null || description.isBlank() - ? title + " 기능을 수행한다." : description; - StringBuilder properties = new StringBuilder(); - StringBuilder required = new StringBuilder(); - Set generatedNames = new LinkedHashSet<>(); - for (FieldDefinition field : inputFields == null ? List.of() : inputFields) { - if (field == null || field.name() == null || field.name().isBlank() - || !generatedNames.add(field.name().trim())) { - continue; - } - appendSchemaProperty(properties, field); - if (field.required()) { - required.append(" - ").append(field.name()).append("\n"); - } - } - if (properties.isEmpty()) { - properties.append(" {}\n"); - } - String requiredBlock = required.isEmpty() ? "" : " required:\n" + required; - String legacyLine = interfaceId == null || interfaceId.isBlank() - ? "" : "legacy_interface_id: " + yamlText(interfaceId) + "\n"; - return """ - name: %s - display_name: %s - version: 1.0.0 - category_key: %s - description: - function: %s - when_to_use: 사용자가 이 업무 기능의 실행 또는 조회를 명확히 요청한 경우 사용한다. - when_not_to_use: 입력값이 확인되지 않았거나 다른 업무 기능이 더 적합한 경우에는 사용하지 않는다. - io_limits: 정의된 입력 항목만 허용하며 응답 DTO에 정의된 업무 결과만 반환한다. - display_description: %s - example_queries: - - %s 처리해줘 - - %s 정보를 확인해줘 - - %s 업무 결과를 알려줘 - read_only: %s - destructive: %s - idempotent: %s - parameters_schema: - type: object - properties: - %s%s additionalProperties: false - tags: [%s] - %srequired_env_keys: [] - owner_org: MCP_TOOL - """.formatted(toolName, yamlText(title), categoryKey.toLowerCase(Locale.ROOT), - yamlText(safeDescription), yamlText(title), yamlText(title), yamlText(title), yamlText(title), - !mutation, mutation, !mutation, properties, requiredBlock, - categoryKey.toLowerCase(Locale.ROOT), legacyLine); - } - - private static String toolDefinitionContentV17(String toolName, String title, String description, - String categoryKey, String interfaceId, - List inputFields, boolean mutation, - ToolDefinitionOptions options) { - String function = option(options == null ? null : options.functionDescription(), - option(description, title + " 기능을 수행한다.")); - String whenToUse = option(options == null ? null : options.whenToUse(), - "사용자가 해당 업무 기능의 실행 또는 조회를 명확히 요청한 경우 사용한다."); - String whenNotToUse = option(options == null ? null : options.whenNotToUse(), - "필수 입력값이 확인되지 않았거나 다른 업무 기능이 더 적합한 경우에는 사용하지 않는다."); - String ioLimits = option(options == null ? null : options.ioLimits(), - "정의된 입력 항목만 허용하며 응답 DTO에 정의된 업무 결과만 반환한다."); - String displayDescription = option(options == null ? null : options.displayDescription(), title); - List examples = normalizedList(options == null ? null : options.exampleQueries(), List.of( - title + " 처리해줘", title + " 정보를 확인해줘", title + " 업무 결과를 알려줘")); - List tags = normalizedList(options == null ? null : options.tags(), - List.of(categoryKey.toLowerCase(Locale.ROOT))); - String ownerOrg = option(options == null ? null : options.ownerOrg(), "MCP_TOOL"); - - StringBuilder properties = new StringBuilder(); - StringBuilder required = new StringBuilder(); - Set generatedNames = new LinkedHashSet<>(); - for (FieldDefinition field : inputFields == null ? List.of() : inputFields) { - if (field == null || field.name() == null || field.name().isBlank() - || !generatedNames.add(field.name().trim())) { - continue; - } - appendSchemaProperty(properties, field); - if (field.required()) { - required.append(" - ").append(field.name().trim()).append("\n"); - } - } - if (properties.isEmpty()) { - properties.append(" {}\n"); - } - String requiredBlock = required.isEmpty() ? "" : " required:\n" + required; - String legacyLine = interfaceId == null || interfaceId.isBlank() - ? "" : "legacy_interface_id: " + yamlText(interfaceId) + "\n"; - String exampleBlock = examples.stream().map(value -> " - " + yamlText(value)) - .collect(java.util.stream.Collectors.joining("\n")); - String tagBlock = tags.stream().map(ToolScaffolder::yamlText) - .collect(java.util.stream.Collectors.joining(", ")); - - return """ - name: %s - display_name: %s - version: 1.0.0 - category_key: %s - description: - function: %s - when_to_use: %s - when_not_to_use: %s - io_limits: %s - display_description: %s - example_queries: - %s - read_only: %s - destructive: %s - idempotent: %s - parameters_schema: - type: object - properties: - %s%s additionalProperties: false - tags: [%s] - %srequired_env_keys: [] - owner_org: %s - """.formatted(toolName, yamlText(title), categoryKey.toLowerCase(Locale.ROOT), - yamlText(function), yamlText(whenToUse), yamlText(whenNotToUse), yamlText(ioLimits), - yamlText(displayDescription), exampleBlock, !mutation, mutation, !mutation, - properties, requiredBlock, tagBlock, legacyLine, yamlText(ownerOrg)); - } - - private static String option(String value, String fallback) { - return value == null || value.isBlank() ? fallback : value.trim(); - } - - private static List normalizedList(List values, List fallback) { - if (values == null) { - return fallback; - } - List normalized = values.stream() - .filter(java.util.Objects::nonNull) - .map(String::trim) - .filter(value -> !value.isBlank()) - .distinct() - .toList(); - return normalized.isEmpty() ? fallback : normalized; - } - - private static boolean isMutationTool(String baseName) { - String value = baseName.toLowerCase(Locale.ROOT); - return value.matches(".*(create|add|update|delete|remove|send|process|approve|reject|register|issue).*"); - } - - private static String jsonSchemaType(String javaType) { - return switch (javaType == null ? "String" : javaType) { - case "Integer", "Long" -> "integer"; - case "Double", "BigDecimal" -> "number"; - case "Boolean" -> "boolean"; - case "List" -> "array"; - default -> "string"; - }; - } - - private static void appendSchemaProperty(StringBuilder properties, FieldDefinition field) { - properties.append(" ").append(field.name().trim()).append(":\n") - .append(" type: ").append(jsonSchemaType(field.type())).append("\n") - .append(" description: ").append(yamlText(richDescription(field))).append("\n"); - if ("Enum".equals(field.type()) && field.enumValues() != null && !field.enumValues().isEmpty()) { - properties.append(" enum: [").append(field.enumValues().stream() - .filter(value -> value != null && !value.isBlank()).map(String::trim) - .collect(java.util.stream.Collectors.joining(", "))).append("]\n"); - } - if ("List".equals(field.type())) { - properties.append(" items:\n") - .append(" type: ").append("Object".equals(field.itemType()) ? "object" : jsonSchemaType(field.itemType())).append("\n"); - if ("Object".equals(field.itemType()) && field.itemFields() != null && !field.itemFields().isEmpty()) { - properties.append(" properties:\n"); - for (FieldDefinition itemField : field.itemFields()) { - properties.append(" ").append(itemField.name()).append(":\n") - .append(" type: ").append(jsonSchemaType(itemField.type())).append("\n") - .append(" description: ").append(yamlText(richDescription(itemField))).append("\n"); - } - } - } - } - - private static String yamlText(String value) { - String safe = value == null ? "" : value.replace("\\", "\\\\").replace("\"", "\\\"") - .replace("\r", " ").replace("\n", " "); - return "\"" + safe + "\""; - } - - private static String toKebabCase(String pascalCase) { - if (pascalCase == null || pascalCase.isEmpty()) return pascalCase; - return pascalCase - .replaceAll("([a-z0-9])([A-Z])", "$1-$2") - .toLowerCase(Locale.ROOT); - } - - private static void ensureLocalHttpApiConfiguration(Path projectRoot, String httpApiName, String toolName) throws IOException { - Path localConfigPath = projectRoot.resolve("src/main/resources/glow/application-glow-local.yml"); - Files.createDirectories(localConfigPath.getParent()); - String existing = Files.exists(localConfigPath) ? Files.readString(localConfigPath, StandardCharsets.UTF_8) : ""; - if (java.util.regex.Pattern.compile("(?m)^\\s*-\\s+name:\\s*" - + java.util.regex.Pattern.quote(httpApiName) + "\\s*$").matcher(existing).find()) { - return; - } - String environmentKey = toPackageSegment(httpApiName).toUpperCase(Locale.ROOT).replace('-', '_'); - String apiEntry = """ - - name: %s - domain: ${AXHUB_%s_HTTP_DOMAIN:http://localhost:${server.port}} - url: ${AXHUB_%s_HTTP_URL:/api/mock/http/%s} - method: POST - content-type: application/json;charset=UTF-8 - biz-pod: false - """.formatted(httpApiName, environmentKey, environmentKey, toolName).stripTrailing() + "\n"; - if (existing.isBlank()) { - existing = """ - spring: - config: - activate: - on-profile: local - - glow: - communication: - http: - api-list: - """ + apiEntry; - } else if (existing.contains("\n 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:")) { - existing += apiEntry; - } else { - throw new IllegalStateException("application-glow-local.yml must define glow.communication.http.api-list"); - } - if (!existing.contains("axhub:\n mock:\n http:\n enabled: true")) { - existing += """ - - axhub: - mock: - http: - enabled: true - """; - } - writeUtf8(localConfigPath, existing); - } - - private static void writeUtf8(Path path, String content) throws IOException { - Files.writeString(path, content, StandardCharsets.UTF_8); - } - - private static String dtoContent(String packageName, String className, List fields, - String author, String createDate, boolean request) { - String body = fieldLines(fields, request ? Set.of() : Set.of("resultCode", "resultMessage"), className); - if (!request) { - body = " private String resultCode;\n\n private String resultMessage;\n" + body; - } - String listImport = hasListField(fields) ? "import java.util.List;\n" : ""; - String patternImport = hasPatternField(fields) ? "import jakarta.validation.constraints.Pattern;\n" : ""; - return """ - package %s; - - import com.fasterxml.jackson.annotation.JsonInclude; - import io.swagger.v3.oas.annotations.media.Schema; - import lombok.Data; - %s%s - - @Data - @JsonInclude(JsonInclude.Include.NON_NULL) - public class %s { - %s%s} - """.formatted(packageName, listImport, patternImport, className, body, innerObjectListClasses(fields)); - } - - private static String mciIoContent(String packageSuffix, String className, List fields, - String author, String createDate) { - String listImport = hasListField(fields) ? "import java.util.List;\n" : ""; - String patternImport = hasPatternField(fields) ? "import jakarta.validation.constraints.Pattern;\n" : ""; - return """ - package %s.%s.io; - - import io.swagger.v3.oas.annotations.media.Schema; - import lombok.Data; - %s%s - - @Data - public class %s { - %s%s} - """.formatted(BASE_PACKAGE, packageSuffix, listImport, patternImport, className, fieldLines(fields, Set.of(), className), - innerObjectListClasses(fields)); - } - - private static boolean hasListField(List fields) { - return fields != null && fields.stream().anyMatch(field -> field != null && "List".equals(field.type())); - } - - private static boolean hasPatternField(List fields) { - if (fields == null) return false; - return fields.stream().anyMatch(field -> { - if (field == null) return false; - if (field.pattern() != null && !field.pattern().isBlank()) return true; - if ("List".equals(field.type()) && "Object".equals(field.itemType())) { - return hasPatternField(field.itemFields()); - } - return false; - }); - } - - private static void writeStructuredFieldTypes(Path directory, String packageName, String ownerClass, - List fields) throws IOException { - for (FieldDefinition field : fields == null ? List.of() : fields) { - if (field == null || field.name() == null || field.name().isBlank()) { - continue; - } - if ("Enum".equals(field.type())) { - String enumName = toPascalCase(field.name()); - List values = field.enumValues() == null ? List.of() : field.enumValues().stream() - .filter(value -> value != null && !value.isBlank()).map(String::trim).distinct().toList(); - if (values.isEmpty()) { - throw new IllegalArgumentException("Enum field needs at least one allowed value: " + field.name()); - } - String constants = values.stream().map(value -> " " + enumConstant(value) + "(\"" + javaText(value) + "\")") - .collect(java.util.stream.Collectors.joining(",\n")); - writeUtf8(directory.resolve(enumName + ".java"), """ - package %s; - - import com.fasterxml.jackson.annotation.JsonCreator; - import com.fasterxml.jackson.annotation.JsonValue; - - public enum %s { - %s; - - private final String value; - - %s(String value) { - this.value = value; - } - - @JsonValue - public String getValue() { - return value; - } - - @JsonCreator - public static %s fromValue(String value) { - for (%s candidate : values()) { - if (candidate.value.equals(value)) return candidate; - } - throw new IllegalArgumentException("Unsupported value: " + value); - } - } - """.formatted(packageName, enumName, constants, enumName, enumName, enumName)); - } - if ("List".equals(field.type()) && "Object".equals(field.itemType()) - && (field.itemFields() == null || field.itemFields().isEmpty())) { - throw new IllegalArgumentException("Object List field needs item fields: " + field.name()); - } - } - } - - private static String enumConstant(String value) { - String constant = value.toUpperCase(Locale.ROOT).replaceAll("[^A-Z0-9]+", "_").replaceAll("^_+|_+$", ""); - return constant.isBlank() ? "VALUE" : (Character.isDigit(constant.charAt(0)) ? "VALUE_" + constant : constant); - } - - private static String listItemClassName(String ownerClass, FieldDefinition field) { - return toPascalCase(field.name()) + "Item"; - } - - private static String innerObjectListClasses(List fields) { - StringBuilder source = new StringBuilder(); - Set generated = new LinkedHashSet<>(); - for (FieldDefinition field : fields == null ? List.of() : fields) { - if (field == null || !"List".equals(field.type()) || !"Object".equals(field.itemType()) - || field.name() == null || field.name().isBlank()) { - continue; - } - String itemName = listItemClassName("", field); - if (!generated.add(itemName)) continue; - List itemFields = field.itemFields() == null ? List.of() : field.itemFields(); - if (itemFields.isEmpty()) { - throw new IllegalArgumentException("Object List field needs item fields: " + field.name()); - } - source.append("\n @Data\n public static class ").append(itemName).append(" {\n") - .append(fieldLines(itemFields, Set.of(), itemName)) - .append(innerObjectListClasses(itemFields)) - .append(" }\n"); - } - return source.toString(); - } - - private static String httpUseCaseImplContent(String bizPackage, String baseName, String httpPackage, - String httpApiClass, String author, String createDate) { - String httpRequestClass = baseName + "HttpRequest"; - String httpResponseClass = baseName + "HttpResponse"; - String httpClientClass = httpApiClass + "Client"; - String clientVariable = Character.toLowerCase(httpClientClass.charAt(0)) + httpClientClass.substring(1); - return """ - package %s.usecase.impl; - - import %s.converter.%sConverter; - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import %s.%s.%s; - import %s.%s.io.%s; - import %s.%s.io.%s; - import %s.usecase.%sUseCase; - import lombok.RequiredArgsConstructor; - import org.springframework.stereotype.Service; - - @Service - @RequiredArgsConstructor - public class %sUseCaseImpl implements %sUseCase { - - private final %sConverter converter; - private final %s %s; - - @Override - public %sResponse execute(%sRequest req) { - %s httpRequest = converter.toHttpRequest(req); - %s httpResponse = %s.call(httpRequest, %s.class); - - %sResponse response = converter.toResponse(httpResponse); - response.setResultCode("SUCCESS"); - response.setResultMessage("HTTP API call completed."); - return response; - } - } - """.formatted( - bizPackage, - bizPackage, baseName, - bizPackage, baseName, - bizPackage, baseName, - BASE_PACKAGE, httpPackage, httpClientClass, - BASE_PACKAGE, httpPackage, httpRequestClass, - BASE_PACKAGE, httpPackage, httpResponseClass, - bizPackage, baseName, - baseName, baseName, - baseName, httpClientClass, clientVariable, - baseName, baseName, - httpRequestClass, httpResponseClass, clientVariable, httpResponseClass, - baseName); - } - - private static String httpClientContent(String httpPackage, String clientClass, String apiName) { - return """ - package %s; - - import io.shinhanlife.dat.lib.integration.http.component.AxhubHttpComponent; - import lombok.RequiredArgsConstructor; - import org.springframework.stereotype.Component; - - @Component - @RequiredArgsConstructor - public class %s { - private static final String API_NAME = "%s"; - - private final AxhubHttpComponent http; - - public O call(I request, Class responseType) { - return http.call(API_NAME, request, responseType); - } - } - """.formatted(httpPackage, clientClass, apiName); - } - - private static String httpConverterContent(String bizPackage, String baseName, String httpPackage) { - return """ - package %s.converter; - - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import %s.io.%sHttpRequest; - import %s.io.%sHttpResponse; - import org.mapstruct.Mapper; - import org.mapstruct.Mapping; - import org.mapstruct.ReportingPolicy; - - @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) - public interface %sConverter { - // Field names differ? Add mappings like this before the method. - // @Mapping(source = "sourceField", target = "targetField") - %sHttpRequest toHttpRequest(%sRequest request); - %sResponse toResponse(%sHttpResponse httpResponse); - } - """.formatted(bizPackage, - bizPackage, baseName, - bizPackage, baseName, - httpPackage, baseName, - httpPackage, baseName, - baseName, baseName, baseName, baseName, baseName); - } - - private static String toPackageSegment(String value) { - String normalized = value == null ? "" : value.toLowerCase(Locale.ROOT) - .replaceAll("[^a-z0-9]+", "_") - .replaceAll("^_+|_+$", ""); - return normalized.isBlank() ? "http_api" : normalized; - } - private static String mciConverterContent(String bizPackage, String baseName, String mciPackage, String interfaceId) { - return """ - package %s.converter; - - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import %s.%s.io.%s_I; - import %s.%s.io.%s_O; - import org.mapstruct.Mapper; - import org.mapstruct.Mapping; - import org.mapstruct.ReportingPolicy; - - @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) - public interface %sConverter { - // Field names differ? Add mappings like this before the method. - // @Mapping(source = "sourceField", target = "targetField") - %s_I toLegacyRequest(%sRequest request); - %sRequest toRequest(%s_I mciRequest); - %sResponse toResponse(%s_O mciRes); - } - """.formatted(bizPackage, bizPackage, baseName, bizPackage, baseName, - BASE_PACKAGE, mciPackage, interfaceId, BASE_PACKAGE, mciPackage, interfaceId, - baseName, interfaceId, baseName, baseName, interfaceId, baseName, interfaceId); - } - - private static String legacyConverterContent(String bizPackage, String baseName) { - return """ - package %s.converter; - - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import %s.legacy.%sLegacyRequest; - import %s.legacy.%sLegacyResponse; - import org.mapstruct.Mapper; - import org.mapstruct.Mapping; - import org.mapstruct.ReportingPolicy; - - @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) - public interface %sConverter { - // Field names differ? Add mappings like this before the method. - // @Mapping(source = "sourceField", target = "targetField") - %sLegacyRequest toLegacyRequest(%sRequest request); - %sRequest toRequest(%sLegacyRequest legacyRequest); - %sResponse toResponse(%sLegacyResponse legacyResponse); - } - """.formatted( - bizPackage, bizPackage, baseName, bizPackage, baseName, - bizPackage, baseName, bizPackage, baseName, - baseName, baseName, baseName, baseName, baseName, baseName, baseName); - } - private static String fieldLines(List fields) { - return fieldLines(fields, Set.of(), ""); - } - - private static String fieldLines(List fields, Set excludedNames) { - return fieldLines(fields, excludedNames, ""); - } - - private static String fieldLines(List fields, Set excludedNames, String ownerClass) { - StringBuilder source = new StringBuilder(); - Set generatedNames = new LinkedHashSet<>(); - for (FieldDefinition field : fields == null ? List.of() : fields) { - if (field == null || field.name() == null || field.name().isBlank()) { - continue; - } - String fieldName = field.name().trim(); - if (excludedNames.contains(fieldName) || !generatedNames.add(fieldName)) { - continue; - } - String type = javaFieldType(field, ownerClass); - String description = richDescription(field).replace("\"", "\\\""); - String example = ""; - if (field.examples() != null && !field.examples().isEmpty()) { - example = field.examples().get(0).replace("\"", "\\\""); - } - source.append(" @Schema(description = \"").append(description).append("\""); - if (!example.isEmpty()) { - source.append(", example = \"").append(example).append("\""); - } - if (field.required()) { - source.append(", requiredMode = Schema.RequiredMode.REQUIRED"); - } - source.append(")\n"); - if (field.pattern() != null && !field.pattern().isBlank()) { - source.append(" @Pattern(regexp = \"").append(field.pattern().replace("\"", "\\\"")).append("\")\n"); - } - source.append(" private ").append(type).append(' ').append(fieldName).append(";\n\n"); - } - return source.toString(); - } - - private static String richDescription(FieldDefinition field) { - String desc = field.description() == null ? "" : field.description().trim(); - if (field.pattern() != null && !field.pattern().isBlank()) { - desc += " (형식: " + field.pattern() + ")"; - } - if (field.examples() != null && !field.examples().isEmpty()) { - desc += " (예시: " + String.join(", ", field.examples()) + ")"; - } - return desc.trim(); - } - - private static String javaFieldType(FieldDefinition field, String ownerClass) { - return switch (field.type() == null ? "String" : field.type()) { - case "Enum" -> toPascalCase(field.name()); - case "List" -> "List<" + listItemJavaType(field, ownerClass) + ">"; - default -> supportedType(field.type()); - }; - } - - private static String listItemJavaType(FieldDefinition field, String ownerClass) { - String itemType = field.itemType() == null ? "" : field.itemType(); - if ("Object".equals(itemType)) { - return listItemClassName(ownerClass, field); - } - return supportedType(itemType); - } - private static String supportedType(String type) { - if (type == null || type.isBlank()) return "String"; - return type; - } - - - static String wireMockMappingContent(String interfaceId, String bodyFileName) { - return """ - { - "request" : { - "method" : "POST", - "urlPath" : "/%s" - }, - "response" : { - "status" : 200, - "headers" : { - "Content-Type" : "application/json;charset=UTF-8" - }, - "bodyFileName" : "%s" - } - } - """.formatted(jsonEscape(interfaceId), jsonEscape(bodyFileName)); - } - private static String mockResponseContent(List outputFields) { - StringBuilder json = new StringBuilder("{\n"); - List fields = outputFields == null ? List.of() : outputFields; - boolean first = true; - for (FieldDefinition field : fields) { - if (field.name() == null || field.name().isBlank()) { - continue; - } - if (!first) { - json.append(",\n"); - } - json.append(" \"").append(jsonEscape(field.name())).append("\" : ") - .append(mockValue(field)); - first = false; - } - return json.append("\n}\n").toString(); - } - - private static String mockValue(FieldDefinition field) { - if ("List".equals(field.type())) { - if ("Object".equals(field.itemType())) { - StringBuilder object = new StringBuilder("{"); - boolean first = true; - for (FieldDefinition itemField : field.itemFields() == null ? List.of() : field.itemFields()) { - if (!first) object.append(", "); - object.append("\"").append(jsonEscape(itemField.name())).append("\" : ").append(mockValue(itemField)); - first = false; - } - return "[" + object + "]"; - } - FieldDefinition item = new FieldDefinition("item", field.itemType(), "", field.examples(), field.pattern(), false); - return "[" + mockValue(item) + "]"; - } - - String exampleStr = (field.examples() != null && !field.examples().isEmpty()) ? field.examples().get(0) : null; - if (exampleStr == null || exampleStr.isBlank()) { - return "null"; - } - if ("Enum".equals(field.type())) { - return "\"" + jsonEscape(exampleStr) + "\""; - } - return switch (supportedType(field.type())) { - case "Integer", "Long", "Double", "BigDecimal" -> exampleStr; - case "Boolean" -> Boolean.parseBoolean(exampleStr) ? "true" : "false"; - default -> "\"" + jsonEscape(exampleStr) + "\""; - }; - } - - private static String jsonEscape(String value) { - return value.replace("\\", "\\\\").replace("\"", "\\\""); - } - - private static String useCaseTestContent(String bizPackage, String baseName) { - return """ - package %s.usecase; - - import static org.junit.jupiter.api.Assertions.assertNotNull; - - import %s.dto.%sRequest; - import %s.dto.%sResponse; - import org.junit.jupiter.api.Test; - - class %sUseCaseTest { - - @Test - void createsToolRequestAndResponseDtos() { - assertNotNull(new %sRequest()); - assertNotNull(new %sResponse()); - } - } - """.formatted(bizPackage, bizPackage, baseName, bizPackage, baseName, - baseName, baseName, baseName); - } - private static String toToolName(String moduleName, String group, String baseName) { - String normalizedName = baseName.replaceAll("([a-z0-9])([A-Z])", "$1 $2") - .toLowerCase(Locale.ROOT) - .replaceAll("[^a-z0-9]+", " ") - .trim(); - String[] words = normalizedName.split("\\s+"); - String service = words[0]; - String action = words.length == 1 ? "execute" : words[words.length - 1]; - return "%s_%s_%s".formatted( - group.toLowerCase(Locale.ROOT), - service, - action); - } - - private static String toPascalCase(String str) { - if (str == null || str.isEmpty()) { - return str; - } - - boolean hasLowercase = false; - for (char c : str.toCharArray()) { - if (Character.isLowerCase(c)) { - hasLowercase = true; - break; - } - } - - StringBuilder result = new StringBuilder(); - boolean capitalizeNext = true; - for (char c : str.toCharArray()) { - if (c == '_' || c == '-' || c == ' ') { - capitalizeNext = true; - } else if (capitalizeNext) { - result.append(Character.toUpperCase(c)); - capitalizeNext = false; - } else { - result.append(hasLowercase ? c : Character.toLowerCase(c)); - } - } - if (result.length() > 0) { - result.setCharAt(0, Character.toUpperCase(result.charAt(0))); - } - return result.toString(); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolSchemaResolver.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolSchemaResolver.java deleted file mode 100644 index 52fc19557..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolSchemaResolver.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dat.lib.annotation.McpOutputSchema; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; -import org.springframework.core.io.ClassPathResource; - -/** Resolves MCP Tool schemas from resources or DTO metadata. */ -public class ToolSchemaResolver { - - private final ObjectMapper objectMapper; - - public ToolSchemaResolver(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - public Map resolve(org.springaicommunity.mcp.annotation.McpTool function, - GrowToolHint hint, Class requestType) { - if (hint != null && !hint.inputSchemaResource().isBlank()) { - return loadResource(hint.inputSchemaResource()); - } - return JsonSchemaGenerator.generateSchema(requestType); - } - - /** - * Resolves a response schema only when it is explicitly declared. - * A JSON resource has precedence over a DTO marker annotation. - */ - public Map resolveOutput(org.springaicommunity.mcp.annotation.McpTool function, - Class responseType, GrowToolHint hint) { - if (hint != null && !hint.outputSchemaResource().isBlank()) { - return loadResource(hint.outputSchemaResource()); - } - return resolveOutput(function, responseType); - } - - /** - * Generates a response schema only for DTOs marked with {@link McpOutputSchema}. - */ - public Map resolveOutput(org.springaicommunity.mcp.annotation.McpTool function, - Class responseType) { - if (responseType == null - || responseType == Object.class - || Map.class.isAssignableFrom(responseType) - || responseType == Void.class - || responseType == void.class - || !responseType.isAnnotationPresent(McpOutputSchema.class)) { - return Map.of(); - } - return JsonSchemaGenerator.generateSchema(responseType); - } - - /** - * Retained for callers that use only explicit output schemas. - */ - public Map resolveOutput(org.springaicommunity.mcp.annotation.McpTool function) { - return Map.of(); - } - - private Map loadResource(String location) { - String path = location.startsWith("classpath:") - ? location.substring("classpath:".length()) - : location; - ClassPathResource resource = new ClassPathResource(path); - if (!resource.exists()) { - throw new ToolSchemaResourceException("MCP schema resource not found: " + location); - } - - try (InputStream inputStream = resource.getInputStream()) { - return objectMapper.readValue(inputStream, new TypeReference<>() { }); - } catch (IOException e) { - throw new ToolSchemaResourceException("Failed to load MCP schema resource: " + location, e); - } - } - - public static final class ToolSchemaResourceException extends IllegalStateException { - private static final long serialVersionUID = 1L; - - public ToolSchemaResourceException(String message) { - super(message); - } - - public ToolSchemaResourceException(String message, IOException cause) { - super(message, cause); - } - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolSourceUpdater.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolSourceUpdater.java deleted file mode 100644 index 2d63bca7b..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/util/ToolSourceUpdater.java +++ /dev/null @@ -1,181 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.charset.StandardCharsets; -import java.util.Comparator; - -/** Updates MCP SDK and project-owned metadata in a generated tool source file. */ -public final class ToolSourceUpdater { - - private ToolSourceUpdater() { - } - - public static void updateToolSource(String toolName, String categoryKey, String description, - boolean register, Boolean requiresApproval) throws IOException { - String configuredSourceDirectory = System.getenv("AXHUB_SOURCE_DIR"); - Path rootDirectory = configuredSourceDirectory == null || configuredSourceDirectory.isBlank() - ? Paths.get(".") : Paths.get(configuredSourceDirectory); - updateToolSource(rootDirectory, toolName, categoryKey, description, register, requiresApproval); - } - - static void updateToolSource(Path rootDirectory, String toolName, String categoryKey, String description, - boolean register, Boolean requiresApproval) throws IOException { - Path targetFile = findToolSource(rootDirectory, toolName); - if (targetFile == null) { - throw new IllegalArgumentException("Tool source not found: " + toolName); - } - - String content = Files.readString(targetFile, StandardCharsets.UTF_8); - content = updateMcpTool(content, toolName, description); - content = updateToolHint(content, categoryKey, register, requiresApproval); - Files.writeString(targetFile, content, StandardCharsets.UTF_8); - } - - public static boolean syncToolSource(Path rootDirectory, String toolName, String title, String description, - boolean readOnlyHint, boolean destructiveHint, boolean idempotentHint, - String categoryKey) throws IOException { - Path targetFile = findToolSource(rootDirectory, toolName); - if (targetFile == null) { - throw new IllegalArgumentException("Tool source not found: " + toolName); - } - - String content = Files.readString(targetFile, StandardCharsets.UTF_8); - String originalContent = content; - - AnnotationRange mcpRange = annotationArguments(content, "McpTool", toolName); - if (mcpRange != null) { - if (title != null) { - content = replaceAttribute(content, mcpRange, "title", quote(title)); - mcpRange = annotationArguments(content, "McpTool", toolName); - } - if (description != null) { - content = replaceAttribute(content, mcpRange, "description", quote(description)); - } - } - - AnnotationRange hintRange = annotationArguments(content, "GrowToolHint", null); - if (hintRange != null) { - if (categoryKey != null && !categoryKey.isBlank()) { - content = replaceAttribute(content, hintRange, "categoryKey", quote(categoryKey)); - hintRange = annotationArguments(content, "GrowToolHint", null); - } - content = replaceAttribute(content, hintRange, "destructive", Boolean.toString(destructiveHint)); - hintRange = annotationArguments(content, "GrowToolHint", null); - content = replaceAttribute(content, hintRange, "idempotent", Boolean.toString(idempotentHint)); - } - - if (!content.equals(originalContent)) { - Files.writeString(targetFile, content, StandardCharsets.UTF_8); - return true; - } - return false; - } - - private static Path findToolSource(Path rootDirectory, String toolName) throws IOException { - try (var paths = Files.walk(rootDirectory)) { - return paths.filter(Files::isRegularFile) - .filter(path -> path.getFileName().toString().endsWith("UseCase.java")) - .filter(path -> path.toString().contains("dat-was-") || path.toString().contains("axhub-tool-")) - .sorted(Comparator.naturalOrder()) - .filter(path -> containsMcpTool(path, toolName)) - .findFirst() - .orElse(null); - } - } - - private static boolean containsMcpTool(Path path, String toolName) { - try { - return annotationArguments(Files.readString(path, StandardCharsets.UTF_8), "McpTool", toolName) != null; - } catch (IOException exception) { - throw new IllegalStateException("Failed to read tool source: " + path, exception); - } - } - - private static String updateMcpTool(String content, String toolName, String description) { - AnnotationRange range = annotationArguments(content, "McpTool", toolName); - if (range == null) { - throw new IllegalArgumentException("McpTool declaration not found: " + toolName); - } - return description == null ? content : replaceAttribute(content, range, "description", quote(description)); - } - - private static String updateToolHint(String content, String categoryKey, boolean register, Boolean requiresApproval) { - AnnotationRange range = annotationArguments(content, "GrowToolHint", null); - if (range == null) { - throw new IllegalArgumentException("GrowToolHint declaration not found next to McpTool"); - } - String updated = replaceAttribute(content, range, "register", Boolean.toString(register)); - range = annotationArguments(updated, "GrowToolHint", null); - if (requiresApproval != null) { - updated = replaceAttribute(updated, range, "requiresApproval", Boolean.toString(requiresApproval)); - range = annotationArguments(updated, "GrowToolHint", null); - } - if (categoryKey != null && !categoryKey.isBlank()) { - updated = replaceAttribute(updated, range, "categoryKey", quote(categoryKey)); - } - return updated; - } - - private static String replaceAttribute(String content, AnnotationRange range, String attribute, String value) { - String arguments = content.substring(range.argumentsStart(), range.argumentsEnd()); - String pattern = "\\b" + attribute + "\\s*=\\s*(?:true|false|\\\"(?:\\\\.|[^\\\"\\\\])*\\\")"; - String replacement = arguments.replaceFirst(pattern, attribute + " = " + value); - if (replacement.equals(arguments)) { - replacement = arguments.isBlank() ? attribute + " = " + value : arguments + ", " + attribute + " = " + value; - } - return content.substring(0, range.argumentsStart()) + replacement + content.substring(range.argumentsEnd()); - } - - private static String quote(String value) { - return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\""; - } - - private static AnnotationRange annotationArguments(String content, String annotationName, String toolName) { - int offset = content.indexOf("@" + annotationName); - while (offset >= 0) { - int openingParenthesis = content.indexOf('(', offset); - int closingParenthesis = findAnnotationEnd(content, openingParenthesis); - if (openingParenthesis < 0 || closingParenthesis < 0) { - return null; - } - AnnotationRange range = new AnnotationRange(openingParenthesis + 1, closingParenthesis); - if (toolName == null || content.substring(range.argumentsStart(), range.argumentsEnd()) - .matches("(?s).*\\bname\\s*=\\s*\\\"" + java.util.regex.Pattern.quote(toolName) + "\\\".*")) { - return range; - } - offset = content.indexOf("@" + annotationName, closingParenthesis + 1); - } - return null; - } - - private static int findAnnotationEnd(String content, int openingParenthesis) { - int depth = 0; - boolean inString = false; - boolean escaped = false; - for (int index = openingParenthesis; index < content.length(); index++) { - char character = content.charAt(index); - if (inString) { - if (escaped) { - escaped = false; - } else if (character == '\\') { - escaped = true; - } else if (character == '\"') { - inString = false; - } - } else if (character == '\"') { - inString = true; - } else if (character == '(') { - depth++; - } else if (character == ')' && --depth == 0) { - return index; - } - } - return -1; - } - - private record AnnotationRange(int argumentsStart, int argumentsEnd) { - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/McpToolNameValidationRunner.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/McpToolNameValidationRunner.java deleted file mode 100644 index 3fadb7cbd..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/McpToolNameValidationRunner.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.shinhanlife.dat.lib.validation; - -import java.nio.file.Path; - -/** Gradle entry point for validating unique MCP Tool names before packaging. */ -public final class McpToolNameValidationRunner { - - private McpToolNameValidationRunner() { - } - - static void validate(Path projectRoot) { - McpToolNameValidator.assertUnique(projectRoot); - } - - public static void main(String[] args) { - if (args.length == 0) { - throw new IllegalArgumentException("Project root path must be provided"); - } - validate(Path.of(args[0])); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/McpToolNameValidator.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/McpToolNameValidator.java deleted file mode 100644 index ffe1f971a..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/McpToolNameValidator.java +++ /dev/null @@ -1,173 +0,0 @@ -package io.shinhanlife.dat.lib.validation; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @package io.shinhanlife.dat.lib.validation - * @className McpToolNameValidator - * @description Validates unique MCP SDK tool names across tool modules - * @author 0986406 - * @create 2026.07.27 - *
- * ---------- revision history ----------
- * date       author    description
- * ---------- --------- ---------------------------
- * 2026.07.27 0986406    initial creation
- * 
- */ -public final class McpToolNameValidator { - - private static final Pattern TOOL_NAME_PATTERN = Pattern.compile("\\bname\\s*=\\s*\\\"([^\\\"]+)\\\""); - private static final Pattern TOOL_NAME_CONVENTION = Pattern.compile("^[a-zA-Z0-9_-]{1,128}$"); - - private McpToolNameValidator() { - } - - public static void assertUnique(Path projectRoot) { - Map> declarationsByName = new LinkedHashMap<>(); - - try (var modules = Files.list(projectRoot)) { - modules.filter(Files::isDirectory) - .filter(path -> path.getFileName().toString().startsWith("dat-was-")) - .filter(path -> !path.getFileName().toString().equals("dat-was-lib")) - .sorted() - .forEach(module -> collectDeclarations(module, declarationsByName)); - } catch (IOException exception) { - throw new UncheckedIOException("Failed to scan MCP tool modules", exception); - } - - List>> invalidNames = declarationsByName.entrySet().stream() - .filter(entry -> !TOOL_NAME_CONVENTION.matcher(entry.getKey()).matches()) - .sorted(Map.Entry.comparingByKey()) - .toList(); - - if (!invalidNames.isEmpty()) { - throw new IllegalStateException(buildInvalidNameMessage(invalidNames)); - } - List>> duplicates = declarationsByName.entrySet().stream() - .filter(entry -> entry.getValue().size() > 1) - .sorted(Map.Entry.comparingByKey()) - .toList(); - - if (!duplicates.isEmpty()) { - throw new IllegalStateException(buildDuplicateMessage(duplicates)); - } - } - - private static void collectDeclarations(Path module, Map> declarationsByName) { - Path sourceDirectory = module.resolve("src/main/java"); - if (!Files.isDirectory(sourceDirectory)) { - return; - } - - try (var sources = Files.walk(sourceDirectory)) { - sources.filter(path -> path.toString().endsWith(".java")) - .sorted() - .forEach(source -> collectDeclarations(module.getFileName().toString(), source, declarationsByName)); - } catch (IOException exception) { - throw new UncheckedIOException("Failed to scan module " + module.getFileName(), exception); - } - } - - private static void collectDeclarations(String moduleName, Path source, - Map> declarationsByName) { - String content; - try { - content = Files.readString(source); - } catch (IOException exception) { - throw new UncheckedIOException("Failed to read " + source, exception); - } - - int annotationOffset = content.indexOf("@McpTool"); - while (annotationOffset >= 0) { - int openingParenthesis = content.indexOf('(', annotationOffset); - int closingParenthesis = findAnnotationEnd(content, openingParenthesis); - if (openingParenthesis < 0 || closingParenthesis < 0) { - break; - } - - Matcher matcher = TOOL_NAME_PATTERN.matcher(content.substring(openingParenthesis + 1, closingParenthesis)); - if (matcher.find()) { - String toolName = matcher.group(1); - int line = 1 + (int) content.substring(0, annotationOffset).chars().filter(character -> character == '\n').count(); - declarationsByName.computeIfAbsent(toolName, ignored -> new ArrayList<>()) - .add(new ToolDeclaration(moduleName, source, line)); - } - annotationOffset = content.indexOf("@McpTool", closingParenthesis + 1); - } - } - - private static int findAnnotationEnd(String content, int openingParenthesis) { - if (openingParenthesis < 0) { - return -1; - } - - int depth = 0; - boolean inString = false; - boolean escaped = false; - for (int index = openingParenthesis; index < content.length(); index++) { - char character = content.charAt(index); - if (inString) { - if (escaped) { - escaped = false; - } else if (character == '\\') { - escaped = true; - } else if (character == '\"') { - inString = false; - } - continue; - } - if (character == '\"') { - inString = true; - } else if (character == '(') { - depth++; - } else if (character == ')' && --depth == 0) { - return index; - } - } - return -1; - } - - private static String buildInvalidNameMessage(List>> invalidNames) { - StringBuilder message = new StringBuilder("Invalid MCP tool name(s): expected 1-128 characters using letters, digits, underscores, or hyphens."); - for (Map.Entry> invalidName : invalidNames) { - message.append("\n\n").append(invalidName.getKey()); - invalidName.getValue().stream() - .sorted(Comparator.comparing(ToolDeclaration::moduleName).thenComparing(declaration -> declaration.source().toString())) - .forEach(declaration -> message.append("\n- ") - .append(declaration.moduleName()) - .append(": ") - .append(declaration.source()) - .append(':').append(declaration.line())); - } - return message.toString(); - } - - private static String buildDuplicateMessage(List>> duplicates) { - StringBuilder message = new StringBuilder("Duplicate MCP tool name(s):"); - for (Map.Entry> duplicate : duplicates) { - message.append("\n\n").append(duplicate.getKey()); - duplicate.getValue().stream() - .sorted(Comparator.comparing(ToolDeclaration::moduleName).thenComparing(declaration -> declaration.source().toString())) - .forEach(declaration -> message.append("\n- ") - .append(declaration.moduleName()) - .append(": ") - .append(declaration.source()) - .append(':').append(declaration.line())); - } - return message.toString(); - } - - private record ToolDeclaration(String moduleName, Path source, int line) { - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/ToolArgumentSchemaValidator.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/ToolArgumentSchemaValidator.java deleted file mode 100644 index cc0352438..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/ToolArgumentSchemaValidator.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.shinhanlife.dat.lib.validation; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.json.schema.JsonSchemaValidator; -import io.modelcontextprotocol.json.schema.JsonSchemaValidator.ValidationResponse; -import java.util.Map; - -/** Validates tool arguments with the MCP SDK JsonSchemaValidator (JSON Schema 2020-12). */ -public class ToolArgumentSchemaValidator { - - private final ObjectMapper objectMapper; - private final JsonSchemaValidator jsonSchemaValidator; - - public ToolArgumentSchemaValidator(ObjectMapper objectMapper, JsonSchemaValidator jsonSchemaValidator) { - this.objectMapper = objectMapper; - this.jsonSchemaValidator = jsonSchemaValidator; - } - - public ValidationResponse validate(Map schemaDefinition, Map arguments) { - return validateValue(schemaDefinition, arguments); - } - - public ValidationResponse validateValue(Map schemaDefinition, Object value) { - Object converted = objectMapper.convertValue(value, Object.class); - return jsonSchemaValidator.validate(schemaDefinition, converted); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/ToolSchemaV17ValidationRunner.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/ToolSchemaV17ValidationRunner.java deleted file mode 100644 index b8c5283ea..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/lib/validation/ToolSchemaV17ValidationRunner.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.shinhanlife.dat.lib.validation; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import io.shinhanlife.dat.lib.metadata.ToolDefinition; -import io.shinhanlife.dat.lib.metadata.ToolDefinitionValidator; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -/** CI/CD에서 모든 Java MCP Tool과 V17 정의 파일의 1:1 대응을 검증합니다. */ -public final class ToolSchemaV17ValidationRunner { - private static final Pattern MCP_TOOL_NAME = Pattern.compile( - "@McpTool\\s*\\(\\s*name\\s*=\\s*\"([^\"]+)\""); - - private ToolSchemaV17ValidationRunner() { - } - - static void validate(Path projectRoot) { - ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); - Map definitions = new LinkedHashMap<>(); - Set toolNames = new LinkedHashSet<>(); - try (Stream files = Files.walk(projectRoot)) { - for (Path file : files.filter(Files::isRegularFile).toList()) { - String normalized = file.toString().replace('\\', '/'); - if (normalized.contains("/build/") || normalized.contains("/.gradle/") - || normalized.contains("/tmp_") || normalized.contains("/org/")) { - continue; - } - if (normalized.endsWith(".java") && normalized.contains("/dat-was-") - && !normalized.contains("/dat-was-lib/")) { - Matcher matcher = MCP_TOOL_NAME.matcher(Files.readString(file, StandardCharsets.UTF_8)); - while (matcher.find()) { - toolNames.add(matcher.group(1)); - } - } - if (normalized.contains("/src/main/resources/tool-definitions/") - && (normalized.endsWith(".yml") || normalized.endsWith(".yaml"))) { - ToolDefinition definition = yamlMapper.readValue(file.toFile(), ToolDefinition.class); - ToolDefinitionValidator.validate(definition, file.toString()); - Path previous = definitions.putIfAbsent(definition.name(), file); - if (previous != null) { - throw new IllegalStateException("Duplicate V17 Tool definition: " + definition.name() - + " [" + previous + ", " + file + "]"); - } - } - } - } catch (IOException exception) { - throw new IllegalStateException("Failed to scan Tool schema V17 files", exception); - } - Set missing = new LinkedHashSet<>(toolNames); - missing.removeAll(definitions.keySet()); - if (!missing.isEmpty()) { - throw new IllegalStateException("Missing V17 Tool definitions: " + missing); - } - Set orphan = new LinkedHashSet<>(definitions.keySet()); - orphan.removeAll(toolNames); - if (!orphan.isEmpty()) { - throw new IllegalStateException("V17 definitions without matching @McpTool: " + orphan); - } - System.out.println("Tool schema V17 validation passed: " + toolNames.size() + " tools"); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/dto/ToolMetadata.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/dto/ToolMetadata.java deleted file mode 100644 index eec485aad..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/dto/ToolMetadata.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.shinhanlife.dat.mcc.dto; - -import lombok.Getter; -import lombok.Setter; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.Map; -import java.util.Set; -import java.util.List; -import java.util.HashSet; -import io.shinhanlife.dat.lib.dto.OperationType; -/** - * Tool(Agent)의 명세 및 라우팅 정보를 담고 있는 메타데이터 클래스 - * Redis 레지스트리에 저장되며, Planner와 Router 간의 통신 객체(Plan)로 사용됩니다. - */ -/** - * @package io.shinhanlife.dat.mcg.dto - * @className ToolMetadata - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Getter -@Setter -public class ToolMetadata { - - // 1. Tool 기본 정보 - private String uid; // UUID 형식의 고유 식별자 - private String semver; // 버전 (예: 1.0.0) - private String displayName; // 사람이 읽는 라벨 (1-128자) - private String name; // MCP 서브툴 명칭 (64자 이하, 예: CustomerSearchTool) - private String description; // 툴의 목적 및 설명 (LLM 프롬프트에 활용 가능) - private String functionDescription; - private String whenToUse; - private String whenNotToUse; - private String ioLimits; - private String displayDescription; - private List exampleQueries; - private List tags; - private String ownerOrg; - private List requiredEnvKeys; - - // 2. 파라미터 스키마 (JSON Schema 형태의 Map) - private Map parametersSchema; - private Map outputSchema; - - // 2-0. 프론트엔드 UI용 함수별 프롬프트 매핑 (추가됨) - private Map actionPrompts; - - // 2-1. 도메인 부서 그룹명 (category_key, 슬러그 형식) - private String categoryKey; - - // 2-1-1. 어플리케이션(모듈) 이름 (예: dat-was-cus) - private String moduleName; - - // 2-2. 툴 처리 엔드포인트 URI 경로 (예: /api/tool/customer-info) - private String endpoint; - - // 2-3. Pod 실행 URL (독립적인 Microservice 라우팅용, 예: http://localhost:8082) - private String podUrl; - - // 2-4. 가시성 여부 - @Builder.Default - private Boolean visible = true; - - // 활성화 여부 - @Builder.Default - private Boolean enabled = true; - - // 2-5. Redis 등록 여부 (UI 표출용) - @Builder.Default - private Boolean isRegistered = true; - - // 2-6. HITL 승인 필요 여부 - @Builder.Default - private Boolean requiresApproval = false; - - @Builder.Default - private Boolean readOnlyHint = false; - - @Builder.Default - private Boolean destructiveHint = false; - - @Builder.Default - private Boolean idempotentHint = false; - - @Builder.Default - private Boolean openWorldHint = false; - - - - // 3. 연동 아키텍처 구분 (DIRECT / MCI_EAI) - private String integrationType; // 연동 타입: "DIRECT" 또는 "MCI_EAI" - - // 4. 레거시(MCI/EAI) 연동 시 필수 정보 (integrationType이 "MCI_EAI"일 때 사용) - private String mciServiceId; // MCI/EAI 호출을 위한 서비스 ID (예: CRM_001, LICO_992) - - // 5. 인프라 상태 정보 (DIRECT 연동 시 사용) - private Long lastHeartbeat; // Redis TTL 갱신용 마지막 하트비트 타임스탬프 - - // 6. 동적 서킷 브레이커 & 속도 제어 설정 (Registry 기반) - private Integer failureRateThreshold; // 서킷 브레이커 동작 기준 실패율 (%) - private Integer slidingWindowSize; // 서킷 브레이커 에러율 계산 표본 요청 수 - private Integer rateLimitForPeriod; // 속도 제어: 1초당 허용 최대 요청 수 - - // 7. Gateway 코어 제어용 설정 필드 추가 (재시도, 타임아웃, 오퍼레이션 타입) - @Builder.Default - private OperationType operationType = OperationType.READ; - - @Builder.Default - private Integer retryMaxAttempts = 3; - - @Builder.Default - private Integer circuitBreakerFailureThreshold = 0; - - @Builder.Default - private Long circuitBreakerOpenMillis = 0L; - - @Builder.Default - private Long timeoutMillis = 0L; - - // --- Guardrail 호환성을 위한 메서드 추가 --- - public Set allowedArguments() { - if (parametersSchema == null || !parametersSchema.containsKey("properties")) return Set.of(); - return ((Map) parametersSchema.get("properties")).keySet(); - } - - public Set requiredArguments() { - if (parametersSchema == null || !parametersSchema.containsKey("required")) return Set.of(); - return new HashSet<>((List) parametersSchema.get("required")); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/presentation/BusinessToolController.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/presentation/BusinessToolController.java deleted file mode 100644 index 26e10607f..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/presentation/BusinessToolController.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.shinhanlife.dat.mcc.presentation; - -import io.shinhanlife.dat.lib.mcp.McpRequestHeaders; -import io.shinhanlife.dat.lib.mcp.ToolExecutionResult; -import io.shinhanlife.dat.lib.mcp.McpToolExecutionService; -import io.shinhanlife.dat.lib.mcp.ToolRegistryHeartbeatSender; -import io.shinhanlife.dat.mcc.dto.ToolMetadata; -import java.util.List; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** Legacy REST adapter for Tool Pod execution. */ -@RestController -@RequestMapping("/") -@RequiredArgsConstructor -public class BusinessToolController { - - private final ToolRegistryHeartbeatSender toolRegistryHeartbeatSender; - private final McpToolExecutionService toolExecutionService; - - @GetMapping("/mcp/api/v1/tools/local") - public List getLocalTools() { - return toolRegistryHeartbeatSender.getAllScannedTools(); - } - - @PostMapping("/mcp/{name}") - public ResponseEntity executeDynamicTool( - @PathVariable("name") String functionName, - @RequestHeader(value = "X-Guid", required = false) String guid, - @RequestHeader(value = "X-Praf-No", required = false) String prafNo, - @RequestHeader(value = "X-Request-Id", required = false) String requestId, - @RequestHeader(value = "X-Request-Time", required = false) String requestTime, - @RequestHeader(value = "X-Vrtl-Praf-No", required = false) String vrtlPrafNo, - @RequestHeader(value = "X-App-Code", required = false) String appCode, - @RequestHeader(value = "X-Project-Code", required = false) String projectCode, - @RequestHeader(value = "X-User-Ip", required = false) String userIp, - @RequestHeader(value = "X-Caller-Ip", required = false) String callerIp, - @RequestHeader(value = "X-Caller-Host", required = false) String callerHost, - @RequestHeader(value = "X-Channel", required = false) String channel, - @RequestHeader(value = "X-Agent-Id", required = false) String agentId, - @RequestHeader(value = "mcp-session-id", required = false) String mcpSessionId, - @RequestBody(required = false) Map arguments) { - ToolExecutionResult result = toolExecutionService.execute( - functionName, - new McpRequestHeaders(guid, prafNo, requestId, requestTime, vrtlPrafNo, appCode, - projectCode, userIp, callerIp, callerHost, channel, agentId, mcpSessionId), - arguments); - ResponseEntity.BodyBuilder response = ResponseEntity.status(result.statusCode()); - result.headers().forEach(response::header); - return response.body(result.body()); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/presentation/ToolManifestController.java b/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/presentation/ToolManifestController.java deleted file mode 100644 index 19ec12dfe..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/dat/mcc/presentation/ToolManifestController.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.shinhanlife.dat.mcc.presentation; - -import io.shinhanlife.dat.lib.manifest.ToolManifestResponse; -import io.shinhanlife.dat.lib.manifest.ToolManifestService; -import io.shinhanlife.dat.lib.manifest.ToolServiceManifestResponse; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.PathVariable; - -/** Read-only Tool Service manifest endpoint for MCP background discovery. */ -@RestController -public class ToolManifestController { - - private final ToolManifestService toolManifestService; - - public ToolManifestController(ToolManifestService toolManifestService) { - this.toolManifestService = toolManifestService; - } - - @GetMapping(value = "/tool-manifest", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getManifest( - @RequestHeader(value = HttpHeaders.IF_NONE_MATCH, required = false) String ifNoneMatch) { - return handleManifest(toolManifestService.currentManifest(), ifNoneMatch); - } - - @GetMapping(value = "/tool-manifest/{categoryKey}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getManifestByCategory( - @PathVariable("categoryKey") String categoryKey, - @RequestHeader(value = HttpHeaders.IF_NONE_MATCH, required = false) String ifNoneMatch) { - return handleManifest(toolManifestService.currentManifest(categoryKey), ifNoneMatch); - } - - @GetMapping(value = "/tool-service-manifest", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getToolServiceManifest() { - return ResponseEntity.ok(toolManifestService.currentToolServiceManifest()); - } - - private ResponseEntity handleManifest(ToolManifestResponse manifest, String ifNoneMatch) { - String eTag = '"' + manifest.revision() + '"'; - if (eTag.equals(ifNoneMatch)) { - return ResponseEntity.status(304).eTag(eTag).build(); - } - return ResponseEntity.ok().eTag(eTag).body(manifest); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/BaseException.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/BaseException.java deleted file mode 100644 index ae63fd968..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/BaseException.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.shinhanlife.glow; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Builder -@NoArgsConstructor -@AllArgsConstructor -//@Schema(description = "응답 에러 객체. 성공 케이스일 경우 null. 실제 에러가 발생할 경우에만 예외명, 예외 메시지 필드 세팅 예정.") -public class BaseException { - -// @Schema(description = "Error 코드 Meta 참조 운영. (예) 20001, 50001 등", shinhanlife = "20001") - String code; - -// @Schema(description = "메시지") - String message; - -// @Schema(description = "예외명.", shinhanlife = "NullPointerException") - @Builder.Default - String exceptionName = ""; - -// @Schema(description = "예외상세", shinhanlife = "StackTrace") - @Builder.Default - String exceptionDetail = ""; - -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/BaseResponse.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/BaseResponse.java deleted file mode 100644 index e0d88e139..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/BaseResponse.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.shinhanlife.glow; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; - -@ToString -@Getter -@Builder -@AllArgsConstructor -public class BaseResponse { - -// @Schema(description = "응답 코드 HTTP STATUS 오류 - 실제 Header 는 200 으로 내려감", shinhanlife = "200") - private int code; - -// @Schema(description = "응답 메시지. 응답 코드와 매핑된 메시지.", shinhanlife = "데이터 생성 성공") - private String message; - -// @Schema(description = "응답 본문 데이터. 실제 비즈니스 데이터.") - private T data; - - private BaseException error; - -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/BizException.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/BizException.java deleted file mode 100644 index 5d3665e94..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/BizException.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.shinhanlife.glow; - -/** - * 업무 예외. - * - *

두 가지 방식으로 쓸 수 있다.

- *
    - *
  1. 메시지코드 방식(권장) — {@code throw new BizException("DAH00004", "사번")}
    - * 통합메시지(ZT_UNFC_MSG)에서 문구를 찾아 {0},{1}.. 을 인자로 치환해 응답한다. - * 문구가 화면·서버 한곳(관리 화면)에서 관리되고, 다국어 확장도 여기서 처리된다.
  2. - *
  3. 문구 직접 방식(기존 호환) — {@code throw new BizException("사번은 필수입니다.")}
    - * 메시지코드로 해석되지 않으면 문구 그대로 응답한다.
  4. - *
- * - *

변환은 {@code common/config/GlobalExceptionHandler} 가 수행한다. - * 메시지코드 여부는 코드 형식(영문 대문자+숫자 8자리)으로 판별한다.

- */ -public class BizException extends RuntimeException { - - /** 통합메시지코드 (문구 직접 방식이면 null) */ - private final String msgCd; - - /** 메시지 치환 인자 */ - private final Object[] msgArgs; - - /** - * 문구를 직접 지정하거나, 메시지코드만 던진다. - * - * @param messageOrCode 메시지 문구 또는 통합메시지코드 - */ - public BizException(String messageOrCode) { - super(messageOrCode); - this.msgCd = isMessageCode(messageOrCode) ? messageOrCode : null; - this.msgArgs = new Object[0]; - } - - /** - * 메시지코드 + 치환 인자. - * - * @param msgCd 통합메시지코드 (예: DAH00004) - * @param msgArgs {0},{1}.. 에 순서대로 치환될 인자 - */ - public BizException(String msgCd, Object... msgArgs) { - super(msgCd); - this.msgCd = msgCd; - this.msgArgs = msgArgs == null ? new Object[0] : msgArgs; - } - - public String getMsgCd() { - return msgCd; - } - - public Object[] getMsgArgs() { - return msgArgs; - } - - /** 통합메시지코드 형식인지 — 영문 대문자 3자리 + 숫자 5자리 (예: DAH00001) */ - private static boolean isMessageCode(String value) { - return value != null && value.matches("^[A-Z]{3}\\d{5}$"); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowAppServiceId.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowAppServiceId.java deleted file mode 100644 index 1772ba09a..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowAppServiceId.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.shinhanlife.glow; - -import java.lang.annotation.*; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface GlowAppServiceId { - - String value(); - - String description() default ""; -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowControllerId.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowControllerId.java deleted file mode 100644 index 078ac61f5..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowControllerId.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.shinhanlife.glow; - -public @interface GlowControllerId { - String value(); -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowIndexPaging.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowIndexPaging.java deleted file mode 100644 index 884d75f8f..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowIndexPaging.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.shinhanlife.glow; - -import java.lang.annotation.*; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface GlowIndexPaging { -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowLogTarget.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowLogTarget.java deleted file mode 100644 index c16937eaf..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowLogTarget.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.glow; - -import java.lang.annotation.*; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface GlowLogTarget { - - Target[] value() default {}; - - enum Target { - FILE, CONSOLE - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowLogger.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowLogger.java deleted file mode 100644 index 3da0e4932..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowLogger.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.shinhanlife.glow; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; - -@Component -@Scope("prototype") -public class GlowLogger { - - private Logger log = LoggerFactory.getLogger(GlowLogger.class); - - public void debug(String message, Object... args) { log.debug(message, args); } - public void info(String message, Object... args) { log.info(message, args); } - public void warn(String message, Object... args) { log.warn(message, args); } - public void error(String message, Object... args) { log.error(message, args); } - public void error(String message, Throwable t) { log.error(message, t); } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowMciFieldInfo.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowMciFieldInfo.java deleted file mode 100644 index bce1efef9..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowMciFieldInfo.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.shinhanlife.glow; - - -/** - * @package io.shinhanlife.glow - * @className GlowMciFieldInfo - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -import java.lang.annotation.*; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface GlowMciFieldInfo { - - /** - * 필드 순서 - */ - int order(); - - /** - * 필드 길이 (List인 경우 전체 길이로 활용될 수 있음) - */ - int length(); - - /** - * 필드 설명 - */ - String description() default ""; - - /** - * List 매핑 시 대상 DTO 클래스 - */ - Class target() default void.class; -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowMybatisMapper.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowMybatisMapper.java deleted file mode 100644 index ca50c4420..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowMybatisMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.shinhanlife.glow; - -import org.apache.ibatis.annotations.Mapper; -import org.springframework.stereotype.Component; - -import java.lang.annotation.*; - -/** - * MyBatis Mapper 인터페이스를 나타내는 어노테이션 - * MyBatis Mapper와 동일한 기능을 제공합니다. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Mapper -@Component -public @interface GlowMybatisMapper { -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowServiceGroupId.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowServiceGroupId.java deleted file mode 100644 index 375a3b6bd..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowServiceGroupId.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.shinhanlife.glow; - -public @interface GlowServiceGroupId { - String value(); - - String description(); -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowTrgmField.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowTrgmField.java deleted file mode 100644 index 883d7dda1..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/GlowTrgmField.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.shinhanlife.glow; - -import java.lang.annotation.*; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface GlowTrgmField { - - /** - * 필드 순서 - */ - int order(); - - /** - * 필드 길이 - */ - int length(); - - /** - * 필드 설명 - */ - String description() default ""; - -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/PageInfo.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/PageInfo.java deleted file mode 100644 index a83c5a223..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/PageInfo.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.shinhanlife.glow; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import org.apache.ibatis.session.RowBounds; - -import java.io.Serial; -import java.io.Serializable; - -@Getter -@Setter -@JsonIgnoreProperties(ignoreUnknown = true) -@EqualsAndHashCode(callSuper = true) -public class PageInfo extends RowBounds implements Serializable { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * 페이지번호 (입력값) - */ - @GlowTrgmField(order = 1, length = 5, description = "페이지번호") - private int pageNo; - - /** - * 페이지 데이터 건수 (열 건수, 입력값) - */ - @GlowTrgmField(order = 2, length = 5, description = "페이지데이터건수") - private int pageDataCc; - - /** - * 총페이지 수 (리턴값) - */ - @GlowTrgmField(order = 3, length = 10, description = "총페이지수") - private int totaPageCn; - - /** - * 총 페이지 데이터 건수 (리턴값) - */ - @GlowTrgmField(order = 4, length = 10, description = "총페이지데이터건수") - private int totaPageDataCc; - - public PageInfo(int pageNo, int pageDataCc) { - super(((pageNo <= 0 ? 1 : pageNo) - 1) * pageDataCc, pageDataCc); - this.pageNo = pageNo; - this.pageDataCc = pageDataCc; - } - - public PageInfo() { - - } - - @JsonIgnore - @Override - public int getOffset() { - return super.getOffset(); - } - - @JsonIgnore - @Override - public int getLimit() { - return super.getLimit(); - } - -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/ResponseCode.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/ResponseCode.java deleted file mode 100644 index 63ffb6939..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/ResponseCode.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.shinhanlife.glow; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; - -@Getter -@RequiredArgsConstructor -public enum ResponseCode { - - /* ===================== 성공 ===================== */ - SUCCESS(HttpStatus.OK, "정상 처리되었습니다."), - CREATED(HttpStatus.CREATED, "데이터 생성 성공"), - - /* ===================== 클라이언트 오류 (4xx) ===================== */ - BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), - INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "유효하지 않은 파라미터입니다."), - UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "인증이 필요합니다."), - FORBIDDEN(HttpStatus.FORBIDDEN, "접근 권한이 없습니다."), - NOT_FOUND(HttpStatus.NOT_FOUND, "요청한 리소스를 찾을 수 없습니다."), - METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "허용되지 않은 HTTP 메서드입니다."), - CONFLICT(HttpStatus.CONFLICT, "이미 존재하는 데이터입니다."), - - /* ===================== 서버 오류 (5xx) ===================== */ - INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류가 발생했습니다."), - SERVICE_UNAVAILABLE(HttpStatus.SERVICE_UNAVAILABLE, "서비스를 사용할 수 없습니다."); - - private final HttpStatus status; - private final String message; - -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/ResponseUtil.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/ResponseUtil.java deleted file mode 100644 index 9ebe6b97b..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/ResponseUtil.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.shinhanlife.glow; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -public final class ResponseUtil { - - private ResponseUtil() { - - } - - public static ResponseEntity> ok() { - return ResponseEntity.ok( - BaseResponse.builder() - .code(ResponseCode.SUCCESS.getStatus().value()) - .message(ResponseCode.SUCCESS.getMessage()) - .build() - ); - } - - public static ResponseEntity> ok(T data) { - return ResponseEntity.ok( - BaseResponse.builder() - .code(ResponseCode.SUCCESS.getStatus().value()) - .message(ResponseCode.SUCCESS.getMessage()) - .data(data) - .build() - ); - } - - public static ResponseEntity> ok(T data, ResponseCode responseCode) { - return ResponseEntity.ok( - BaseResponse.builder() - .code(responseCode.getStatus().value()) - .message(responseCode.getMessage()) - .data(data) - .build() - ); - } - - public static ResponseEntity> error( - HttpStatus status, String code, String message - ) { - return ResponseEntity.status(status) - .body( - BaseResponse.builder() - .code(status.value()) - .error( - BaseException.builder() - .code(code) - .message(message) - .build() - ) - .build() - ); - } - - public static ResponseEntity> error( - HttpStatus status, String code, String message, String exceptionName, String stackTrace - ) { - return ResponseEntity.status(status) - .body( - BaseResponse.builder() - .code(status.value()) - .error( - BaseException.builder() - .code(code) - .message(message) - .exceptionName(exceptionName) - .exceptionDetail(stackTrace) - .build() - ) - .build() - ); - } - - public static ResponseEntity> okError( - HttpStatus status, String code, String message - ) { - return ResponseEntity.ok( - BaseResponse.builder() - .code(status.value()) - .error( - BaseException.builder() - .code(code) - .message(message) - .build() - ) - .build() - ); - } - - public static ResponseEntity> okError( - HttpStatus status, String code, String message, String exceptionName, String stackTrace - ) { - return ResponseEntity.ok( - BaseResponse.builder() - .code(status.value()) - .error( - BaseException.builder() - .code(code) - .message(message) - .exceptionName(exceptionName) - .exceptionDetail(stackTrace) - .build() - ) - .build() - ); - } - -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/ICommunication.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/ICommunication.java deleted file mode 100644 index f93b1ea2d..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/ICommunication.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.shinhanlife.glow.communication; - -/** Minimal Glow communication contract used by the temporary compatibility layer. */ -public interface ICommunication { - O sync(I request); -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/annotation/GlowTrgmField.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/annotation/GlowTrgmField.java deleted file mode 100644 index 9463acf2b..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/annotation/GlowTrgmField.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.shinhanlife.glow.communication.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** Declares fixed-width Glow transaction-message metadata for a DTO field. */ -@Target({ElementType.LOCAL_VARIABLE, ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface GlowTrgmField { - - int order(); - - int length() default 0; - - int decimal() default 0; - - String description() default ""; - - String target() default ""; - - String type() default ""; -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/CommonHeader.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/CommonHeader.java deleted file mode 100644 index 7c991d52f..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/CommonHeader.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.shinhanlife.glow.communication.dto; -import lombok.Data; -@Data -public class CommonHeader { - private String itrfId; - private String rcvSvcId; - private String tgrmDalRsltCd; -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/HeaderDefaults.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/HeaderDefaults.java deleted file mode 100644 index c123b30f5..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/HeaderDefaults.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.shinhanlife.glow.communication.dto; - -public enum HeaderDefaults { - ITRF_ID, RCV_SVC_ID, STR_YMD, ACNT_OGNZ_NO, PSMR_ASRT_CD, SBSN_RULP_ASRT_CD, BSDU_CD, BSQU_CD, - INDV_CTIN_ROLE_CD, SCRN_ID, OGNZ_ASRT_CD, OGNZ_LEVE_CD, TGRM_CREA_CHNN_TYPE_CD, ENVR_TYPE_CD, DRTM_CD, USER_ID -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/Transfer.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/Transfer.java deleted file mode 100644 index 31383d775..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/dto/Transfer.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.glow.communication.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * TODO: 실제 Glow Framework 의존성(JAR)이 추가되면 이 Mock 클래스를 삭제하세요. - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Transfer { - private Object header; - private T body; - private Class resBodyClass; - private Object message; -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/exception/ItrfException.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/exception/ItrfException.java deleted file mode 100644 index 464cc52e3..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/exception/ItrfException.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.shinhanlife.glow.communication.exception; - -public class ItrfException extends Exception { - public ItrfException(String msg) { - super(msg); - } - public ItrfException(String msgCd, String msgPrnAttrCd, String msgCt, String anxMsgCt) { - super(msgCd + ": " + msgCt); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/eai/component/GlowEaiComponent.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/eai/component/GlowEaiComponent.java deleted file mode 100644 index 99bc00be7..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/eai/component/GlowEaiComponent.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.shinhanlife.glow.communication.module.eai.component; - -import io.shinhanlife.glow.communication.dto.Transfer; - -import org.springframework.stereotype.Component; - -/** - * TODO: 실제 Glow Framework 의존성(JAR)이 추가되면 이 Mock 클래스를 삭제하세요. - */ -@Component -public class GlowEaiComponent { - public Transfer sync(Transfer request) { - // Mock 구현 - Transfer response = new Transfer<>(); - response.setHeader(request.getHeader()); - response.setResBodyClass((Class) request.getResBodyClass()); - return response; - } - - public Transfer call(Transfer request, Class resBody) { - Transfer response = new Transfer<>(); - response.setHeader(request.getHeader()); - response.setResBodyClass(resBody); - return response; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/component/GlowHttpComponent.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/component/GlowHttpComponent.java deleted file mode 100644 index 34265031e..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/component/GlowHttpComponent.java +++ /dev/null @@ -1,98 +0,0 @@ -package io.shinhanlife.glow.communication.module.http.component; - -import io.shinhanlife.glow.communication.ICommunication; -import io.shinhanlife.glow.communication.module.http.dto.HttpBody; -import io.shinhanlife.glow.communication.module.http.dto.HttpHeader; -import io.shinhanlife.glow.communication.module.http.dto.HttpTransfer; -import java.util.List; -import java.util.Map; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.client.RestClient; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * Temporary compatibility implementation of the internal Glow HTTP component. - * Replace this class with the official Glow HTTP JAR when it is supplied. - */ -@Component -public class GlowHttpComponent implements ICommunication, ResponseEntity> { - - private final RestClient restClient; - - public GlowHttpComponent(RestClient.Builder restClientBuilder) { - this.restClient = restClientBuilder.build(); - } - - @Override - public ResponseEntity sync(HttpTransfer request) { - if (request == null || request.getHeader() == null || request.getMethod() == null - || !StringUtils.hasText(request.getDomain())) { - throw new IllegalArgumentException("Glow HTTP request header, domain, and method are required."); - } - if (HttpMethod.GET.equals(request.getMethod())) { - return get(request); - } - if (HttpMethod.POST.equals(request.getMethod())) { - return post(request); - } - if (HttpMethod.PUT.equals(request.getMethod())) { - return put(request); - } - if (HttpMethod.DELETE.equals(request.getMethod())) { - return delete(request); - } - throw new IllegalArgumentException("Unsupported HTTP method: " + request.getMethod()); - } - - private ResponseEntity get(HttpTransfer request) { - return toHttpBody(restClient.get().uri(buildUri(request, true)) - .headers(headers -> applyHeaders(headers, request.getHeader())) - .retrieve().toEntity(String.class)); - } - - private ResponseEntity post(HttpTransfer request) { - return toHttpBody(restClient.post().uri(buildUri(request, false)) - .contentType(contentType(request)).headers(headers -> applyHeaders(headers, request.getHeader())) - .body(request.getBody()).retrieve().toEntity(String.class)); - } - - private ResponseEntity put(HttpTransfer request) { - return toHttpBody(restClient.put().uri(buildUri(request, false)) - .contentType(contentType(request)).headers(headers -> applyHeaders(headers, request.getHeader())) - .body(request.getBody()).retrieve().toEntity(String.class)); - } - - private ResponseEntity delete(HttpTransfer request) { - return toHttpBody(restClient.delete().uri(buildUri(request, true)) - .headers(headers -> applyHeaders(headers, request.getHeader())) - .retrieve().toEntity(String.class)); - } - - private String buildUri(HttpTransfer request, boolean includeQueryParameters) { - String uri = request.getDomain() + (request.getUri() == null ? "" : request.getUri()); - if (!includeQueryParameters || !(request.getBody() instanceof Map parameters)) { - return uri; - } - UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uri); - parameters.forEach((key, value) -> { if (key != null && value != null) builder.queryParam(String.valueOf(key), value); }); - return builder.build().encode().toUriString(); - } - - private MediaType contentType(HttpTransfer request) { - return request.getContentType() == null ? MediaType.APPLICATION_JSON : request.getContentType(); - } - - private void applyHeaders(HttpHeaders target, HttpHeader source) { - target.setAccept(List.of(MediaType.APPLICATION_JSON)); - source.getValues().forEach((name, value) -> { if (StringUtils.hasText(name) && StringUtils.hasText(value)) target.set(name, value); }); - } - - private ResponseEntity toHttpBody(ResponseEntity response) { - return new ResponseEntity<>(new HttpBody(response.getBody()), response.getHeaders(), response.getStatusCode()); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpBody.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpBody.java deleted file mode 100644 index 2406e7b52..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpBody.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.shinhanlife.glow.communication.module.http.dto; - -/** Raw body returned by the Glow HTTP transport. */ -public record HttpBody(String content) { -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpHeader.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpHeader.java deleted file mode 100644 index 396f4a7d1..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpHeader.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.shinhanlife.glow.communication.module.http.dto; - -import java.util.LinkedHashMap; -import java.util.Map; -import lombok.Getter; -import lombok.Setter; - -/** Glow HTTP request headers and timeout metadata. */ -@Getter -@Setter -public class HttpHeader { - private Map values = new LinkedHashMap<>(); - private int readTimeout; - - public void set(String name, String value) { - values.put(name, value); - } -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpTransfer.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpTransfer.java deleted file mode 100644 index 30fb28e9f..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/http/dto/HttpTransfer.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.shinhanlife.glow.communication.module.http.dto; - -import lombok.Builder; -import lombok.Getter; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; - -/** Glow HTTP request envelope. Use HttpTransfer.http() to build a request. */ -@Getter -@Builder(builderMethodName = "http") -public class HttpTransfer { - private final HttpHeader header; - private final String domain; - private final String uri; - private final HttpMethod method; - private final MediaType contentType; - private final Class responseEntity; - private final T body; -} \ No newline at end of file diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/mci/component/GlowMciComponent.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/mci/component/GlowMciComponent.java deleted file mode 100644 index 3cdf3179d..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/module/mci/component/GlowMciComponent.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.shinhanlife.glow.communication.module.mci.component; - -import io.shinhanlife.glow.communication.dto.Transfer; -import org.springframework.stereotype.Component; - -/** - * TODO: 실제 Glow Framework 의존성(JAR)이 추가되면 이 Mock 클래스를 삭제하세요. - */ -@Component -public class GlowMciComponent { - - public Transfer sync(Transfer request) { - // 실제 Glow HTTP 통신 (GlowHttpHeaderUtil, HttpClient 등) 수행 시뮬레이션 - Object responseBody = new Object(); - if (request != null && request.getResBodyClass() != null) { - try { - responseBody = request.getResBodyClass().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - // Ignore and fallback to Object - } - } - - return (Transfer) Transfer.builder() - .header(request != null ? request.getHeader() : null) - .body(responseBody) - .build(); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/util/CommonHeaderFactory.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/util/CommonHeaderFactory.java deleted file mode 100644 index fbb154cd6..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/communication/util/CommonHeaderFactory.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.shinhanlife.glow.communication.util; -import io.shinhanlife.glow.communication.dto.CommonHeader; -import io.shinhanlife.glow.communication.dto.HeaderDefaults; -import java.util.Map; -public class CommonHeaderFactory { - public static CommonHeader createRequestHeader(Map commonHeaderMap) { - CommonHeader header = new CommonHeader(); - header.setItrfId(commonHeaderMap.get(HeaderDefaults.ITRF_ID)); - header.setRcvSvcId(commonHeaderMap.get(HeaderDefaults.RCV_SVC_ID)); - return header; - } - public static CommonHeader createRequestHeader(String itrfId, String rcvSvcId) { - CommonHeader header = new CommonHeader(); - header.setItrfId(itrfId); - header.setRcvSvcId(rcvSvcId); - return header; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/db/dto/AuditInfo.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/db/dto/AuditInfo.java deleted file mode 100644 index 605362c19..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/db/dto/AuditInfo.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.shinhanlife.glow.db.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.Date; - -@Getter -@Setter -public class AuditInfo { - private Date systRgiDt; // 시스템등록일시 - private String systRgiPrafNo; // 시스템등록인사번호 - private String systRgiOgnzNo; // 시스템등록조직번호 - private String systRgiSystCd; // 시스템등록시스템코드 - private String systRgiPrgrId; // 시스템등록프로그램ID - private Date systChgDt; // 시스템변경일시 - private String systChgPrafNo; // 시스템변경인사번호 - private String systChgOgnzNo; // 시스템변경조직번호 - private String systChgSystCd; // 시스템변경시스템코드 - private String systChgPrgrId; // 시스템변경프로그램ID -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/db/typehandler/CodeEnum.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/db/typehandler/CodeEnum.java deleted file mode 100644 index 3e7b94d64..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/db/typehandler/CodeEnum.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.shinhanlife.glow.db.typehandler; - -/** - * glow 프레임워크 스텁 (실제 라이브러리가 없는 로컬 개발 환경용 더미). - * 코드값을 갖는 enum이 공통으로 구현하는 인터페이스 — {@link CodeEnumTypeHandler}가 이 getCode()로 - * DB 컬럼(String)과 enum 상수를 상호 변환한다. - */ -public interface CodeEnum { - - String getCode(); -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/db/typehandler/CodeEnumTypeHandler.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/db/typehandler/CodeEnumTypeHandler.java deleted file mode 100644 index b0dc8a8ea..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/db/typehandler/CodeEnumTypeHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.shinhanlife.glow.db.typehandler; - -import org.apache.ibatis.type.BaseTypeHandler; -import org.apache.ibatis.type.JdbcType; - -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -/** - * glow 프레임워크 스텁 (실제 라이브러리가 없는 로컬 개발 환경용 더미). - * {@link CodeEnum}을 구현하는 코드 enum과 DB 문자열 컬럼(코드값)을 상호 변환하는 MyBatis TypeHandler - * 공통 베이스. common/enums/type의 {@code {ClassName}TypeHandler}는 모두 이 클래스를 상속하고, - * 생성자에서 자신의 enum 타입을 super(...)로 넘기기만 한다. - */ -public abstract class CodeEnumTypeHandler & CodeEnum> extends BaseTypeHandler { - - private final Class type; - - protected CodeEnumTypeHandler(Class type) { - if (type == null) { - throw new IllegalArgumentException("Type argument cannot be null"); - } - this.type = type; - } - - @Override - public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { - ps.setString(i, parameter.getCode()); - } - - @Override - public E getNullableResult(ResultSet rs, String columnName) throws SQLException { - return toEnum(rs.getString(columnName)); - } - - @Override - public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { - return toEnum(rs.getString(columnIndex)); - } - - @Override - public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { - return toEnum(cs.getString(columnIndex)); - } - - private E toEnum(String code) { - if (code == null) { - return null; - } - for (E constant : type.getEnumConstants()) { - if (constant.getCode().equals(code)) { - return constant; - } - } - throw new IllegalArgumentException("알 수 없는 코드 [" + code + "] - " + type.getName()); - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/util/GlowMciParser.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/util/GlowMciParser.java deleted file mode 100644 index 5216ae1e8..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/util/GlowMciParser.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.shinhanlife.glow.util; - -import io.shinhanlife.glow.GlowMciFieldInfo; -import lombok.extern.slf4j.Slf4j; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -/** - * @package io.shinhanlife.glow.util - * @className GlowMciParser - * @description MCI 고정 길이 전문 파싱 유틸리티 (GlowMciFieldInfo 기반) - * @author 0986406 - * @create 2026.09.01 - */ -@Slf4j -public class GlowMciParser { - - public static T parse(String mciString, Class clazz) { - if (mciString == null || mciString.isEmpty()) { - return null; - } - - try { - T instance = clazz.getDeclaredConstructor().newInstance(); - - List fields = new ArrayList<>(); - for (Field field : clazz.getDeclaredFields()) { - if (field.isAnnotationPresent(GlowMciFieldInfo.class)) { - fields.add(field); - } - } - - fields.sort(Comparator.comparingInt(f -> f.getAnnotation(GlowMciFieldInfo.class).order())); - - int currentIndex = 0; - for (Field field : fields) { - GlowMciFieldInfo annotation = field.getAnnotation(GlowMciFieldInfo.class); - int length = annotation.length(); - - if (currentIndex >= mciString.length()) { - break; - } - - int endIndex = Math.min(currentIndex + length, mciString.length()); - String value = mciString.substring(currentIndex, endIndex); - - field.setAccessible(true); - setFieldValue(instance, field, value, annotation); - - currentIndex += length; - } - - return instance; - } catch (Exception e) { - log.error("GlowMciFieldInfo 파싱 중 오류 발생: {}", e.getMessage(), e); - throw new RuntimeException("MCI 전문 파싱 오류", e); - } - } - - private static void setFieldValue(Object instance, Field field, String value, GlowMciFieldInfo annotation) throws IllegalAccessException { - Class fieldType = field.getType(); - String trimmedValue = value.trim(); - - if (fieldType == String.class) { - field.set(instance, trimmedValue); - } else if (fieldType == int.class || fieldType == Integer.class) { - field.set(instance, trimmedValue.isEmpty() ? 0 : Integer.parseInt(trimmedValue)); - } else if (fieldType == long.class || fieldType == Long.class) { - field.set(instance, trimmedValue.isEmpty() ? 0L : Long.parseLong(trimmedValue)); - } else if (fieldType == boolean.class || fieldType == Boolean.class) { - field.set(instance, Boolean.parseBoolean(trimmedValue)); - } else if (fieldType == double.class || fieldType == Double.class) { - field.set(instance, trimmedValue.isEmpty() ? 0.0 : Double.parseDouble(trimmedValue)); - } else if (List.class.isAssignableFrom(fieldType)) { - Class targetClass = annotation.target(); - if (targetClass != void.class) { - List list = new ArrayList<>(); - int itemLength = calculateTotalLength(targetClass); - if (itemLength > 0) { - for (int i = 0; i < value.length(); i += itemLength) { - int end = Math.min(i + itemLength, value.length()); - String itemStr = value.substring(i, end); - if (itemStr.trim().isEmpty()) continue; - list.add(parse(itemStr, targetClass)); - } - } - field.set(instance, list); - } - } else { - field.set(instance, trimmedValue); - } - } - - private static int calculateTotalLength(Class clazz) { - int total = 0; - for (Field field : clazz.getDeclaredFields()) { - if (field.isAnnotationPresent(GlowMciFieldInfo.class)) { - total += field.getAnnotation(GlowMciFieldInfo.class).length(); - } - } - return total; - } -} diff --git a/dat-was-lib/src/main/java/io/shinhanlife/glow/util/GlowTrgmParser.java b/dat-was-lib/src/main/java/io/shinhanlife/glow/util/GlowTrgmParser.java deleted file mode 100644 index 1b89ede9a..000000000 --- a/dat-was-lib/src/main/java/io/shinhanlife/glow/util/GlowTrgmParser.java +++ /dev/null @@ -1,92 +0,0 @@ -package io.shinhanlife.glow.util; - -import io.shinhanlife.glow.communication.annotation.GlowTrgmField; -import lombok.extern.slf4j.Slf4j; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -/** - * @package io.shinhanlife.glow.util - * @className GlowTrgmParser - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -public class GlowTrgmParser { - - /** - * 고정 길이 문자열 전문을 @GlowTrgmField 어노테이션 정보에 따라 DTO 객체로 파싱합니다. - * @param trgmString 원본 전문 문자열 - * @param clazz 매핑할 DTO 클래스 - * @return 파싱된 DTO 인스턴스 - */ - public static T parse(String trgmString, Class clazz) { - if (trgmString == null || trgmString.isEmpty()) { - return null; - } - - try { - T instance = clazz.getDeclaredConstructor().newInstance(); - - List fields = new ArrayList<>(); - for (Field field : clazz.getDeclaredFields()) { - if (field.isAnnotationPresent(GlowTrgmField.class)) { - fields.add(field); - } - } - - fields.sort(Comparator.comparingInt(f -> f.getAnnotation(GlowTrgmField.class).order())); - - int currentIndex = 0; - for (Field field : fields) { - GlowTrgmField annotation = field.getAnnotation(GlowTrgmField.class); - int length = annotation.length(); - - if (currentIndex >= trgmString.length()) { - break; - } - - int endIndex = Math.min(currentIndex + length, trgmString.length()); - String value = trgmString.substring(currentIndex, endIndex).trim(); - - field.setAccessible(true); - setFieldValue(instance, field, value); - - currentIndex += length; - } - - return instance; - } catch (Exception e) { - log.error("GlowTrgmField 파싱 중 오류 발생: {}", e.getMessage(), e); - throw new RuntimeException("전문 파싱 오류", e); - } - } - - private static void setFieldValue(Object instance, Field field, String value) throws IllegalAccessException { - Class fieldType = field.getType(); - - if (fieldType == String.class) { - field.set(instance, value); - } else if (fieldType == int.class || fieldType == Integer.class) { - field.set(instance, value.isEmpty() ? 0 : Integer.parseInt(value)); - } else if (fieldType == long.class || fieldType == Long.class) { - field.set(instance, value.isEmpty() ? 0L : Long.parseLong(value)); - } else if (fieldType == boolean.class || fieldType == Boolean.class) { - field.set(instance, Boolean.parseBoolean(value)); - } else if (fieldType == double.class || fieldType == Double.class) { - field.set(instance, value.isEmpty() ? 0.0 : Double.parseDouble(value)); - } else { - field.set(instance, value); - } - } -} diff --git a/dat-was-lib/src/main/resources/glow/application-glow-dev.yml b/dat-was-lib/src/main/resources/glow/application-glow-dev.yml deleted file mode 100644 index e8b864cf6..000000000 --- a/dat-was-lib/src/main/resources/glow/application-glow-dev.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Glow 개발 환경 설정 -spring: - config: - activate: - on-profile: dev - -glow: - communication: - common: - env-type: D - mci: - host: ${GLOW_COMMUNICATION_MCI_HOST:https://dev-ichmci.shinhanlife.co.kr} - port: ${GLOW_COMMUNICATION_MCI_PORT:26160} - extmci: - host: ${GLOW_COMMUNICATION_EXTMCI_HOST:http://host.docker.internal} - port: ${GLOW_COMMUNICATION_EXTMCI_PORT:8080} - eai: - host: ${GLOW_COMMUNICATION_EAI_HOST:tcp://host.docker.internal} - port: ${GLOW_COMMUNICATION_EAI_PORT:9999} \ No newline at end of file diff --git a/dat-was-lib/src/main/resources/glow/application-glow-local.yml b/dat-was-lib/src/main/resources/glow/application-glow-local.yml deleted file mode 100644 index 0a9b014e9..000000000 --- a/dat-was-lib/src/main/resources/glow/application-glow-local.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Glow 로컬 환경 설정 -spring: - config: - activate: - on-profile: local - -glow: - communication: - common: - env-type: D - # Direct local process uses WireMock host port mapped by docker-compose. - http: - connection-timeout: 5 - read-timeout: 5 - api-list: - - name: memo - domain: ${AXHUB_MEMO_HTTP_DOMAIN:http://localhost:${server.port}} - url: ${AXHUB_MEMO_HTTP_URL:/api/mock/http/cmm_memo_retriever} - 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} - extmci: - host: ${GLOW_COMMUNICATION_EXTMCI_HOST:http://localhost} - port: ${GLOW_COMMUNICATION_EXTMCI_PORT:8080} - eai: - host: ${GLOW_COMMUNICATION_EAI_HOST:http://localhost} - port: ${GLOW_COMMUNICATION_EAI_PORT:8080} - -axhub: - mock: - http: - enabled: true diff --git a/dat-was-lib/src/main/resources/glow/application-glow-prod.yml b/dat-was-lib/src/main/resources/glow/application-glow-prod.yml deleted file mode 100644 index cf51ecb25..000000000 --- a/dat-was-lib/src/main/resources/glow/application-glow-prod.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Glow 운영 환경 설정 -spring: - config: - activate: - on-profile: prod - -glow: - communication: - common: - env-type: P - mci: - host: ${GLOW_COMMUNICATION_MCI_HOST} - port: ${GLOW_COMMUNICATION_MCI_PORT} - extmci: - host: ${GLOW_COMMUNICATION_EXTMCI_HOST} - port: ${GLOW_COMMUNICATION_EXTMCI_PORT} - eai: - host: ${GLOW_COMMUNICATION_EAI_HOST} - port: ${GLOW_COMMUNICATION_EAI_PORT} \ No newline at end of file diff --git a/dat-was-lib/src/main/resources/glow/application-glow-test.yml b/dat-was-lib/src/main/resources/glow/application-glow-test.yml deleted file mode 100644 index 600840906..000000000 --- a/dat-was-lib/src/main/resources/glow/application-glow-test.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Glow 테스트 환경 설정 -spring: - config: - activate: - on-profile: test - -glow: - communication: - common: - env-type: T - mci: - host: ${GLOW_COMMUNICATION_MCI_HOST} - port: ${GLOW_COMMUNICATION_MCI_PORT} - extmci: - host: ${GLOW_COMMUNICATION_EXTMCI_HOST} - port: ${GLOW_COMMUNICATION_EXTMCI_PORT} - eai: - host: ${GLOW_COMMUNICATION_EAI_HOST} - port: ${GLOW_COMMUNICATION_EAI_PORT} \ No newline at end of file diff --git a/dat-was-lib/src/main/resources/glow/application-glow.yml b/dat-was-lib/src/main/resources/glow/application-glow.yml deleted file mode 100644 index 9beff3ed5..000000000 --- a/dat-was-lib/src/main/resources/glow/application-glow.yml +++ /dev/null @@ -1,41 +0,0 @@ -# AX HUB Tool Pod 공통 Glow Framework 설정입니다. -# Tool Pod의 application-{profile}.yml에서 이 파일과 환경별 Glow 설정을 함께 import합니다. -logging: - level: - root: INFO - io.shinhanlife.axhub: DEBUG - io.shinhanlife.glow: INFO - org.springframework.web: INFO - pattern: - console: "[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%thread] %logger{36} - %msg%n" - -spring: - data: - redis: - host: ${SPRING_DATA_REDIS_HOST:127.0.0.1} - port: ${SPRING_DATA_REDIS_PORT:6379} - password: ${SPRING_DATA_REDIS_PASSWORD:} - -glow: - communication: - http: - connection-timeout: 5 - read-timeout: 5 - # HTTP Tool target catalog. Scaffold adds local mock entries after a Tool is created. - api-list: [] - mci: - uri: /ntl_mci/dap_rcv - receive-uri: /itrf/mciReceive - connection-timeout: 300 - read-timeout: 300 - encoding: UTF-8 - extmci: - uri: /extmci - json-uri: /extmciJson - receive-uri: /app/extMciReceive - connection-timeout: 5 - read-timeout: 30 - encoding: EUC-KR - websocket: - endpoint: /ws-glow - allowed-origins: "*" diff --git a/dat-was-lib/src/main/resources/mock-responses.json b/dat-was-lib/src/main/resources/mock-responses.json deleted file mode 100644 index 7d6abfed8..000000000 --- a/dat-was-lib/src/main/resources/mock-responses.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "BILL_001": { - "status": "SUCCESS", - "message": "청구 심사 상태 조회가 완료되었습니다.", - "data": { - "billingStatus": "PROCESSING", - "expectedCompletionDate": "2026-07-05" - } - }, - "BILL_002": { - "status": "SUCCESS", - "message": "청구 처리가 완료되었습니다.", - "data": { - "processId": "PRC-998811", - "result": "APPROVED" - } - }, - "BOND_001": { - "status": "SUCCESS", - "message": "디지털 증권 발행 가능 한도 조회가 완료되었습니다.", - "data": { - "availableLimit": 500000000, - "currency": "KRW" - } - }, - "BOND_002": { - "status": "SUCCESS", - "message": "디지털 증권 발행이 성공적으로 완료되었습니다.", - "data": { - "bondId": "BND-2026-07-04-1234", - "issuedAmount": 10000000 - } - }, - "HR_VAC_01": { - "status": "SUCCESS", - "message": "연차 휴가 등록이 완료되었습니다.", - "data": { - "vacationId": "VAC-8877", - "status": "APPROVED" - } - }, - "HR_VAC_02": { - "status": "SUCCESS", - "message": "잔여 연차 일수 조회가 완료되었습니다.", - "data": { - "totalDays": 15, - "usedDays": 3, - "remainingDays": 12 - } - }, - "COM_SMS_01": { - "status": "SUCCESS", - "message": "SMS 발송이 성공적으로 완료되었습니다.", - "data": { - "messageId": "SMS-11223344", - "sentTime": "2026-07-04 10:00:00" - } - }, - "COM_EML_01": { - "status": "SUCCESS", - "message": "이메일 발송이 성공적으로 완료되었습니다.", - "data": { - "emailId": "EML-998877", - "sentTime": "2026-07-04 10:00:00" - } - }, - "CNTR_001": { - "status": "SUCCESS", - "message": "계약 상태 조회가 완료되었습니다.", - "data": { - "contractStatus": "ACTIVE", - "startDate": "2024-01-01" - } - }, - "CNTR_002": { - "status": "SUCCESS", - "message": "계약 상세 내역 조회가 완료되었습니다.", - "data": { - "productName": "신한 라이프 스마트 연금보험", - "monthlyPremium": 500000 - } - }, - "CRM_001": { - "status": "SUCCESS", - "message": "고객 등급 조회가 완료되었습니다.", - "data": { - "customerGrade": "VIP", - "loyaltyPoints": 15000 - } - }, - "CRM_002": { - "status": "SUCCESS", - "message": "고객 상세 정보 조회가 완료되었습니다.", - "data": { - "address": "서울특별시 중구 세종대로 9", - "phoneNumber": "010-XXXX-XXXX" - } - }, - "PAY_001": { - "status": "SUCCESS", - "message": "결제가 성공적으로 승인되었습니다.", - "data": { - "processStatus": "COMPLETED" - } - } -} diff --git a/dat-was-lib/src/main/resources/static/tool-test-console.html b/dat-was-lib/src/main/resources/static/tool-test-console.html deleted file mode 100644 index 31aa9f5ba..000000000 --- a/dat-was-lib/src/main/resources/static/tool-test-console.html +++ /dev/null @@ -1,374 +0,0 @@ - - - - - - AX HUB Tool Test Console - - - - - -
-
- -
- - Manifest loading - v0.0.1 -
-
-
-
-

Schema 기반 Tool 테스트

Tool을 선택하고 요청 JSON을 확인한 뒤 실행하세요. 검증한 요청은 브라우저에 저장되며, 저장된 케이스 전체를 한 번에 다시 실행할 수 있습니다.

-
- -
-

2. 요청 JSON

필수값과 형식은 Tool의 inputSchema 기준입니다. MCI·외부 연동 Tool은 업무에 맞는 테스트 데이터를 입력한 후 저장하세요.
-

3. 실행 결과

대기-X-Guid: -X-Request-Id: -
Tool을 선택하고 실행하세요.
-
-
- -
- - - - diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/adapter/test/MockEimsHttpServerTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/adapter/test/MockEimsHttpServerTest.java deleted file mode 100644 index 6deab3f94..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/adapter/test/MockEimsHttpServerTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.shinhanlife.dat.lib.adapter.test; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.io.InputStream; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -class MockEimsHttpServerTest { - - @Test - void returnsTheScaffoldGeneratedJsonResponse() { - MockEimsHttpServer server = new MockEimsHttpServer(new ObjectMapper()); - - var response = server.mockToolHttpResponse("cmm_memo_retriever", null); - - assertThat(response.getStatusCode().is2xxSuccessful()).isTrue(); - assertThat(response.getBody().path("resultCode").asText()).isEqualTo("SUCCESS"); - } - - @Test - void returnsInternalServerErrorForMalformedMockJson() { - MockEimsHttpServer server = new MockEimsHttpServer(new ObjectMapper()); - - var response = server.mockToolHttpResponse("invalid_mock_response", null); - - assertThat(response.getStatusCode().value()).isEqualTo(500); - } - - @Test - void doesNotSwallowUnexpectedRuntimeExceptions() { - ObjectMapper failingMapper = new ObjectMapper() { - @Override - public JsonNode readTree(InputStream input) throws IOException { - throw new IllegalStateException("unexpected mapper failure"); - } - }; - MockEimsHttpServer server = new MockEimsHttpServer(failingMapper); - - assertThatThrownBy(() -> server.mockToolHttpResponse("cmm_memo_retriever", null)) - .isInstanceOf(IllegalStateException.class) - .hasMessage("unexpected mapper failure"); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/annotation/GrowToolHintTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/annotation/GrowToolHintTest.java deleted file mode 100644 index c49cf30e1..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/annotation/GrowToolHintTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.shinhanlife.dat.lib.annotation; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -class GrowToolHintTest { - - @Test - void exposesResilienceDefaults() throws Exception { - assertEquals(5000L, GrowToolHint.class.getMethod("timeoutMillis").getDefaultValue()); - assertEquals(3, GrowToolHint.class.getMethod("retryMaxAttempts").getDefaultValue()); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/aop/ToolSlaMonitoringAspectTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/aop/ToolSlaMonitoringAspectTest.java deleted file mode 100644 index 24c0cbc97..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/aop/ToolSlaMonitoringAspectTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.shinhanlife.dat.lib.aop; - -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import io.shinhanlife.dat.lib.config.McpProperties; -import java.lang.reflect.Method; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.reflect.MethodSignature; -import org.junit.jupiter.api.Test; -import org.springaicommunity.mcp.annotation.McpTool; - -class ToolSlaMonitoringAspectTest { - - @Test - void propagatesJvmErrorsWithoutInspectingThem() throws Throwable { - ProceedingJoinPoint joinPoint = mock(ProceedingJoinPoint.class); - MethodSignature signature = mock(MethodSignature.class); - Method method = SampleTool.class.getDeclaredMethod("execute"); - MessageAccessError error = new MessageAccessError(); - when(joinPoint.getSignature()).thenReturn(signature); - when(signature.getMethod()).thenReturn(method); - when(joinPoint.proceed()).thenThrow(error); - - MessageAccessError thrown = assertThrows(MessageAccessError.class, - () -> new ToolSlaMonitoringAspect(new McpProperties()).monitorToolSla(joinPoint)); - - assertSame(error, thrown); - } - - static class SampleTool { - @McpTool(name = "sample_tool") - public void execute() { - } - } - - static class MessageAccessError extends Error { - @Override - public String getMessage() { - throw new AssertionError("JVM errors must not be inspected by the SLA aspect"); - } - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/common/adapter/sender/ShinhanMciSenderTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/common/adapter/sender/ShinhanMciSenderTest.java deleted file mode 100644 index b4011b6e8..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/common/adapter/sender/ShinhanMciSenderTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.shinhanlife.dat.lib.common.adapter.sender; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dat.lib.integration.mci.dto.MciRequestWrapper; -import io.shinhanlife.dat.lib.integration.mci.dto.ShinhanCommonHeaderDto; -import lombok.Data; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @package io.shinhanlife.dat.lib.adapter.sender - * @className ShinhanMciSenderTest - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -class ShinhanMciSenderTest { - - @Data - static class SampleBody { - private String msgCd; - private String anxMsgCt; - } - - @Test - void testJsonUnwrapped() throws Exception { - ObjectMapper mapper = new ObjectMapper(); - - ShinhanCommonHeaderDto header = new ShinhanCommonHeaderDto(); - header.setGlbId("20211115150135679009808815NCS17007887"); - header.setPgrsSriaNo("002"); - header.setItrIfId("NCSBACO00001"); - - SampleBody body = new SampleBody(); - body.setMsgCd("12345"); - body.setAnxMsgCt("Test Message"); - - MciRequestWrapper wrapper = new MciRequestWrapper<>(); - wrapper.setTgrmCmnnhddValu(header); - wrapper.setBody(body); - - String json = mapper.writeValueAsString(wrapper); - - System.out.println(json); - - // 검증: body 필드가 json root 레벨에 평탄화되어 있는지 확인 - assertThat(json).contains("\"tgrmCmnnhddValu\":{"); - assertThat(json).contains("\"msgCd\":\"12345\""); - assertThat(json).contains("\"anxMsgCt\":\"Test Message\""); - assertThat(json).doesNotContain("\"body\":"); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/config/AxhubHttpConfigurationTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/config/AxhubHttpConfigurationTest.java deleted file mode 100644 index c2e763e57..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/config/AxhubHttpConfigurationTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.shinhanlife.dat.lib.config; - -import static org.assertj.core.api.Assertions.assertThat; - -import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.web.client.RestClient; - -class AxhubHttpConfigurationTest { - - @Test - void registersGlowHttpComponentFromDapLibConfiguration() { - try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class)) { - assertThat(context.getBean(GlowHttpComponent.class)).isNotNull(); - } - } - - @Configuration - @Import(AxhubHttpConfiguration.class) - static class TestConfiguration { - - @Bean - RestClient.Builder restClientBuilder() { - return RestClient.builder(); - } - } -} \ No newline at end of file diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/config/ToolSchemaConfigurationTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/config/ToolSchemaConfigurationTest.java deleted file mode 100644 index 335daca54..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/config/ToolSchemaConfigurationTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.shinhanlife.dat.lib.config; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.json.schema.JsonSchemaValidator; -import io.shinhanlife.dat.lib.validation.ToolArgumentSchemaValidator; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; - -class ToolSchemaConfigurationTest { - - @Test - void providesToolArgumentSchemaValidatorWithoutComponentScanning() { - try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { - context.registerBean(ObjectMapper.class); - context.register(ToolSchemaConfiguration.class); - context.refresh(); - - assertNotNull(context.getBean(ToolArgumentSchemaValidator.class)); - assertNotNull(context.getBean(JsonSchemaValidator.class)); - } - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpComponentTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpComponentTest.java deleted file mode 100644 index c5e5e0466..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/integration/http/component/AxhubHttpComponentTest.java +++ /dev/null @@ -1,147 +0,0 @@ -package io.shinhanlife.dat.lib.integration.http.component; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dat.lib.config.GlowCommunicationProperties; -import io.shinhanlife.dat.lib.mcp.McpRequestHeaderContext; -import io.shinhanlife.dat.lib.mcp.McpRequestHeaders; -import io.shinhanlife.glow.communication.module.http.component.GlowHttpComponent; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpMethod; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.client.RestClient; - -class AxhubHttpComponentTest { - - @Test - void callByApiNameBuildsGlowTransferAndDeserializesJsonResponse() { - RestClient.Builder builder = RestClient.builder(); - MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build(); - GlowHttpComponent glowHttpComponent = new GlowHttpComponent(builder); - AxhubHttpProperties properties = new AxhubHttpProperties(); - properties.setApiList(List.of(new AxhubHttpProperties.ApiDefinition( - "status", "https://api.example.test", "/v1", HttpMethod.GET, "application/json", false))); - AxhubHttpComponent component = new AxhubHttpComponent( - glowHttpComponent, new ObjectMapper(), new GlowCommunicationProperties(), properties); - - server.expect(requestTo("https://api.example.test/v1/status")) - .andExpect(header("X-ANONYMOUS-REQ", "AXHUB-TOOL")) - .andRespond(withSuccess("{\"status\":\"OK\"}", APPLICATION_JSON)); - - SampleResponse response = component.call("status", "/status", null, SampleResponse.class); - - assertThat(response.status()).isEqualTo("OK"); - server.verify(); - } - - @Test - void callByApiNameUsesConfiguredUrlMethodContentTypeAndBizPodHeader() { - RestClient.Builder builder = RestClient.builder(); - MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build(); - GlowHttpComponent glowHttpComponent = new GlowHttpComponent(builder); - AxhubHttpProperties properties = new AxhubHttpProperties(); - properties.setApiList(List.of(new AxhubHttpProperties.ApiDefinition( - "employee", "https://employee.example.test", "/itrf/employee", HttpMethod.POST, - "application/json;charset=UTF-8", true))); - AxhubHttpComponent component = new AxhubHttpComponent( - glowHttpComponent, new ObjectMapper(), new GlowCommunicationProperties(), properties); - - server.expect(requestTo("https://employee.example.test/itrf/employee")) - .andExpect(method(HttpMethod.POST)) - .andExpect(header("Content-Type", "application/json;charset=UTF-8")) - .andExpect(header("X-POD-TO-POD", "true")) - .andRespond(withSuccess("{\"status\":\"OK\"}", APPLICATION_JSON)); - - SampleResponse response = component.call("employee", "{\"employeeId\":\"EMP10001\"}", SampleResponse.class); - - assertThat(response.status()).isEqualTo("OK"); - server.verify(); - } - - @Test - void forwardsCanonicalToolHeadersToTheConfiguredHttpService() throws Exception { - RestClient.Builder builder = RestClient.builder(); - MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build(); - AxhubHttpProperties properties = new AxhubHttpProperties(); - properties.setApiList(List.of(new AxhubHttpProperties.ApiDefinition( - "status", "https://api.example.test", "/v1", HttpMethod.POST, "application/json", false))); - AxhubHttpComponent component = new AxhubHttpComponent( - new GlowHttpComponent(builder), new ObjectMapper(), new GlowCommunicationProperties(), properties); - - server.expect(requestTo("https://api.example.test/v1")) - .andExpect(header("X-Guid", "guid-1")) - .andExpect(header("X-Praf-No", "praf-1")) - .andExpect(header("X-Request-Id", "request-1")) - .andExpect(header("X-Request-Time", "2026-08-25T12:34:56+09:00")) - .andExpect(header("X-Vrtl-Praf-No", "virtual-1")) - .andExpect(header("X-App-Code", "app-1")) - .andExpect(header("X-Project-Code", "project-1")) - .andExpect(header("X-User-Ip", "10.0.0.1")) - .andExpect(header("X-Caller-Ip", "10.0.0.2")) - .andExpect(header("X-Caller-Host", "caller.example.test")) - .andExpect(header("X-Channel", "MCP")) - .andExpect(header("X-Agent-Id", "agent-1")) - .andExpect(header("mcp-session-id", "session-1")) - .andRespond(withSuccess("{\"status\":\"OK\"}", APPLICATION_JSON)); - - setRequestHeaders(headers(Map.ofEntries( - Map.entry("guid", "guid-1"), - Map.entry("prafNo", "praf-1"), - Map.entry("requestId", "request-1"), - Map.entry("requestTime", "2026-08-25T12:34:56+09:00"), - Map.entry("vrtlPrafNo", "virtual-1"), - Map.entry("appCode", "app-1"), - Map.entry("projectCode", "project-1"), - Map.entry("userIp", "10.0.0.1"), - Map.entry("callerIp", "10.0.0.2"), - Map.entry("callerHost", "caller.example.test"), - Map.entry("channel", "MCP"), - Map.entry("agentId", "agent-1"), - Map.entry("mcpSessionId", "session-1")))); - try { - assertThat(component.call("status", Map.of(), SampleResponse.class).status()).isEqualTo("OK"); - server.verify(); - } finally { - clearRequestHeaders(); - } - } - - private McpRequestHeaders headers(Map values) { - try { - Class[] types = Arrays.stream(McpRequestHeaders.class.getRecordComponents()) - .map(component -> component.getType()) - .toArray(Class[]::new); - Object[] arguments = Arrays.stream(McpRequestHeaders.class.getRecordComponents()) - .map(component -> values.get(component.getName())) - .toArray(); - return McpRequestHeaders.class.getDeclaredConstructor(types).newInstance(arguments); - } catch (ReflectiveOperationException exception) { - throw new AssertionError(exception); - } - } - - private void setRequestHeaders(McpRequestHeaders headers) throws Exception { - Method method = McpRequestHeaderContext.class.getDeclaredMethod("set", McpRequestHeaders.class); - method.setAccessible(true); - method.invoke(null, headers); - } - - private void clearRequestHeaders() throws Exception { - Method method = McpRequestHeaderContext.class.getDeclaredMethod("clear"); - method.setAccessible(true); - method.invoke(null); - } - - record SampleResponse(String status) { - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/integration/mci/component/AxhubMciComponentTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/integration/mci/component/AxhubMciComponentTest.java deleted file mode 100644 index 87b33b05e..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/integration/mci/component/AxhubMciComponentTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.shinhanlife.dat.lib.integration.mci.component; - -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import io.shinhanlife.dat.lib.config.GlowCommunicationProperties; -import io.shinhanlife.glow.communication.module.mci.component.GlowMciComponent; -import org.junit.jupiter.api.Test; - -class AxhubMciComponentTest { - - @Test - @SuppressWarnings({"rawtypes", "unchecked"}) - void doesNotReclassifyUnexpectedRuntimeFailuresAsBusinessFailures() { - GlowMciComponent glowMci = mock(GlowMciComponent.class); - IllegalStateException failure = new IllegalStateException("unexpected MCI adapter defect"); - when(glowMci.sync(any())).thenThrow(failure); - AxhubMciComponent component = new AxhubMciComponent(glowMci, new GlowCommunicationProperties()); - - IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> - component.callTo("ONBSA0790", "ONBSA", new Object(), Object.class)); - - assertSame(failure, thrown); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/manifest/ToolManifestServiceTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/manifest/ToolManifestServiceTest.java deleted file mode 100644 index 1d7e69e08..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/manifest/ToolManifestServiceTest.java +++ /dev/null @@ -1,118 +0,0 @@ -package io.shinhanlife.dat.lib.manifest; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dat.lib.config.McpProperties; -import io.shinhanlife.dat.mcc.dto.ToolMetadata; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class ToolManifestServiceTest { - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Test - void buildsStandardManifestAndDerivesRevisionFromToolDefinition() { - McpProperties properties = manifestProperties("insurance-processing", "processing."); - ToolManifestService service = new ToolManifestService( - () -> List.of(tool("processing.contract.inquiry", "1.2.0", 3000)), objectMapper, properties); - - ToolManifestResponse manifest = service.currentManifest(); - - assertEquals("insurance-processing", manifest.bundleId()); - assertTrue(manifest.revision().matches("\\d+")); - assertEquals(1, manifest.tools().size()); - ToolManifestItem item = manifest.tools().getFirst(); - assertEquals("processing.contract.inquiry", item.name()); - assertEquals("http://tool-processing.ax-hub.svc.cluster.local:8080/mcp/processing.contract.inquiry", - item.endpoint()); - assertEquals("계약 조회", item.title()); - assertEquals("object", item.inputSchema().get("type")); - assertTrue(item.annotations().readOnlyHint()); - assertEquals("1.2.0", item.meta().version()); - assertEquals(3000, item.meta().timeoutMillis()); - assertEquals(5, item.meta().retryMaxAttempts()); - assertEquals(List.of("계약 상태를 알려줘", "내 계약을 조회해줘", "계약번호로 찾아줘"), - item.meta().exampleQueries()); - } - - @Test - void changesRevisionWhenToolDefinitionChanges() { - McpProperties properties = manifestProperties("insurance-processing", "processing."); - ToolManifestService before = new ToolManifestService( - () -> List.of(tool("processing.contract.inquiry", "1.2.0", 3000)), objectMapper, properties); - ToolManifestService after = new ToolManifestService( - () -> List.of(tool("processing.contract.inquiry", "1.2.0", 5000)), objectMapper, properties); - - assertTrue(!before.currentManifest().revision().equals(after.currentManifest().revision())); - } - - @Test - void serializesToolServiceManifestWithServiceIdInsteadOfBundleId() throws Exception { - McpProperties properties = manifestProperties("was-cus", "cus."); - ToolManifestService service = new ToolManifestService(() -> List.of(), objectMapper, properties); - - String json = objectMapper.writeValueAsString(service.currentToolServiceManifest()); - - assertTrue(json.contains("\"serviceId\":\"was-cus\"")); - assertTrue(!json.contains("\"bundleId\"")); - } - - @Test - void rejectsEntireManifestWhenToolNameDoesNotMatchConfiguredPrefix() { - McpProperties properties = manifestProperties("insurance-processing", "processing."); - ToolManifestService service = new ToolManifestService( - () -> List.of(tool("notification_sms_send", "1.0.0", 3000)), objectMapper, properties); - - IllegalStateException error = assertThrows(IllegalStateException.class, service::currentManifest); - - assertTrue(error.getMessage().contains("name-prefix")); - } - - @Test - void rejectsEntireManifestWhenToolNamesAreDuplicated() { - McpProperties properties = manifestProperties("insurance-processing", "processing."); - ToolManifestService service = new ToolManifestService( - () -> List.of(tool("processing.contract.inquiry", "1.0.0", 3000), - tool("processing.contract.inquiry", "1.0.1", 3000)), objectMapper, properties); - - IllegalStateException error = assertThrows(IllegalStateException.class, service::currentManifest); - - assertTrue(error.getMessage().contains("duplicate")); - } - - private McpProperties manifestProperties(String bundleId, String namePrefix) { - McpProperties properties = new McpProperties(); - McpProperties.Manifest manifest = new McpProperties.Manifest(); - manifest.setBundleId(bundleId); - manifest.setNamePrefix(namePrefix); - properties.setManifest(manifest); - return properties; - } - - private ToolMetadata tool(String name, String version, long timeoutMillis) { - return ToolMetadata.builder() - .name(name) - .podUrl("http://tool-processing.ax-hub.svc.cluster.local:8080") - .displayName("계약 조회") - .description("계약번호로 계약 정보를 조회합니다.") - .exampleQueries(List.of("계약 상태를 알려줘", "내 계약을 조회해줘", "계약번호로 찾아줘")) - .tags(List.of("계약")) - .ownerOrg("MCP_TOOL") - .parametersSchema(Map.of("type", "object", "properties", Map.of("contractNo", Map.of("type", "string")), - "required", List.of("contractNo"), "additionalProperties", false)) - .semver(version) - .timeoutMillis(timeoutMillis) - .retryMaxAttempts(5) - .enabled(true) - .readOnlyHint(true) - .destructiveHint(false) - .idempotentHint(true) - .openWorldHint(false) - .build(); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/McpSdk2CompatibilityTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/McpSdk2CompatibilityTest.java deleted file mode 100644 index 807b3cf0f..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/McpSdk2CompatibilityTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import io.modelcontextprotocol.json.schema.jackson2.DefaultJsonSchemaValidator; -import io.modelcontextprotocol.spec.McpSchema; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class McpSdk2CompatibilityTest { - - @Test - void usesMcpJavaSdkTwo() { - assertEquals("2.0.0", McpSchema.class.getPackage().getImplementationVersion()); - } - - @Test - void validatesJsonSchemaWithTheNetworkntVersionRequiredByMcpSdkTwo() { - DefaultJsonSchemaValidator validator = new DefaultJsonSchemaValidator(); - - var response = validator.validateSchema(Map.of( - "$schema", McpSchema.JSON_SCHEMA_DIALECT_2020_12, - "type", "object", - "properties", Map.of("employeeId", Map.of("type", "string")))); - - assertTrue(response.valid(), response.errorMessage()); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/McpToolMethodRegistryTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/McpToolMethodRegistryTest.java deleted file mode 100644 index 8a09edc96..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/McpToolMethodRegistryTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import io.shinhanlife.dat.lib.config.McpProperties; -import org.junit.jupiter.api.Test; -import org.springaicommunity.mcp.annotation.McpTool; -import org.springframework.context.support.StaticApplicationContext; - -class McpToolMethodRegistryTest { - - @Test - void indexesAnnotatedToolDuringInitialization() { - StaticApplicationContext context = contextWith(new EchoTool()); - McpToolMethodRegistry registry = new McpToolMethodRegistry(context, new McpProperties()); - - registry.initialize(); - - McpToolMethodRegistry.RegisteredTool tool = registry.find("cmm_echo_search"); - assertNotNull(tool); - assertEquals("execute", tool.method().getName()); - assertEquals("cmm_echo_search", tool.annotation().name()); - } - - @Test - void rejectsDuplicateToolNamesDuringInitialization() { - StaticApplicationContext context = contextWith(new EchoTool(), new DuplicateEchoTool()); - McpToolMethodRegistry registry = new McpToolMethodRegistry(context, new McpProperties()); - - assertThrows(IllegalStateException.class, registry::initialize); - } - - private StaticApplicationContext contextWith(Object... tools) { - StaticApplicationContext context = new StaticApplicationContext(); - for (int index = 0; index < tools.length; index++) { - context.getBeanFactory().registerSingleton("tool" + index, tools[index]); - } - context.refresh(); - return context; - } - - static class EchoTool { - @McpTool(name = "cmm_echo_search") - public String execute(String request) { - return request; - } - } - - static class DuplicateEchoTool { - @McpTool(name = "cmm_echo_search") - public String execute(String request) { - return request; - } - } -} \ No newline at end of file diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolExecutionServiceTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolExecutionServiceTest.java deleted file mode 100644 index 66d58f381..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolExecutionServiceTest.java +++ /dev/null @@ -1,187 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.json.schema.jackson2.DefaultJsonSchemaValidator; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.lib.config.McpProperties; -import io.shinhanlife.dat.lib.util.ToolSchemaResolver; -import io.shinhanlife.dat.lib.validation.ToolArgumentSchemaValidator; -import java.util.Arrays; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.springaicommunity.mcp.annotation.McpTool; -import org.springaicommunity.mcp.annotation.McpToolParam; -import org.springframework.context.support.StaticApplicationContext; - -class McpToolExecutionServiceTest { - - @Test - void returnsNotFoundWhenNoToolMatchesTheRequestedName() { - McpToolExecutionService service = serviceWith(new Object()); - ToolExecutionResult result = service.execute("missing.cmm.tool.inquiry", null, Map.of()); - assertEquals(404, result.statusCode()); - assertEquals("TOOL_NOT_FOUND", ((Map) result.body()).get("code")); - } - - @Test - void convertsArgumentsToDtoAndExecutesTheMatchedTool() { - McpToolExecutionService service = serviceWith(new EchoTool()); - ToolExecutionResult result = service.execute("sample.cmm.value.echo", - headers(Map.ofEntries( - Map.entry("guid", "guid-1"), - Map.entry("prafNo", "praf-1"), - Map.entry("requestId", "request-1"), - Map.entry("requestTime", "2026-08-25T12:34:56+09:00"), - Map.entry("vrtlPrafNo", "virtual-1"), - Map.entry("appCode", "app-1"), - Map.entry("projectCode", "project-1"), - Map.entry("userIp", "10.0.0.1"), - Map.entry("callerIp", "10.0.0.2"), - Map.entry("callerHost", "caller.example.test"), - Map.entry("channel", "MCP"), - Map.entry("agentId", "agent-1"), - Map.entry("mcpSessionId", "session-1"))), - Map.of("value", "hello")); - assertEquals(200, result.statusCode()); - assertEquals("hello", ((Map) result.body()).get("value")); - assertEquals("request-1", result.headers().get("X-Request-Id")); - assertEquals("guid-1", result.headers().get("X-Guid")); - assertEquals("session-1", result.headers().get("mcp-session-id")); - } - - @Test - void doesNotHideUnexpectedInputSchemaResolverFailures() { - ToolSchemaResolver resolver = mock(ToolSchemaResolver.class); - when(resolver.resolve(any(), any(), any())) - .thenThrow(new IllegalStateException("unexpected resolver defect")); - McpToolExecutionService service = serviceWith(new EchoTool(), resolver); - - assertThrows(IllegalStateException.class, () -> - service.execute("sample.cmm.value.echo", null, Map.of("value", "hello"))); - } - - @Test - void doesNotHideUnexpectedOutputSchemaResolverFailures() { - ToolSchemaResolver resolver = mock(ToolSchemaResolver.class); - when(resolver.resolveOutput(any(), any(), any())) - .thenThrow(new IllegalStateException("unexpected resolver defect")); - McpToolExecutionService service = serviceWith(new MapTool(), resolver); - - assertThrows(IllegalStateException.class, () -> - service.execute("sample.cmm.map.echo", null, Map.of("value", "hello"))); - } - - @Test - void propagatesUnexpectedRuntimeFailuresThrownByTheTool() { - UnexpectedToolFailure failure = new UnexpectedToolFailure(); - McpToolExecutionService service = serviceWith(new RuntimeFailureTool(failure)); - - UnexpectedToolFailure thrown = assertThrows(UnexpectedToolFailure.class, () -> - service.execute("sample.cmm.runtime.failure", null, Map.of())); - - assertSame(failure, thrown); - } - - @Test - void propagatesJvmErrorsThrownByTheTool() { - ToolJvmError error = new ToolJvmError(); - McpToolExecutionService service = serviceWith(new ErrorTool(error)); - - ToolJvmError thrown = assertThrows(ToolJvmError.class, () -> - service.execute("sample.cmm.error.failure", null, Map.of())); - - assertSame(error, thrown); - } - - private McpRequestHeaders headers(Map values) { - try { - Class[] types = Arrays.stream(McpRequestHeaders.class.getRecordComponents()) - .map(component -> component.getType()) - .toArray(Class[]::new); - Object[] arguments = Arrays.stream(McpRequestHeaders.class.getRecordComponents()) - .map(component -> values.get(component.getName())) - .toArray(); - return McpRequestHeaders.class.getDeclaredConstructor(types).newInstance(arguments); - } catch (ReflectiveOperationException exception) { - throw new AssertionError(exception); - } - } - - private McpToolExecutionService serviceWith(Object toolBean) { - ObjectMapper objectMapper = new ObjectMapper(); - return serviceWith(toolBean, new ToolSchemaResolver(objectMapper)); - } - - private McpToolExecutionService serviceWith(Object toolBean, ToolSchemaResolver resolver) { - ObjectMapper objectMapper = new ObjectMapper(); - StaticApplicationContext context = new StaticApplicationContext(); - context.getBeanFactory().registerSingleton("toolBean", toolBean); - context.refresh(); - McpToolMethodRegistry registry = new McpToolMethodRegistry(context, new McpProperties()); - registry.initialize(); - return new McpToolExecutionService(registry, objectMapper, - new ToolArgumentSchemaValidator(objectMapper, new DefaultJsonSchemaValidator()), - resolver); - } - - static class EchoTool { - @McpTool(name = "sample.cmm.value.echo", description = "Echoes a value") - @GrowToolHint - public Map execute(EchoRequest request) { - return Map.of("value", request.value); - } - } - - static class MapTool { - @McpTool(name = "sample.cmm.map.echo", description = "Echoes a map") - public Map execute(Map request) { - return request; - } - } - - static class RuntimeFailureTool { - private final UnexpectedToolFailure failure; - - RuntimeFailureTool(UnexpectedToolFailure failure) { - this.failure = failure; - } - - @McpTool(name = "sample.cmm.runtime.failure") - public Map execute(Map request) { - throw failure; - } - } - - static class ErrorTool { - private final ToolJvmError error; - - ErrorTool(ToolJvmError error) { - this.error = error; - } - - @McpTool(name = "sample.cmm.error.failure") - public Map execute(Map request) { - throw error; - } - } - - static class UnexpectedToolFailure extends RuntimeException { - } - - static class ToolJvmError extends Error { - } - - static class EchoRequest { - @McpToolParam(description = "Value", required = true) - private String value; - public String getValue() { return value; } - public void setValue(String value) { this.value = value; } - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolMetadataMcpMapperTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolMetadataMcpMapperTest.java deleted file mode 100644 index 5888e3805..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolMetadataMcpMapperTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -import io.shinhanlife.dat.mcc.dto.ToolMetadata; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class ToolMetadataMcpMapperTest { - - @Test - void exposesResilienceSettingsInMcpToolMeta() { - ToolMetadata metadata = ToolMetadata.builder() - .timeoutMillis(12000L) - .retryMaxAttempts(5) - .build(); - - Map meta = ToolMetadataMcpMapper.meta(metadata); - - assertEquals(12000L, meta.get("timeoutMillis")); - assertFalse(meta.containsKey("retryEnabled")); - assertEquals(5, meta.get("retryMaxAttempts")); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolRegistryHeartbeatSenderTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolRegistryHeartbeatSenderTest.java deleted file mode 100644 index 71c015107..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/ToolRegistryHeartbeatSenderTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package io.shinhanlife.dat.lib.mcp; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.sun.net.httpserver.HttpServer; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.lib.config.McpProperties; -import io.shinhanlife.dat.lib.util.ToolSchemaResolver; -import jakarta.annotation.PreDestroy; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.net.InetSocketAddress; -import java.time.Duration; -import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.Test; -import org.springaicommunity.mcp.annotation.McpTool; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationContext; -import org.springframework.context.event.EventListener; - -class ToolRegistryHeartbeatSenderTest { - - @Test - void applicationReadyDoesNotPushToolsToGateway() throws Exception { - try (GatewayProbe gateway = new GatewayProbe()) { - ToolRegistryHeartbeatSender sender = senderWithOneTool(gateway.url()); - - sender.init(); - invokeLifecycleMethods(sender, EventListener.class); - - assertThat(gateway.receivedRequestWithin(Duration.ofMillis(300))).isFalse(); - } - } - - @Test - void shutdownDoesNotDeregisterToolsFromGateway() throws Exception { - try (GatewayProbe gateway = new GatewayProbe()) { - ToolRegistryHeartbeatSender sender = senderWithOneTool(gateway.url()); - - sender.init(); - invokeLifecycleMethods(sender, PreDestroy.class); - - assertThat(gateway.receivedRequestWithin(Duration.ofMillis(300))).isFalse(); - } - } - - @Test - void readsResilienceValuesOnlyFromGrowToolHint() throws Exception { - ToolRegistryHeartbeatSender sender = senderWithOneTool("http://127.0.0.1:1"); - - sender.init(); - - var metadata = sender.getAllScannedTools().getFirst(); - assertThat(metadata.getTimeoutMillis()).isEqualTo(12000L); - assertThat(metadata.getRetryMaxAttempts()).isEqualTo(1); - } - - private ToolRegistryHeartbeatSender senderWithOneTool(String gatewayUrl) throws Exception { - ApplicationContext applicationContext = mock(ApplicationContext.class); - when(applicationContext.getBeansOfType(Object.class)).thenReturn(Map.of("tool", new SampleTool())); - ToolRegistryHeartbeatSender sender = new ToolRegistryHeartbeatSender( - applicationContext, new ObjectMapper(), new McpProperties(), mock(ToolSchemaResolver.class)); - setFieldIfPresent(sender, "gatewayUrl", gatewayUrl); - setField(sender, "podUrl", "http://localhost:8084"); - return sender; - } - - private void invokeLifecycleMethods(Object target, Class annotationType) throws Exception { - for (Method method : target.getClass().getDeclaredMethods()) { - if (method.getAnnotation(annotationType) == null) { - continue; - } - if (method.getParameterCount() == 0) { - method.invoke(target); - } else if (method.getParameterTypes()[0] == ApplicationReadyEvent.class) { - method.invoke(target, mock(ApplicationReadyEvent.class)); - } - } - } - - private void setField(Object target, String name, Object value) throws Exception { - var field = target.getClass().getDeclaredField(name); - field.setAccessible(true); - field.set(target, value); - } - - private void setFieldIfPresent(Object target, String name, Object value) throws Exception { - try { - setField(target, name, value); - } catch (NoSuchFieldException ignored) { - // The desired implementation has no Gateway registration configuration. - } - } - - static class SampleTool { - @McpTool(name = "test_sample_tool") - @GrowToolHint(timeoutMillis = 12000L, retryMaxAttempts = 1) - void execute() { - } - } - - private static final class GatewayProbe implements AutoCloseable { - private final HttpServer server; - private final CountDownLatch requestReceived = new CountDownLatch(1); - - private GatewayProbe() throws Exception { - server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0); - server.createContext("/", exchange -> { - requestReceived.countDown(); - exchange.sendResponseHeaders(204, -1); - exchange.close(); - }); - server.start(); - } - - private String url() { - return "http://127.0.0.1:" + server.getAddress().getPort(); - } - - private boolean receivedRequestWithin(Duration timeout) throws InterruptedException { - return requestReceived.await(timeout.toMillis(), TimeUnit.MILLISECONDS); - } - - @Override - public void close() { - server.stop(0); - } - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/config/WebConfigToolExecutionSecurityTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/config/WebConfigToolExecutionSecurityTest.java deleted file mode 100644 index 31a2cf597..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/config/WebConfigToolExecutionSecurityTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.config; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import io.shinhanlife.dat.lib.mcp.security.ApiKeyInterceptor; -import io.shinhanlife.dat.lib.mcp.security.SecurityProperties; -import java.util.Map; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.mock.web.MockServletContext; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; - -class WebConfigToolExecutionSecurityTest { - private AnnotationConfigWebApplicationContext context; - private MockMvc mockMvc; - - @BeforeEach - void setUp() { - context = new AnnotationConfigWebApplicationContext(); - context.setServletContext(new MockServletContext()); - context.register(TestConfiguration.class); - context.refresh(); - mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); - } - - @AfterEach - void tearDown() { - context.close(); - } - - @Test - void protectsTheRestToolExecutionPathWithTheDapmsApiKey() throws Exception { - mockMvc.perform(post("/mcp/sample").contentType("application/json").content("{}")) - .andExpect(status().isUnauthorized()); - mockMvc.perform(post("/mcp/sample") - .header("X-Tool-Server-API-Key", "tool-server-key") - .contentType("application/json") - .content("{}")) - .andExpect(status().isOk()); - } - - @Configuration - @EnableWebMvc - @Import(WebConfig.class) - static class TestConfiguration { - @Bean - SecurityProperties securityProperties() { - SecurityProperties properties = new SecurityProperties(); - properties.setApiKeys(Map.of("tool-server-key", "dapms")); - return properties; - } - - @Bean - ApiKeyInterceptor apiKeyInterceptor(SecurityProperties properties) { - return new ApiKeyInterceptor(properties); - } - - @Bean - TestController testController() { - return new TestController(); - } - } - - @RestController - static class TestController { - @PostMapping("/mcp/{name}") - Map execute(@PathVariable String name) { - return Map.of("name", name); - } - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/security/ApiKeyInterceptorTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/security/ApiKeyInterceptorTest.java deleted file mode 100644 index 950d82a19..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/mcp/security/ApiKeyInterceptorTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.shinhanlife.dat.lib.mcp.security; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; - -class ApiKeyInterceptorTest { - - @Test - void authenticatesWithTheToolServerApiKeyHeaderSentByDapms() throws Exception { - SecurityProperties properties = new SecurityProperties(); - properties.setApiKeys(Map.of("tool-server-key", "dapms")); - ApiKeyInterceptor interceptor = new ApiKeyInterceptor(properties); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("X-Tool-Server-API-Key", "tool-server-key"); - MockHttpServletResponse response = new MockHttpServletResponse(); - - boolean allowed = interceptor.preHandle(request, response, new Object()); - - assertThat(allowed).isTrue(); - assertThat(request.getAttribute("tenantId")).isEqualTo("dapms"); - } - - @Test - void rejectsARequestWhenItDoesNotMatchTheConfiguredSingleApiKey() throws Exception { - SecurityProperties properties = bindSingleApiKey("tool-server-key"); - ApiKeyInterceptor interceptor = new ApiKeyInterceptor(properties); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("X-Tool-Server-API-Key", "wrong-key"); - MockHttpServletResponse response = new MockHttpServletResponse(); - - boolean allowed = interceptor.preHandle(request, response, new Object()); - - assertThat(allowed).isFalse(); - assertThat(response.getStatus()).isEqualTo(401); - } - - @Test - void authenticatesARequestWithTheConfiguredSingleApiKey() throws Exception { - SecurityProperties properties = bindSingleApiKey("tool-server-key"); - ApiKeyInterceptor interceptor = new ApiKeyInterceptor(properties); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("X-Tool-Server-API-Key", "tool-server-key"); - MockHttpServletResponse response = new MockHttpServletResponse(); - - boolean allowed = interceptor.preHandle(request, response, new Object()); - - assertThat(allowed).isTrue(); - assertThat(request.getAttribute("tenantId")).isEqualTo("dapms"); - } - - private SecurityProperties bindSingleApiKey(String apiKey) { - SecurityProperties properties = new SecurityProperties(); - Binder binder = new Binder(new MapConfigurationPropertySource( - Map.of("mcp.security.api-key", apiKey))); - binder.bind("mcp.security", Bindable.ofInstance(properties)); - return properties; - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionRepositoryTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionRepositoryTest.java deleted file mode 100644 index 3d6f4ec57..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/metadata/ToolDefinitionRepositoryTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.shinhanlife.dat.lib.metadata; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.core.io.DefaultResourceLoader; - -class ToolDefinitionRepositoryTest { - - @Test - void springCanCreateRepositoryWithoutDefaultConstructor() { - try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { - context.register(ToolDefinitionRepository.class); - context.refresh(); - - assertTrue(context.getBean(ToolDefinitionRepository.class) - .findByName("cmm_claim_search").isPresent()); - } - } - - @Test - void loadsAndCachesAValidV17DefinitionByToolName() { - ToolDefinitionRepository repository = new ToolDefinitionRepository( - new ObjectMapper(new YAMLFactory()), new DefaultResourceLoader(), - "classpath*:tool-definitions/**/*.yml"); - - ToolDefinition definition = repository.findByName("cmm_claim_search").orElseThrow(); - - assertEquals("보험금 청구 상태 조회", definition.displayName()); - assertEquals("cmm", definition.categoryKey()); - assertEquals(3, definition.exampleQueries().size()); - assertEquals(false, definition.parametersSchema().get("additionalProperties")); - } - - @Test - void rejectsDefinitionWithFewerThanThreeExampleQueries() { - ToolDefinition invalid = validDefinition().withExampleQueries(java.util.List.of("청구 상태 알려줘")); - - IllegalStateException error = assertThrows(IllegalStateException.class, - () -> ToolDefinitionValidator.validate(invalid, "memory:invalid")); - - assertTrue(error.getMessage().contains("example_queries")); - } - - @Test - void rejectsNonStandardToolName() { - ToolDefinition invalid = validDefinition().withName("cmm_claim.Search"); - - IllegalStateException error = assertThrows(IllegalStateException.class, - () -> ToolDefinitionValidator.validate(invalid, "memory:invalid")); - - assertTrue(error.getMessage().contains("name")); - } - - private ToolDefinition validDefinition() { - return new ToolDefinition( - "cmm_claim_search", "보험금 청구 상태 조회", "1.0.0", "cmm", - new ToolDescription("청구 상태를 조회한다.", "상태 확인 시 사용한다.", - "청구 접수 시 사용하지 않는다.", "청구번호가 필요하다."), - "보험금 청구 상태를 조회합니다.", - java.util.List.of("청구 상태 알려줘", "심사 결과 조회해줘", "계약번호로 청구를 찾아줘"), - true, false, true, - java.util.Map.of("type", "object", "properties", java.util.Map.of(), - "additionalProperties", false), - null, - java.util.List.of("보험금"), null, java.util.List.of(), "MCP_TOOL"); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/session/mci/AxhubSessionServiceTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/session/mci/AxhubSessionServiceTest.java deleted file mode 100644 index 17bc4de34..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/session/mci/AxhubSessionServiceTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package io.shinhanlife.dat.lib.session.mci; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent; -import io.shinhanlife.dat.lib.session.dto.SessionDto; -import io.shinhanlife.glow.BizException; -import io.shinhanlife.glow.communication.annotation.GlowTrgmField; -import io.shinhanlife.glow.communication.dto.Transfer; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.stream.IntStream; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -class AxhubSessionServiceTest { - - private static final String PACKAGE = "io.shinhanlife.dat.lib.session.mci."; - - @Test - @SuppressWarnings({"rawtypes", "unchecked"}) - void fetchesSessionThroughOnbsz0460ListContract() throws Exception { - Class requestClass = requiredClass("ONBSZ0460_I"); - Class responseClass = requiredClass("ONBSZ0460_O"); - Class outputClass = requiredClass("ONBSZ0460_O$CmmnPrafIfinOutDto"); - Class clientClass = requiredClass("MciOnbszClient"); - - Object output = outputClass.getDeclaredConstructor().newInstance(); - set(outputClass, output, "PrafNo", "1234567"); - set(outputClass, output, "OgnzAsrtCd", "ORG001"); - set(outputClass, output, "BrafNo", "BR001"); - set(outputClass, output, "PsmrAsrtCd", "PSMR01"); - set(outputClass, output, "SbsnRulpAsrtCd", "RULE01"); - set(outputClass, output, "BsduCd", "BSD"); - set(outputClass, output, "BsquCd", "BQ"); - set(outputClass, output, "OgnzLeveCd", "LEVEL1"); - - Object response = responseClass.getDeclaredConstructor().newInstance(); - responseClass.getMethod("setCmmnPrafIfinOutDto", List.class).invoke(response, List.of(output)); - Transfer transfer = mock(Transfer.class); - when(transfer.getBody()).thenReturn(response); - AxhubMciComponent mci = mock(AxhubMciComponent.class); - when(mci.callTo(eq("ONBSZ0460"), eq("ONBSZ"), any(), eq((Class) responseClass))) - .thenReturn(transfer); - - Object client = clientClass.getDeclaredConstructor(AxhubMciComponent.class).newInstance(mci); - AxhubSessionService service = AxhubSessionService.class.getDeclaredConstructor(clientClass).newInstance(client); - - SessionDto session = service.fetchUserSession("1234567"); - - assertEquals("1234567", session.getPrafNo()); - assertEquals("ORG001", session.getOgnzNo()); - assertEquals("BR001", session.getBrafNo()); - ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(Object.class); - verify(mci).callTo(eq("ONBSZ0460"), eq("ONBSZ"), requestCaptor.capture(), eq((Class) responseClass)); - Object request = requestCaptor.getValue(); - assertEquals(requestClass, request.getClass()); - List inputs = (List) requestClass.getMethod("getCmmnPrafIfinInDto").invoke(request); - assertEquals(1, inputs.size()); - assertEquals("1234567", inputs.get(0).getClass().getMethod("getPrafNo").invoke(inputs.get(0))); - } - - @Test - void exposesSeventyEightOutputMappingSlots() { - Class outputClass = requiredClass("ONBSZ0460_O$CmmnPrafIfinOutDto"); - Field[] fields = outputClass.getDeclaredFields(); - - assertEquals(78, fields.length); - List actualOrders = Arrays.stream(fields) - .map(field -> field.getAnnotation(GlowTrgmField.class)) - .map(GlowTrgmField::order) - .sorted() - .toList(); - assertEquals(IntStream.rangeClosed(1, 78).boxed().toList(), actualOrders); - } - - @Test - void removesOldOnbsa0790Types() { - assertThrows(ClassNotFoundException.class, () -> Class.forName(PACKAGE + "ONBSA0790_I")); - assertThrows(ClassNotFoundException.class, () -> Class.forName(PACKAGE + "ONBSA0790_O")); - } - - @Test - @SuppressWarnings({"rawtypes", "unchecked"}) - void returnsNullWhenMciReportsABusinessCommunicationFailure() throws Exception { - Class responseClass = requiredClass("ONBSZ0460_O"); - Class clientClass = requiredClass("MciOnbszClient"); - AxhubMciComponent mci = mock(AxhubMciComponent.class); - when(mci.callTo(eq("ONBSZ0460"), eq("ONBSZ"), any(), eq((Class) responseClass))) - .thenThrow(bizException()); - Object client = clientClass.getDeclaredConstructor(AxhubMciComponent.class).newInstance(mci); - AxhubSessionService service = AxhubSessionService.class.getDeclaredConstructor(clientClass).newInstance(client); - - assertNull(service.fetchUserSession("1234567")); - } - - @Test - @SuppressWarnings({"rawtypes", "unchecked"}) - void doesNotHideUnexpectedRuntimeFailures() throws Exception { - Class responseClass = requiredClass("ONBSZ0460_O"); - Class clientClass = requiredClass("MciOnbszClient"); - AxhubMciComponent mci = mock(AxhubMciComponent.class); - when(mci.callTo(eq("ONBSZ0460"), eq("ONBSZ"), any(), eq((Class) responseClass))) - .thenThrow(new IllegalStateException("unexpected defect")); - Object client = clientClass.getDeclaredConstructor(AxhubMciComponent.class).newInstance(mci); - AxhubSessionService service = AxhubSessionService.class.getDeclaredConstructor(clientClass).newInstance(client); - - assertThrows(IllegalStateException.class, () -> service.fetchUserSession("1234567")); - } - - private Class requiredClass(String simpleName) { - return assertDoesNotThrow(() -> Class.forName(PACKAGE + simpleName)); - } - - private void set(Class type, Object target, String property, String value) throws Exception { - Method setter = type.getMethod("set" + property, String.class); - setter.invoke(target, value); - } - - private BizException bizException() { - return new BizException("CST00477", new String[]{"MCI communication failure"}, - new RuntimeException("connection closed")); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/JsonSchemaGeneratorTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/JsonSchemaGeneratorTest.java deleted file mode 100644 index c49c88e67..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/JsonSchemaGeneratorTest.java +++ /dev/null @@ -1,128 +0,0 @@ -package io.shinhanlife.dat.lib.util; - - -import org.springaicommunity.mcp.annotation.McpToolParam; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.json.schema.JsonSchemaValidator; -import io.modelcontextprotocol.json.schema.jackson2.DefaultJsonSchemaValidator; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class JsonSchemaGeneratorTest { - - @Test - void includesMcpParameterConstraintsInGeneratedSchema() { - Map schema = JsonSchemaGenerator.generateSchema(ValidatedRequest.class); - Map> properties = properties(schema); - - assertTrue(((List) schema.get("required")).contains("phoneNumber")); - assertTrue(((List) schema.get("required")).contains("approvalStatus")); - assertEquals("^01[0-9]{8,9}$", properties.get("phoneNumber").get("pattern")); - assertEquals(1L, properties.get("amount").get("minimum")); - assertEquals(List.of("APPROVE", "REJECT"), properties.get("approvalStatus").get("enum")); - } - - @Test - void includesExtendedMcpValidationConstraintsInGeneratedSchema() { - Map schema = JsonSchemaGenerator.generateSchema(ValidatedRequest.class); - - assertEquals(50L, property(schema, "pageSize").get("maximum")); - assertEquals(20L, property(schema, "pageSize").get("default")); - assertEquals(1, property(schema, "reference").get("minLength")); - assertEquals(30, property(schema, "reference").get("maxLength")); - } - - @Test - void includesNestedDtoConstraintsInGeneratedSchema() { - Map schema = JsonSchemaGenerator.generateSchema(NestedRequest.class); - Map childSchema = property(schema, "child"); - - assertEquals("object", childSchema.get("type")); - assertTrue(required(childSchema).contains("businessDate")); - assertEquals("^\\\\d{8}$", property(childSchema, "businessDate").get("pattern")); - } - - @Test - void includesNestedDtoSchemaForListItems() { - Map schema = JsonSchemaGenerator.generateSchema(ListRequest.class); - Map itemSchema = map(property(schema, "items").get("items")); - - assertEquals("object", itemSchema.get("type")); - assertTrue(required(itemSchema).contains("businessDate")); - } - - @Test - void validatorRejectsInvalidNestedValue() throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - JsonSchemaValidator validator = new DefaultJsonSchemaValidator(); - Map schema = JsonSchemaGenerator.generateSchema(NestedRequest.class); - Object arguments = objectMapper.convertValue( - Map.of("child", Map.of("businessDate", "2026-07-28")), Object.class); - JsonSchemaValidator.ValidationResponse result = validator.validate(schema, arguments); - - assertFalse(result.valid()); - } - - @SuppressWarnings("unchecked") - private Map> properties(Map schema) { - return (Map>) schema.get("properties"); - } - - private Map property(Map schema, String name) { - return properties(schema).get(name); - } - - @SuppressWarnings("unchecked") - private Map map(Object value) { - return (Map) value; - } - - @SuppressWarnings("unchecked") - private List required(Map schema) { - return (List) schema.get("required"); - } - - private static class ValidatedRequest { - @McpToolParam(description = "recipient phone number", required = true) - @io.swagger.v3.oas.annotations.media.Schema(pattern = "^01[0-9]{8,9}$") - private String phoneNumber; - - @McpToolParam(description = "issue amount", required = true) - @io.swagger.v3.oas.annotations.media.Schema(minimum = "1") - private Long amount; - - @McpToolParam(description = "approval result") - @io.swagger.v3.oas.annotations.media.Schema( - requiredMode = io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED, - allowableValues = {"APPROVE", "REJECT"}) - private String approvalStatus; - - @McpToolParam(description = "page size") - @io.swagger.v3.oas.annotations.media.Schema(maximum = "50", defaultValue = "20") - private Integer pageSize; - - @McpToolParam(description = "reference") - @io.swagger.v3.oas.annotations.media.Schema(minLength = 1, maxLength = 30) - private String reference; - } - - private static class NestedRequest { - private NestedChild child; - } - - private static class ListRequest { - private List items; - } - - private static class NestedChild { - @io.swagger.v3.oas.annotations.media.Schema( - requiredMode = io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED, - pattern = "^\\\\d{8}$") - private String businessDate; - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/PodScaffolderTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/PodScaffolderTest.java deleted file mode 100644 index 456ac00c0..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/PodScaffolderTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.nio.file.Files; -import java.nio.file.Path; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -class PodScaffolderTest { - - @TempDir - Path root; - - @Test - void generatesBuildGradleUsingRepositoryStandard() throws Exception { - PodScaffolder.scaffoldPod(root, "dat-was-sample", "8099", "sample", "tester", "2026.08.07"); - - String buildGradle = Files.readString(root.resolve("dat-was-sample/build.gradle")); - assertTrue(buildGradle.contains("id 'org.springframework.boot'")); - assertTrue(buildGradle.contains("Spring Boot 3.5.11")); - assertTrue(buildGradle.contains("implementation project(':dat-was-lib')")); - assertFalse(buildGradle.contains("compileOnly 'org.projectlombok:lombok")); - } - - @Test - void generatesOthStandardRuntimeResources() throws Exception { - PodScaffolder.scaffoldPod(root, "dat-was-sample", "8099", "sample", "tester", "2026.08.07"); - - Path resources = root.resolve("dat-was-sample/src/main/resources"); - String dockerfile = Files.readString(root.resolve("dat-was-sample/Dockerfile")); - String application = Files.readString(resources.resolve("application.yml")); - - assertTrue(dockerfile.contains("RUN apk add --no-cache tzdata")); - assertTrue(dockerfile.contains("ENV TZ=Asia/Seoul")); - assertTrue(dockerfile.contains("COPY dat-was-sample/build/libs/*-SNAPSHOT.jar app.jar")); - assertTrue(dockerfile.contains("EXPOSE 8099")); - assertTrue(application.contains("api-key: ${TOOL_SERVER_API_KEY:tool-server-key}")); - assertTrue(Files.exists(resources.resolve("application-test.yml"))); - assertTrue(Files.exists(resources.resolve("application-prod.yml"))); - assertTrue(Files.readString(resources.resolve("application-test.yml")) - .contains("${AXHUB_GATEWAY_URL}")); - assertTrue(Files.readString(resources.resolve("application-prod.yml")) - .contains("${AXHUB_TOOL_URL}")); - assertTrue(Files.readString(resources.resolve("application-local.yml")) - .contains("classpath:glow/application-glow-local.yml")); - assertTrue(Files.readString(resources.resolve("application-dev.yml")) - .contains("classpath:glow/application-glow-dev.yml")); - assertTrue(Files.readString(resources.resolve("application-test.yml")) - .contains("classpath:glow/application-glow-test.yml")); - assertTrue(Files.readString(resources.resolve("application-prod.yml")) - .contains("classpath:glow/application-glow-prod.yml")); - } - - @Test - void generatesRedisOptionalPodComposeConfiguration() throws Exception { - Files.writeString(root.resolve("settings.gradle"), "rootProject.name = 'test'\n"); - Files.writeString(root.resolve("docker-compose.yml"), "services:\n"); - - PodScaffolder.scaffoldPod(root, "dat-was-pro", "8085", "pro", "tester", "2026.08.13"); - - String compose = Files.readString(root.resolve("docker-compose.yml")); - assertTrue(compose.contains("was-pro:")); - assertTrue(compose.contains("TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key}")); - assertFalse(compose.contains("depends_on:\n - redis")); - assertFalse(compose.contains("SPRING_REDIS_HOST=redis")); - } - - @Test - void addsNewPodToReferencedExistingConfusableServerManifests() throws Exception { - Path proManifest = existingManifest("dat-was-pro", "dat-was-cus"); - Path cusManifest = existingManifest("dat-was-cus", "dat-was-pro"); - String newManifest = """ - mcp: - manifest: - routing-functions: - - name: route_to_dat-was-cla - server-id: dat-was-cla - category-key: cla - confusable-servers: - - dat-was-pro - - dat-was-cus - - dat-was-missing - - dat-was-cla - """; - - String result = PodScaffolder.scaffoldPod(root, "dat-was-cla", "8098", "cla", "tester", - "2026.09.02", newManifest); - - assertTrue(Files.readString(proManifest).contains("confusable-servers: [dat-was-cus, dat-was-cla]")); - assertTrue(Files.readString(cusManifest).contains("confusable-servers: [dat-was-pro, dat-was-cla]")); - assertFalse(Files.exists(root.resolve("dat-was-missing"))); - assertTrue(result.contains("dat-was-pro/src/main/resources/tool-service-manifest.yml"), result); - assertTrue(result.contains("dat-was-cus/src/main/resources/tool-service-manifest.yml"), result); - } - - private Path existingManifest(String moduleName, String confusableServer) throws Exception { - Path path = root.resolve(moduleName + "/src/main/resources/tool-service-manifest.yml"); - Files.createDirectories(path.getParent()); - Files.writeString(path, """ - mcp: - manifest: - routing-functions: - - name: route_to_%s - server-id: %s - category-key: %s - confusable-servers: [%s] - """.formatted(moduleName, moduleName, moduleName.replace("dat-was-", ""), confusableServer)); - return path; - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolScaffolderTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolScaffolderTest.java deleted file mode 100644 index 09c417f17..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolScaffolderTest.java +++ /dev/null @@ -1,495 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.charset.StandardCharsets; -import java.util.List; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -class ToolScaffolderTest { - - @Test - void providesBusinessProposalGuideWithContractMasterSample() { - String guide = ToolScaffolder.businessProposalGuide(); - - assertTrue(guide.contains("업무 제안서 작성 가이드"), guide); - assertTrue(guide.contains("통합계약원부 조회"), guide); - assertTrue(guide.contains("조회 대상·식별 기준"), guide); - assertTrue(guide.contains("계약번호가 있으면 상세 원부를 조회"), guide); - assertTrue(guide.contains("해지·대출·보험금 상세는 별도 Tool"), guide); - } - - @Test - void exposesGroupedUseCaseScaffoldApi() { - assertDoesNotThrow(() -> ToolScaffolder.class.getMethod( - "scaffoldUseCase", String.class, String.class, String.class, String.class, List.class)); - } - - @Test - void generatesOneUseCaseWithTwoMcpToolMethodsAndTypedClients() throws Exception { - String moduleName = root.resolve("dat-was-customer").toString(); - ToolScaffolder.scaffoldUseCase("Customer", moduleName, "tester", "2026.08.12", List.of( - new ToolScaffolder.ToolMethodDefinition( - "CustomerGuidance", "searchGuidance", "CTMNILO00007", "Customer guidance", "Search guidance", "cmm", "MCI", - false, "NILD", null, - List.of(new ToolScaffolder.FieldDefinition("customerId", "String", "Customer ID", List.of("C001"), "", true)), - List.of(new ToolScaffolder.FieldDefinition("guidanceStatus", "String", "Guidance status", List.of("OPEN"), "", false)), null), - new ToolScaffolder.ToolMethodDefinition( - "CustomerContract", "searchContract", "CTMCNT00001", "Customer contract", "Search contract", "cmm", "MCI", - false, "CNTD", null, - List.of(new ToolScaffolder.FieldDefinition("customerId", "String", "Customer ID", List.of("C001"), "", true)), - List.of(new ToolScaffolder.FieldDefinition("contractStatus", "String", "Contract status", List.of("ACTIVE"), "", false)), null))); - - Path sourceRoot = root.resolve("dat-was-customer/src/main/java/io/shinhanlife/dat/mcc"); - String useCase = Files.readString(sourceRoot.resolve("biz/cmm/usecase/CustomerUseCase.java")); - String implementation = Files.readString(sourceRoot.resolve("biz/cmm/usecase/impl/CustomerUseCaseImpl.java")); - String guidanceClient = Files.readString(sourceRoot.resolve("infra/itrf/mci/nil/d/MciNildClient.java")); - - assertTrue(useCase.contains("CustomerGuidanceResponse searchGuidance(CustomerGuidanceRequest req)"), useCase); - assertTrue(useCase.contains("CustomerContractResponse searchContract(CustomerContractRequest req)"), useCase); - assertTrue(implementation.contains("private final MciNildClient mciNildClient;"), implementation); - assertTrue(implementation.contains("mciNildClient.callTo(\"CTMNILO00007\", null, request, CTMNILO00007_O.class)"), implementation); - assertTrue(guidanceClient.contains("class MciNildClient"), guidanceClient); - } - - @Test - void groupedHttpToolRegistersItsGlowApiCatalogEntry() throws Exception { - String moduleName = root.resolve("dat-was-http").toString(); - - ToolScaffolder.scaffoldUseCase("Employee", moduleName, "tester", "2026.08.13", List.of( - new ToolScaffolder.ToolMethodDefinition( - "EmployeeSearch", "searchEmployee", null, "Employee search", "Search employee", "smp", "HTTP", - false, null, "employee-search", - List.of(new ToolScaffolder.FieldDefinition("employeeNo", "String", "Employee number", List.of("10001"), "", true)), - List.of(new ToolScaffolder.FieldDefinition("employeeName", "String", "Employee name", List.of("Hong"), "", false)), null))); - - Path glowConfig = root.resolve("dat-was-http/src/main/resources/glow/application-glow-local.yml"); - assertTrue(Files.exists(glowConfig)); - assertTrue(Files.readString(glowConfig).contains("- name: employee-search")); - } - - @Test - void generatesEnumAndListFieldsInDtoSchemaAndMockResponse() throws Exception { - String moduleName = root.resolve("dat-was-claim").toString(); - List inputFields = List.of( - new ToolScaffolder.FieldDefinition("claimStatus", "Enum", "Claim status", List.of("OPEN"), "", true, - List.of("OPEN", "CLOSED"), null, List.of()), - new ToolScaffolder.FieldDefinition("customerIds", "List", "Customer IDs", List.of("C001"), "", false, - List.of(), "String", List.of())); - List outputFields = List.of( - new ToolScaffolder.FieldDefinition("guidanceItems", "List", "Guidance items", List.of(), "", false, - List.of(), "Object", List.of(new ToolScaffolder.FieldDefinition("status", "String", "Status", List.of("OPEN"), "", true)))); - - ToolScaffolder.scaffold("claim search", "CLM0001", "Claim search", "cmm", "MCI", moduleName, - "tester", "2026.08.12", false, "CLM1", null, null, inputFields, outputFields); - - Path dtoRoot = root.resolve("dat-was-claim/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto"); - String request = Files.readString(dtoRoot.resolve("ClaimSearchRequest.java")); - String response = Files.readString(dtoRoot.resolve("ClaimSearchResponse.java")); - String mock = Files.readString(root.resolve("dat-was-claim/src/main/resources/mock-responses/cmm_claim_search.json")); - - assertTrue(request.contains("private ClaimStatus claimStatus;"), request); - assertTrue(request.contains("private List customerIds;"), request); - assertTrue(Files.exists(dtoRoot.resolve("ClaimStatus.java"))); - assertTrue(response.contains("private List guidanceItems;"), response); - assertTrue(response.contains("public static class GuidanceItemsItem"), response); - assertFalse(Files.exists(dtoRoot.resolve("ClaimSearchResponseGuidanceItemsItem.java"))); - assertTrue(mock.contains("\"guidanceItems\" : [{"), mock); - } - - @Test - void generatesEveryToolSourceAsUtf8WithoutBrokenKoreanOrBom() throws Exception { - String moduleName = root.resolve("dat-was-korean").toString(); - - ToolScaffolder.scaffold("analysis data query", "CLYMCI00001", "분석 데이터 조회", - "분석 데이터를 조회하기 위한 도구로 다양한 분석 결과를 제공합니다.", - "cmm", "MCI", moduleName, "테스터", "2026.08.12", - false, null, null, null, - List.of(new ToolScaffolder.FieldDefinition("query", "String", "조회 조건", List.of("계약 분석"), "", true)), - List.of(new ToolScaffolder.FieldDefinition("analysisResult", "String", "분석 결과", List.of("정상"), "", false))); - - Path sourceRoot = root.resolve("dat-was-korean/src/main/java/io/shinhanlife/dat/mcc"); - Path useCase = sourceRoot.resolve("biz/cmm/usecase/AnalysisDataQueryUseCase.java"); - Path implementation = sourceRoot.resolve("biz/cmm/usecase/impl/AnalysisDataQueryUseCaseImpl.java"); - - String useCaseSource = Files.readString(useCase, StandardCharsets.UTF_8); - String implementationSource = Files.readString(implementation, StandardCharsets.UTF_8); - assertTrue(useCaseSource.contains("title = \"분석 데이터 조회\""), useCaseSource); - assertTrue(useCaseSource.contains("description = \"분석 데이터를 조회하기 위한 도구로 다양한 분석 결과를 제공합니다.\""), useCaseSource); - assertTrue(useCaseSource.contains("AX HUB 시스템 처리 클래스"), useCaseSource); - assertTrue(useCaseSource.contains("개정이력"), useCaseSource); - assertTrue(useCaseSource.contains("최초생성"), useCaseSource); - assertTrue(implementationSource.contains("요청 수신"), implementationSource); - assertTrue(implementationSource.contains("MapStruct를 이용한 자동 매핑"), implementationSource); - assertTrue(implementationSource.contains("연동 중 오류 발생"), implementationSource); - - try (var files = Files.walk(root.resolve("dat-was-korean"))) { - for (Path file : files.filter(Files::isRegularFile).toList()) { - byte[] bytes = Files.readAllBytes(file); - assertFalse(bytes.length >= 3 - && (bytes[0] & 0xff) == 0xef - && (bytes[1] & 0xff) == 0xbb - && (bytes[2] & 0xff) == 0xbf, - "UTF-8 BOM must not be generated: " + file); - String content = Files.readString(file, StandardCharsets.UTF_8); - assertFalse(content.contains("�"), "Invalid replacement character: " + file); - } - } - } - - @Test - void generatesGrowToolHintMetadataTogetherWithToolSources() throws Exception { - String moduleName = root.resolve("dat-was-v17-definition").toString(); - - ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "직원 조회", - "사번으로 재직 중인 직원을 조회한다.", "smp", "HTTP", moduleName, - "tester", "2026.08.12", false, null, null, null, - List.of(new ToolScaffolder.FieldDefinition("employeeNo", "String", "조회할 사번", List.of("09860000"), "", true)), - List.of(), "employee"); - - Path useCasePath = root.resolve("dat-was-v17-definition/src/main/java/io/shinhanlife/dat/mcc/biz/smp/usecase/EmployeeSearchUseCase.java"); - String useCase = Files.readString(useCasePath); - - assertTrue(useCase.contains("name = \"smp_employee_search\""), useCase); - assertTrue(useCase.contains("timeoutMillis = 5000L"), useCase); - assertTrue(useCase.contains("retryMaxAttempts = 3"), useCase); - assertTrue(useCase.contains("whenToUse = \"사용자가 이 업무 기능의 실행 또는 조회를 요청할 때 사용합니다.\""), useCase); - assertTrue(useCase.contains("exampleQueries = {\"직원 조회 정보를 보여줘\""), useCase); - assertTrue(useCase.contains("ownerOrg = \"MCP_TOOL\""), useCase); - } - - @Test - void appliesV17MetadataEnteredByScaffoldUser() throws Exception { - String moduleName = root.resolve("dat-was-v17-options").toString(); - ToolScaffolder.ToolDefinitionOptions options = new ToolScaffolder.ToolDefinitionOptions( - "사번으로 직원을 조회한다.", - "직원 정보 조회 요청에 사용한다.", - "사번이 없으면 사용하지 않는다.", - "최대 1건만 반환한다.", - "직원 기본 정보 조회", - List.of("사번 10001을 조회해줘", "직원 10001 소속을 알려줘", "10001 직원을 찾아줘"), - List.of("employee", "search"), "HR_TEAM"); - - ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "직원 조회", "직원을 조회한다.", - "smp", "HTTP", moduleName, "tester", "2026.08.12", false, null, null, null, - List.of(new ToolScaffolder.FieldDefinition("employeeNo", "String", "조회할 사번", List.of("10001"), "", true)), - List.of(), "employee", options); - - Path useCasePath = root.resolve("dat-was-v17-options/src/main/java/io/shinhanlife/dat/mcc/biz/smp/usecase/EmployeeSearchUseCase.java"); - String useCase = Files.readString(useCasePath); - assertTrue(useCase.contains("functionDescription = \"사번으로 직원을 조회한다.\""), useCase); - assertTrue(useCase.contains("exampleQueries = {\"사번 10001을 조회해줘\", \"직원 10001 소속을 알려줘\", \"10001 직원을 찾아줘\"}"), useCase); - assertTrue(useCase.contains("tags = {\"employee\", \"search\"}"), useCase); - assertTrue(useCase.contains("ownerOrg = \"HR_TEAM\""), useCase); - } - - @TempDir - Path root; - - @Test - void generatesSpringAiToolAndResponseDto() throws Exception { - String moduleName = "build/scaffold-manifest-test"; - - ToolScaffolder.scaffold("claim search", "CLM0001", "청구 조회", "cmm", "HTTP", moduleName, - "tester", "2026.08.04", true, null); - - Path root = Path.of(moduleName, "src/main/java/io/shinhanlife/dat/mcc/biz/cmm"); - String useCase = Files.readString(root.resolve("usecase/ClaimSearchUseCase.java")); - String response = Files.readString(root.resolve("dto/ClaimSearchResponse.java")); - - assertTrue(useCase.contains("name = \"cmm_claim_search\"")); - assertTrue(useCase.contains("requiresApproval = false"), useCase); - assertTrue(useCase.contains("categoryKey = \"cmm\""), useCase); - assertTrue(useCase.contains("mappingId = \"CLM0001\""), useCase); - assertTrue(response.contains("private String resultCode;")); - assertTrue(response.contains("private String resultMessage;")); - } - @Test - void usesWasModuleNameAsToolPodPrefix() throws Exception { - String moduleName = "build/dat-was-sal"; - - ToolScaffolder.scaffold("notification send", "SMS0001", "SMS 발송", "cmm", "HTTP", moduleName, - "tester", "2026.08.05", true, null); - - Path useCasePath = Path.of(moduleName, - "src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/NotificationSendUseCase.java"); - String useCase = Files.readString(useCasePath); - - assertTrue(useCase.contains("name = \"cmm_notification_send\"")); - } - - @Test - void createsSchemaResourcesInToolSchemaCategoryDirectory() throws Exception { - String moduleName = root.resolve("dat-was-sample").toString(); - - ToolScaffolder.scaffold("claim search", "CLM0001", "청구 조회", "cmm", "HTTP", moduleName, - "tester", "2026.08.09", true, null, - "classpath:tool-schemas/cmm/claim-search-resource-input-schema.json", - "classpath:tool-schemas/cmm/claim-search-resource-output-schema.json"); - - Path schemas = root.resolve("dat-was-sample/src/main/resources/tool-schemas/cmm"); - assertTrue(Files.exists(schemas.resolve("claim-search-resource-input-schema.json"))); - assertTrue(Files.exists(schemas.resolve("claim-search-resource-output-schema.json"))); String useCase = Files.readString(root.resolve("dat-was-sample/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/ClaimSearchUseCase.java")); - assertTrue(useCase.contains("categoryKey = \"cmm\""), useCase); - assertTrue(useCase.contains("mappingId = \"CLM0001\""), useCase); - assertTrue(useCase.contains("inputSchemaResource = \"classpath:tool-schemas/cmm/claim-search-resource-input-schema.json\""), useCase); - assertTrue(useCase.contains("outputSchemaResource = \"classpath:tool-schemas/cmm/claim-search-resource-output-schema.json\""), useCase); - } - - @Test - void generatesRequestDtoFromDeclaredFields() throws Exception { - String moduleName = root.resolve("dat-was-pay").toString(); - - ToolScaffolder.scaffold("search hr", "SHEARCH_01", "HR 조회", "pay", "MCI", moduleName, - "tester", "2026.08.09", true, "DFAG"); - - Path requestPath = root.resolve("dat-was-pay/src/main/java/io/shinhanlife/dat/mcc/biz/pay/dto/SearchHrRequest.java"); - String request = Files.readString(requestPath); - - assertTrue(request.contains("import io.swagger.v3.oas.annotations.media.Schema;")); - assertTrue(request.contains("@Schema(description = \"Search query (예시: example)\", example = \"example\")")); - assertTrue(request.contains("private String query;")); - assertFalse(request.contains("McpToolParam")); - - Path implementationPath = root.resolve("dat-was-pay/src/main/java/io/shinhanlife/dat/mcc/biz/pay/usecase/impl/SearchHrUseCaseImpl.java"); - String implementation = Files.readString(implementationPath); - assertTrue(implementation.contains("public SearchHrResponse execute(SearchHrRequest req)")); - assertTrue(implementation.contains("response.setResultCode(\"SUCCESS\")")); - } - - @Test - void generatesMciToolFromDeclaredInputAndOutputFields() throws Exception { - String moduleName = root.resolve("dat-was-pay").toString(); - List inputFields = List.of( - new ToolScaffolder.FieldDefinition("employeeId", "String", "Employee identifier", List.of("EMP10001"), "", true), - new ToolScaffolder.FieldDefinition("page", "Integer", "Page number", List.of("1"), "", false)); - List outputFields = List.of( - new ToolScaffolder.FieldDefinition("employeeName", "String", "Employee name", List.of("Hong Gildong"), "", true)); - - ToolScaffolder.scaffold("search hr", "SHEARCH_01", "HR search", "pay", "MCI", moduleName, - "tester", "2026.08.09", true, "DFAG", null, null, inputFields, outputFields); - - Path sourceRoot = root.resolve("dat-was-pay/src/main/java/io/shinhanlife/dat/mcc"); - String request = Files.readString(sourceRoot.resolve("biz/pay/dto/SearchHrRequest.java")); - String response = Files.readString(sourceRoot.resolve("biz/pay/dto/SearchHrResponse.java")); - String mciRequest = Files.readString(sourceRoot.resolve("infra/itrf/mci/dfa/g/io/SHEARCH_01_I.java")); - String mciResponse = Files.readString(sourceRoot.resolve("infra/itrf/mci/dfa/g/io/SHEARCH_01_O.java")); - String converter = Files.readString(sourceRoot.resolve("biz/pay/converter/SearchHrConverter.java")); String useCase = Files.readString(sourceRoot.resolve("biz/pay/usecase/SearchHrUseCase.java")); - String implementation = Files.readString(sourceRoot.resolve("biz/pay/usecase/impl/SearchHrUseCaseImpl.java")); - assertTrue(mciRequest.contains("package io.shinhanlife.dat.mcc.infra.itrf.mci.dfa.g.io;"), mciRequest); - assertTrue(useCase.contains("categoryKey = \"pay\""), useCase); - assertTrue(useCase.contains("mappingId = \"SHEARCH_01\""), useCase); - assertTrue(implementation.contains("import io.shinhanlife.dat.mcc.infra.itrf.mci.dfa.g.io.SHEARCH_01_O;")); - - assertTrue(request.contains("private String employeeId;")); - assertTrue(request.contains("private Integer page;")); - assertFalse(request.contains("phoneNumber")); - assertTrue(response.contains("private String employeeName;")); - assertTrue(mciRequest.contains("private String employeeId;")); - assertTrue(mciResponse.contains("private String employeeName;")); - assertTrue(converter.contains("SearchHrResponse toResponse(SHEARCH_01_O mciRes);")); - } - @Test - void generatesMockResponseAndUnitTestSkeletonFromOutputFields() throws Exception { - String moduleName = root.resolve("dat-was-cus").toString(); - List outputFields = List.of( - new ToolScaffolder.FieldDefinition("status", "String", "Claim status", List.of("RECEIVED"), "", true)); - - ToolScaffolder.scaffold("claim search", "CLM0001", "Claim search", "cmm", "MCI", moduleName, - "tester", "2026.08.10", true, null, null, null, List.of(), outputFields); - - Path mockResponse = root.resolve("dat-was-cus/src/main/resources/mock-responses/cmm_claim_search.json"); - Path useCaseTest = root.resolve("dat-was-cus/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java"); - - assertTrue(Files.exists(mockResponse)); - assertTrue(Files.readString(mockResponse).contains("\"status\" : \"RECEIVED\"")); - assertTrue(Files.exists(useCaseTest)); - assertTrue(Files.readString(useCaseTest).contains("class ClaimSearchUseCaseTest")); - } - @Test - void createsWireMockMappingForHttpTool() { - String mapping = ToolScaffolder.wireMockMappingContent("HR_EMPLOYEE_SEARCH", "smp_employee_search.json"); - - assertTrue(mapping.contains("\"method\" : \"POST\""), mapping); - assertTrue(mapping.contains("\"urlPath\" : \"/HR_EMPLOYEE_SEARCH\""), mapping); - assertTrue(mapping.contains("\"bodyFileName\" : \"smp_employee_search.json\""), mapping); - } - - - @Test - void generatesDtoPackageAndRemovesDuplicateResponseFields() throws Exception { - String moduleName = root.resolve("dat-was-http").toString(); - List outputFields = List.of( - new ToolScaffolder.FieldDefinition("resultCode", "String", "API result", List.of("SUCCESS"), "", true), - new ToolScaffolder.FieldDefinition("employeeName", "String", "Employee name", List.of("Hong Gildong"), "", false), - new ToolScaffolder.FieldDefinition("employeeName", "String", "Duplicate name", List.of("Duplicate"), "", false)); - - List inputFields = List.of( - new ToolScaffolder.FieldDefinition("employeeId", "String", "Employee identifier", List.of("EMP10001"), "", true)); - 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("dat-was-http/src/main/java/io/shinhanlife/dat/mcc/biz/smp/dto"); - String request = Files.readString(dtoRoot.resolve("EmployeeSearchRequest.java")); - String response = Files.readString(dtoRoot.resolve("EmployeeSearchResponse.java")); - - assertTrue(request.contains("package io.shinhanlife.dat.mcc.biz.smp.dto;"), request); - assertTrue(response.contains("package io.shinhanlife.dat.mcc.biz.smp.dto;"), response); - assertTrue(response.indexOf("private String resultCode;") == response.lastIndexOf("private String resultCode;"), response); - assertTrue(response.indexOf("private String employeeName;") == response.lastIndexOf("private String employeeName;"), response); - String converter = Files.readString(root.resolve("dat-was-http/src/main/java/io/shinhanlife/dat/mcc/biz/smp/converter/EmployeeSearchConverter.java")); - String httpRequest = Files.readString(root.resolve("dat-was-http/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/employee_search/io/EmployeeSearchHttpRequest.java")); - String httpResponse = Files.readString(root.resolve("dat-was-http/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/employee_search/io/EmployeeSearchHttpResponse.java")); - String httpClient = Files.readString(root.resolve("dat-was-http/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/employee_search/EmployeeSearchClient.java")); - assertFalse(converter.contains("phoneNumber"), converter); - assertTrue(converter.contains("infra.itrf.http.employee_search.io.EmployeeSearchHttpRequest"), converter); - assertFalse(converter.contains("io.shinhanlife.dat.mcc.io.shinhanlife.dat.mcc"), converter); - assertTrue(converter.contains("// @Mapping(source = \"sourceField\", target = \"targetField\")"), converter); - assertTrue(httpRequest.contains("private String employeeId;"), httpRequest); - assertTrue(httpResponse.contains("private String employeeName;"), httpResponse); - assertTrue(httpClient.contains("http.call(API_NAME, request, responseType)"), httpClient); - assertFalse(Files.exists(root.resolve("dat-was-http/src/main/java/io/shinhanlife/dat/mcc/biz/smp/legacy"))); - String implementation = Files.readString(root.resolve("dat-was-http/src/main/java/io/shinhanlife/dat/mcc/biz/smp/usecase/impl/EmployeeSearchUseCaseImpl.java")); - assertTrue(implementation.contains("public EmployeeSearchResponse execute(EmployeeSearchRequest req)"), implementation); - assertTrue(implementation.contains("import io.shinhanlife.dat.mcc.infra.itrf.http.employee_search.EmployeeSearchClient;"), implementation); - assertTrue(implementation.contains("private final EmployeeSearchClient employeeSearchClient;"), implementation); - assertTrue(implementation.contains("employeeSearchClient.call(httpRequest, EmployeeSearchHttpResponse.class)"), implementation); - assertFalse(implementation.contains("AxhubHttpComponent"), implementation); - assertFalse(implementation.contains("executeLegacy(\"HTTP\""), implementation); - Path wireMockResponse = root.resolve("mci-mock/__files/smp_employee_search.json"); - Path wireMockMapping = root.resolve("mci-mock/mappings/smp_employee_search.json"); - assertTrue(Files.exists(wireMockResponse), wireMockResponse.toString()); - assertTrue(Files.exists(wireMockMapping), wireMockMapping.toString()); - assertTrue(Files.readString(wireMockMapping).contains("\"urlPath\" : \"/HR_EMPLOYEE_SEARCH\"")); - Path localConfig = root.resolve("src/main/resources/glow/application-glow-local.yml"); - assertTrue(Files.exists(localConfig), localConfig.toString()); - assertTrue(Files.readString(localConfig).contains("name: employee-search")); - assertTrue(Files.readString(localConfig).contains("url: ${AXHUB_EMPLOYEE_SEARCH_HTTP_URL:/api/mock/http/smp_employee_search}")); - Path podMockResponse = root.resolve("dat-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); - long apiNameCount = Files.readAllLines(localConfig).stream() - .filter(line -> line.trim().equals("- name: employee-search")) - .count(); - assertEquals(1, apiNameCount); - } - @Test - void generatesSeparateToolTitleAndDescription() throws Exception { - String moduleName = root.resolve("dat-was-title").toString(); - - ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "직원 정보 조회", - "사번을 입력받아 재직 중인 직원의 기본 정보를 조회한다.", "smp", "HTTP", moduleName, - "tester", "2026.08.11", false, null, null, null, List.of(), List.of()); - - Path useCasePath = root.resolve("dat-was-title/src/main/java/io/shinhanlife/dat/mcc/biz/smp/usecase/EmployeeSearchUseCase.java"); - String useCase = Files.readString(useCasePath); - - assertTrue(useCase.contains("title = \"직원 정보 조회\""), useCase); - assertTrue(useCase.contains("description = \"사번을 입력받아 재직 중인 직원의 기본 정보를 조회한다.\""), useCase); - } - @Test - void generatesHttpToolUsingConfiguredApiNameAndConfiguredUrlOnly() throws Exception { - String moduleName = root.resolve("dat-was-http-api-name").toString(); - - ToolScaffolder.scaffold("employee search", "HR_EMPLOYEE_SEARCH", "직원 정보 조회", - "사번으로 직원을 조회한다.", "smp", "HTTP", moduleName, "tester", "2026.08.11", - false, null, null, null, List.of(), List.of(), "employee"); - - Path implementationPath = root.resolve("dat-was-http-api-name/src/main/java/io/shinhanlife/dat/mcc/biz/smp/usecase/impl/EmployeeSearchUseCaseImpl.java"); - String implementation = Files.readString(implementationPath); - - assertTrue(implementation.contains("import io.shinhanlife.dat.mcc.infra.itrf.http.employee.EmployeeClient;"), implementation); - assertTrue(implementation.contains("employeeClient.call(httpRequest, EmployeeSearchHttpResponse.class)"), implementation); - assertFalse(implementation.contains("AxhubHttpDomain"), implementation); - assertFalse(implementation.contains("\"/HR_EMPLOYEE_SEARCH\""), implementation); - } - - @Test - void addsHttpApiEntryOnItsOwnYamlLineBeforeMciConfiguration() throws Exception { - String moduleName = root.resolve("dat-was-http-yaml").toString(); - Path localConfig = root.resolve("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(" - name: insurance"), yaml); - assertTrue(yaml.indexOf(" - name: insurance") < yaml.indexOf(" mci:"), yaml); - } - - @Test - void generatesObjectListAsNestedInnerClassWithoutSeparateItemSource() throws Exception { - String moduleName = root.resolve("dat-was-inner-list").toString(); - List fields = List.of( - new ToolScaffolder.FieldDefinition("data", "List", "activity data", List.of(), "", false, - List.of(), "Object", List.of( - new ToolScaffolder.FieldDefinition("date", "String", "date", List.of("2026-08-13"), "", true), - new ToolScaffolder.FieldDefinition("users", "Integer", "users", List.of("10"), "", false)))); - - ToolScaffolder.scaffold("ga activity status", "GA001", "GA status", "GA status", "ana", "HTTP", - moduleName, "tester", "2026.08.13", false, null, null, null, List.of(), fields); - - Path dtoDir = root.resolve("dat-was-inner-list/src/main/java/io/shinhanlife/dat/mcc/biz/ana/dto"); - String response = Files.readString(dtoDir.resolve("GaActivityStatusResponse.java")); - assertTrue(response.contains("private List data;"), response); - assertTrue(response.contains("public static class DataItem"), response); - assertTrue(response.contains("private String date;"), response); - assertFalse(Files.exists(dtoDir.resolve("GaActivityStatusResponseDataItem.java"))); - } - - @Test - void groupedUseCaseSupportsHttpAndMciTools() throws Exception { - String moduleName = root.resolve("dat-was-mixed").toString(); - ToolScaffolder.scaffoldUseCase("Customer", moduleName, "tester", "2026.08.13", List.of( - new ToolScaffolder.ToolMethodDefinition("CustomerProfile", "getProfile", "CUST001", "Profile", "profile", "cmm", "MCI", - false, "CSTM", null, List.of(), List.of(), null), - new ToolScaffolder.ToolMethodDefinition("CustomerNotice", "getNotice", "NOTICE001", "Notice", "notice", "cmm", "HTTP", - false, null, "customer-notice", List.of(), List.of(), null))); - - Path sourceRoot = root.resolve("dat-was-mixed/src/main/java/io/shinhanlife/dat/mcc"); - String impl = Files.readString(sourceRoot.resolve("biz/cmm/usecase/impl/CustomerUseCaseImpl.java")); - assertTrue(impl.contains("getProfile(CustomerProfileRequest req)"), impl); - assertTrue(impl.contains("getNotice(CustomerNoticeRequest req)"), impl); - assertTrue(Files.exists(sourceRoot.resolve("infra/itrf/http/customer_notice/CustomerNoticeClient.java"))); - } - - @Test - void addsToolMethodToExistingUseCaseInsteadOfOverwritingIt() throws Exception { - String moduleName = root.resolve("dat-was-existing").toString(); - ToolScaffolder.scaffoldUseCase("Customer", moduleName, "tester", "2026.08.13", List.of( - new ToolScaffolder.ToolMethodDefinition("CustomerProfile", "getProfile", "CUST001", "Profile", "profile", "cmm", "MCI", - false, "CSTM", null, List.of(), List.of(), null))); - ToolScaffolder.scaffoldUseCase("Customer", moduleName, "tester", "2026.08.13", List.of( - new ToolScaffolder.ToolMethodDefinition("CustomerNotice", "getNotice", "NOTICE001", "Notice", "notice", "cmm", "HTTP", - false, null, "customer-notice", List.of(), List.of(), null))); - - Path sourceRoot = root.resolve("dat-was-existing/src/main/java/io/shinhanlife/dat/mcc"); - String useCase = Files.readString(sourceRoot.resolve("biz/cmm/usecase/CustomerUseCase.java")); - String impl = Files.readString(sourceRoot.resolve("biz/cmm/usecase/impl/CustomerUseCaseImpl.java")); - assertTrue(useCase.contains("getProfile(CustomerProfileRequest req)"), useCase); - assertTrue(useCase.contains("getNotice(CustomerNoticeRequest req)"), useCase); - assertTrue(impl.contains("private final MciCstmClient mciCstmClient;"), impl); - assertTrue(impl.contains("private final CustomerNoticeClient customerNoticeClient;"), impl); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolSchemaResolverTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolSchemaResolverTest.java deleted file mode 100644 index c44b43eb6..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolSchemaResolverTest.java +++ /dev/null @@ -1,126 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dat.lib.annotation.McpOutputSchema; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.lib.util.ToolSchemaResolver.ToolSchemaResourceException; -import io.swagger.v3.oas.annotations.media.Schema; -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.springaicommunity.mcp.annotation.McpTool; - -class ToolSchemaResolverTest { - - private final ToolSchemaResolver resolver = new ToolSchemaResolver(new ObjectMapper()); - - @Test - void generatesSchemaFromRequestDtoWhenNoExplicitSchemaIsConfigured() throws Exception { - Method method = AutomaticSchemaTool.class.getDeclaredMethod("search", AutomaticRequest.class); - - Map schema = resolver.resolve(method.getAnnotation(McpTool.class), null, AutomaticRequest.class); - - assertTrue(properties(schema).containsKey("differentField")); - } - - @Test - void generatesOutputSchemaFromMarkedResponseDto() throws Exception { - Method method = AutomaticOutputSchemaTool.class.getDeclaredMethod("search", AutomaticRequest.class); - - Map schema = resolver.resolveOutput( - method.getAnnotation(McpTool.class), SimpleResponse.class); - - assertEquals(List.of("resultCode"), schema.get("required")); - assertEquals(List.of("SUCCESS", "FAILURE"), property(schema, "resultCode").get("enum")); - } - - @Test - void doesNotEnableOutputValidationWhenOutputSchemaIsNotDeclared() throws Exception { - Method method = AutomaticSchemaTool.class.getDeclaredMethod("search", AutomaticRequest.class); - - Map schema = resolver.resolveOutput( - method.getAnnotation(McpTool.class), AutomaticRequest.class); - - assertTrue(schema.isEmpty()); - } - - @Test - void identifiesMalformedSchemaResourcesAsSchemaResourceFailures() throws Exception { - Method method = ResourceSchemaTool.class.getDeclaredMethod("search", AutomaticRequest.class); - - ToolSchemaResourceException exception = assertThrows(ToolSchemaResourceException.class, () -> - resolver.resolve(method.getAnnotation(McpTool.class), - method.getAnnotation(GrowToolHint.class), - AutomaticRequest.class)); - - assertInstanceOf(IOException.class, exception.getCause()); - } - - @Test - void identifiesMissingSchemaResourcesAsSchemaResourceFailures() throws Exception { - Method method = MissingResourceSchemaTool.class.getDeclaredMethod("search", AutomaticRequest.class); - - ToolSchemaResourceException exception = assertThrows(ToolSchemaResourceException.class, () -> - resolver.resolve(method.getAnnotation(McpTool.class), - method.getAnnotation(GrowToolHint.class), AutomaticRequest.class)); - - assertTrue(exception.getMessage().contains("missing-tool-schema.json")); - } - - @SuppressWarnings("unchecked") - private Map properties(Map schema) { - return (Map) schema.get("properties"); - } - - @SuppressWarnings("unchecked") - private Map property(Map schema, String name) { - return (Map) properties(schema).get(name); - } - - static class AutomaticSchemaTool { - @McpTool(name = "oth_test_automatic_search") - void search(AutomaticRequest request) { - } - } - - static class AutomaticOutputSchemaTool { - @McpTool(name = "oth_test_output_search") - SimpleResponse search(AutomaticRequest request) { - return null; - } - } - - static class ResourceSchemaTool { - @McpTool(name = "oth_test_resource_schema") - @GrowToolHint( - inputSchemaResource = "classpath:schemas/malformed-tool-schema.json") - void search(AutomaticRequest request) { - } - } - - static class MissingResourceSchemaTool { - @McpTool(name = "oth_test_missing_resource_schema") - @GrowToolHint(inputSchemaResource = "classpath:schemas/missing-tool-schema.json") - void search(AutomaticRequest request) { - } - } - - @McpOutputSchema - static class SimpleResponse { - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, allowableValues = {"SUCCESS", "FAILURE"}) - private String resultCode; - - private String message; - } - - static class AutomaticRequest { - private String differentField; - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolSourceUpdaterTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolSourceUpdaterTest.java deleted file mode 100644 index 9fc53510b..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/util/ToolSourceUpdaterTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.shinhanlife.dat.lib.util; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.nio.file.Files; -import java.nio.file.Path; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -class ToolSourceUpdaterTest { - - @TempDir - Path temporaryRoot; - - @Test - void updatesSdkAndProjectOwnedMetadataOnTheSameToolMethod() throws Exception { - Path source = temporaryRoot.resolve("dat-was-cus/src/main/java/example/SampleUseCase.java"); - Files.createDirectories(source.getParent()); - Files.writeString(source, """ - package example; - import org.springaicommunity.mcp.annotation.McpTool; - import io.shinhanlife.dat.lib.annotation.GrowToolHint; - interface SampleUseCase { - @McpTool(name = "cmm_sample_search", description = "old") - @GrowToolHint(requiresApproval = false) - void search(); - } - """); - - ToolSourceUpdater.updateToolSource(temporaryRoot, "cmm_sample_search", "customer", "new", true, true); - - String updated = Files.readString(source); - assertTrue(updated.contains("@McpTool(name = \"cmm_sample_search\", description = \"new\")")); - assertTrue(updated.contains("register = true"), updated); - assertTrue(updated.contains("requiresApproval = true"), updated); - assertTrue(updated.contains("categoryKey = \"customer\""), updated); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/validation/McpToolNameValidatorTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/validation/McpToolNameValidatorTest.java deleted file mode 100644 index ce93ec241..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/lib/validation/McpToolNameValidatorTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package io.shinhanlife.dat.lib.validation; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -/** - * @package io.shinhanlife.dat.lib.validation - * @className McpToolNameValidatorTest - * @description MCP tool name uniqueness validation test - * @author 0986406 - * @create 2026.07.27 - *
- * ---------- revision history ----------
- * date       author    description
- * ---------- --------- ---------------------------
- * 2026.07.27 0986406    initial creation
- * 
- */ -class McpToolNameValidatorTest { - - @TempDir - Path temporaryRoot; - - @Test - void rejectsDuplicateMcpToolNamesAcrossToolModules() throws IOException { - writeToolSource("dat-was-first", "FirstTool.java", "first", "oth_sms_notification_send"); - writeToolSource("dat-was-second", "SecondTool.java", "second", "oth_sms_notification_send"); - - IllegalStateException exception = assertThrows(IllegalStateException.class, - () -> McpToolNameValidator.assertUnique(temporaryRoot)); - - assertTrue(exception.getMessage().contains("oth_sms_notification_send")); - assertTrue(exception.getMessage().contains("dat-was-first")); - assertTrue(exception.getMessage().contains("dat-was-second")); - } - - @Test - void validationRunnerRejectsDuplicateMcpToolNamesBeforePackaging() throws IOException { - writeToolSource("dat-was-first", "FirstTool.java", "first", "oth_sms_notification_send"); - writeToolSource("dat-was-second", "SecondTool.java", "second", "oth_sms_notification_send"); - - assertThrows(IllegalStateException.class, - () -> McpToolNameValidationRunner.validate(temporaryRoot)); - } - - @Test - void rejectsToolNameOutsideConfiguredPattern() throws IOException { - writeToolSource("dat-was-first", "FirstTool.java", "first", "bond.issue"); - - IllegalStateException exception = assertThrows(IllegalStateException.class, - () -> McpToolNameValidator.assertUnique(temporaryRoot)); - - assertTrue(exception.getMessage().contains("Invalid MCP tool name(s)")); - assertTrue(exception.getMessage().contains("bond.issue")); - } - @Test - void acceptsLettersDigitsUnderscoresAndDashesWithin128Characters() throws IOException { - String validName = "Tool_Name-" + "a".repeat(118); - writeToolSource("dat-was-first", "FirstTool.java", "first", validName); - - assertDoesNotThrow(() -> McpToolNameValidator.assertUnique(temporaryRoot)); - } - - @Test - void rejectsToolNameLongerThan128Characters() throws IOException { - String invalidName = "a".repeat(129); - writeToolSource("dat-was-first", "FirstTool.java", "first", invalidName); - - IllegalStateException exception = assertThrows(IllegalStateException.class, - () -> McpToolNameValidator.assertUnique(temporaryRoot)); - - assertTrue(exception.getMessage().contains(invalidName)); - } - @Test - void acceptsCurrentProjectToolNames() { - assertDoesNotThrow(() -> McpToolNameValidator.assertUnique(findProjectRoot())); - } - - private void writeToolSource(String moduleName, String fileName, String className, String toolName) throws IOException { - Path source = temporaryRoot.resolve(moduleName).resolve("src/main/java/example").resolve(fileName); - Files.createDirectories(source.getParent()); - Files.writeString(source, """ - package example; - - import org.springaicommunity.mcp.annotation.McpTool; - - class %s { - @McpTool(name = "%s") - void execute() { } - } - """.formatted(className, toolName)); - } - - private Path findProjectRoot() { - Path current = Path.of("").toAbsolutePath(); - while (current != null && !Files.exists(current.resolve("settings.gradle"))) { - current = current.getParent(); - } - if (current == null) { - throw new IllegalStateException("Could not locate Gradle project root"); - } - return current; - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/mcp/McpRequestHeaderFilterTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/mcp/McpRequestHeaderFilterTest.java deleted file mode 100644 index 6e0512f38..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/mcp/McpRequestHeaderFilterTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.shinhanlife.dat.mcc.mcp; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNull; - -import io.shinhanlife.dat.lib.mcp.McpRequestHeaderContext; -import io.shinhanlife.dat.lib.mcp.McpRequestHeaderFilter; -import io.shinhanlife.dat.lib.mcp.McpRequestHeaders; -import java.lang.reflect.RecordComponent; -import java.util.LinkedHashMap; -import java.util.Map; -import org.junit.jupiter.api.Test; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; - -class McpRequestHeaderFilterTest { - - @Test - void capturesCanonicalToolHeadersOnlyForTheCurrentRequest() throws Exception { - MockHttpServletRequest request = new MockHttpServletRequest("POST", "/mcp"); - request.addHeader("X-Guid", "guid-001"); - request.addHeader("X-Praf-No", "praf-001"); - request.addHeader("X-Request-Id", "request-001"); - request.addHeader("X-Request-Time", "2026-08-25T12:34:56+09:00"); - request.addHeader("X-Vrtl-Praf-No", "virtual-001"); - request.addHeader("X-App-Code", "app-001"); - request.addHeader("X-Project-Code", "project-001"); - request.addHeader("X-User-Ip", "10.0.0.1"); - request.addHeader("X-Caller-Ip", "10.0.0.2"); - request.addHeader("X-Caller-Host", "caller.example.test"); - request.addHeader("X-Channel", "MCP"); - request.addHeader("X-Agent-Id", "agent-001"); - request.addHeader("mcp-session-id", "session-001"); - - new McpRequestHeaderFilter().doFilter(request, new MockHttpServletResponse(), (req, res) -> - assertThat(asMap(McpRequestHeaderContext.current())).containsExactly( - Map.entry("guid", "guid-001"), - Map.entry("prafNo", "praf-001"), - Map.entry("requestId", "request-001"), - Map.entry("requestTime", "2026-08-25T12:34:56+09:00"), - Map.entry("vrtlPrafNo", "virtual-001"), - Map.entry("appCode", "app-001"), - Map.entry("projectCode", "project-001"), - Map.entry("userIp", "10.0.0.1"), - Map.entry("callerIp", "10.0.0.2"), - Map.entry("callerHost", "caller.example.test"), - Map.entry("channel", "MCP"), - Map.entry("agentId", "agent-001"), - Map.entry("mcpSessionId", "session-001"))); - - assertNull(McpRequestHeaderContext.current()); - } - - private Map asMap(McpRequestHeaders headers) { - try { - Map values = new LinkedHashMap<>(); - for (RecordComponent component : McpRequestHeaders.class.getRecordComponents()) { - values.put(component.getName(), component.getAccessor().invoke(headers)); - } - return values; - } catch (ReflectiveOperationException exception) { - throw new AssertionError(exception); - } - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/mcp/ToolMcpServerConfigurationTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/mcp/ToolMcpServerConfigurationTest.java deleted file mode 100644 index 43fcfc455..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/mcp/ToolMcpServerConfigurationTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.shinhanlife.dat.mcc.mcp; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.assertj.core.api.Assertions.assertThat; - -import io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider; -import io.modelcontextprotocol.server.McpSyncServer; -import io.shinhanlife.dat.lib.mcp.ToolMcpServerConfiguration; -import org.junit.jupiter.api.Test; -import org.springframework.boot.web.servlet.ServletRegistrationBean; - -class ToolMcpServerConfigurationTest { - - private final ToolMcpServerConfiguration configuration = new ToolMcpServerConfiguration(); - - @Test - void exposesOnlyExactMcpEndpointSoLegacyMcpApiPathsRemainAvailable() { - HttpServletStreamableServerTransportProvider transport = configuration.toolMcpTransportProvider(); - ServletRegistrationBean registration = configuration.toolMcpServlet(transport); - - assertThat(registration.getUrlMappings()).containsExactlyInAnyOrder("/mcp", "/mcp/message"); - } - - @Test - void createsMcpSdkTwoSyncServerWithoutSpringAiAutoConfiguration() { - HttpServletStreamableServerTransportProvider transport = configuration.toolMcpTransportProvider(); - - McpSyncServer server = configuration.toolMcpServer(transport); - - assertEquals("dap-tool-pod", server.getServerInfo().name()); - server.close(); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/BusinessToolControllerHeaderContractTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/BusinessToolControllerHeaderContractTest.java deleted file mode 100644 index 8a1d75610..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/BusinessToolControllerHeaderContractTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.shinhanlife.dat.mcc.presentation; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.Test; -import org.springframework.web.bind.annotation.RequestHeader; - -class BusinessToolControllerHeaderContractTest { - - @Test - void receivesTheCanonicalToolHeadersAsOptionalValues() { - Method method = Arrays.stream(BusinessToolController.class.getDeclaredMethods()) - .filter(candidate -> candidate.getName().equals("executeDynamicTool")) - .findFirst() - .orElseThrow(); - - List headerNames = Arrays.stream(method.getParameters()) - .map(parameter -> parameter.getAnnotation(RequestHeader.class)) - .filter(java.util.Objects::nonNull) - .peek(header -> assertThat(header.required()).isFalse()) - .map(RequestHeader::value) - .toList(); - - assertThat(headerNames).containsExactly( - "X-Guid", - "X-Praf-No", - "X-Request-Id", - "X-Request-Time", - "X-Vrtl-Praf-No", - "X-App-Code", - "X-Project-Code", - "X-User-Ip", - "X-Caller-Ip", - "X-Caller-Host", - "X-Channel", - "X-Agent-Id", - "mcp-session-id"); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/ToolArgumentSchemaValidatorTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/ToolArgumentSchemaValidatorTest.java deleted file mode 100644 index 72bbadad5..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/ToolArgumentSchemaValidatorTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.shinhanlife.dat.mcc.presentation; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.json.schema.jackson2.DefaultJsonSchemaValidator; -import io.shinhanlife.dat.lib.validation.ToolArgumentSchemaValidator; -import java.util.List; -import java.util.Map; -import org.junit.jupiter.api.Test; - -class ToolArgumentSchemaValidatorTest { - - private final ToolArgumentSchemaValidator validator = - new ToolArgumentSchemaValidator(new ObjectMapper(), new DefaultJsonSchemaValidator()); - - @Test - void validatesJsonSchema202012WithTheMcpSdkValidator() { - Map schema = Map.of( - "type", "object", - "properties", Map.of("name", Map.of("type", "string")), - "required", List.of("name")); - - assertTrue(validator.validate(schema, Map.of("name", "Hong")).valid()); - assertFalse(validator.validate(schema, Map.of()).valid()); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/ToolTestConsoleResourceTest.java b/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/ToolTestConsoleResourceTest.java deleted file mode 100644 index 0d235f9ce..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/dat/mcc/presentation/ToolTestConsoleResourceTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.shinhanlife.dat.mcc.presentation; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import org.junit.jupiter.api.Test; - -class ToolTestConsoleResourceTest { - - @Test - void publishesManifestDrivenToolTestConsole() throws Exception { - try (InputStream resource = getClass().getResourceAsStream("/static/tool-test-console.html")) { - assertNotNull(resource); - String html = new String(resource.readAllBytes(), StandardCharsets.UTF_8); - - assertTrue(html.contains("/tool-manifest")); - assertTrue(html.contains("Run saved cases")); - assertTrue(html.contains("localStorage")); - assertTrue(html.contains("X-Guid")); - assertTrue(html.contains("X-Praf-No")); - assertTrue(html.contains("X-Request-Id")); - assertTrue(html.contains("X-Request-Time")); - assertTrue(html.contains("X-Vrtl-Praf-No")); - assertTrue(html.contains("X-App-Code")); - assertTrue(html.contains("X-Project-Code")); - assertTrue(html.contains("X-User-Ip")); - assertTrue(html.contains("X-Caller-Ip")); - assertTrue(html.contains("X-Caller-Host")); - assertTrue(html.contains("X-Channel")); - assertTrue(html.contains("X-Agent-Id")); - assertFalse(html.contains("'guid':")); - assertFalse(html.contains("'employee-no':")); - assertFalse(html.contains("'virtual-employee-no':")); - } - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/glow/communication/annotation/GlowTrgmFieldContractTest.java b/dat-was-lib/src/test/java/io/shinhanlife/glow/communication/annotation/GlowTrgmFieldContractTest.java deleted file mode 100644 index 3740a24ae..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/glow/communication/annotation/GlowTrgmFieldContractTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.shinhanlife.glow.communication.annotation; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.lang.reflect.Field; -import org.junit.jupiter.api.Test; - -class GlowTrgmFieldContractTest { - - private static class StandardTrgmDto { - @GlowTrgmField(order = 1, length = 8, decimal = 2, description = "금액", target = "body", type = "gs") - private String amount; - } - - @Test - void exposesTheStandardGlowTrgmFieldMetadata() throws Exception { - Field field = StandardTrgmDto.class.getDeclaredField("amount"); - GlowTrgmField metadata = field.getAnnotation(GlowTrgmField.class); - - assertEquals(1, metadata.order()); - assertEquals(8, metadata.length()); - assertEquals(2, metadata.decimal()); - assertEquals("금액", metadata.description()); - assertEquals("body", metadata.target()); - assertEquals("gs", metadata.type()); - } -} diff --git a/dat-was-lib/src/test/java/io/shinhanlife/glow/util/GlowMciParserTest.java b/dat-was-lib/src/test/java/io/shinhanlife/glow/util/GlowMciParserTest.java deleted file mode 100644 index 283a129a9..000000000 --- a/dat-was-lib/src/test/java/io/shinhanlife/glow/util/GlowMciParserTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.shinhanlife.glow.util; - - -/** - * @package io.shinhanlife.glow.util - * @className GlowMciParserTest - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -import io.shinhanlife.glow.GlowMciFieldInfo; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -public class GlowMciParserTest { - - public static class DummyMciDto { - @GlowMciFieldInfo(order = 1, length = 10) - private String customerId; - - @GlowMciFieldInfo(order = 2, length = 15) - private String name; - - @GlowMciFieldInfo(order = 3, length = 3) - private int age; - - public String getCustomerId() { return customerId; } - public String getName() { return name; } - public int getAge() { return age; } - } - - @Test - @DisplayName("고정 길이 MCI 문자열을 DTO로 파싱하는 테스트") - public void testParseFixedLengthString() { - // given: 고정 길이 텍스트 (총 28자리) - // ID(10) + Name(15) + Age(3) - String rawMciString = "CUST000001KIM SHINHAN 035"; - - // when - DummyMciDto result = GlowMciParser.parse(rawMciString, DummyMciDto.class); - - // then - System.out.println("=================================================="); - System.out.println(" [원본 MCI 전문] : [" + rawMciString + "]"); - System.out.println(" [파싱된 ID (10자리)] : [" + result.getCustomerId() + "]"); - System.out.println(" [파싱된 Name (15자리)] : [" + result.getName() + "]"); - System.out.println(" [파싱된 Age (3자리)] : [" + result.getAge() + "]"); - System.out.println("=================================================="); - - assertNotNull(result); - assertEquals("CUST000001", result.getCustomerId()); - assertEquals("KIM SHINHAN", result.getName()); - assertEquals(35, result.getAge()); - } -} diff --git a/dat-was-lib/src/test/resources/mock-responses/cmm_memo_retriever.json b/dat-was-lib/src/test/resources/mock-responses/cmm_memo_retriever.json deleted file mode 100644 index a949502b1..000000000 --- a/dat-was-lib/src/test/resources/mock-responses/cmm_memo_retriever.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "resultCode": "SUCCESS" -} diff --git a/dat-was-lib/src/test/resources/mock-responses/invalid_mock_response.json b/dat-was-lib/src/test/resources/mock-responses/invalid_mock_response.json deleted file mode 100644 index 828e21da4..000000000 --- a/dat-was-lib/src/test/resources/mock-responses/invalid_mock_response.json +++ /dev/null @@ -1 +0,0 @@ -{ invalid json diff --git a/dat-was-lib/src/test/resources/schemas/malformed-tool-schema.json b/dat-was-lib/src/test/resources/schemas/malformed-tool-schema.json deleted file mode 100644 index 72dc08b36..000000000 --- a/dat-was-lib/src/test/resources/schemas/malformed-tool-schema.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "object", - "properties": { -} diff --git a/dat-was-lib/src/test/resources/tool-definitions/cmm/cmm_claim_search.yml b/dat-was-lib/src/test/resources/tool-definitions/cmm/cmm_claim_search.yml deleted file mode 100644 index e3193c639..000000000 --- a/dat-was-lib/src/test/resources/tool-definitions/cmm/cmm_claim_search.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: cmm_claim_search -display_name: 보험금 청구 상태 조회 -version: 1.0.0 -category_key: cmm -description: - function: 청구번호 또는 계약번호로 보험금 청구 상태를 조회한다. - when_to_use: 사용자가 보험금 청구 진행 상태나 심사 결과를 확인하려는 경우 사용한다. - when_not_to_use: 보험금 청구를 새로 접수하거나 기존 청구를 변경하려는 경우에는 사용하지 않는다. - io_limits: 청구번호 또는 계약번호 중 하나 이상이 필요하며 조회 결과만 반환한다. -display_description: 보험금 청구 상태와 심사 결과를 조회합니다. -example_queries: - - 내 보험금 청구가 어디까지 진행됐는지 알려줘 - - 계약번호로 최근 청구 상태를 확인해줘 - - 청구 심사 결과가 나왔는지 조회해줘 -read_only: true -destructive: false -idempotent: true -parameters_schema: - type: object - properties: - claimNo: - type: string - description: 조회할 보험금 청구번호 - required: - - claimNo - additionalProperties: false -tags: - - 보험금 - - 청구조회 -owner_org: MCP_TOOL diff --git a/dat-was-pro/Dockerfile b/dat-was-pro/Dockerfile deleted file mode 100644 index 6b01bead2..000000000 --- a/dat-was-pro/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM eclipse-temurin:21-jre-alpine -WORKDIR /app -RUN apk add --no-cache tzdata -ENV TZ=Asia/Seoul -COPY dat-was-pro/build/libs/*-SNAPSHOT.jar app.jar -EXPOSE 8085 -ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/dat-was-pro/build.gradle b/dat-was-pro/build.gradle deleted file mode 100644 index e08421661..000000000 --- a/dat-was-pro/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - // Spring Boot 3.5.11 version is managed by the root build.gradle. - id 'org.springframework.boot' -} - -dependencies { - // Shared MCP, Glow integration, validation, logging, Lombok and MapStruct configuration. - implementation project(':dat-was-lib') -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/converter/IndividualCustomerDetailInquiryConverter.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/converter/IndividualCustomerDetailInquiryConverter.java deleted file mode 100644 index b6030e272..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/converter/IndividualCustomerDetailInquiryConverter.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.converter; - -import io.shinhanlife.dat.mcc.biz.pro.dto.IndividualCustomerDetailInquiryRequest; -import io.shinhanlife.dat.mcc.biz.pro.dto.IndividualCustomerDetailInquiryResponse; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c.io.ONCSC1340_I; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c.io.ONCSC1340_O; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.ReportingPolicy; - -@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface IndividualCustomerDetailInquiryConverter { - // Field names differ? Add mappings like this before the method. - // @Mapping(source = "sourceField", target = "targetField") - ONCSC1340_I toLegacyRequest(IndividualCustomerDetailInquiryRequest request); - IndividualCustomerDetailInquiryRequest toRequest(ONCSC1340_I mciRequest); - IndividualCustomerDetailInquiryResponse toResponse(ONCSC1340_O mciRes); -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/converter/RecruitmentFeeDisclosureDataConverter.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/converter/RecruitmentFeeDisclosureDataConverter.java deleted file mode 100644 index 2866e8598..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/converter/RecruitmentFeeDisclosureDataConverter.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.converter; - -import io.shinhanlife.dat.mcc.biz.pro.dto.RecruitmentFeeDisclosureDataRequest; -import io.shinhanlife.dat.mcc.biz.pro.dto.RecruitmentFeeDisclosureDataResponse; -import io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i.io.ONBTI2370_I; -import io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i.io.ONBTI2370_O; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.ReportingPolicy; - -@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface RecruitmentFeeDisclosureDataConverter { - // Field names differ? Add mappings like this before the method. - // @Mapping(source = "sourceField", target = "targetField") - ONBTI2370_I toLegacyRequest(RecruitmentFeeDisclosureDataRequest request); - RecruitmentFeeDisclosureDataRequest toRequest(ONBTI2370_I mciRequest); - RecruitmentFeeDisclosureDataResponse toResponse(ONBTI2370_O mciRes); -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/IndividualCustomerDetailInquiryRequest.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/IndividualCustomerDetailInquiryRequest.java deleted file mode 100644 index 4f29e9fcd..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/IndividualCustomerDetailInquiryRequest.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.dto; - -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import jakarta.validation.constraints.Pattern; - - -@Data -@JsonInclude(JsonInclude.Include.NON_NULL) -public class IndividualCustomerDetailInquiryRequest { - @Schema(description = "조회할 고객의 고유 ID (형식: ^[A-Za-z0-9]{1,20}$) (예시: 12345, CUST00001)", example = "12345", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[A-Za-z0-9]{1,20}$") - private String customerId; - - @Schema(description = "고객의 이름 (선택적 검색 조건) (형식: ^[가-힣a-zA-Z\s]{2,50}$) (예시: 김철수, 이영희)", example = "김철수") - @Pattern(regexp = "^[가-힣a-zA-Z\s]{2,50}$") - private String name; - - @Schema(description = "고객의 연락처 번호 (선택적 검색 조건) (형식: {10,11}$) (예시: 010-1234-5678, 01012345678)", example = "010-1234-5678") - private String phoneNumber; - - @Schema(description = "고객의 이메일 주소 (선택적 검색 조건) (형식: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$) (예시: test@example.com, user@test.co.kr)", example = "test@example.com") - private String email; - - @Schema(description = "고객의 생년월일 (선택적 검색 조건, YYYY-MM-DD 형식) (형식: {2}$) (예시: 1990-01-15, 1985-12-31)", example = "1990-01-15") - private String birthDate; - -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/IndividualCustomerDetailInquiryResponse.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/IndividualCustomerDetailInquiryResponse.java deleted file mode 100644 index d41f52379..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/IndividualCustomerDetailInquiryResponse.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.dto; - -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import jakarta.validation.constraints.Pattern; - - -@Data -@JsonInclude(JsonInclude.Include.NON_NULL) -public class IndividualCustomerDetailInquiryResponse { - private String resultCode; - - private String resultMessage; - @Schema(description = "고객의 고유 ID (예시: 12345, CUST00001)", example = "12345", requiredMode = Schema.RequiredMode.REQUIRED) - private String customerId; - - @Schema(description = "고객의 전체 이름 (예시: 김철수, 이영희)", example = "김철수", requiredMode = Schema.RequiredMode.REQUIRED) - private String name; - - @Schema(description = "고객의 연락처 번호 (예시: 010-1234-5678, 01012345678)", example = "010-1234-5678", requiredMode = Schema.RequiredMode.REQUIRED) - private String phoneNumber; - - @Schema(description = "고객의 이메일 주소 (예시: test@example.com, user@test.co.kr)", example = "test@example.com", requiredMode = Schema.RequiredMode.REQUIRED) - private String email; - - @Schema(description = "고객의 주소 정보 (예시: 서울시 강남구 테헤란로 123, 부산시 해운대구 해운대해변로 456)", example = "서울시 강남구 테헤란로 123", requiredMode = Schema.RequiredMode.REQUIRED) - private String address; - - @Schema(description = "고객 가입일 (형식: $) (예시: 2023-01-15, 2022-11-30)", example = "2023-01-15", requiredMode = Schema.RequiredMode.REQUIRED) - private String registrationDate; - - @Schema(description = "고객 등급 정보 (예시: VIP, GOLD, SILVER, BRONZE)", example = "VIP") - private String grade; - -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/ProductToolRequest.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/ProductToolRequest.java deleted file mode 100644 index 15218e1bc..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/ProductToolRequest.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.dto; - -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class ProductToolRequest { - @McpToolParam(description = "고객번호 또는 계약번호", required = false) - private String subjectId; - - @McpToolParam(description = "조회 기준일(YYYY-MM-DD)", required = false) - private String queryDate; - - @McpToolParam(description = "조회 상세 수준", required = false) - private String detailLevel; -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/ProductToolResponse.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/ProductToolResponse.java deleted file mode 100644 index a6499f4de..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/ProductToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class ProductToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String asOfDate; - private List details; -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/RecruitmentFeeDisclosureDataRequest.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/RecruitmentFeeDisclosureDataRequest.java deleted file mode 100644 index f445b1098..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/RecruitmentFeeDisclosureDataRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.dto; - -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import jakarta.validation.constraints.Pattern; - - -@Data -@JsonInclude(JsonInclude.Include.NON_NULL) -public class RecruitmentFeeDisclosureDataRequest { - @Schema(description = "조회할 회사의 코드 (형식: ^[0-9]{6}$) (예시: 005930, 035420)", example = "005930", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[0-9]{6}$") - private String companyCode; - - @Schema(description = "조회할 연도 (형식: ^[0-9]{4}$) (예시: 2023, 2024)", example = "2023", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[0-9]{4}$") - private String year; - - @Schema(description = "조회할 분기 (1-4) (형식: ^[1-4]$) (예시: 1, 2, 3, 4)", example = "1") - @Pattern(regexp = "^[1-4]$") - private String quarter; - - @Schema(description = "조회 시작일 (YYYY-MM-DD) (형식: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$) (예시: 2024-01-01, 2024-04-01)", example = "2024-01-01") - @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$") - private String startDate; - - @Schema(description = "조회 종료일 (YYYY-MM-DD) (형식: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$) (예시: 2024-03-31, 2024-06-30)", example = "2024-03-31") - @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$") - private String endDate; - -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/RecruitmentFeeDisclosureDataResponse.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/RecruitmentFeeDisclosureDataResponse.java deleted file mode 100644 index f625bf841..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/dto/RecruitmentFeeDisclosureDataResponse.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.dto; - -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import java.util.List; -import jakarta.validation.constraints.Pattern; - - -@Data -@JsonInclude(JsonInclude.Include.NON_NULL) -public class RecruitmentFeeDisclosureDataResponse { - private String resultCode; - - private String resultMessage; - @Schema(description = "모집수수료 공시 데이터 목록", requiredMode = Schema.RequiredMode.REQUIRED) - private List data; - - @Schema(description = "조회된 총 데이터 건수 (형식: ^[0-9]+$) (예시: 10, 25)", example = "10", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[0-9]+$") - private Integer totalCount; - - @Schema(description = "처리 결과 메시지 (예시: 조회 성공, 데이터가 없습니다)", example = "조회 성공", requiredMode = Schema.RequiredMode.REQUIRED) - private String message; - - - @Data - public static class DataItem { - @Schema(description = "회사명 (예시: 삼성전자, 카카오)", example = "삼성전자", requiredMode = Schema.RequiredMode.REQUIRED) - private String companyName; - - @Schema(description = "공시일 (형식: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$) (예시: 2024-01-15, 2024-04-20)", example = "2024-01-15", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$") - private String disclosureDate; - - @Schema(description = "공시 유형 (예시: 신규공시, 정정공시)", example = "신규공시", requiredMode = Schema.RequiredMode.REQUIRED) - private String disclosureType; - - } -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/IndividualCustomerDetailInquiryUseCase.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/IndividualCustomerDetailInquiryUseCase.java deleted file mode 100644 index cd93489bf..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/IndividualCustomerDetailInquiryUseCase.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.usecase; - -import org.springaicommunity.mcp.annotation.McpTool; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.pro.dto.IndividualCustomerDetailInquiryRequest; -import io.shinhanlife.dat.mcc.biz.pro.dto.IndividualCustomerDetailInquiryResponse; - -/** - * @package io.shinhanlife.dat.mcc.biz.pro.usecase - * @className IndividualCustomerDetailInquiryUseCase - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.25 - *
- * ---------- 개정이력 ----------
- * 수정일       수정자     수정내용
- * ---------- -------- ---------------------------
- * 2026.08.25  jade    최초생성
- *
- * 
- */ -public interface IndividualCustomerDetailInquiryUseCase { - - @McpTool(name = "pro_individual_inquiry", title = "개인고객 상세조회", description = "개인고객의 상세 정보를 조회하는 도구로, 기본 정보부터 연락처, 주소까지 모든 고객 데이터를 제공합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pro", - mappingId = "DATNCSO00001", - functionDescription = "등록된 개인고객의 상세 정보를 조회하여 고객 관리 및 서비스 제공에 필요한 모든 데이터를 반환합니다.", - whenToUse = "특정 고객의 상세 정보가 필요한 경우, 예를 들어 고객 지원, 마케팅, 또는 서비스 제공 시 사용됩니다.", - whenNotToUse = "집계된 통계 데이터만 필요한 경우, 개별 고객 정보 조회는 적합하지 않습니다.", - ioLimits = "최대 100명의 고객 정보를 한 번에 조회할 수 있으며, 각 고객 정보는 최대 5KB까지 지원됩니다.", - displayDescription = "개인고객의 모든 상세 정보를 한눈에 확인할 수 있는 조회 도구입니다.", - exampleQueries = {"고객 ID 12345에 대한 상세 정보를 조회해주세요.", "김철수 고객의 연락처 정보를 알려주세요.", "주민등록번호 800101-123456으로 등록된 고객의 주소를 조회해주세요.", "최근 가입한 고객 10명의 정보를 조회해주세요.", "이메일 주소가 test@example.com인 고객의 상세 정보를 찾아주세요.", "전화번호가 010-1234-5678인 고객의 정보를 조회해주세요.", "2023년에 가입한 고객의 상세 정보를 조회해주세요.", "주소가 서울인 고객의 정보를 조회해주세요.", "VIP 등급의 고객 정보를 조회해주세요.", "최근 30일 이내에 활동한 고객의 상세 정보를 조회해주세요.", "고객 ID 99999에 대한 모든 정보를 조회해주세요.", "고객 이름 '이영희'로 검색해주세요.", "마지막 활동일이 지난 고객의 정보를 조회해주세요.", "마케팅 동의한 고객의 연락처 정보를 조회해주세요.", "신규 가입 고객의 기본 정보를 조회해주세요."}, - destructive = false, - idempotent = true, - tags = {"customer", "inquiry"}, - ownerOrg = "MCP_TOOL" - ) - IndividualCustomerDetailInquiryResponse execute(IndividualCustomerDetailInquiryRequest req); -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/RecruitmentFeeDisclosureDataUseCase.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/RecruitmentFeeDisclosureDataUseCase.java deleted file mode 100644 index 505c5ec98..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/RecruitmentFeeDisclosureDataUseCase.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.usecase; - -import org.springaicommunity.mcp.annotation.McpTool; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.pro.dto.RecruitmentFeeDisclosureDataRequest; -import io.shinhanlife.dat.mcc.biz.pro.dto.RecruitmentFeeDisclosureDataResponse; - -/** - * @package io.shinhanlife.dat.mcc.biz.pro.usecase - * @className RecruitmentFeeDisclosureDataUseCase - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.25 - *
- * ---------- 개정이력 ----------
- * 수정일       수정자     수정내용
- * ---------- -------- ---------------------------
- * 2026.08.25  jade    최초생성
- *
- * 
- */ -public interface RecruitmentFeeDisclosureDataUseCase { - - @McpTool(name = "pro_recruitment_data", title = "모집수수료 공시", description = "금융감독원 공시 시스템에서 모집수수료 관련 공시 정보를 조회하고 관리하는 도구입니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pro", - mappingId = "DATNBTO00001", - functionDescription = "모집수수료 공시 데이터를 조회하고 관리하는 핵심 비즈니스 함수입니다.", - whenToUse = "모집수수료 공시 정보, 금융감독원 공시 데이터, 수수료 공시 내역이 필요한 경우", - whenNotToUse = "모집수수료와 무관한 다른 유형의 금융 데이터, 비공시 정보, 내부 데이터가 필요한 경우", - ioLimits = "연도별, 분기별, 기간별 모집수수료 공시 데이터를 조회할 수 있으며, 최대 1000건의 결과를 반환합니다.", - displayDescription = "모집수수료 공시 정보를 간편하게 조회할 수 있는 포털 서비스입니다.", - exampleQueries = {"2023년 모집수수료 공시 데이터 조회", "상반기 모집수수료 공시 정보 요청", "특정 회사의 2024년 3분기 모집수수료 공시", "모집수수료 공시 기간별 검색"}, - destructive = false, - idempotent = true, - tags = {"finance", "disclosure"}, - ownerOrg = "MCP_TOOL" - ) - RecruitmentFeeDisclosureDataResponse execute(RecruitmentFeeDisclosureDataRequest req); -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/impl/IndividualCustomerDetailInquiryUseCaseImpl.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/impl/IndividualCustomerDetailInquiryUseCaseImpl.java deleted file mode 100644 index dc07f8965..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/impl/IndividualCustomerDetailInquiryUseCaseImpl.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.pro.dto.IndividualCustomerDetailInquiryRequest; -import io.shinhanlife.dat.mcc.biz.pro.dto.IndividualCustomerDetailInquiryResponse; -import io.shinhanlife.dat.mcc.biz.pro.usecase.IndividualCustomerDetailInquiryUseCase; -import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent; -import io.shinhanlife.glow.communication.dto.Transfer; -import org.springframework.stereotype.Service; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import io.shinhanlife.dat.mcc.biz.pro.converter.IndividualCustomerDetailInquiryConverter; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c.io.ONCSC1340_I; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c.io.ONCSC1340_O; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c.MciNcscClient; - - -/** - * @package io.shinhanlife.dat.mcc.biz.pro.usecase.impl - * @className IndividualCustomerDetailInquiryUseCaseImpl - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.25 - *
- * ---------- 개정이력 ----------
- * 수정일       수정자     수정내용
- * ---------- -------- ---------------------------
- * 2026.08.25  jade    최초생성
- *
- * 
- */ -@Slf4j -@Service -@RequiredArgsConstructor -public class IndividualCustomerDetailInquiryUseCaseImpl implements IndividualCustomerDetailInquiryUseCase { - - private final MciNcscClient mci; - private final IndividualCustomerDetailInquiryConverter converter; - - @Override - public IndividualCustomerDetailInquiryResponse execute(IndividualCustomerDetailInquiryRequest req) { - log.info("[MCI Tool] {} 요청 수신.", "pro_individual_inquiry"); - try { - // MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO) - ONCSC1340_I mciReq = converter.toLegacyRequest(req); - - Transfer resTransfer = mci.callTo( - "DATNCSO00001", - null, - mciReq, - ONCSC1340_O.class - ); - IndividualCustomerDetailInquiryResponse response = new IndividualCustomerDetailInquiryResponse(); - if (resTransfer.getBody() != null) { - response = converter.toResponse(resTransfer.getBody()); - } - response.setResultCode("SUCCESS"); - response.setResultMessage(resTransfer.getBody() != null - ? "MCI call completed." - : "MCI call completed without a response body."); - return response; - } catch (Exception e) { - log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e); - IndividualCustomerDetailInquiryResponse response = new IndividualCustomerDetailInquiryResponse(); - response.setResultCode("ERROR"); - response.setResultMessage(e.getMessage() != null ? e.getMessage() : "Unknown error"); - return response; - } - } -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/impl/RecruitmentFeeDisclosureDataUseCaseImpl.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/impl/RecruitmentFeeDisclosureDataUseCaseImpl.java deleted file mode 100644 index 6e05b6fb2..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/biz/pro/usecase/impl/RecruitmentFeeDisclosureDataUseCaseImpl.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.pro.dto.RecruitmentFeeDisclosureDataRequest; -import io.shinhanlife.dat.mcc.biz.pro.dto.RecruitmentFeeDisclosureDataResponse; -import io.shinhanlife.dat.mcc.biz.pro.usecase.RecruitmentFeeDisclosureDataUseCase; -import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent; -import io.shinhanlife.glow.communication.dto.Transfer; -import org.springframework.stereotype.Service; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import io.shinhanlife.dat.mcc.biz.pro.converter.RecruitmentFeeDisclosureDataConverter; -import io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i.io.ONBTI2370_I; -import io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i.io.ONBTI2370_O; -import io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i.MciNbtiClient; - - -/** - * @package io.shinhanlife.dat.mcc.biz.pro.usecase.impl - * @className RecruitmentFeeDisclosureDataUseCaseImpl - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.25 - *
- * ---------- 개정이력 ----------
- * 수정일       수정자     수정내용
- * ---------- -------- ---------------------------
- * 2026.08.25  jade    최초생성
- *
- * 
- */ -@Slf4j -@Service -@RequiredArgsConstructor -public class RecruitmentFeeDisclosureDataUseCaseImpl implements RecruitmentFeeDisclosureDataUseCase { - - private final MciNbtiClient mci; - private final RecruitmentFeeDisclosureDataConverter converter; - - @Override - public RecruitmentFeeDisclosureDataResponse execute(RecruitmentFeeDisclosureDataRequest req) { - log.info("[MCI Tool] {} 요청 수신.", "pro_recruitment_data"); - try { - // MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO) - ONBTI2370_I mciReq = converter.toLegacyRequest(req); - - Transfer resTransfer = mci.callTo( - "DATNBTO00001", - null, - mciReq, - ONBTI2370_O.class - ); - RecruitmentFeeDisclosureDataResponse response = new RecruitmentFeeDisclosureDataResponse(); - if (resTransfer.getBody() != null) { - response = converter.toResponse(resTransfer.getBody()); - } - response.setResultCode("SUCCESS"); - response.setResultMessage(resTransfer.getBody() != null - ? "MCI call completed." - : "MCI call completed without a response body."); - return response; - } catch (Exception e) { - log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e); - RecruitmentFeeDisclosureDataResponse response = new RecruitmentFeeDisclosureDataResponse(); - response.setResultCode("ERROR"); - response.setResultMessage(e.getMessage() != null ? e.getMessage() : "Unknown error"); - return response; - } - } -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/MciNbtiClient.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/MciNbtiClient.java deleted file mode 100644 index 1d96f91d5..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/MciNbtiClient.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i; - -import org.springframework.stereotype.Component; -import lombok.RequiredArgsConstructor; -import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent; -import io.shinhanlife.glow.communication.dto.Transfer; - -/** - * @package io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i - * @className MciNbtiClient - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.25 - *
- * ---------- 개정이력 ----------
- * 수정일       수정자     수정내용
- * ---------- -------- ---------------------------
- * 2026.08.25  jade    최초생성
- *
- * 
- */ -@Component -@RequiredArgsConstructor -public class MciNbtiClient { - private final AxhubMciComponent mci; - - public Transfer callTo(String interfaceId, String dummy, Object mciReq, Class resType) throws Exception { - return mci.callTo(interfaceId, dummy, mciReq, resType); - } -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/io/ONBTI2370_I.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/io/ONBTI2370_I.java deleted file mode 100644 index 8f9446596..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/io/ONBTI2370_I.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i.io; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import jakarta.validation.constraints.Pattern; - - -@Data -public class ONBTI2370_I { - @Schema(description = "조회할 회사의 코드 (형식: ^[0-9]{6}$) (예시: 005930, 035420)", example = "005930", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[0-9]{6}$") - private String companyCode; - - @Schema(description = "조회할 연도 (형식: ^[0-9]{4}$) (예시: 2023, 2024)", example = "2023", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[0-9]{4}$") - private String year; - - @Schema(description = "조회할 분기 (1-4) (형식: ^[1-4]$) (예시: 1, 2, 3, 4)", example = "1") - @Pattern(regexp = "^[1-4]$") - private String quarter; - - @Schema(description = "조회 시작일 (YYYY-MM-DD) (형식: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$) (예시: 2024-01-01, 2024-04-01)", example = "2024-01-01") - @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$") - private String startDate; - - @Schema(description = "조회 종료일 (YYYY-MM-DD) (형식: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$) (예시: 2024-03-31, 2024-06-30)", example = "2024-03-31") - @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$") - private String endDate; - -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/io/ONBTI2370_O.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/io/ONBTI2370_O.java deleted file mode 100644 index 66c3a705d..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/nbt/i/io/ONBTI2370_O.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.nbt.i.io; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import java.util.List; -import jakarta.validation.constraints.Pattern; - - -@Data -public class ONBTI2370_O { - @Schema(description = "결과 코드 (예시: SUCCESS, ERROR)", example = "SUCCESS", requiredMode = Schema.RequiredMode.REQUIRED) - private String resultCode; - - @Schema(description = "모집수수료 공시 데이터 목록", requiredMode = Schema.RequiredMode.REQUIRED) - private List data; - - @Schema(description = "조회된 총 데이터 건수 (형식: ^[0-9]+$) (예시: 10, 25)", example = "10", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[0-9]+$") - private Integer totalCount; - - @Schema(description = "처리 결과 메시지 (예시: 조회 성공, 데이터가 없습니다)", example = "조회 성공", requiredMode = Schema.RequiredMode.REQUIRED) - private String message; - - - @Data - public static class DataItem { - @Schema(description = "회사명 (예시: 삼성전자, 카카오)", example = "삼성전자", requiredMode = Schema.RequiredMode.REQUIRED) - private String companyName; - - @Schema(description = "공시일 (형식: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$) (예시: 2024-01-15, 2024-04-20)", example = "2024-01-15", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$") - private String disclosureDate; - - - @Schema(description = "공시 유형 (예시: 신규공시, 정정공시)", example = "신규공시", requiredMode = Schema.RequiredMode.REQUIRED) - private String disclosureType; - - } -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/MciNcscClient.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/MciNcscClient.java deleted file mode 100644 index b1a142132..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/MciNcscClient.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c; - -import org.springframework.stereotype.Component; -import lombok.RequiredArgsConstructor; -import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent; -import io.shinhanlife.glow.communication.dto.Transfer; - -/** - * @package io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c - * @className MciNcscClient - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.25 - *
- * ---------- 개정이력 ----------
- * 수정일       수정자     수정내용
- * ---------- -------- ---------------------------
- * 2026.08.25  jade    최초생성
- *
- * 
- */ -@Component -@RequiredArgsConstructor -public class MciNcscClient { - private final AxhubMciComponent mci; - - public Transfer callTo(String interfaceId, String dummy, Object mciReq, Class resType) throws Exception { - return mci.callTo(interfaceId, dummy, mciReq, resType); - } -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/io/ONCSC1340_I.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/io/ONCSC1340_I.java deleted file mode 100644 index 156ff19ab..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/io/ONCSC1340_I.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c.io; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import jakarta.validation.constraints.Pattern; - - -@Data -public class ONCSC1340_I { - @Schema(description = "조회할 고객의 고유 ID (형식: ^[A-Za-z0-9]{1,20}$) (예시: 12345, CUST00001)", example = "12345", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^[A-Za-z0-9]{1,20}") - private String customerId; - - @Schema(description = "고객의 이름 (선택적 검색 조건) (형식: ^[가-힣a-zA-Z\s]{2,50}$) (예시: 김철수, 이영희)", example = "김철수") - @Pattern(regexp = "^[가-힣a-zA-Zs]{2,50}") - private String name; - - @Schema(description = "고객의 연락처 번호 (선택적 검색 조건) (형식: 10,11}$) (예시: 010-1234-5678, 01012345678)", example = "010-1234-5678") - private String phoneNumber; - - @Schema(description = "고객의 이메일 주소 (선택적 검색 조건) (형식: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$) (예시: test@example.com, user@test.co.kr)", example = "test@example.com") - @Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}") - private String email; - - @Schema(description = "고객의 생년월일 (선택적 검색 조건, YYYY-MM-DD 형식) (형식: ^d{4}-d{2}-d{2}$) (예시: 1990-01-15, 1985-12-31)", example = "1990-01-15") - @Pattern(regexp = "d{4}-d{2}-d{2}") - private String birthDate; - -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/io/ONCSC1340_O.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/io/ONCSC1340_O.java deleted file mode 100644 index 35d2ab9fe..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncs/c/io/ONCSC1340_O.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.ncs.c.io; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import jakarta.validation.constraints.Pattern; - - -@Data -public class ONCSC1340_O { - @Schema(description = "조회 결과 코드 (예시: SUCCESS, ERROR_001)", example = "SUCCESS", requiredMode = Schema.RequiredMode.REQUIRED) - private String resultCode; - - @Schema(description = "고객의 고유 ID (예시: 12345, CUST00001)", example = "12345", requiredMode = Schema.RequiredMode.REQUIRED) - private String customerId; - - @Schema(description = "고객의 전체 이름 (예시: 김철수, 이영희)", example = "김철수", requiredMode = Schema.RequiredMode.REQUIRED) - private String name; - - @Schema(description = "고객의 연락처 번호 (예시: 010-1234-5678, 01012345678)", example = "010-1234-5678", requiredMode = Schema.RequiredMode.REQUIRED) - private String phoneNumber; - - @Schema(description = "고객의 이메일 주소 (예시: test@example.com, user@test.co.kr)", example = "test@example.com", requiredMode = Schema.RequiredMode.REQUIRED) - private String email; - - @Schema(description = "고객의 주소 정보 (예시: 서울시 강남구 테헤란로 123, 부산시 해운대구 해운대해변로 456)", example = "서울시 강남구 테헤란로 123", requiredMode = Schema.RequiredMode.REQUIRED) - private String address; - - @Schema(description = "고객 가입일 (형식: ^d{4}-d{2}-d{2}$) (예시: 2023-01-15, 2022-11-30)", example = "2023-01-15", requiredMode = Schema.RequiredMode.REQUIRED) - @Pattern(regexp = "^d{4}-d{2}-d{2}$") - private String registrationDate; - - @Schema(description = "고객 등급 정보 (예시: VIP, GOLD, SILVER, BRONZE)", example = "VIP") - private String grade; - -} diff --git a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/pro/DatWasProApplication.java b/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/pro/DatWasProApplication.java deleted file mode 100644 index 99246b8ed..000000000 --- a/dat-was-pro/src/main/java/io/shinhanlife/dat/mcc/pro/DatWasProApplication.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.shinhanlife.dat.mcc.pro; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationPropertiesScan; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Import; -import io.shinhanlife.dat.lib.mcp.ToolMcpServerConfiguration; - -/** - * @package io.shinhanlife.dat.mcc.pro - * @className DatWasProApplication - * @description AX HUB 시스템 처리 클래스 - * @author system - * @create 2026.08.13 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.08.13  system    최초생성
- *
- * 
- */ -@SpringBootApplication(scanBasePackages = {"io.shinhanlife.dat.mcc", "io.shinhanlife.dat.lib"}) -@ConfigurationPropertiesScan(basePackages = {"io.shinhanlife.dat.mcc", "io.shinhanlife.dat.lib"}) -@EnableCaching -@Import(ToolMcpServerConfiguration.class) -public class DatWasProApplication { - public static void main(String[] args) { - SpringApplication.run(DatWasProApplication.class, args); - } -} diff --git a/dat-was-pro/src/main/resources/application-dev.yml b/dat-was-pro/src/main/resources/application-dev.yml deleted file mode 100644 index 44d61de2b..000000000 --- a/dat-was-pro/src/main/resources/application-dev.yml +++ /dev/null @@ -1,46 +0,0 @@ -# OCI 클라우드 환경 전용 설정 -server: - port: ${PORT:8085} - -spring: - config: - activate: - on-profile: dev - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-dev.yml - -axhub: - gateway: - url: https://axhubmcp.devjun.net - tool: - url: http://144.24.70.100:8085 - -eims: - http: - url: http://localhost:${server.port}/api/gateway - tcp: - host: 127.0.0.1 - port: 8090 - timeout: 5000 - jsp: - form: - url: http://localhost:${server.port}/mock/jsp-form - json: - url: http://localhost:${server.port}/mock/jsp-json - mci: - url: http://localhost:${server.port}/api/mock/esb/api - mcistring: - url: http://localhost:${server.port}/api/mock/esb/string - -shinhan: - integration: - envrTypeCd: D - eai: - url: http://10.176.32.181 - internalMci: - url: http://10.176.32.173 - bancaMci: - url: http://10.176.32.117 - externalMci: - url: http://10.176.32.176 diff --git a/dat-was-pro/src/main/resources/application-local.yml b/dat-was-pro/src/main/resources/application-local.yml deleted file mode 100644 index 6f71fa089..000000000 --- a/dat-was-pro/src/main/resources/application-local.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Local 환경 전용 설정 (H2 메모리 DB 등) -spring: - config: - activate: - on-profile: local - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-local.yml - -axhub: - tool: - url: ${AXHUB_TOOL_URL:http://localhost:${server.port}} diff --git a/dat-was-pro/src/main/resources/application-prod.yml b/dat-was-pro/src/main/resources/application-prod.yml deleted file mode 100644 index 682467fd7..000000000 --- a/dat-was-pro/src/main/resources/application-prod.yml +++ /dev/null @@ -1,16 +0,0 @@ -server: - port: ${PORT:8085} - -spring: - config: - activate: - on-profile: prod - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-prod.yml - -axhub: - gateway: - url: ${AXHUB_GATEWAY_URL} - tool: - url: ${AXHUB_TOOL_URL} diff --git a/dat-was-pro/src/main/resources/application-test.yml b/dat-was-pro/src/main/resources/application-test.yml deleted file mode 100644 index e2bc327dd..000000000 --- a/dat-was-pro/src/main/resources/application-test.yml +++ /dev/null @@ -1,16 +0,0 @@ -server: - port: ${PORT:8085} - -spring: - config: - activate: - on-profile: test - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-test.yml - -axhub: - gateway: - url: ${AXHUB_GATEWAY_URL} - tool: - url: ${AXHUB_TOOL_URL} diff --git a/dat-was-pro/src/main/resources/application.yml b/dat-was-pro/src/main/resources/application.yml deleted file mode 100644 index ba11656cb..000000000 --- a/dat-was-pro/src/main/resources/application.yml +++ /dev/null @@ -1,21 +0,0 @@ -server: - port: 8085 -spring: - application: - name: dat-was-pro - config: - import: optional:classpath:tool-service-manifest.yml - profiles: - active: local -logging: - level: - org.apache.kafka: ERROR -mcp: - namespace: "" - manifest: - bundle-id: was-pro - name-prefix: "" - security: - api-key: ${TOOL_SERVER_API_KEY:tool-server-key} - tenant-domains: - TESTER-DEV: ALL diff --git a/dat-was-pro/src/main/resources/logback-spring.xml b/dat-was-pro/src/main/resources/logback-spring.xml deleted file mode 100644 index f3f897dfa..000000000 --- a/dat-was-pro/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - ${LOG_PATTERN} - - - - logs/dat-was-pro.log - - logs/dat-was-pro-%d{yyyy-MM-dd}.log - 30 - - - ${LOG_PATTERN} - - - - - - - - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_activity_status.json b/dat-was-pro/src/main/resources/mock-responses/agt_activity_status.json deleted file mode 100644 index 7b8ce2854..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_activity_status.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_activity_status\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 활동상태 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 활동상태 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_affiliation_history.json b/dat-was-pro/src/main/resources/mock-responses/agt_affiliation_history.json deleted file mode 100644 index 4b573fb5c..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_affiliation_history.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_affiliation_history\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 소속변경 이력 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 소속변경 이력 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_agent_compliance.json b/dat-was-pro/src/main/resources/mock-responses/agt_agent_compliance.json deleted file mode 100644 index a0d8b3c52..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_agent_compliance.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_agent_compliance\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 준법정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 준법정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_agent_education.json b/dat-was-pro/src/main/resources/mock-responses/agt_agent_education.json deleted file mode 100644 index 394e47a98..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_agent_education.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_agent_education\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 교육이력 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 교육이력 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_agent_license.json b/dat-was-pro/src/main/resources/mock-responses/agt_agent_license.json deleted file mode 100644 index 64c55e405..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_agent_license.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_agent_license\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 자격정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 자격정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_agent_profile.json b/dat-was-pro/src/main/resources/mock-responses/agt_agent_profile.json deleted file mode 100644 index 904f04fc8..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_agent_profile.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_agent_profile\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 기본정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 기본정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_appointment_status.json b/dat-was-pro/src/main/resources/mock-responses/agt_appointment_status.json deleted file mode 100644 index df1d06cee..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_appointment_status.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_appointment_status\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 위촉상태 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 위촉상태 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_branch_list.json b/dat-was-pro/src/main/resources/mock-responses/agt_branch_list.json deleted file mode 100644 index 1dc63fc4f..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_branch_list.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_branch_list\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 지점목록 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 지점목록 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_campaign.json b/dat-was-pro/src/main/resources/mock-responses/agt_campaign.json deleted file mode 100644 index b147a7536..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_campaign.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_campaign\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 영업 캠페인 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 영업 캠페인 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_claim_support.json b/dat-was-pro/src/main/resources/mock-responses/agt_claim_support.json deleted file mode 100644 index ae1472c6a..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_claim_support.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_claim_support\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 보험금 청구지원 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 보험금 청구지원 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_commission_grade.json b/dat-was-pro/src/main/resources/mock-responses/agt_commission_grade.json deleted file mode 100644 index 7c92bc791..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_commission_grade.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_commission_grade\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 수수료등급 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 수수료등급 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_commission_summary.json b/dat-was-pro/src/main/resources/mock-responses/agt_commission_summary.json deleted file mode 100644 index 62b58a4e2..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_commission_summary.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_commission_summary\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 수수료 요약 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 수수료 요약 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_contract_count.json b/dat-was-pro/src/main/resources/mock-responses/agt_contract_count.json deleted file mode 100644 index fddf80dc5..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_contract_count.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_contract_count\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 계약건수 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 계약건수 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_customer_assignment.json b/dat-was-pro/src/main/resources/mock-responses/agt_customer_assignment.json deleted file mode 100644 index 2c1fb0554..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_customer_assignment.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_customer_assignment\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 고객배정 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 고객배정 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_ga_profile.json b/dat-was-pro/src/main/resources/mock-responses/agt_ga_profile.json deleted file mode 100644 index a5ba867b5..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_ga_profile.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_ga_profile\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 조직 기본정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 조직 기본정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_notice_list.json b/dat-was-pro/src/main/resources/mock-responses/agt_notice_list.json deleted file mode 100644 index dfc71983b..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_notice_list.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_notice_list\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 공지목록 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 공지목록 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_organization_agent_list.json b/dat-was-pro/src/main/resources/mock-responses/agt_organization_agent_list.json deleted file mode 100644 index e14bf9a81..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_organization_agent_list.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_organization_agent_list\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 소속 설계사 목록 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 소속 설계사 목록 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_organization_manager.json b/dat-was-pro/src/main/resources/mock-responses/agt_organization_manager.json deleted file mode 100644 index fc55fc793..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_organization_manager.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_organization_manager\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 조직 책임자 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 조직 책임자 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_organization_tree.json b/dat-was-pro/src/main/resources/mock-responses/agt_organization_tree.json deleted file mode 100644 index 304b2f4bc..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_organization_tree.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_organization_tree\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 조직도 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 조직도 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_persistency_rate.json b/dat-was-pro/src/main/resources/mock-responses/agt_persistency_rate.json deleted file mode 100644 index 9938d7a5e..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_persistency_rate.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_persistency_rate\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 계약유지율 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 계약유지율 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_premium_summary.json b/dat-was-pro/src/main/resources/mock-responses/agt_premium_summary.json deleted file mode 100644 index d5d25beee..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_premium_summary.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_premium_summary\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 보험료 실적 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 보험료 실적 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_production_summary.json b/dat-was-pro/src/main/resources/mock-responses/agt_production_summary.json deleted file mode 100644 index 61fe3063b..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_production_summary.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_production_summary\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 신계약 실적 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 신계약 실적 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_sales_area.json b/dat-was-pro/src/main/resources/mock-responses/agt_sales_area.json deleted file mode 100644 index 9c68e4f67..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_sales_area.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_sales_area\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 영업지역 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 영업지역 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_support_contact.json b/dat-was-pro/src/main/resources/mock-responses/agt_support_contact.json deleted file mode 100644 index d9c8f4bbd..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_support_contact.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_support_contact\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 업무지원 연락처 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 업무지원 연락처 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/agt_task_list.json b/dat-was-pro/src/main/resources/mock-responses/agt_task_list.json deleted file mode 100644 index 244646a74..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/agt_task_list.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"agt_task_list\",\"category\":\"AGT\",\"referenceId\":\"AGT-000001\",\"status\":\"정상\",\"summary\":\"GA 설계사 업무목록 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 설계사 업무목록 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_claim_accident.json b/dat-was-pro/src/main/resources/mock-responses/pro_claim_accident.json deleted file mode 100644 index 9d4c089b0..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_claim_accident.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_claim_accident\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험사고 정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험사고 정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_claim_beneficiary.json b/dat-was-pro/src/main/resources/mock-responses/pro_claim_beneficiary.json deleted file mode 100644 index e53e25011..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_claim_beneficiary.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_claim_beneficiary\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험금 수익자 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험금 수익자 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_claim_contact.json b/dat-was-pro/src/main/resources/mock-responses/pro_claim_contact.json deleted file mode 100644 index 6eee428c5..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_claim_contact.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_claim_contact\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험금 청구 담당자 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험금 청구 담당자 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_claim_documents.json b/dat-was-pro/src/main/resources/mock-responses/pro_claim_documents.json deleted file mode 100644 index 6c09b4dfe..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_claim_documents.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_claim_documents\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험금 청구서류 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험금 청구서류 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_claim_eligibility.json b/dat-was-pro/src/main/resources/mock-responses/pro_claim_eligibility.json deleted file mode 100644 index 996bd8d68..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_claim_eligibility.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_claim_eligibility\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험금 청구 가능여부 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험금 청구 가능여부 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_claim_history.json b/dat-was-pro/src/main/resources/mock-responses/pro_claim_history.json deleted file mode 100644 index 9a2d5d9bd..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_claim_history.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_claim_history\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험금 청구이력 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험금 청구이력 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_claim_payment.json b/dat-was-pro/src/main/resources/mock-responses/pro_claim_payment.json deleted file mode 100644 index e794dced0..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_claim_payment.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_claim_payment\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험금 지급내역 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험금 지급내역 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_claim_status.json b/dat-was-pro/src/main/resources/mock-responses/pro_claim_status.json deleted file mode 100644 index 515c1c0a4..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_claim_status.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_claim_status\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험금 청구상태 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험금 청구상태 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_beneficiary.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_beneficiary.json deleted file mode 100644 index c0cb8437c..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_beneficiary.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_beneficiary\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험계약 수익자 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험계약 수익자 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_coverage.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_coverage.json deleted file mode 100644 index f287f0eb4..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_coverage.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_coverage\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험계약 보장내역 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험계약 보장내역 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_detail.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_detail.json deleted file mode 100644 index 1ccd171f8..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_detail.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_detail\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험계약 상세 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험계약 상세 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_list.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_list.json deleted file mode 100644 index d945c38a0..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_list.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_list\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험계약 목록 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험계약 목록 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_loan_status.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_loan_status.json deleted file mode 100644 index 7157fa8bf..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_loan_status.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_loan_status\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험계약 대출현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험계약 대출현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_payment_status.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_payment_status.json deleted file mode 100644 index 66396b4a6..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_payment_status.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_payment_status\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험료 납입상태 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험료 납입상태 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_premium.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_premium.json deleted file mode 100644 index 0feacdaf6..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_premium.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_premium\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험계약 보험료 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험계약 보험료 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_rider_list.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_rider_list.json deleted file mode 100644 index 26020cba2..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_rider_list.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_rider_list\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험계약 특약목록 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험계약 특약목록 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_contract_surrender_value.json b/dat-was-pro/src/main/resources/mock-responses/pro_contract_surrender_value.json deleted file mode 100644 index a9045beb1..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_contract_surrender_value.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_contract_surrender_value\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"보험계약 해지환급금 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험계약 해지환급금 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_customer_address.json b/dat-was-pro/src/main/resources/mock-responses/pro_customer_address.json deleted file mode 100644 index 2ecf34bbb..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_customer_address.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_customer_address\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"고객 주소정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 주소정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_customer_consent.json b/dat-was-pro/src/main/resources/mock-responses/pro_customer_consent.json deleted file mode 100644 index 11c7191c6..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_customer_consent.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_customer_consent\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"고객 동의정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 동의정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_customer_contact.json b/dat-was-pro/src/main/resources/mock-responses/pro_customer_contact.json deleted file mode 100644 index 3c5d4963c..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_customer_contact.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_customer_contact\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"고객 연락처 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 연락처 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_customer_family.json b/dat-was-pro/src/main/resources/mock-responses/pro_customer_family.json deleted file mode 100644 index e50625e86..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_customer_family.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_customer_family\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"고객 가족정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 가족정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_customer_identity.json b/dat-was-pro/src/main/resources/mock-responses/pro_customer_identity.json deleted file mode 100644 index d14d7ada5..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_customer_identity.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_customer_identity\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"고객 본인확인정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 본인확인정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_customer_occupation.json b/dat-was-pro/src/main/resources/mock-responses/pro_customer_occupation.json deleted file mode 100644 index 24839754a..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_customer_occupation.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_customer_occupation\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"고객 직업정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 직업정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_customer_profile.json b/dat-was-pro/src/main/resources/mock-responses/pro_customer_profile.json deleted file mode 100644 index de5be84b2..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_customer_profile.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_customer_profile\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"고객 기본정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 기본정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_customer_risk_grade.json b/dat-was-pro/src/main/resources/mock-responses/pro_customer_risk_grade.json deleted file mode 100644 index d0b3ebe8a..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_customer_risk_grade.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"pro_customer_risk_grade\",\"category\":\"PRO\",\"referenceId\":\"CUST-000001\",\"status\":\"정상\",\"summary\":\"고객 위험등급 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 위험등급 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} - diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_individual_inquiry.json b/dat-was-pro/src/main/resources/mock-responses/pro_individual_inquiry.json deleted file mode 100644 index 35b1731ac..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_individual_inquiry.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "resultCode" : "SUCCESS", - "customerId" : "12345", - "name" : "김철수", - "phoneNumber" : "010-1234-5678", - "email" : "test@example.com", - "address" : "서울시 강남구 테헤란로 123", - "registrationDate" : "2023-01-15", - "grade" : "VIP" -} diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_personal_inquiry.json b/dat-was-pro/src/main/resources/mock-responses/pro_personal_inquiry.json deleted file mode 100644 index 817d4d472..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_personal_inquiry.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "resultCode" : "SUCCESS", - "customerId" : "12345", - "name" : "김철수", - "phoneNumber" : "010-1234-5678", - "email" : "customer@example.com", - "address" : "서울시 강남구 테헤란로 123", - "birthDate" : "1990-01-15", - "membershipLevel" : "GOLD" -} diff --git a/dat-was-pro/src/main/resources/mock-responses/pro_recruitment_data.json b/dat-was-pro/src/main/resources/mock-responses/pro_recruitment_data.json deleted file mode 100644 index 4c9133793..000000000 --- a/dat-was-pro/src/main/resources/mock-responses/pro_recruitment_data.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "resultCode" : "SUCCESS", - "data" : [{"companyName" : "삼성전자", "disclosureDate" : "2024-01-15", "feeAmount" : 1000000.50, "disclosureType" : "신규공시"], - "totalCount" : 10, - "message" : "조회 성공" -} diff --git a/dat-was-pro/src/main/resources/tool-service-manifest.yml b/dat-was-pro/src/main/resources/tool-service-manifest.yml deleted file mode 100644 index bf0fc262e..000000000 --- a/dat-was-pro/src/main/resources/tool-service-manifest.yml +++ /dev/null @@ -1,16 +0,0 @@ -mcp: - manifest: - routing-functions: - - name: route_to_dat-was-pro - description-serialization: 처리계 업무 서버로 요청을 라우팅합니다. 보험 계약, 청약, 변경 및 보험금 지급 업무를 처리합니다. 요청의 주요 엔티티와 업무 영역을 기준으로 처리계 서버를 선택합니다. - server-id: dat-was-pro - category-key: pro - product-boundary: insurance - business-domain: 보험 계약·청약·보험금 처리 - business-outcome: 보험 계약, 청약, 변경 및 보험금 지급 업무를 처리합니다. - primary-entities: [계약, 청약, 보험금, 지급, 고객] - capabilities: [계약 조회, 청약 처리, 계약 변경, 보험금 지급 조회] - select-if: 보험 계약, 청약, 계약 변경, 보험금 또는 지급 관련 요청인 경우 - reject-if: 상품 소개, 고객 일반정보, 영업조직, 시스템 운영 업무가 주된 요청인 경우 - confusable-servers: [dat-was-cus, dat-was-sal, dat-was-sys] - decision-policy: 요청의 주요 엔티티와 업무 영역을 기준으로 처리계 서버를 선택합니다. diff --git a/dat-was-pro/src/test/java/io/shinhanlife/dat/mcc/biz/pro/usecase/IndividualCustomerDetailInquiryUseCaseTest.java b/dat-was-pro/src/test/java/io/shinhanlife/dat/mcc/biz/pro/usecase/IndividualCustomerDetailInquiryUseCaseTest.java deleted file mode 100644 index 6263af84e..000000000 --- a/dat-was-pro/src/test/java/io/shinhanlife/dat/mcc/biz/pro/usecase/IndividualCustomerDetailInquiryUseCaseTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.usecase; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import io.shinhanlife.dat.mcc.biz.pro.dto.IndividualCustomerDetailInquiryRequest; -import io.shinhanlife.dat.mcc.biz.pro.dto.IndividualCustomerDetailInquiryResponse; -import org.junit.jupiter.api.Test; - -class IndividualCustomerDetailInquiryUseCaseTest { - - @Test - void createsToolRequestAndResponseDtos() { - assertNotNull(new IndividualCustomerDetailInquiryRequest()); - assertNotNull(new IndividualCustomerDetailInquiryResponse()); - } -} diff --git a/dat-was-pro/src/test/java/io/shinhanlife/dat/mcc/biz/pro/usecase/RecruitmentFeeDisclosureDataUseCaseTest.java b/dat-was-pro/src/test/java/io/shinhanlife/dat/mcc/biz/pro/usecase/RecruitmentFeeDisclosureDataUseCaseTest.java deleted file mode 100644 index 6c8a33a0c..000000000 --- a/dat-was-pro/src/test/java/io/shinhanlife/dat/mcc/biz/pro/usecase/RecruitmentFeeDisclosureDataUseCaseTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pro.usecase; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import io.shinhanlife.dat.mcc.biz.pro.dto.RecruitmentFeeDisclosureDataRequest; -import io.shinhanlife.dat.mcc.biz.pro.dto.RecruitmentFeeDisclosureDataResponse; -import org.junit.jupiter.api.Test; - -class RecruitmentFeeDisclosureDataUseCaseTest { - - @Test - void createsToolRequestAndResponseDtos() { - assertNotNull(new RecruitmentFeeDisclosureDataRequest()); - assertNotNull(new RecruitmentFeeDisclosureDataResponse()); - } -} diff --git a/dat-was-sal/Dockerfile b/dat-was-sal/Dockerfile deleted file mode 100644 index b087be906..000000000 --- a/dat-was-sal/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM eclipse-temurin:21-jre-alpine -WORKDIR /app -RUN apk add --no-cache tzdata -ENV TZ=Asia/Seoul -COPY dat-was-sal/build/libs/*-SNAPSHOT.jar app.jar -EXPOSE 8082 -ENTRYPOINT ["java", "-jar", "app.jar"] - diff --git a/dat-was-sal/build.gradle b/dat-was-sal/build.gradle deleted file mode 100644 index c04cf80f4..000000000 --- a/dat-was-sal/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - // SMS Tool Pod를 독립 실행 가능한 Spring Boot JAR로 생성합니다. - id 'org.springframework.boot' -} - -dependencies { - // MCP Server, Tool 공통 처리, MCI/EAI 연동 기반은 dat-was-lib에서 상속합니다. - implementation project(':dat-was-lib') -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/converter/ClaimSearchConverter.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/converter/ClaimSearchConverter.java deleted file mode 100644 index 3f41a8fce..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/converter/ClaimSearchConverter.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.converter; - -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchResponse; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_I; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_O; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "spring") -public interface ClaimSearchConverter { - CLCNNB00001_I toLegacyRequest(ClaimSearchRequest request); - ClaimSearchRequest toRequest(CLCNNB00001_I mciRequest); - ClaimSearchResponse toResponse(CLCNNB00001_O mciRes); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/converter/MemoListRetrieverConverter.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/converter/MemoListRetrieverConverter.java deleted file mode 100644 index 889e4a494..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/converter/MemoListRetrieverConverter.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.converter; - -import io.shinhanlife.dat.mcc.biz.cmm.dto.MemoListRetrieverRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.MemoListRetrieverResponse; -import io.shinhanlife.dat.mcc.infra.itrf.http.memo.io.MemoListRetrieverHttpRequest; -import io.shinhanlife.dat.mcc.infra.itrf.http.memo.io.MemoListRetrieverHttpResponse; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.ReportingPolicy; - -@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) -public interface MemoListRetrieverConverter { - // Field names differ? Add mappings like this before the method. - // @Mapping(source = "sourceField", target = "targetField") - MemoListRetrieverHttpRequest toHttpRequest(MemoListRetrieverRequest request); - MemoListRetrieverResponse toResponse(MemoListRetrieverHttpResponse httpResponse); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/ClaimSearchRequest.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/ClaimSearchRequest.java deleted file mode 100644 index 53c285d25..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/ClaimSearchRequest.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.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 ClaimSearchRequest { - @Schema(description = "보험금 청구번호", example = "CLM202608100001", requiredMode = Schema.RequiredMode.REQUIRED) - private String claimNo; - - @Schema(description = "보험 계약번호", example = "10023456789") - private String contractNo; - -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/ClaimSearchResponse.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/ClaimSearchResponse.java deleted file mode 100644 index 55fbfe6e8..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/ClaimSearchResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.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 ClaimSearchResponse { - private String resultCode; - - private String resultMessage; - @Schema(description = "청구 처리 상태 코드", example = "RECEIVED", requiredMode = Schema.RequiredMode.REQUIRED) - private String status; - - @Schema(description = "청구 처리 상태명", example = "접수", requiredMode = Schema.RequiredMode.REQUIRED) - private String statusLabel; - - @Schema(description = "승인 금액", example = "150000") - private Long approvedAmount; - -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/MemoListRetrieverRequest.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/MemoListRetrieverRequest.java deleted file mode 100644 index 4b39c29bf..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/MemoListRetrieverRequest.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.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 MemoListRetrieverRequest { - @Schema(description = "조회할 의뢰서 상태", example = "OPEN", requiredMode = Schema.RequiredMode.REQUIRED) - private String memoStatus; - - @Schema(description = "검색 키워드", example = "프로젝트", requiredMode = Schema.RequiredMode.REQUIRED) - private String searchKeyword; - -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/MemoListRetrieverResponse.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/MemoListRetrieverResponse.java deleted file mode 100644 index 1d3183a71..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/dto/MemoListRetrieverResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.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 MemoListRetrieverResponse { - private String resultCode; - - private String resultMessage; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/ClaimSearchUseCase.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/ClaimSearchUseCase.java deleted file mode 100644 index ce9968f68..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/ClaimSearchUseCase.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.usecase; - -import org.springaicommunity.mcp.annotation.McpTool; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchResponse; - -/** - * @package io.shinhanlife.dat.mcc.biz.cmm.usecase - * @className ClaimSearchUseCase - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.10 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.08.10  jade    최초생성
- *
- * 
- */ -public interface ClaimSearchUseCase { - - @McpTool(name = "cmm_claim_search", title = "청구번호 또는 계약번호로 보험금 청구 상태를 조회한다.", description = "청구번호 또는 계약번호로 보험금 청구 상태를 조회한다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "cmm", - mappingId = "DIRECT_CMM_CLAIM_SEARCH", - functionDescription = "청구번호 또는 계약번호로 보험금 청구 상태와 심사 결과를 조회한다.", - whenToUse = "사용자가 기존 보험금 청구의 진행 상태나 지급 결과를 확인하려는 경우 사용한다.", - whenNotToUse = "보험금 청구를 새로 접수하거나 기존 청구 내용을 변경하려는 경우에는 사용하지 않는다.", - ioLimits = "청구번호 또는 계약번호 중 하나 이상이 필요하며 조회 결과만 반환한다.", - displayDescription = "보험금 청구 상태와 심사 결과를 조회합니다.", - exampleQueries = {"내 보험금 청구가 어디까지 진행됐는지 알려줘", "계약번호로 최근 청구 상태를 확인해줘", "청구 심사 결과가 나왔는지 조회해줘"}, - destructive = false, - idempotent = true, - tags = {"보험금", "청구조회"}, - ownerOrg = "MCP_TOOL" - ) - ClaimSearchResponse searchClaim(ClaimSearchRequest req); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/MemoListRetrieverUseCase.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/MemoListRetrieverUseCase.java deleted file mode 100644 index d96225a27..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/MemoListRetrieverUseCase.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.usecase; - -import org.springaicommunity.mcp.annotation.McpTool; -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.cmm.dto.MemoListRetrieverRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.MemoListRetrieverResponse; - -/** - * @package io.shinhanlife.dat.mcc.biz.cmm.usecase - * @className MemoListRetrieverUseCase - * @description AX HUB 시스템 처리 클래스 - * @author Admin - * @create 2026.08.11 - *
- * ---------- 개정이력 ----------
- * 수정일       수정자     수정내용
- * ---------- -------- ---------------------------
- * 2026.08.11  Admin    최초생성
- *
- * 
- */ -public interface MemoListRetrieverUseCase { - - @McpTool(name = "cmm_memo_retriever", title = "의뢰서 목록 조회", description = "의뢰서 목록을 조회하여 의뢰 정보를 반환합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "cmm", - mappingId = "DIRECT_CMM_MEMO_RETRIEVER", - functionDescription = "상태와 검색어 조건으로 의뢰서 목록을 조회한다.", - whenToUse = "사용자가 등록된 의뢰서의 목록이나 처리 현황을 찾으려는 경우 사용한다.", - whenNotToUse = "의뢰서를 신규 등록하거나 내용을 변경하려는 경우에는 사용하지 않는다.", - ioLimits = "선택 조건에 해당하는 의뢰서 요약 목록만 반환한다.", - displayDescription = "조건에 맞는 의뢰서 목록을 조회합니다.", - exampleQueries = {"처리 중인 의뢰서를 보여줘", "지난번 계약 관련 의뢰서를 찾아줘", "완료된 의뢰서 목록을 조회해줘"}, - destructive = false, - idempotent = true, - tags = {"의뢰서", "목록조회"}, - ownerOrg = "MCP_TOOL" - ) - MemoListRetrieverResponse retrieveMemoList(MemoListRetrieverRequest req); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImpl.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImpl.java deleted file mode 100644 index 801b01255..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImpl.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchResponse; -import io.shinhanlife.dat.mcc.biz.cmm.usecase.ClaimSearchUseCase; -import io.shinhanlife.glow.communication.dto.Transfer; -import io.shinhanlife.glow.BizException; -import org.springframework.stereotype.Service; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import io.shinhanlife.dat.mcc.biz.cmm.converter.ClaimSearchConverter; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_I; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_O; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.MciNclaClient; - - -/** - * @package io.shinhanlife.dat.mcc.biz.cmm.usecase.impl - * @className ClaimSearchUseCaseImpl - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.10 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.08.10  jade    최초생성
- *
- * 
- */ -@Slf4j -@Service -@RequiredArgsConstructor -public class ClaimSearchUseCaseImpl implements ClaimSearchUseCase { - - private final MciNclaClient mci; - private final ClaimSearchConverter converter; - - @Override - public ClaimSearchResponse searchClaim(ClaimSearchRequest req) { - log.info("[MCI Tool] {} 요청 수신.", "cmm_claim_search"); - try { - // MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO) - CLCNNB00001_I mciReq = converter.toLegacyRequest(req); - - Transfer resTransfer = mci.callTo( - "CLCNNB00001", - null, - mciReq, - CLCNNB00001_O.class - ); - ClaimSearchResponse response = new ClaimSearchResponse(); - if (resTransfer.getBody() != null) { - response = converter.toResponse(resTransfer.getBody()); - } - response.setResultCode("SUCCESS"); - response.setResultMessage(resTransfer.getBody() != null - ? "MCI call completed." - : "MCI call completed without a response body."); - return response; - } catch (BizException e) { - log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e); - ClaimSearchResponse response = new ClaimSearchResponse(); - response.setResultCode("ERROR"); - response.setResultMessage(e.getMessage() != null ? e.getMessage() : "Unknown error"); - return response; - } - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/MemoListRetrieverUseCaseImpl.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/MemoListRetrieverUseCaseImpl.java deleted file mode 100644 index c3b4b1cd8..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/MemoListRetrieverUseCaseImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.cmm.converter.MemoListRetrieverConverter; -import io.shinhanlife.dat.mcc.biz.cmm.dto.MemoListRetrieverRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.MemoListRetrieverResponse; -import io.shinhanlife.dat.mcc.infra.itrf.http.memo.MemoClient; -import io.shinhanlife.dat.mcc.infra.itrf.http.memo.io.MemoListRetrieverHttpRequest; -import io.shinhanlife.dat.mcc.infra.itrf.http.memo.io.MemoListRetrieverHttpResponse; -import io.shinhanlife.dat.mcc.biz.cmm.usecase.MemoListRetrieverUseCase; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class MemoListRetrieverUseCaseImpl implements MemoListRetrieverUseCase { - - private final MemoListRetrieverConverter converter; - private final MemoClient memoClient; - - @Override - public MemoListRetrieverResponse retrieveMemoList(MemoListRetrieverRequest req) { - MemoListRetrieverHttpRequest httpRequest = converter.toHttpRequest(req); - MemoListRetrieverHttpResponse httpResponse = memoClient.call(httpRequest, MemoListRetrieverHttpResponse.class); - - MemoListRetrieverResponse response = converter.toResponse(httpResponse); - - // Sparrow NULL_RETURN (널 역참조) 방어 로직 - if (response == null) { - response = new MemoListRetrieverResponse(); - response.setResultCode("ERROR"); - response.setResultMessage("HTTP API 응답이 없거나 변환에 실패했습니다."); - return response; - } - - response.setResultCode("SUCCESS"); - response.setResultMessage("HTTP API call completed."); - return response; - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/dto/LicToolRequest.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/dto/LicToolRequest.java deleted file mode 100644 index d4a09433f..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/dto/LicToolRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.lic.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class LicToolRequest { - @McpToolParam(description = "상품코드", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String productCode; - - @McpToolParam(description = "계약심사번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String contractId; - - @McpToolParam(description = "조회 기준일(YYYY-MM-DD)", required = false) - @Schema(pattern = "^\\d{4}-\\d{2}-\\d{2}$") - private String queryDate; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/dto/LicToolResponse.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/dto/LicToolResponse.java deleted file mode 100644 index 695a46c8c..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/dto/LicToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.lic.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class LicToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String asOfDate; - private List details; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/usecase/LicUnderwritingUseCase.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/usecase/LicUnderwritingUseCase.java deleted file mode 100644 index ef6a2ce57..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/usecase/LicUnderwritingUseCase.java +++ /dev/null @@ -1,198 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.lic.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.lic.dto.LicToolRequest; -import io.shinhanlife.dat.mcc.biz.lic.dto.LicToolResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface LicUnderwritingUseCase { - @McpTool(name = "lic_product_info", title = "상품 정보 조회", description = "상품 정보 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_PRODUCT_INFO", - functionDescription = "상품 정보 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 상품 정보 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "상품 정보 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"상품 정보 조회 정보를 보여줘", "상품 정보 조회 결과를 확인해줘", "현재 상품 정보 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getProductInfo(LicToolRequest request); - - @McpTool(name = "lic_product_coverage", title = "상품 보장내용 조회", description = "상품 보장내용 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_PRODUCT_COVERAGE", - functionDescription = "상품 보장내용 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 상품 보장내용 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "상품 보장내용 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"상품 보장내용 조회 정보를 보여줘", "상품 보장내용 조회 결과를 확인해줘", "현재 상품 보장내용 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getProductCoverage(LicToolRequest request); - - @McpTool(name = "lic_product_premium_table", title = "상품 보험료 요율표 조회", description = "상품 보험료 요율표 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_PRODUCT_PREMIUM_TABLE", - functionDescription = "상품 보험료 요율표 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 상품 보험료 요율표 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "상품 보험료 요율표 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"상품 보험료 요율표 조회 정보를 보여줘", "상품 보험료 요율표 조회 결과를 확인해줘", "현재 상품 보험료 요율표 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getProductPremiumTable(LicToolRequest request); - - @McpTool(name = "lic_contract_review_status", title = "계약심사 진행상태 조회", description = "계약심사 진행상태 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_CONTRACT_REVIEW_STATUS", - functionDescription = "계약심사 진행상태 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 계약심사 진행상태 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "계약심사 진행상태 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"계약심사 진행상태 조회 정보를 보여줘", "계약심사 진행상태 조회 결과를 확인해줘", "현재 계약심사 진행상태 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getContractReviewStatus(LicToolRequest request); - - @McpTool(name = "lic_contract_review_document", title = "계약심사 제출서류 현황 조회", description = "계약심사 제출서류 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_CONTRACT_REVIEW_DOCUMENT", - functionDescription = "계약심사 제출서류 현황 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 계약심사 제출서류 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "계약심사 제출서류 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"계약심사 제출서류 현황 조회 정보를 보여줘", "계약심사 제출서류 현황 조회 결과를 확인해줘", "현재 계약심사 제출서류 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getContractReviewDocument(LicToolRequest request); - - @McpTool(name = "lic_contract_review_result", title = "계약심사 결과 조회", description = "계약심사 결과 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_CONTRACT_REVIEW_RESULT", - functionDescription = "계약심사 결과 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 계약심사 결과 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "계약심사 결과 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"계약심사 결과 조회 정보를 보여줘", "계약심사 결과 조회 결과를 확인해줘", "현재 계약심사 결과 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getContractReviewResult(LicToolRequest request); - - @McpTool(name = "lic_contract_review_history", title = "계약심사 이력 조회", description = "계약심사 이력 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_CONTRACT_REVIEW_HISTORY", - functionDescription = "계약심사 이력 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 계약심사 이력 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "계약심사 이력 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"계약심사 이력 조회 정보를 보여줘", "계약심사 이력 조회 결과를 확인해줘", "현재 계약심사 이력 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getContractReviewHistory(LicToolRequest request); - - @McpTool(name = "lic_underwriting_result", title = "언더라이팅 인수심사 결과 조회", description = "언더라이팅 인수심사 결과 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_UNDERWRITING_RESULT", - functionDescription = "언더라이팅 인수심사 결과 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 언더라이팅 인수심사 결과 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "언더라이팅 인수심사 결과 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"언더라이팅 인수심사 결과 조회 정보를 보여줘", "언더라이팅 인수심사 결과 조회 결과를 확인해줘", "현재 언더라이팅 인수심사 결과 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getUnderwritingResult(LicToolRequest request); - - @McpTool(name = "lic_underwriting_risk_grade", title = "언더라이팅 위험등급 조회", description = "언더라이팅 위험등급 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_UNDERWRITING_RISK_GRADE", - functionDescription = "언더라이팅 위험등급 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 언더라이팅 위험등급 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "언더라이팅 위험등급 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"언더라이팅 위험등급 조회 정보를 보여줘", "언더라이팅 위험등급 조회 결과를 확인해줘", "현재 언더라이팅 위험등급 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getUnderwritingRiskGrade(LicToolRequest request); - - @McpTool(name = "lic_underwriting_additional_request", title = "언더라이팅 추가자료 요청 현황 조회", description = "언더라이팅 추가자료 요청 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "lic", - mappingId = "DIRECT_LIC_UNDERWRITING_ADDITIONAL_REQUEST", - functionDescription = "언더라이팅 추가자료 요청 현황 조회 정보를 조회합니다.", - whenToUse = "상품·계약심사 업무에서 언더라이팅 추가자료 요청 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "언더라이팅 추가자료 요청 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"언더라이팅 추가자료 요청 현황 조회 정보를 보여줘", "언더라이팅 추가자료 요청 현황 조회 결과를 확인해줘", "현재 언더라이팅 추가자료 요청 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"lic", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - LicToolResponse getUnderwritingAdditionalRequest(LicToolRequest request); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/usecase/impl/LicUnderwritingUseCaseImpl.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/usecase/impl/LicUnderwritingUseCaseImpl.java deleted file mode 100644 index ec6811ab0..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/lic/usecase/impl/LicUnderwritingUseCaseImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.lic.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.lic.dto.LicToolRequest; -import io.shinhanlife.dat.mcc.biz.lic.dto.LicToolResponse; -import io.shinhanlife.dat.mcc.biz.lic.usecase.LicUnderwritingUseCase; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class LicUnderwritingUseCaseImpl implements LicUnderwritingUseCase { - @Override - public LicToolResponse getProductInfo(LicToolRequest request) { - return mockResponse(request, "lic_product_info", "상품 정보 조회"); - } - - @Override - public LicToolResponse getProductCoverage(LicToolRequest request) { - return mockResponse(request, "lic_product_coverage", "상품 보장내용 조회"); - } - - @Override - public LicToolResponse getProductPremiumTable(LicToolRequest request) { - return mockResponse(request, "lic_product_premium_table", "상품 보험료 요율표 조회"); - } - - @Override - public LicToolResponse getContractReviewStatus(LicToolRequest request) { - return mockResponse(request, "lic_contract_review_status", "계약심사 진행상태 조회"); - } - - @Override - public LicToolResponse getContractReviewDocument(LicToolRequest request) { - return mockResponse(request, "lic_contract_review_document", "계약심사 제출서류 현황 조회"); - } - - @Override - public LicToolResponse getContractReviewResult(LicToolRequest request) { - return mockResponse(request, "lic_contract_review_result", "계약심사 결과 조회"); - } - - @Override - public LicToolResponse getContractReviewHistory(LicToolRequest request) { - return mockResponse(request, "lic_contract_review_history", "계약심사 이력 조회"); - } - - @Override - public LicToolResponse getUnderwritingResult(LicToolRequest request) { - return mockResponse(request, "lic_underwriting_result", "언더라이팅 인수심사 결과 조회"); - } - - @Override - public LicToolResponse getUnderwritingRiskGrade(LicToolRequest request) { - return mockResponse(request, "lic_underwriting_risk_grade", "언더라이팅 위험등급 조회"); - } - - @Override - public LicToolResponse getUnderwritingAdditionalRequest(LicToolRequest request) { - return mockResponse(request, "lic_underwriting_additional_request", "언더라이팅 추가자료 요청 현황 조회"); - } - - private LicToolResponse mockResponse(LicToolRequest request, String toolName, String title) { - LicToolResponse response = new LicToolResponse(); - response.setToolName(toolName); - response.setCategory("LIC"); - response.setReferenceId(request != null && hasText(request.getProductCode()) ? request.getProductCode() - : request != null && hasText(request.getContractId()) ? request.getContractId() : "LIC-000001"); - response.setStatus("정상"); - response.setSummary(title + " 결과"); - response.setAsOfDate(request != null && hasText(request.getQueryDate()) ? request.getQueryDate() : "2026-08-14"); - response.setDetails(List.of(title + " 모의 상세정보", "외부 시스템을 변경하지 않는 조회 결과")); - return response; - } - - private boolean hasText(String value) { - return value != null && !value.isBlank(); - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/dto/SofToolRequest.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/dto/SofToolRequest.java deleted file mode 100644 index d704a3b38..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/dto/SofToolRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sof.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class SofToolRequest { - @McpToolParam(description = "설계사번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String agentId; - - @McpToolParam(description = "GA 조직번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String organizationId; - - @McpToolParam(description = "조회 기준일(YYYY-MM-DD)", required = false) - @Schema(pattern = "^\\d{4}-\\d{2}-\\d{2}$") - private String queryDate; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/dto/SofToolResponse.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/dto/SofToolResponse.java deleted file mode 100644 index ff3bdc207..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/dto/SofToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sof.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class SofToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String asOfDate; - private List details; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/usecase/SofFieldActivityUseCase.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/usecase/SofFieldActivityUseCase.java deleted file mode 100644 index f138c15ac..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/usecase/SofFieldActivityUseCase.java +++ /dev/null @@ -1,198 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sof.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.sof.dto.SofToolRequest; -import io.shinhanlife.dat.mcc.biz.sof.dto.SofToolResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface SofFieldActivityUseCase { - @McpTool(name = "sof_activity_daily", title = "GA 일별 활동 현황 조회", description = "GA 일별 활동 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_ACTIVITY_DAILY", - functionDescription = "GA 일별 활동 현황 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 GA 일별 활동 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "GA 일별 활동 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"GA 일별 활동 현황 조회 정보를 보여줘", "GA 일별 활동 현황 조회 결과를 확인해줘", "현재 GA 일별 활동 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getActivityDaily(SofToolRequest request); - - @McpTool(name = "sof_visit_count", title = "고객 방문 건수 조회", description = "고객 방문 건수 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_VISIT_COUNT", - functionDescription = "고객 방문 건수 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 고객 방문 건수 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "고객 방문 건수 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 방문 건수 조회 정보를 보여줘", "고객 방문 건수 조회 결과를 확인해줘", "현재 고객 방문 건수 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getVisitCount(SofToolRequest request); - - @McpTool(name = "sof_call_activity", title = "콜 활동 현황 조회", description = "콜 활동 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_CALL_ACTIVITY", - functionDescription = "콜 활동 현황 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 콜 활동 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "콜 활동 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"콜 활동 현황 조회 정보를 보여줘", "콜 활동 현황 조회 결과를 확인해줘", "현재 콜 활동 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getCallActivity(SofToolRequest request); - - @McpTool(name = "sof_customer_consulting", title = "고객 상담 현황 조회", description = "고객 상담 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_CUSTOMER_CONSULTING", - functionDescription = "고객 상담 현황 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 고객 상담 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "고객 상담 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객 상담 현황 조회 정보를 보여줘", "고객 상담 현황 조회 결과를 확인해줘", "현재 고객 상담 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getCustomerConsulting(SofToolRequest request); - - @McpTool(name = "sof_prospect_list", title = "신규 발굴 고객 목록 조회", description = "신규 발굴 고객 목록 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_PROSPECT_LIST", - functionDescription = "신규 발굴 고객 목록 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 신규 발굴 고객 목록 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "신규 발굴 고객 목록 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"신규 발굴 고객 목록 조회 정보를 보여줘", "신규 발굴 고객 목록 조회 결과를 확인해줘", "현재 신규 발굴 고객 목록 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getProspectList(SofToolRequest request); - - @McpTool(name = "sof_activity_calendar", title = "활동 일정 조회", description = "활동 일정 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_ACTIVITY_CALENDAR", - functionDescription = "활동 일정 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 활동 일정 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "활동 일정 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"활동 일정 조회 정보를 보여줘", "활동 일정 조회 결과를 확인해줘", "현재 활동 일정 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getActivityCalendar(SofToolRequest request); - - @McpTool(name = "sof_activity_report_status", title = "활동일지 제출 현황 조회", description = "활동일지 제출 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_ACTIVITY_REPORT_STATUS", - functionDescription = "활동일지 제출 현황 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 활동일지 제출 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "활동일지 제출 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"활동일지 제출 현황 조회 정보를 보여줘", "활동일지 제출 현황 조회 결과를 확인해줘", "현재 활동일지 제출 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getActivityReportStatus(SofToolRequest request); - - @McpTool(name = "sof_training_completion", title = "교육 이수 현황 조회", description = "교육 이수 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_TRAINING_COMPLETION", - functionDescription = "교육 이수 현황 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 교육 이수 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "교육 이수 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"교육 이수 현황 조회 정보를 보여줘", "교육 이수 현황 조회 결과를 확인해줘", "현재 교육 이수 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getTrainingCompletion(SofToolRequest request); - - @McpTool(name = "sof_field_support_request", title = "현장 지원 요청 현황 조회", description = "현장 지원 요청 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_FIELD_SUPPORT_REQUEST", - functionDescription = "현장 지원 요청 현황 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 현장 지원 요청 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "현장 지원 요청 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"현장 지원 요청 현황 조회 정보를 보여줘", "현장 지원 요청 현황 조회 결과를 확인해줘", "현재 현장 지원 요청 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getFieldSupportRequest(SofToolRequest request); - - @McpTool(name = "sof_activity_summary", title = "GA 활동 종합 현황 조회", description = "GA 활동 종합 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sof", - mappingId = "DIRECT_SOF_ACTIVITY_SUMMARY", - functionDescription = "GA 활동 종합 현황 조회 정보를 조회합니다.", - whenToUse = "GA 활동 업무에서 GA 활동 종합 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "GA 활동 종합 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"GA 활동 종합 현황 조회 정보를 보여줘", "GA 활동 종합 현황 조회 결과를 확인해줘", "현재 GA 활동 종합 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sof", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SofToolResponse getActivitySummary(SofToolRequest request); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/usecase/impl/SofFieldActivityUseCaseImpl.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/usecase/impl/SofFieldActivityUseCaseImpl.java deleted file mode 100644 index 8c8712819..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sof/usecase/impl/SofFieldActivityUseCaseImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sof.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.sof.dto.SofToolRequest; -import io.shinhanlife.dat.mcc.biz.sof.dto.SofToolResponse; -import io.shinhanlife.dat.mcc.biz.sof.usecase.SofFieldActivityUseCase; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class SofFieldActivityUseCaseImpl implements SofFieldActivityUseCase { - @Override - public SofToolResponse getActivityDaily(SofToolRequest request) { - return mockResponse(request, "sof_activity_daily", "GA 일별 활동 현황 조회"); - } - - @Override - public SofToolResponse getVisitCount(SofToolRequest request) { - return mockResponse(request, "sof_visit_count", "고객 방문 건수 조회"); - } - - @Override - public SofToolResponse getCallActivity(SofToolRequest request) { - return mockResponse(request, "sof_call_activity", "콜 활동 현황 조회"); - } - - @Override - public SofToolResponse getCustomerConsulting(SofToolRequest request) { - return mockResponse(request, "sof_customer_consulting", "고객 상담 현황 조회"); - } - - @Override - public SofToolResponse getProspectList(SofToolRequest request) { - return mockResponse(request, "sof_prospect_list", "신규 발굴 고객 목록 조회"); - } - - @Override - public SofToolResponse getActivityCalendar(SofToolRequest request) { - return mockResponse(request, "sof_activity_calendar", "활동 일정 조회"); - } - - @Override - public SofToolResponse getActivityReportStatus(SofToolRequest request) { - return mockResponse(request, "sof_activity_report_status", "활동일지 제출 현황 조회"); - } - - @Override - public SofToolResponse getTrainingCompletion(SofToolRequest request) { - return mockResponse(request, "sof_training_completion", "교육 이수 현황 조회"); - } - - @Override - public SofToolResponse getFieldSupportRequest(SofToolRequest request) { - return mockResponse(request, "sof_field_support_request", "현장 지원 요청 현황 조회"); - } - - @Override - public SofToolResponse getActivitySummary(SofToolRequest request) { - return mockResponse(request, "sof_activity_summary", "GA 활동 종합 현황 조회"); - } - - private SofToolResponse mockResponse(SofToolRequest request, String toolName, String title) { - SofToolResponse response = new SofToolResponse(); - response.setToolName(toolName); - response.setCategory("SOF"); - response.setReferenceId(request != null && hasText(request.getAgentId()) ? request.getAgentId() - : request != null && hasText(request.getOrganizationId()) ? request.getOrganizationId() : "SOF-000001"); - response.setStatus("정상"); - response.setSummary(title + " 결과"); - response.setAsOfDate(request != null && hasText(request.getQueryDate()) ? request.getQueryDate() : "2026-08-14"); - response.setDetails(List.of(title + " 모의 상세정보", "외부 시스템을 변경하지 않는 조회 결과")); - return response; - } - - private boolean hasText(String value) { - return value != null && !value.isBlank(); - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/dto/SogToolRequest.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/dto/SogToolRequest.java deleted file mode 100644 index a057e5e0b..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/dto/SogToolRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sog.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class SogToolRequest { - @McpToolParam(description = "설계사번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String agentId; - - @McpToolParam(description = "GA 조직번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String organizationId; - - @McpToolParam(description = "조회 기준일(YYYY-MM-DD)", required = false) - @Schema(pattern = "^\\d{4}-\\d{2}-\\d{2}$") - private String queryDate; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/dto/SogToolResponse.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/dto/SogToolResponse.java deleted file mode 100644 index 7b3ac4a84..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/dto/SogToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sog.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class SogToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String asOfDate; - private List details; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/usecase/SogSalesPerformanceUseCase.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/usecase/SogSalesPerformanceUseCase.java deleted file mode 100644 index fbf046023..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/usecase/SogSalesPerformanceUseCase.java +++ /dev/null @@ -1,198 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sog.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.sog.dto.SogToolRequest; -import io.shinhanlife.dat.mcc.biz.sog.dto.SogToolResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface SogSalesPerformanceUseCase { - @McpTool(name = "sog_target_summary", title = "GA 개인별 영업목표 요약 조회", description = "GA 개인별 영업목표 요약 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_TARGET_SUMMARY", - functionDescription = "GA 개인별 영업목표 요약 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 GA 개인별 영업목표 요약 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "GA 개인별 영업목표 요약 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"GA 개인별 영업목표 요약 조회 정보를 보여줘", "GA 개인별 영업목표 요약 조회 결과를 확인해줘", "현재 GA 개인별 영업목표 요약 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getTargetSummary(SogToolRequest request); - - @McpTool(name = "sog_target_progress", title = "영업목표 달성률 조회", description = "영업목표 달성률 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_TARGET_PROGRESS", - functionDescription = "영업목표 달성률 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 영업목표 달성률 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "영업목표 달성률 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"영업목표 달성률 조회 정보를 보여줘", "영업목표 달성률 조회 결과를 확인해줘", "현재 영업목표 달성률 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getTargetProgress(SogToolRequest request); - - @McpTool(name = "sog_monthly_performance", title = "GA 월별 실적 조회", description = "GA 월별 실적 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_MONTHLY_PERFORMANCE", - functionDescription = "GA 월별 실적 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 GA 월별 실적 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "GA 월별 실적 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"GA 월별 실적 조회 정보를 보여줘", "GA 월별 실적 조회 결과를 확인해줘", "현재 GA 월별 실적 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getMonthlyPerformance(SogToolRequest request); - - @McpTool(name = "sog_product_performance", title = "상품별 실적 조회", description = "상품별 실적 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_PRODUCT_PERFORMANCE", - functionDescription = "상품별 실적 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 상품별 실적 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "상품별 실적 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"상품별 실적 조회 정보를 보여줘", "상품별 실적 조회 결과를 확인해줘", "현재 상품별 실적 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getProductPerformance(SogToolRequest request); - - @McpTool(name = "sog_new_contract_count", title = "신계약 건수 실적 조회", description = "신계약 건수 실적 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_NEW_CONTRACT_COUNT", - functionDescription = "신계약 건수 실적 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 신계약 건수 실적 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "신계약 건수 실적 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"신계약 건수 실적 조회 정보를 보여줘", "신계약 건수 실적 조회 결과를 확인해줘", "현재 신계약 건수 실적 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getNewContractCount(SogToolRequest request); - - @McpTool(name = "sog_premium_performance", title = "보험료 실적 조회", description = "보험료 실적 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_PREMIUM_PERFORMANCE", - functionDescription = "보험료 실적 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 보험료 실적 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "보험료 실적 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"보험료 실적 조회 정보를 보여줘", "보험료 실적 조회 결과를 확인해줘", "현재 보험료 실적 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getPremiumPerformance(SogToolRequest request); - - @McpTool(name = "sog_sales_ranking", title = "GA 영업 순위 조회", description = "GA 영업 순위 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_SALES_RANKING", - functionDescription = "GA 영업 순위 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 GA 영업 순위 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "GA 영업 순위 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"GA 영업 순위 조회 정보를 보여줘", "GA 영업 순위 조회 결과를 확인해줘", "현재 GA 영업 순위 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getSalesRanking(SogToolRequest request); - - @McpTool(name = "sog_incentive_summary", title = "영업 인센티브 요약 조회", description = "영업 인센티브 요약 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_INCENTIVE_SUMMARY", - functionDescription = "영업 인센티브 요약 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 영업 인센티브 요약 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "영업 인센티브 요약 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"영업 인센티브 요약 조회 정보를 보여줘", "영업 인센티브 요약 조회 결과를 확인해줘", "현재 영업 인센티브 요약 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getIncentiveSummary(SogToolRequest request); - - @McpTool(name = "sog_target_history", title = "영업목표 변경 이력 조회", description = "영업목표 변경 이력 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_TARGET_HISTORY", - functionDescription = "영업목표 변경 이력 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 영업목표 변경 이력 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "영업목표 변경 이력 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"영업목표 변경 이력 조회 정보를 보여줘", "영업목표 변경 이력 조회 결과를 확인해줘", "현재 영업목표 변경 이력 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getTargetHistory(SogToolRequest request); - - @McpTool(name = "sog_performance_comparison", title = "전년 동기 대비 실적 비교 조회", description = "전년 동기 대비 실적 비교 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sog", - mappingId = "DIRECT_SOG_PERFORMANCE_COMPARISON", - functionDescription = "전년 동기 대비 실적 비교 조회 정보를 조회합니다.", - whenToUse = "GA 영업 업무에서 전년 동기 대비 실적 비교 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "전년 동기 대비 실적 비교 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"전년 동기 대비 실적 비교 조회 정보를 보여줘", "전년 동기 대비 실적 비교 조회 결과를 확인해줘", "현재 전년 동기 대비 실적 비교 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sog", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SogToolResponse getPerformanceComparison(SogToolRequest request); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/usecase/impl/SogSalesPerformanceUseCaseImpl.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/usecase/impl/SogSalesPerformanceUseCaseImpl.java deleted file mode 100644 index 5fc07cb9b..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sog/usecase/impl/SogSalesPerformanceUseCaseImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sog.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.sog.dto.SogToolRequest; -import io.shinhanlife.dat.mcc.biz.sog.dto.SogToolResponse; -import io.shinhanlife.dat.mcc.biz.sog.usecase.SogSalesPerformanceUseCase; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class SogSalesPerformanceUseCaseImpl implements SogSalesPerformanceUseCase { - @Override - public SogToolResponse getTargetSummary(SogToolRequest request) { - return mockResponse(request, "sog_target_summary", "GA 개인별 영업목표 요약 조회"); - } - - @Override - public SogToolResponse getTargetProgress(SogToolRequest request) { - return mockResponse(request, "sog_target_progress", "영업목표 달성률 조회"); - } - - @Override - public SogToolResponse getMonthlyPerformance(SogToolRequest request) { - return mockResponse(request, "sog_monthly_performance", "GA 월별 실적 조회"); - } - - @Override - public SogToolResponse getProductPerformance(SogToolRequest request) { - return mockResponse(request, "sog_product_performance", "상품별 실적 조회"); - } - - @Override - public SogToolResponse getNewContractCount(SogToolRequest request) { - return mockResponse(request, "sog_new_contract_count", "신계약 건수 실적 조회"); - } - - @Override - public SogToolResponse getPremiumPerformance(SogToolRequest request) { - return mockResponse(request, "sog_premium_performance", "보험료 실적 조회"); - } - - @Override - public SogToolResponse getSalesRanking(SogToolRequest request) { - return mockResponse(request, "sog_sales_ranking", "GA 영업 순위 조회"); - } - - @Override - public SogToolResponse getIncentiveSummary(SogToolRequest request) { - return mockResponse(request, "sog_incentive_summary", "영업 인센티브 요약 조회"); - } - - @Override - public SogToolResponse getTargetHistory(SogToolRequest request) { - return mockResponse(request, "sog_target_history", "영업목표 변경 이력 조회"); - } - - @Override - public SogToolResponse getPerformanceComparison(SogToolRequest request) { - return mockResponse(request, "sog_performance_comparison", "전년 동기 대비 실적 비교 조회"); - } - - private SogToolResponse mockResponse(SogToolRequest request, String toolName, String title) { - SogToolResponse response = new SogToolResponse(); - response.setToolName(toolName); - response.setCategory("SOG"); - response.setReferenceId(request != null && hasText(request.getAgentId()) ? request.getAgentId() - : request != null && hasText(request.getOrganizationId()) ? request.getOrganizationId() : "SOG-000001"); - response.setStatus("정상"); - response.setSummary(title + " 결과"); - response.setAsOfDate(request != null && hasText(request.getQueryDate()) ? request.getQueryDate() : "2026-08-14"); - response.setDetails(List.of(title + " 모의 상세정보", "외부 시스템을 변경하지 않는 조회 결과")); - return response; - } - - private boolean hasText(String value) { - return value != null && !value.isBlank(); - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/dto/SotToolRequest.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/dto/SotToolRequest.java deleted file mode 100644 index 5e0bc6983..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/dto/SotToolRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sot.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class SotToolRequest { - @McpToolParam(description = "고객번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String customerId; - - @McpToolParam(description = "가입설계번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String contractId; - - @McpToolParam(description = "조회 기준일(YYYY-MM-DD)", required = false) - @Schema(pattern = "^\\d{4}-\\d{2}-\\d{2}$") - private String queryDate; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/dto/SotToolResponse.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/dto/SotToolResponse.java deleted file mode 100644 index e73159972..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/dto/SotToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sot.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class SotToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String asOfDate; - private List details; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/usecase/SotConsentUseCase.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/usecase/SotConsentUseCase.java deleted file mode 100644 index 015620c08..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/usecase/SotConsentUseCase.java +++ /dev/null @@ -1,198 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sot.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.sot.dto.SotToolRequest; -import io.shinhanlife.dat.mcc.biz.sot.dto.SotToolResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface SotConsentUseCase { - @McpTool(name = "sot_marketing_consent_status", title = "마케팅 수신동의 현황 조회", description = "마케팅 수신동의 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_MARKETING_CONSENT_STATUS", - functionDescription = "마케팅 수신동의 현황 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 마케팅 수신동의 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "마케팅 수신동의 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"마케팅 수신동의 현황 조회 정보를 보여줘", "마케팅 수신동의 현황 조회 결과를 확인해줘", "현재 마케팅 수신동의 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getMarketingConsentStatus(SotToolRequest request); - - @McpTool(name = "sot_marketing_consent_history", title = "마케팅 동의 이력 조회", description = "마케팅 동의 이력 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_MARKETING_CONSENT_HISTORY", - functionDescription = "마케팅 동의 이력 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 마케팅 동의 이력 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "마케팅 동의 이력 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"마케팅 동의 이력 조회 정보를 보여줘", "마케팅 동의 이력 조회 결과를 확인해줘", "현재 마케팅 동의 이력 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getMarketingConsentHistory(SotToolRequest request); - - @McpTool(name = "sot_marketing_consent_channel", title = "채널별 마케팅 동의 현황 조회", description = "채널별 마케팅 동의 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_MARKETING_CONSENT_CHANNEL", - functionDescription = "채널별 마케팅 동의 현황 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 채널별 마케팅 동의 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "채널별 마케팅 동의 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"채널별 마케팅 동의 현황 조회 정보를 보여줘", "채널별 마케팅 동의 현황 조회 결과를 확인해줘", "현재 채널별 마케팅 동의 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getMarketingConsentChannel(SotToolRequest request); - - @McpTool(name = "sot_subscription_design_consent", title = "가입설계 동의 현황 조회", description = "가입설계 동의 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_SUBSCRIPTION_DESIGN_CONSENT", - functionDescription = "가입설계 동의 현황 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 가입설계 동의 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "가입설계 동의 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"가입설계 동의 현황 조회 정보를 보여줘", "가입설계 동의 현황 조회 결과를 확인해줘", "현재 가입설계 동의 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getSubscriptionDesignConsent(SotToolRequest request); - - @McpTool(name = "sot_subscription_design_detail", title = "가입설계 동의 상세 조회", description = "가입설계 동의 상세 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_SUBSCRIPTION_DESIGN_DETAIL", - functionDescription = "가입설계 동의 상세 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 가입설계 동의 상세 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "가입설계 동의 상세 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"가입설계 동의 상세 조회 정보를 보여줘", "가입설계 동의 상세 조회 결과를 확인해줘", "현재 가입설계 동의 상세 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getSubscriptionDesignDetail(SotToolRequest request); - - @McpTool(name = "sot_customer_info_consent", title = "고객정보 수집·이용 동의 현황 조회", description = "고객정보 수집·이용 동의 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_CUSTOMER_INFO_CONSENT", - functionDescription = "고객정보 수집·이용 동의 현황 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 고객정보 수집·이용 동의 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "고객정보 수집·이용 동의 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객정보 수집·이용 동의 현황 조회 정보를 보여줘", "고객정보 수집·이용 동의 현황 조회 결과를 확인해줘", "현재 고객정보 수집·이용 동의 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getCustomerInfoConsent(SotToolRequest request); - - @McpTool(name = "sot_customer_info_consent_scope", title = "고객정보 동의 범위 조회", description = "고객정보 동의 범위 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_CUSTOMER_INFO_CONSENT_SCOPE", - functionDescription = "고객정보 동의 범위 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 고객정보 동의 범위 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "고객정보 동의 범위 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"고객정보 동의 범위 조회 정보를 보여줘", "고객정보 동의 범위 조회 결과를 확인해줘", "현재 고객정보 동의 범위 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getCustomerInfoConsentScope(SotToolRequest request); - - @McpTool(name = "sot_consent_expiry", title = "동의 유효기간 만료 현황 조회", description = "동의 유효기간 만료 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_CONSENT_EXPIRY", - functionDescription = "동의 유효기간 만료 현황 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 동의 유효기간 만료 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "동의 유효기간 만료 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"동의 유효기간 만료 현황 조회 정보를 보여줘", "동의 유효기간 만료 현황 조회 결과를 확인해줘", "현재 동의 유효기간 만료 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getConsentExpiry(SotToolRequest request); - - @McpTool(name = "sot_consent_withdrawal", title = "동의 철회 이력 조회", description = "동의 철회 이력 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_CONSENT_WITHDRAWAL", - functionDescription = "동의 철회 이력 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 동의 철회 이력 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "동의 철회 이력 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"동의 철회 이력 조회 정보를 보여줘", "동의 철회 이력 조회 결과를 확인해줘", "현재 동의 철회 이력 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getConsentWithdrawal(SotToolRequest request); - - @McpTool(name = "sot_tm_call_eligibility", title = "텔레마케팅 콜 가능여부 종합 조회", description = "텔레마케팅 콜 가능여부 종합 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "sot", - mappingId = "DIRECT_SOT_TM_CALL_ELIGIBILITY", - functionDescription = "텔레마케팅 콜 가능여부 종합 조회 정보를 조회합니다.", - whenToUse = "텔레마케팅 동의 업무에서 텔레마케팅 콜 가능여부 종합 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "텔레마케팅 콜 가능여부 종합 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"텔레마케팅 콜 가능여부 종합 조회 정보를 보여줘", "텔레마케팅 콜 가능여부 종합 조회 결과를 확인해줘", "현재 텔레마케팅 콜 가능여부 종합 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"sot", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SotToolResponse getTmCallEligibility(SotToolRequest request); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/usecase/impl/SotConsentUseCaseImpl.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/usecase/impl/SotConsentUseCaseImpl.java deleted file mode 100644 index c9590436f..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/sot/usecase/impl/SotConsentUseCaseImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sot.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.sot.dto.SotToolRequest; -import io.shinhanlife.dat.mcc.biz.sot.dto.SotToolResponse; -import io.shinhanlife.dat.mcc.biz.sot.usecase.SotConsentUseCase; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class SotConsentUseCaseImpl implements SotConsentUseCase { - @Override - public SotToolResponse getMarketingConsentStatus(SotToolRequest request) { - return mockResponse(request, "sot_marketing_consent_status", "마케팅 수신동의 현황 조회"); - } - - @Override - public SotToolResponse getMarketingConsentHistory(SotToolRequest request) { - return mockResponse(request, "sot_marketing_consent_history", "마케팅 동의 이력 조회"); - } - - @Override - public SotToolResponse getMarketingConsentChannel(SotToolRequest request) { - return mockResponse(request, "sot_marketing_consent_channel", "채널별 마케팅 동의 현황 조회"); - } - - @Override - public SotToolResponse getSubscriptionDesignConsent(SotToolRequest request) { - return mockResponse(request, "sot_subscription_design_consent", "가입설계 동의 현황 조회"); - } - - @Override - public SotToolResponse getSubscriptionDesignDetail(SotToolRequest request) { - return mockResponse(request, "sot_subscription_design_detail", "가입설계 동의 상세 조회"); - } - - @Override - public SotToolResponse getCustomerInfoConsent(SotToolRequest request) { - return mockResponse(request, "sot_customer_info_consent", "고객정보 수집·이용 동의 현황 조회"); - } - - @Override - public SotToolResponse getCustomerInfoConsentScope(SotToolRequest request) { - return mockResponse(request, "sot_customer_info_consent_scope", "고객정보 동의 범위 조회"); - } - - @Override - public SotToolResponse getConsentExpiry(SotToolRequest request) { - return mockResponse(request, "sot_consent_expiry", "동의 유효기간 만료 현황 조회"); - } - - @Override - public SotToolResponse getConsentWithdrawal(SotToolRequest request) { - return mockResponse(request, "sot_consent_withdrawal", "동의 철회 이력 조회"); - } - - @Override - public SotToolResponse getTmCallEligibility(SotToolRequest request) { - return mockResponse(request, "sot_tm_call_eligibility", "텔레마케팅 콜 가능여부 종합 조회"); - } - - private SotToolResponse mockResponse(SotToolRequest request, String toolName, String title) { - SotToolResponse response = new SotToolResponse(); - response.setToolName(toolName); - response.setCategory("SOT"); - response.setReferenceId(request != null && hasText(request.getCustomerId()) ? request.getCustomerId() - : request != null && hasText(request.getContractId()) ? request.getContractId() : "SOT-000001"); - response.setStatus("정상"); - response.setSummary(title + " 결과"); - response.setAsOfDate(request != null && hasText(request.getQueryDate()) ? request.getQueryDate() : "2026-08-14"); - response.setDetails(List.of(title + " 모의 상세정보", "외부 시스템을 변경하지 않는 조회 결과")); - return response; - } - - private boolean hasText(String value) { - return value != null && !value.isBlank(); - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/dto/SprToolRequest.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/dto/SprToolRequest.java deleted file mode 100644 index f5fcd3828..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/dto/SprToolRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.spr.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class SprToolRequest { - @McpToolParam(description = "현장문의번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String inquiryId; - - @McpToolParam(description = "설계사번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String agentId; - - @McpToolParam(description = "조회 기준일(YYYY-MM-DD)", required = false) - @Schema(pattern = "^\\d{4}-\\d{2}-\\d{2}$") - private String queryDate; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/dto/SprToolResponse.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/dto/SprToolResponse.java deleted file mode 100644 index 7190eaf2f..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/dto/SprToolResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.spr.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class SprToolResponse { - private String toolName; - private String category; - private String referenceId; - private String status; - private String summary; - private String asOfDate; - private List details; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/usecase/SprFieldInquiryUseCase.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/usecase/SprFieldInquiryUseCase.java deleted file mode 100644 index bdab584be..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/usecase/SprFieldInquiryUseCase.java +++ /dev/null @@ -1,198 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.spr.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.spr.dto.SprToolRequest; -import io.shinhanlife.dat.mcc.biz.spr.dto.SprToolResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface SprFieldInquiryUseCase { - @McpTool(name = "spr_field_inquiry_list", title = "GA현장문의 목록 조회", description = "GA현장문의 목록 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_LIST", - functionDescription = "GA현장문의 목록 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 GA현장문의 목록 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "GA현장문의 목록 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"GA현장문의 목록 조회 정보를 보여줘", "GA현장문의 목록 조회 결과를 확인해줘", "현재 GA현장문의 목록 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquiryList(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_detail", title = "GA현장문의 상세 조회", description = "GA현장문의 상세 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_DETAIL", - functionDescription = "GA현장문의 상세 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 GA현장문의 상세 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "GA현장문의 상세 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"GA현장문의 상세 조회 정보를 보여줘", "GA현장문의 상세 조회 결과를 확인해줘", "현재 GA현장문의 상세 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquiryDetail(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_status", title = "문의 처리상태 조회", description = "문의 처리상태 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_STATUS", - functionDescription = "문의 처리상태 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 문의 처리상태 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "문의 처리상태 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"문의 처리상태 조회 정보를 보여줘", "문의 처리상태 조회 결과를 확인해줘", "현재 문의 처리상태 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquiryStatus(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_category", title = "문의 유형별 통계 조회", description = "문의 유형별 통계 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_CATEGORY", - functionDescription = "문의 유형별 통계 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 문의 유형별 통계 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "문의 유형별 통계 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"문의 유형별 통계 조회 정보를 보여줘", "문의 유형별 통계 조회 결과를 확인해줘", "현재 문의 유형별 통계 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquiryCategory(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_answer", title = "문의 답변 조회", description = "문의 답변 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_ANSWER", - functionDescription = "문의 답변 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 문의 답변 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "문의 답변 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"문의 답변 조회 정보를 보여줘", "문의 답변 조회 결과를 확인해줘", "현재 문의 답변 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquiryAnswer(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_escalation", title = "에스컬레이션 현황 조회", description = "에스컬레이션 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_ESCALATION", - functionDescription = "에스컬레이션 현황 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 에스컬레이션 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "에스컬레이션 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"에스컬레이션 현황 조회 정보를 보여줘", "에스컬레이션 현황 조회 결과를 확인해줘", "현재 에스컬레이션 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquiryEscalation(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_faq", title = "자주묻는질문 FAQ 조회", description = "자주묻는질문 FAQ 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_FAQ", - functionDescription = "자주묻는질문 FAQ 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 자주묻는질문 FAQ 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "자주묻는질문 FAQ 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"자주묻는질문 FAQ 조회 정보를 보여줘", "자주묻는질문 FAQ 조회 결과를 확인해줘", "현재 자주묻는질문 FAQ 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquiryFaq(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_manager", title = "담당자 배정 현황 조회", description = "담당자 배정 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_MANAGER", - functionDescription = "담당자 배정 현황 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 담당자 배정 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "담당자 배정 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"담당자 배정 현황 조회 정보를 보여줘", "담당자 배정 현황 조회 결과를 확인해줘", "현재 담당자 배정 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquiryManager(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_sla", title = "처리기한 SLA 현황 조회", description = "처리기한 SLA 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_SLA", - functionDescription = "처리기한 SLA 현황 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 처리기한 SLA 현황 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "처리기한 SLA 현황 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"처리기한 SLA 현황 조회 정보를 보여줘", "처리기한 SLA 현황 조회 결과를 확인해줘", "현재 처리기한 SLA 현황 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquirySla(SprToolRequest request); - - @McpTool(name = "spr_field_inquiry_satisfaction", title = "문의 만족도 조사 결과 조회", description = "문의 만족도 조사 결과 조회 정보를 안전한 모의 데이터로 제공합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "spr", - mappingId = "DIRECT_SPR_FIELD_INQUIRY_SATISFACTION", - functionDescription = "문의 만족도 조사 결과 조회 정보를 조회합니다.", - whenToUse = "GA현장지원 업무에서 문의 만족도 조사 결과 조회 정보가 필요할 때 사용합니다.", - whenNotToUse = "정보 변경, 승인 또는 실제 업무처리에는 사용하지 않습니다.", - ioLimits = "입력 식별자에 대한 모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "문의 만족도 조사 결과 조회 정보를 안전한 모의 데이터로 제공합니다.", - exampleQueries = {"문의 만족도 조사 결과 조회 정보를 보여줘", "문의 만족도 조사 결과 조회 결과를 확인해줘", "현재 문의 만족도 조사 결과 조회 내용을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"spr", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SprToolResponse getFieldInquirySatisfaction(SprToolRequest request); -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/usecase/impl/SprFieldInquiryUseCaseImpl.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/usecase/impl/SprFieldInquiryUseCaseImpl.java deleted file mode 100644 index db5439d0e..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/biz/spr/usecase/impl/SprFieldInquiryUseCaseImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.spr.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.spr.dto.SprToolRequest; -import io.shinhanlife.dat.mcc.biz.spr.dto.SprToolResponse; -import io.shinhanlife.dat.mcc.biz.spr.usecase.SprFieldInquiryUseCase; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class SprFieldInquiryUseCaseImpl implements SprFieldInquiryUseCase { - @Override - public SprToolResponse getFieldInquiryList(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_list", "GA현장문의 목록 조회"); - } - - @Override - public SprToolResponse getFieldInquiryDetail(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_detail", "GA현장문의 상세 조회"); - } - - @Override - public SprToolResponse getFieldInquiryStatus(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_status", "문의 처리상태 조회"); - } - - @Override - public SprToolResponse getFieldInquiryCategory(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_category", "문의 유형별 통계 조회"); - } - - @Override - public SprToolResponse getFieldInquiryAnswer(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_answer", "문의 답변 조회"); - } - - @Override - public SprToolResponse getFieldInquiryEscalation(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_escalation", "에스컬레이션 현황 조회"); - } - - @Override - public SprToolResponse getFieldInquiryFaq(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_faq", "자주묻는질문 FAQ 조회"); - } - - @Override - public SprToolResponse getFieldInquiryManager(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_manager", "담당자 배정 현황 조회"); - } - - @Override - public SprToolResponse getFieldInquirySla(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_sla", "처리기한 SLA 현황 조회"); - } - - @Override - public SprToolResponse getFieldInquirySatisfaction(SprToolRequest request) { - return mockResponse(request, "spr_field_inquiry_satisfaction", "문의 만족도 조사 결과 조회"); - } - - private SprToolResponse mockResponse(SprToolRequest request, String toolName, String title) { - SprToolResponse response = new SprToolResponse(); - response.setToolName(toolName); - response.setCategory("SPR"); - response.setReferenceId(request != null && hasText(request.getInquiryId()) ? request.getInquiryId() - : request != null && hasText(request.getAgentId()) ? request.getAgentId() : "SPR-000001"); - response.setStatus("정상"); - response.setSummary(title + " 결과"); - response.setAsOfDate(request != null && hasText(request.getQueryDate()) ? request.getQueryDate() : "2026-08-14"); - response.setDetails(List.of(title + " 모의 상세정보", "외부 시스템을 변경하지 않는 조회 결과")); - return response; - } - - private boolean hasText(String value) { - return value != null && !value.isBlank(); - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/MemoClient.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/MemoClient.java deleted file mode 100644 index dfd0ac2d8..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/MemoClient.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.http.memo; - -import io.shinhanlife.dat.lib.integration.http.component.AxhubHttpComponent; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class MemoClient { - private static final String API_NAME = "memo"; - - private final AxhubHttpComponent http; - - public O call(I request, Class responseType) { - return http.call(API_NAME, request, responseType); - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/io/MemoListRetrieverHttpRequest.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/io/MemoListRetrieverHttpRequest.java deleted file mode 100644 index 475856124..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/io/MemoListRetrieverHttpRequest.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.http.memo.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 MemoListRetrieverHttpRequest { - @Schema(description = "조회할 의뢰서 상태", example = "OPEN", requiredMode = Schema.RequiredMode.REQUIRED) - private String memoStatus; - - @Schema(description = "검색 키워드", example = "프로젝트", requiredMode = Schema.RequiredMode.REQUIRED) - private String searchKeyword; - -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/io/MemoListRetrieverHttpResponse.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/io/MemoListRetrieverHttpResponse.java deleted file mode 100644 index 983f7afbe..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/http/memo/io/MemoListRetrieverHttpResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.http.memo.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 MemoListRetrieverHttpResponse { - private String resultCode; - - private String resultMessage; -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/MciNclaClient.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/MciNclaClient.java deleted file mode 100644 index 6bac3b0c6..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/MciNclaClient.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.ncla; - -import org.springframework.stereotype.Component; -import lombok.RequiredArgsConstructor; -import io.shinhanlife.dat.lib.integration.mci.component.AxhubMciComponent; -import io.shinhanlife.glow.communication.dto.Transfer; - -/** - * @package io.shinhanlife.dat.mcc.infra.itrf.mci.ncla - * @className MciNclaClient - * @description AX HUB 시스템 처리 클래스 - * @author jade - * @create 2026.08.10 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.08.10  jade    최초생성
- *
- * 
- */ -@Component -@RequiredArgsConstructor -public class MciNclaClient { - private final AxhubMciComponent mci; - - public Transfer callTo(String interfaceId, String dummy, Object mciReq, Class resType) { - return mci.callTo(interfaceId, dummy, mciReq, resType); - } -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_I.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_I.java deleted file mode 100644 index ef6b436d9..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_I.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.io; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -public class CLCNNB00001_I { - @Schema(description = "보험금 청구번호", example = "CLM202608100001", requiredMode = Schema.RequiredMode.REQUIRED) - private String claimNo; - - @Schema(description = "보험 계약번호", example = "10023456789") - private String contractNo; - -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_O.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_O.java deleted file mode 100644 index e81ba4aca..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/infra/itrf/mci/ncla/io/CLCNNB00001_O.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.io; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Data -public class CLCNNB00001_O { - @Schema(description = "청구 처리 상태 코드", example = "RECEIVED", requiredMode = Schema.RequiredMode.REQUIRED) - private String status; - - @Schema(description = "청구 처리 상태명", example = "접수", requiredMode = Schema.RequiredMode.REQUIRED) - private String statusLabel; - - @Schema(description = "승인 금액", example = "150000") - private Long approvedAmount; - -} diff --git a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/sal/DatWasSalApplication.java b/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/sal/DatWasSalApplication.java deleted file mode 100644 index 00d40003e..000000000 --- a/dat-was-sal/src/main/java/io/shinhanlife/dat/mcc/sal/DatWasSalApplication.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.shinhanlife.dat.mcc.sal; - - -/** - * @package io.shinhanlife.dat.mcc.sal - * @className DatWasSalApplication - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationPropertiesScan; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Import; -import io.shinhanlife.dat.lib.mcp.ToolMcpServerConfiguration; - -@SpringBootApplication(scanBasePackages = {"io.shinhanlife.dat.mcc", "io.shinhanlife.dat.lib"}) -@ConfigurationPropertiesScan(basePackages = {"io.shinhanlife.dat.mcc", "io.shinhanlife.dat.lib"}) -@EnableCaching -@Import(ToolMcpServerConfiguration.class) -public class DatWasSalApplication { - public static void main(String[] args) { - SpringApplication.run(DatWasSalApplication.class, args); - } -} diff --git a/dat-was-sal/src/main/resources/application-dev.yml b/dat-was-sal/src/main/resources/application-dev.yml deleted file mode 100644 index 6aed956e9..000000000 --- a/dat-was-sal/src/main/resources/application-dev.yml +++ /dev/null @@ -1,15 +0,0 @@ -# OCI 클라우드 개발 환경 전용 설정 -server: - port: ${PORT:8082} - -axhub: - gateway: - url: https://axhubmcp.devjun.net - -spring: - config: - activate: - on-profile: dev - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-dev.yml diff --git a/dat-was-sal/src/main/resources/application-local.yml b/dat-was-sal/src/main/resources/application-local.yml deleted file mode 100644 index 6f71fa089..000000000 --- a/dat-was-sal/src/main/resources/application-local.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Local 환경 전용 설정 (H2 메모리 DB 등) -spring: - config: - activate: - on-profile: local - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-local.yml - -axhub: - tool: - url: ${AXHUB_TOOL_URL:http://localhost:${server.port}} diff --git a/dat-was-sal/src/main/resources/application-prod.yml b/dat-was-sal/src/main/resources/application-prod.yml deleted file mode 100644 index 8d6f1ab04..000000000 --- a/dat-was-sal/src/main/resources/application-prod.yml +++ /dev/null @@ -1,16 +0,0 @@ -server: - port: ${PORT:8082} - -spring: - config: - activate: - on-profile: prod - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-prod.yml - -axhub: - gateway: - url: ${AXHUB_GATEWAY_URL} - tool: - url: ${AXHUB_TOOL_URL} \ No newline at end of file diff --git a/dat-was-sal/src/main/resources/application-test.yml b/dat-was-sal/src/main/resources/application-test.yml deleted file mode 100644 index a394e88df..000000000 --- a/dat-was-sal/src/main/resources/application-test.yml +++ /dev/null @@ -1,16 +0,0 @@ -server: - port: ${PORT:8082} - -spring: - config: - activate: - on-profile: test - import: - - classpath:glow/application-glow.yml - - classpath:glow/application-glow-test.yml - -axhub: - gateway: - url: ${AXHUB_GATEWAY_URL} - tool: - url: ${AXHUB_TOOL_URL} \ No newline at end of file diff --git a/dat-was-sal/src/main/resources/application.yml b/dat-was-sal/src/main/resources/application.yml deleted file mode 100644 index 5b53d9746..000000000 --- a/dat-was-sal/src/main/resources/application.yml +++ /dev/null @@ -1,22 +0,0 @@ -server: - port: 8082 -spring: - application: - name: dat-was-sal - config: - import: optional:classpath:tool-service-manifest.yml - profiles: - active: local -logging: - level: - org.apache.kafka: ERROR -mcp: - namespace: "" - manifest: - bundle-id: was-sal - # Set the AA-assigned prefix before MCP pull activation (for example: sal.). - name-prefix: "" - security: - api-key: ${TOOL_SERVER_API_KEY:tool-server-key} - tenant-domains: - TESTER-DEV: ALL diff --git a/dat-was-sal/src/main/resources/logback-spring.xml b/dat-was-sal/src/main/resources/logback-spring.xml deleted file mode 100644 index eda389e1a..000000000 --- a/dat-was-sal/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - ${LOG_PATTERN} - - - - - - - - - /swlog/dat-was-sal/A01/${HOSTNAME}_A01.log - - - /swlog/dat-was-sal/A01/${HOSTNAME}_A01_%d{yyyyMMdd}.%i.log - - 100MB - 30 - 1GB - - - ${LOG_PATTERN} - - - - - - - - - - - - diff --git a/dat-was-sal/src/main/resources/mock-responses/cmm_memo_retriever.json b/dat-was-sal/src/main/resources/mock-responses/cmm_memo_retriever.json deleted file mode 100644 index a949502b1..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/cmm_memo_retriever.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "resultCode": "SUCCESS" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_document.json b/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_document.json deleted file mode 100644 index 99650ca3a..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_document.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_contract_review_document\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"계약심사 제출서류 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"계약심사 제출서류 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_history.json b/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_history.json deleted file mode 100644 index bf49c60e4..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_history.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_contract_review_history\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"계약심사 이력 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"계약심사 이력 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_result.json b/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_result.json deleted file mode 100644 index 2020e2bd6..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_result.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_contract_review_result\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"계약심사 결과 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"계약심사 결과 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_status.json b/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_status.json deleted file mode 100644 index f813199df..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_contract_review_status.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_contract_review_status\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"계약심사 진행상태 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"계약심사 진행상태 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_product_coverage.json b/dat-was-sal/src/main/resources/mock-responses/lic_product_coverage.json deleted file mode 100644 index 9e1d63c64..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_product_coverage.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_product_coverage\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"상품 보장내용 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"상품 보장내용 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_product_info.json b/dat-was-sal/src/main/resources/mock-responses/lic_product_info.json deleted file mode 100644 index fbb220f73..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_product_info.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_product_info\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"상품 정보 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"상품 정보 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_product_premium_table.json b/dat-was-sal/src/main/resources/mock-responses/lic_product_premium_table.json deleted file mode 100644 index ea30062ec..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_product_premium_table.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_product_premium_table\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"상품 보험료 요율표 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"상품 보험료 요율표 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_additional_request.json b/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_additional_request.json deleted file mode 100644 index 3bf048d43..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_additional_request.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_underwriting_additional_request\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"언더라이팅 추가자료 요청 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"언더라이팅 추가자료 요청 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_result.json b/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_result.json deleted file mode 100644 index 13d7d7b6c..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_result.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_underwriting_result\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"언더라이팅 인수심사 결과 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"언더라이팅 인수심사 결과 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_risk_grade.json b/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_risk_grade.json deleted file mode 100644 index 24d54cf58..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/lic_underwriting_risk_grade.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"lic_underwriting_risk_grade\",\"category\":\"LIC\",\"referenceId\":\"LIC-000001\",\"status\":\"정상\",\"summary\":\"언더라이팅 위험등급 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"언더라이팅 위험등급 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sms_cmm_claim_search.json b/dat-was-sal/src/main/resources/mock-responses/sms_cmm_claim_search.json deleted file mode 100644 index 9c4f9c037..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sms_cmm_claim_search.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "status" : "RECEIVED", - "statusLabel" : "접수", - "approvedAmount" : 150000 -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_activity_calendar.json b/dat-was-sal/src/main/resources/mock-responses/sof_activity_calendar.json deleted file mode 100644 index 8aa12eb8d..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_activity_calendar.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_activity_calendar\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"활동 일정 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"활동 일정 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_activity_daily.json b/dat-was-sal/src/main/resources/mock-responses/sof_activity_daily.json deleted file mode 100644 index e10f8dc3d..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_activity_daily.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_activity_daily\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"GA 일별 활동 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 일별 활동 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_activity_report_status.json b/dat-was-sal/src/main/resources/mock-responses/sof_activity_report_status.json deleted file mode 100644 index 533993ac1..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_activity_report_status.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_activity_report_status\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"활동일지 제출 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"활동일지 제출 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_activity_summary.json b/dat-was-sal/src/main/resources/mock-responses/sof_activity_summary.json deleted file mode 100644 index 1f46cc369..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_activity_summary.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_activity_summary\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"GA 활동 종합 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 활동 종합 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_call_activity.json b/dat-was-sal/src/main/resources/mock-responses/sof_call_activity.json deleted file mode 100644 index 78d5a136f..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_call_activity.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_call_activity\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"콜 활동 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"콜 활동 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_customer_consulting.json b/dat-was-sal/src/main/resources/mock-responses/sof_customer_consulting.json deleted file mode 100644 index 2fa0e9a55..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_customer_consulting.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_customer_consulting\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"고객 상담 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 상담 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_field_support_request.json b/dat-was-sal/src/main/resources/mock-responses/sof_field_support_request.json deleted file mode 100644 index 3bec29c52..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_field_support_request.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_field_support_request\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"현장 지원 요청 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"현장 지원 요청 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_prospect_list.json b/dat-was-sal/src/main/resources/mock-responses/sof_prospect_list.json deleted file mode 100644 index 205d8dc9a..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_prospect_list.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_prospect_list\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"신규 발굴 고객 목록 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"신규 발굴 고객 목록 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_training_completion.json b/dat-was-sal/src/main/resources/mock-responses/sof_training_completion.json deleted file mode 100644 index f2cabec6a..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_training_completion.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_training_completion\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"교육 이수 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"교육 이수 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sof_visit_count.json b/dat-was-sal/src/main/resources/mock-responses/sof_visit_count.json deleted file mode 100644 index f43017c74..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sof_visit_count.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sof_visit_count\",\"category\":\"SOF\",\"referenceId\":\"SOF-000001\",\"status\":\"정상\",\"summary\":\"고객 방문 건수 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객 방문 건수 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_incentive_summary.json b/dat-was-sal/src/main/resources/mock-responses/sog_incentive_summary.json deleted file mode 100644 index 2ab905bbf..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_incentive_summary.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_incentive_summary\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"영업 인센티브 요약 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"영업 인센티브 요약 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_monthly_performance.json b/dat-was-sal/src/main/resources/mock-responses/sog_monthly_performance.json deleted file mode 100644 index 79fdc1b78..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_monthly_performance.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_monthly_performance\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"GA 월별 실적 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 월별 실적 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_new_contract_count.json b/dat-was-sal/src/main/resources/mock-responses/sog_new_contract_count.json deleted file mode 100644 index 3d8bd3945..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_new_contract_count.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_new_contract_count\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"신계약 건수 실적 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"신계약 건수 실적 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_performance_comparison.json b/dat-was-sal/src/main/resources/mock-responses/sog_performance_comparison.json deleted file mode 100644 index 0fa8fd9bb..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_performance_comparison.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_performance_comparison\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"전년 동기 대비 실적 비교 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"전년 동기 대비 실적 비교 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_premium_performance.json b/dat-was-sal/src/main/resources/mock-responses/sog_premium_performance.json deleted file mode 100644 index 2b3e876e3..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_premium_performance.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_premium_performance\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"보험료 실적 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"보험료 실적 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_product_performance.json b/dat-was-sal/src/main/resources/mock-responses/sog_product_performance.json deleted file mode 100644 index 1d1ba6cbf..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_product_performance.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_product_performance\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"상품별 실적 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"상품별 실적 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_sales_ranking.json b/dat-was-sal/src/main/resources/mock-responses/sog_sales_ranking.json deleted file mode 100644 index c02ed4a18..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_sales_ranking.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_sales_ranking\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"GA 영업 순위 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 영업 순위 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_target_history.json b/dat-was-sal/src/main/resources/mock-responses/sog_target_history.json deleted file mode 100644 index 6e1aab445..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_target_history.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_target_history\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"영업목표 변경 이력 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"영업목표 변경 이력 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_target_progress.json b/dat-was-sal/src/main/resources/mock-responses/sog_target_progress.json deleted file mode 100644 index ee5adc78a..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_target_progress.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_target_progress\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"영업목표 달성률 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"영업목표 달성률 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sog_target_summary.json b/dat-was-sal/src/main/resources/mock-responses/sog_target_summary.json deleted file mode 100644 index 70053ed32..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sog_target_summary.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sog_target_summary\",\"category\":\"SOG\",\"referenceId\":\"SOG-000001\",\"status\":\"정상\",\"summary\":\"GA 개인별 영업목표 요약 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA 개인별 영업목표 요약 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_consent_expiry.json b/dat-was-sal/src/main/resources/mock-responses/sot_consent_expiry.json deleted file mode 100644 index 9683c2002..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_consent_expiry.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_consent_expiry\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"동의 유효기간 만료 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"동의 유효기간 만료 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_consent_withdrawal.json b/dat-was-sal/src/main/resources/mock-responses/sot_consent_withdrawal.json deleted file mode 100644 index 1b83f6920..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_consent_withdrawal.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_consent_withdrawal\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"동의 철회 이력 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"동의 철회 이력 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_customer_info_consent.json b/dat-was-sal/src/main/resources/mock-responses/sot_customer_info_consent.json deleted file mode 100644 index d000e4977..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_customer_info_consent.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_customer_info_consent\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"고객정보 수집·이용 동의 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객정보 수집·이용 동의 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_customer_info_consent_scope.json b/dat-was-sal/src/main/resources/mock-responses/sot_customer_info_consent_scope.json deleted file mode 100644 index bce43d9b7..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_customer_info_consent_scope.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_customer_info_consent_scope\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"고객정보 동의 범위 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"고객정보 동의 범위 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_channel.json b/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_channel.json deleted file mode 100644 index 2120afb03..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_channel.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_marketing_consent_channel\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"채널별 마케팅 동의 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"채널별 마케팅 동의 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_history.json b/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_history.json deleted file mode 100644 index 5697fff0d..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_history.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_marketing_consent_history\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"마케팅 동의 이력 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"마케팅 동의 이력 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_status.json b/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_status.json deleted file mode 100644 index e6d4e0fb0..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_marketing_consent_status.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_marketing_consent_status\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"마케팅 수신동의 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"마케팅 수신동의 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_subscription_design_consent.json b/dat-was-sal/src/main/resources/mock-responses/sot_subscription_design_consent.json deleted file mode 100644 index ae8ee1bbc..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_subscription_design_consent.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_subscription_design_consent\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"가입설계 동의 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"가입설계 동의 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_subscription_design_detail.json b/dat-was-sal/src/main/resources/mock-responses/sot_subscription_design_detail.json deleted file mode 100644 index 6c2847774..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_subscription_design_detail.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_subscription_design_detail\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"가입설계 동의 상세 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"가입설계 동의 상세 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/sot_tm_call_eligibility.json b/dat-was-sal/src/main/resources/mock-responses/sot_tm_call_eligibility.json deleted file mode 100644 index 3c7d45a59..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/sot_tm_call_eligibility.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"sot_tm_call_eligibility\",\"category\":\"SOT\",\"referenceId\":\"SOT-000001\",\"status\":\"정상\",\"summary\":\"텔레마케팅 콜 가능여부 종합 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"텔레마케팅 콜 가능여부 종합 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_answer.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_answer.json deleted file mode 100644 index d861b08a4..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_answer.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_answer\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"문의 답변 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"문의 답변 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_category.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_category.json deleted file mode 100644 index c1b03bbd8..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_category.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_category\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"문의 유형별 통계 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"문의 유형별 통계 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_detail.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_detail.json deleted file mode 100644 index 7a8f3b7a2..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_detail.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_detail\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"GA현장문의 상세 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA현장문의 상세 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_escalation.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_escalation.json deleted file mode 100644 index 8b6eaebd5..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_escalation.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_escalation\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"에스컬레이션 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"에스컬레이션 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_faq.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_faq.json deleted file mode 100644 index cd43237ec..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_faq.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_faq\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"자주묻는질문 FAQ 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"자주묻는질문 FAQ 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_list.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_list.json deleted file mode 100644 index f7daea8dc..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_list.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_list\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"GA현장문의 목록 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"GA현장문의 목록 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_manager.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_manager.json deleted file mode 100644 index 5c2811280..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_manager.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_manager\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"담당자 배정 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"담당자 배정 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_satisfaction.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_satisfaction.json deleted file mode 100644 index 2c03b22e1..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_satisfaction.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_satisfaction\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"문의 만족도 조사 결과 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"문의 만족도 조사 결과 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_sla.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_sla.json deleted file mode 100644 index 5fe9a343f..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_sla.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_sla\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"처리기한 SLA 현황 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"처리기한 SLA 현황 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_status.json b/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_status.json deleted file mode 100644 index b89d0d9a2..000000000 --- a/dat-was-sal/src/main/resources/mock-responses/spr_field_inquiry_status.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "resultCode": "SUCCESS", - "data": "{\"toolName\":\"spr_field_inquiry_status\",\"category\":\"SPR\",\"referenceId\":\"SPR-000001\",\"status\":\"정상\",\"summary\":\"문의 처리상태 조회 결과\",\"asOfDate\":\"2026-08-14\",\"details\":[\"문의 처리상태 조회 모의 상세정보\",\"외부 시스템을 변경하지 않는 조회 결과\"]}" -} diff --git a/dat-was-sal/src/main/resources/tool-schemas/cmm/claim-search-resource-input-schema.json b/dat-was-sal/src/main/resources/tool-schemas/cmm/claim-search-resource-input-schema.json deleted file mode 100644 index e6f63087e..000000000 --- a/dat-was-sal/src/main/resources/tool-schemas/cmm/claim-search-resource-input-schema.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "object", - "additionalProperties": false, - "properties": { - "TODO_FIELD": { - "type": "string", - "description": "TODO: 파라미터 설명을 입력하세요." - } - }, - "required": [] -} diff --git a/dat-was-sal/src/main/resources/tool-schemas/cmm/claim-search-resource-output-schema.json b/dat-was-sal/src/main/resources/tool-schemas/cmm/claim-search-resource-output-schema.json deleted file mode 100644 index 9c280db87..000000000 --- a/dat-was-sal/src/main/resources/tool-schemas/cmm/claim-search-resource-output-schema.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "object", - "additionalProperties": false, - "properties": { - "status": { - "type": "string", - "description": "처리 결과 상태 (SUCCESS / FAILURE)", - "enum": ["SUCCESS", "FAILURE"] - }, - "message": { - "type": "string", - "description": "처리 결과 메시지" - } - }, - "required": ["status"] -} diff --git a/dat-was-sal/src/main/resources/tool-service-manifest.yml b/dat-was-sal/src/main/resources/tool-service-manifest.yml deleted file mode 100644 index d37a7ff13..000000000 --- a/dat-was-sal/src/main/resources/tool-service-manifest.yml +++ /dev/null @@ -1,16 +0,0 @@ -mcp: - manifest: - routing-functions: - - name: route_to_dat-was-sal - description-serialization: 영업 업무 서버로 요청을 라우팅합니다. 영업 조직과 설계사 업무를 처리합니다. 요청의 주요 엔티티와 업무 영역을 기준으로 영업 서버를 선택합니다. - server-id: dat-was-sal - category-key: sal - product-boundary: insurance - business-domain: 영업 관리 - business-outcome: 영업 조직과 설계사 업무를 처리합니다. - primary-entities: [설계사, 영업조직, 실적] - capabilities: [설계사 조회, 영업조직 조회, 실적 조회] - select-if: 설계사, 영업조직 또는 영업실적 관련 요청인 경우 - reject-if: 고객, 계약, 상품, 시스템 운영 업무가 주된 요청인 경우 - confusable-servers: [dat-was-cus, dat-was-pro, dat-was-sys] - decision-policy: 요청의 주요 엔티티와 업무 영역을 기준으로 영업 서버를 선택합니다. diff --git a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java b/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java deleted file mode 100644 index d588a5943..000000000 --- a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/ClaimSearchUseCaseTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.usecase; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchResponse; -import org.junit.jupiter.api.Test; - -class ClaimSearchUseCaseTest { - - @Test - void createsToolRequestAndResponseDtos() { - assertNotNull(new ClaimSearchRequest()); - assertNotNull(new ClaimSearchResponse()); - } -} diff --git a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/MemoListRetrieverUseCaseTest.java b/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/MemoListRetrieverUseCaseTest.java deleted file mode 100644 index 68164eeac..000000000 --- a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/MemoListRetrieverUseCaseTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.usecase; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import io.shinhanlife.dat.mcc.biz.cmm.dto.MemoListRetrieverRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.MemoListRetrieverResponse; -import org.junit.jupiter.api.Test; - -class MemoListRetrieverUseCaseTest { - - @Test - void createsToolRequestAndResponseDtos() { - assertNotNull(new MemoListRetrieverRequest()); - assertNotNull(new MemoListRetrieverResponse()); - } -} diff --git a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImplTest.java b/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImplTest.java deleted file mode 100644 index cdccd7d6b..000000000 --- a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/cmm/usecase/impl/ClaimSearchUseCaseImplTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.cmm.usecase.impl; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import io.shinhanlife.dat.mcc.biz.cmm.converter.ClaimSearchConverter; -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchRequest; -import io.shinhanlife.dat.mcc.biz.cmm.dto.ClaimSearchResponse; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.MciNclaClient; -import io.shinhanlife.dat.mcc.infra.itrf.mci.ncla.io.CLCNNB00001_O; -import io.shinhanlife.glow.BizException; -import org.junit.jupiter.api.Test; - -class ClaimSearchUseCaseImplTest { - - @Test - void returnsErrorResponseWhenMciReportsABusinessCommunicationFailure() { - MciNclaClient mci = mock(MciNclaClient.class); - when(mci.callTo(eq("CLCNNB00001"), any(), any(), eq(CLCNNB00001_O.class))) - .thenThrow(bizException()); - ClaimSearchUseCaseImpl useCase = new ClaimSearchUseCaseImpl(mci, mock(ClaimSearchConverter.class)); - - ClaimSearchResponse response = useCase.searchClaim(mock(ClaimSearchRequest.class)); - - assertEquals("ERROR", response.getResultCode()); - } - - @Test - void doesNotHideUnexpectedRuntimeFailures() { - MciNclaClient mci = mock(MciNclaClient.class); - when(mci.callTo(eq("CLCNNB00001"), any(), any(), eq(CLCNNB00001_O.class))) - .thenThrow(new IllegalStateException("unexpected defect")); - ClaimSearchUseCaseImpl useCase = new ClaimSearchUseCaseImpl(mci, mock(ClaimSearchConverter.class)); - - assertThrows(IllegalStateException.class, - () -> useCase.searchClaim(mock(ClaimSearchRequest.class))); - } - - private BizException bizException() { - return new BizException("CST00477", new String[]{"MCI communication failure"}, - new RuntimeException("connection closed")); - } -} diff --git a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/lic/usecase/impl/LicUnderwritingUseCaseImplTest.java b/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/lic/usecase/impl/LicUnderwritingUseCaseImplTest.java deleted file mode 100644 index b11e28076..000000000 --- a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/lic/usecase/impl/LicUnderwritingUseCaseImplTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.lic.usecase.impl; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -import io.shinhanlife.dat.mcc.biz.lic.dto.LicToolRequest; -import org.junit.jupiter.api.Test; - -class LicUnderwritingUseCaseImplTest { - - @Test - void returnsProductInfoForRequestedProductCode() { - LicToolRequest request = new LicToolRequest(); - request.setProductCode("LIC-PRD-000123"); - request.setQueryDate("2026-08-14"); - - var response = new LicUnderwritingUseCaseImpl().getProductInfo(request); - - assertEquals("lic_product_info", response.getToolName()); - assertEquals("LIC-PRD-000123", response.getReferenceId()); - assertEquals("정상", response.getStatus()); - assertFalse(response.getDetails().isEmpty()); - } -} diff --git a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sof/usecase/impl/SofFieldActivityUseCaseImplTest.java b/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sof/usecase/impl/SofFieldActivityUseCaseImplTest.java deleted file mode 100644 index 8d3b531c1..000000000 --- a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sof/usecase/impl/SofFieldActivityUseCaseImplTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sof.usecase.impl; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -import io.shinhanlife.dat.mcc.biz.sof.dto.SofToolRequest; -import org.junit.jupiter.api.Test; - -class SofFieldActivityUseCaseImplTest { - - @Test - void returnsActivityDailyForRequestedAgentId() { - SofToolRequest request = new SofToolRequest(); - request.setAgentId("SOF-AGT-000123"); - request.setQueryDate("2026-08-14"); - - var response = new SofFieldActivityUseCaseImpl().getActivityDaily(request); - - assertEquals("sof_activity_daily", response.getToolName()); - assertEquals("SOF-AGT-000123", response.getReferenceId()); - assertEquals("정상", response.getStatus()); - assertFalse(response.getDetails().isEmpty()); - } -} diff --git a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sog/usecase/impl/SogSalesPerformanceUseCaseImplTest.java b/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sog/usecase/impl/SogSalesPerformanceUseCaseImplTest.java deleted file mode 100644 index 5026656bf..000000000 --- a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sog/usecase/impl/SogSalesPerformanceUseCaseImplTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sog.usecase.impl; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -import io.shinhanlife.dat.mcc.biz.sog.dto.SogToolRequest; -import org.junit.jupiter.api.Test; - -class SogSalesPerformanceUseCaseImplTest { - - @Test - void returnsTargetSummaryForRequestedAgentId() { - SogToolRequest request = new SogToolRequest(); - request.setAgentId("SOG-AGT-000123"); - request.setQueryDate("2026-08-14"); - - var response = new SogSalesPerformanceUseCaseImpl().getTargetSummary(request); - - assertEquals("sog_target_summary", response.getToolName()); - assertEquals("SOG-AGT-000123", response.getReferenceId()); - assertEquals("정상", response.getStatus()); - assertFalse(response.getDetails().isEmpty()); - } -} diff --git a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sot/usecase/impl/SotConsentUseCaseImplTest.java b/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sot/usecase/impl/SotConsentUseCaseImplTest.java deleted file mode 100644 index c3ad2f259..000000000 --- a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/sot/usecase/impl/SotConsentUseCaseImplTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.sot.usecase.impl; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -import io.shinhanlife.dat.mcc.biz.sot.dto.SotToolRequest; -import org.junit.jupiter.api.Test; - -class SotConsentUseCaseImplTest { - - @Test - void returnsMarketingConsentStatusForRequestedCustomerId() { - SotToolRequest request = new SotToolRequest(); - request.setCustomerId("SOT-CUS-000123"); - request.setQueryDate("2026-08-14"); - - var response = new SotConsentUseCaseImpl().getMarketingConsentStatus(request); - - assertEquals("sot_marketing_consent_status", response.getToolName()); - assertEquals("SOT-CUS-000123", response.getReferenceId()); - assertEquals("정상", response.getStatus()); - assertFalse(response.getDetails().isEmpty()); - } -} diff --git a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/spr/usecase/impl/SprFieldInquiryUseCaseImplTest.java b/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/spr/usecase/impl/SprFieldInquiryUseCaseImplTest.java deleted file mode 100644 index 9a1a29835..000000000 --- a/dat-was-sal/src/test/java/io/shinhanlife/dat/mcc/biz/spr/usecase/impl/SprFieldInquiryUseCaseImplTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.spr.usecase.impl; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -import io.shinhanlife.dat.mcc.biz.spr.dto.SprToolRequest; -import org.junit.jupiter.api.Test; - -class SprFieldInquiryUseCaseImplTest { - - @Test - void returnsFieldInquiryListForRequestedInquiryId() { - SprToolRequest request = new SprToolRequest(); - request.setInquiryId("SPR-INQ-000123"); - request.setQueryDate("2026-08-14"); - - var response = new SprFieldInquiryUseCaseImpl().getFieldInquiryList(request); - - assertEquals("spr_field_inquiry_list", response.getToolName()); - assertEquals("SPR-INQ-000123", response.getReferenceId()); - assertEquals("정상", response.getStatus()); - assertFalse(response.getDetails().isEmpty()); - } -} diff --git a/dat-was-sys/build.gradle b/dat-was-sys/build.gradle index e08421661..eabbf1417 100644 --- a/dat-was-sys/build.gradle +++ b/dat-was-sys/build.gradle @@ -4,6 +4,8 @@ plugins { } dependencies { - // Shared MCP, Glow integration, validation, logging, Lombok and MapStruct configuration. - implementation project(':dat-was-lib') + // dat-lib-datmt의 JAR/POM을 Maven Local 또는 사내 Nexus에서 내려받습니다. + // includeBuild 없이 이 Maven 좌표만으로 공통 MCP Server, Tool, MCI/EAI 기능을 사용합니다. + // 라이브러리 코드가 변경되면 먼저 dat-lib-datmt에서 publishToMavenLocal 또는 Nexus publish를 실행합니다. + implementation 'io.shinhanlife:dat-lib-datmt:0.0.1-SNAPSHOT' } diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/AuthenticationInquiryUseCase.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/AuthenticationInquiryUseCase.java deleted file mode 100644 index 6d1099a3b..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/AuthenticationInquiryUseCase.java +++ /dev/null @@ -1,116 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.iam.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusRequest; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface AuthenticationInquiryUseCase { - @McpTool(name = "iam_login_history", title = "로그인 이력 조회", description = "모의 로그인 이력 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_LOGIN_HISTORY", - functionDescription = "모의 로그인 이력 조회 정보를 조회합니다.", - whenToUse = "로그인 이력 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "로그인 이력 조회 정보를 조회합니다.", - exampleQueries = {"로그인 이력 조회 정보를 보여줘", "로그인 이력 조회을 확인해줘", "현재 로그인 이력 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getPrimary(SystemStatusRequest request); - - @McpTool(name = "iam_authentication_method", title = "인증 수단 조회", description = "모의 인증 수단 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_AUTHENTICATION_METHOD", - functionDescription = "모의 인증 수단 조회 정보를 조회합니다.", - whenToUse = "인증 수단 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "인증 수단 조회 정보를 조회합니다.", - exampleQueries = {"인증 수단 조회 정보를 보여줘", "인증 수단 조회을 확인해줘", "현재 인증 수단 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getList(SystemStatusRequest request); - - @McpTool(name = "iam_mfa_status", title = "다중 인증 상태 조회", description = "모의 다중 인증 상태 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_MFA_STATUS", - functionDescription = "모의 다중 인증 상태 조회 정보를 조회합니다.", - whenToUse = "다중 인증 상태 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "다중 인증 상태 조회 정보를 조회합니다.", - exampleQueries = {"다중 인증 상태 조회 정보를 보여줘", "다중 인증 상태 조회을 확인해줘", "현재 다중 인증 상태 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getStatus(SystemStatusRequest request); - - @McpTool(name = "iam_access_token_status", title = "접근 토큰 상태 조회", description = "모의 접근 토큰 상태 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_ACCESS_TOKEN_STATUS", - functionDescription = "모의 접근 토큰 상태 조회 정보를 조회합니다.", - whenToUse = "접근 토큰 상태 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "접근 토큰 상태 조회 정보를 조회합니다.", - exampleQueries = {"접근 토큰 상태 조회 정보를 보여줘", "접근 토큰 상태 조회을 확인해줘", "현재 접근 토큰 상태 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getDetail(SystemStatusRequest request); - - @McpTool(name = "iam_session_list", title = "세션 목록 조회", description = "모의 세션 목록 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_SESSION_LIST", - functionDescription = "모의 세션 목록 조회 정보를 조회합니다.", - whenToUse = "세션 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "세션 목록 조회 정보를 조회합니다.", - exampleQueries = {"세션 목록 조회 정보를 보여줘", "세션 목록 조회을 확인해줘", "현재 세션 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getContact(SystemStatusRequest request); - - @McpTool(name = "iam_trusted_device_list", title = "신뢰 기기 목록 조회", description = "모의 신뢰 기기 목록 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_TRUSTED_DEVICE_LIST", - functionDescription = "모의 신뢰 기기 목록 조회 정보를 조회합니다.", - whenToUse = "신뢰 기기 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "신뢰 기기 목록 조회 정보를 조회합니다.", - exampleQueries = {"신뢰 기기 목록 조회 정보를 보여줘", "신뢰 기기 목록 조회을 확인해줘", "현재 신뢰 기기 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getResult(SystemStatusRequest request); -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/AuthorizationInquiryUseCase.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/AuthorizationInquiryUseCase.java deleted file mode 100644 index 18ffe23dc..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/AuthorizationInquiryUseCase.java +++ /dev/null @@ -1,116 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.iam.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusRequest; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface AuthorizationInquiryUseCase { - @McpTool(name = "iam_permission_list", title = "권한 목록 조회", description = "모의 권한 목록 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_PERMISSION_LIST", - functionDescription = "모의 권한 목록 조회 정보를 조회합니다.", - whenToUse = "권한 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "권한 목록 조회 정보를 조회합니다.", - exampleQueries = {"권한 목록 조회 정보를 보여줘", "권한 목록 조회을 확인해줘", "현재 권한 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getPrimary(SystemStatusRequest request); - - @McpTool(name = "iam_role_list", title = "역할 목록 조회", description = "모의 역할 목록 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_ROLE_LIST", - functionDescription = "모의 역할 목록 조회 정보를 조회합니다.", - whenToUse = "역할 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "역할 목록 조회 정보를 조회합니다.", - exampleQueries = {"역할 목록 조회 정보를 보여줘", "역할 목록 조회을 확인해줘", "현재 역할 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getList(SystemStatusRequest request); - - @McpTool(name = "iam_menu_access", title = "메뉴 접근 권한 조회", description = "모의 메뉴 접근 권한 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_MENU_ACCESS", - functionDescription = "모의 메뉴 접근 권한 조회 정보를 조회합니다.", - whenToUse = "메뉴 접근 권한 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "메뉴 접근 권한 조회 정보를 조회합니다.", - exampleQueries = {"메뉴 접근 권한 조회 정보를 보여줘", "메뉴 접근 권한 조회을 확인해줘", "현재 메뉴 접근 권한 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getStatus(SystemStatusRequest request); - - @McpTool(name = "iam_resource_access", title = "자원 접근 권한 조회", description = "모의 자원 접근 권한 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_RESOURCE_ACCESS", - functionDescription = "모의 자원 접근 권한 조회 정보를 조회합니다.", - whenToUse = "자원 접근 권한 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "자원 접근 권한 조회 정보를 조회합니다.", - exampleQueries = {"자원 접근 권한 조회 정보를 보여줘", "자원 접근 권한 조회을 확인해줘", "현재 자원 접근 권한 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getDetail(SystemStatusRequest request); - - @McpTool(name = "iam_approval_authority", title = "결재 권한 조회", description = "모의 결재 권한 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_APPROVAL_AUTHORITY", - functionDescription = "모의 결재 권한 조회 정보를 조회합니다.", - whenToUse = "결재 권한 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "결재 권한 조회 정보를 조회합니다.", - exampleQueries = {"결재 권한 조회 정보를 보여줘", "결재 권한 조회을 확인해줘", "현재 결재 권한 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getContact(SystemStatusRequest request); - - @McpTool(name = "iam_delegation_status", title = "권한 위임 상태 조회", description = "모의 권한 위임 상태 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_DELEGATION_STATUS", - functionDescription = "모의 권한 위임 상태 조회 정보를 조회합니다.", - whenToUse = "권한 위임 상태 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "권한 위임 상태 조회 정보를 조회합니다.", - exampleQueries = {"권한 위임 상태 조회 정보를 보여줘", "권한 위임 상태 조회을 확인해줘", "현재 권한 위임 상태 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getResult(SystemStatusRequest request); -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/SystemStatusUseCase.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/SystemStatusUseCase.java deleted file mode 100644 index c0cdfead2..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/SystemStatusUseCase.java +++ /dev/null @@ -1,190 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.iam.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusRequest; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface SystemStatusUseCase { - @McpTool(name = "iam_system_status", title = "시스템 상태 조회", - description = "조회 전용 모의 시스템 상태를 반환합니다.", - annotations = @McpTool.McpAnnotations(openWorldHint = false)) - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_SYSTEM_STATUS", - functionDescription = "모의 시스템 상태를 조회합니다.", - whenToUse = "시스템 상태 확인이 필요할 때 사용합니다.", - whenNotToUse = "시스템 변경에는 사용하지 않습니다.", - ioLimits = "모의 데이터만 반환합니다.", - displayDescription = "시스템 상태를 조회합니다.", - exampleQueries = {"시스템 상태를 보여줘", "개발 환경 상태를 확인해줘", "현재 서비스가 정상인가요?"}, - destructive = false, - idempotent = true, - tags = {"iam", "상태", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getSystemStatus(SystemStatusRequest request); - - @McpTool(name = "iam_service_health", title = "서비스 상태 조회", description = "모의 서비스 상태를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_SERVICE_HEALTH", - functionDescription = "모의 서비스 상태를 조회합니다.", - whenToUse = "서비스 상태 확인이 필요할 때 사용합니다.", - whenNotToUse = "서비스 변경에는 사용하지 않습니다.", - ioLimits = "모의 데이터만 반환합니다.", - displayDescription = "서비스 상태를 조회합니다.", - exampleQueries = {"서비스 상태를 보여줘", "서비스가 정상인가요?", "개발 서비스 상태를 확인해줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "서비스", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getServiceHealth(SystemStatusRequest request); - - @McpTool(name = "iam_environment_info", title = "환경 정보 조회", description = "모의 환경 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_ENVIRONMENT_INFO", - functionDescription = "모의 환경 정보를 조회합니다.", - whenToUse = "환경 정보 확인이 필요할 때 사용합니다.", - whenNotToUse = "환경 변경에는 사용하지 않습니다.", - ioLimits = "모의 데이터만 반환합니다.", - displayDescription = "환경 정보를 조회합니다.", - exampleQueries = {"환경 정보를 보여줘", "개발 환경을 확인해줘", "현재 환경 정보를 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "환경", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getEnvironmentInfo(SystemStatusRequest request); - - @McpTool(name = "iam_deployment_version", title = "배포 버전 조회", description = "모의 배포 버전을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_DEPLOYMENT_VERSION", - functionDescription = "모의 배포 버전을 조회합니다.", - whenToUse = "배포 버전 확인이 필요할 때 사용합니다.", - whenNotToUse = "배포 실행에는 사용하지 않습니다.", - ioLimits = "모의 데이터만 반환합니다.", - displayDescription = "배포 버전을 조회합니다.", - exampleQueries = {"배포 버전을 보여줘", "현재 버전을 확인해줘", "개발 환경 버전을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "배포", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getDeploymentVersion(SystemStatusRequest request); - - @McpTool(name = "iam_access_policy", title = "접근 정책 조회", description = "모의 접근 정책을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_ACCESS_POLICY", - functionDescription = "모의 접근 정책 조회 정보를 조회합니다.", - whenToUse = "접근 정책 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "접근 정책 조회 정보를 조회합니다.", - exampleQueries = {"접근 정책 조회 정보를 보여줘", "접근 정책 조회을 확인해줘", "현재 접근 정책 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getAccessPolicy(SystemStatusRequest request); - - @McpTool(name = "iam_session_status", title = "세션 상태 조회", description = "모의 세션 상태를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_SESSION_STATUS", - functionDescription = "모의 세션 상태 조회 정보를 조회합니다.", - whenToUse = "세션 상태 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "세션 상태 조회 정보를 조회합니다.", - exampleQueries = {"세션 상태 조회 정보를 보여줘", "세션 상태 조회을 확인해줘", "현재 세션 상태 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getSessionStatus(SystemStatusRequest request); - - @McpTool(name = "iam_identity_provider_list", title = "인증 제공자 목록 조회", description = "모의 인증 제공자 목록을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_IDENTITY_PROVIDER_LIST", - functionDescription = "모의 인증 제공자 목록 조회 정보를 조회합니다.", - whenToUse = "인증 제공자 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "인증 제공자 목록 조회 정보를 조회합니다.", - exampleQueries = {"인증 제공자 목록 조회 정보를 보여줘", "인증 제공자 목록 조회을 확인해줘", "현재 인증 제공자 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getIdentityProviderList(SystemStatusRequest request); - - @McpTool(name = "iam_security_event_list", title = "보안 이벤트 목록 조회", description = "모의 보안 이벤트 목록을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_SECURITY_EVENT_LIST", - functionDescription = "모의 보안 이벤트 목록 조회 정보를 조회합니다.", - whenToUse = "보안 이벤트 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "보안 이벤트 목록 조회 정보를 조회합니다.", - exampleQueries = {"보안 이벤트 목록 조회 정보를 보여줘", "보안 이벤트 목록 조회을 확인해줘", "현재 보안 이벤트 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getSecurityEventList(SystemStatusRequest request); - - @McpTool(name = "iam_privilege_change_history", title = "권한 변경 이력 조회", description = "모의 권한 변경 이력을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_PRIVILEGE_CHANGE_HISTORY", - functionDescription = "모의 권한 변경 이력 조회 정보를 조회합니다.", - whenToUse = "권한 변경 이력 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "권한 변경 이력 조회 정보를 조회합니다.", - exampleQueries = {"권한 변경 이력 조회 정보를 보여줘", "권한 변경 이력 조회을 확인해줘", "현재 권한 변경 이력 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getPrivilegeChangeHistory(SystemStatusRequest request); - - @McpTool(name = "iam_user_group_list", title = "사용자 그룹 목록 조회", description = "모의 사용자 그룹 목록을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "iam", - mappingId = "DIRECT_IAM_USER_GROUP_LIST", - functionDescription = "모의 사용자 그룹 목록 조회 정보를 조회합니다.", - whenToUse = "사용자 그룹 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "사용자 그룹 목록 조회 정보를 조회합니다.", - exampleQueries = {"사용자 그룹 목록 조회 정보를 보여줘", "사용자 그룹 목록 조회을 확인해줘", "현재 사용자 그룹 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"iam", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemStatusResponse getUserGroupList(SystemStatusRequest request); -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/TeamContactUseCase.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/TeamContactUseCase.java deleted file mode 100644 index f37743ed8..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/TeamContactUseCase.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.iam.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.iam.dto.TeamContactRequest; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface TeamContactUseCase { - - @McpTool(name = "iam_team_contact", title = "팀 연락처 및 담당자 조회", description = "AX 팀, MCP 팀, TOOL 팀 등 특정 팀이나 전체 팀의 인원 및 담당자 정보를 조회합니다. 대상을 입력하여 원하는 정보만 필터링하여 받을 수 있습니다.") - @GrowToolHint(requiresApproval = false, categoryKey = "iam", mappingId = "DIRECT_IAM_TEAM_CONTACT") - String getTeamContactInfo(TeamContactRequest request); -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/AuthenticationInquiryUseCaseImpl.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/AuthenticationInquiryUseCaseImpl.java deleted file mode 100644 index a8d87e1df..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/AuthenticationInquiryUseCaseImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.iam.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusRequest; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusResponse; -import io.shinhanlife.dat.mcc.biz.iam.usecase.AuthenticationInquiryUseCase; -import org.springframework.stereotype.Service; - -@Service -public class AuthenticationInquiryUseCaseImpl implements AuthenticationInquiryUseCase { - @Override - public SystemStatusResponse getPrimary(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getList(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getStatus(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getDetail(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getContact(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getResult(SystemStatusRequest request) { - return mockResponse(request); - } - - private SystemStatusResponse mockResponse(SystemStatusRequest request) { - SystemStatusResponse response = new SystemStatusResponse(); - response.setSystemName("AX Hub IAM"); - response.setEnvironment(request != null && request.getEnvironment() != null && !request.getEnvironment().isBlank() ? request.getEnvironment() : "개발"); - response.setStatus("정상"); - response.setCheckedAt(java.time.OffsetDateTime.now().toString()); - return response; - } -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/AuthorizationInquiryUseCaseImpl.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/AuthorizationInquiryUseCaseImpl.java deleted file mode 100644 index 40e1487d2..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/AuthorizationInquiryUseCaseImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.iam.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusRequest; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusResponse; -import io.shinhanlife.dat.mcc.biz.iam.usecase.AuthorizationInquiryUseCase; -import org.springframework.stereotype.Service; - -@Service -public class AuthorizationInquiryUseCaseImpl implements AuthorizationInquiryUseCase { - @Override - public SystemStatusResponse getPrimary(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getList(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getStatus(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getDetail(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getContact(SystemStatusRequest request) { - return mockResponse(request); - } - - @Override - public SystemStatusResponse getResult(SystemStatusRequest request) { - return mockResponse(request); - } - - private SystemStatusResponse mockResponse(SystemStatusRequest request) { - SystemStatusResponse response = new SystemStatusResponse(); - response.setSystemName("AX Hub IAM"); - response.setEnvironment(request != null && request.getEnvironment() != null && !request.getEnvironment().isBlank() ? request.getEnvironment() : "개발"); - response.setStatus("정상"); - response.setCheckedAt(java.time.OffsetDateTime.now().toString()); - return response; - } -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/SystemStatusUseCaseImpl.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/SystemStatusUseCaseImpl.java deleted file mode 100644 index ed5ea6c61..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/SystemStatusUseCaseImpl.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.iam.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusRequest; -import io.shinhanlife.dat.mcc.biz.iam.dto.SystemStatusResponse; -import io.shinhanlife.dat.mcc.biz.iam.usecase.SystemStatusUseCase; -import java.time.OffsetDateTime; -import org.springframework.stereotype.Service; - -@Service -public class SystemStatusUseCaseImpl implements SystemStatusUseCase { - @Override public SystemStatusResponse getServiceHealth(SystemStatusRequest request) { return getSystemStatus(request); } - @Override public SystemStatusResponse getEnvironmentInfo(SystemStatusRequest request) { return getSystemStatus(request); } - @Override public SystemStatusResponse getDeploymentVersion(SystemStatusRequest request) { return getSystemStatus(request); } - @Override public SystemStatusResponse getAccessPolicy(SystemStatusRequest request) { return getSystemStatus(request); } - @Override public SystemStatusResponse getSessionStatus(SystemStatusRequest request) { return getSystemStatus(request); } - @Override public SystemStatusResponse getIdentityProviderList(SystemStatusRequest request) { return getSystemStatus(request); } - @Override public SystemStatusResponse getSecurityEventList(SystemStatusRequest request) { return getSystemStatus(request); } - @Override public SystemStatusResponse getPrivilegeChangeHistory(SystemStatusRequest request) { return getSystemStatus(request); } - @Override public SystemStatusResponse getUserGroupList(SystemStatusRequest request) { return getSystemStatus(request); } - @Override - public SystemStatusResponse getSystemStatus(SystemStatusRequest request) { - SystemStatusResponse response = new SystemStatusResponse(); - response.setSystemName("AX Hub 시스템"); - response.setEnvironment(request != null && request.getEnvironment() != null && !request.getEnvironment().isBlank() - ? request.getEnvironment() : "개발"); - response.setStatus("정상"); - response.setCheckedAt(OffsetDateTime.now().toString()); - return response; - } -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/TeamContactUseCaseImpl.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/TeamContactUseCaseImpl.java deleted file mode 100644 index 29877f2ef..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/iam/usecase/impl/TeamContactUseCaseImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.iam.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.iam.dto.TeamContactRequest; -import io.shinhanlife.dat.mcc.biz.iam.usecase.TeamContactUseCase; -import org.springframework.stereotype.Service; - -@Service -public class TeamContactUseCaseImpl implements TeamContactUseCase { - - @Override - public String getTeamContactInfo(TeamContactRequest request) { - String target = request != null && request.getTarget() != null ? request.getTarget() : "전체"; - - if (target.contains("AX") || target.contains("추진")) { - return "- AX 추진팀: 박세진 프로\n"; - } else if (target.contains("이사") || target.contains("담당자") || target.contains("윤희준")) { - return "- MCP & TOOL 팀 담당자: 윤희준 이사\n"; - } else if (target.contains("MCP")) { - return "- MCP 팀: 고석민 수석, 장효원 책임\n"; - } else if (target.contains("TOOL") || target.contains("툴")) { - return "- TOOL 팀: 김형식 수석, 김영진 책임, 김도겸 대리, 이주희 선임, 문주현 선임, 이보람 대리, 박수빈 대리\n"; - } else { - return "=================================================\n" + - "[ 신한라이프 AX HUB 팀 전체 인원 안내 ]\n" + - "=================================================\n" + - "- AX 추진팀: 박세진 프로\n" + - "- MCP & TOOL 팀 담당자: 윤희준 이사\n" + - "- MCP 팀: 고석민 수석, 장효원 책임\n" + - "- TOOL 팀: 김형식 수석, 김영진 책임, 김도겸 대리, 이주희 선임, 문주현 선임, 이보람 대리, 박수빈 대리\n" + - "================================================="; - } - } -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/dto/SystemNoticeRequest.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/dto/SystemNoticeRequest.java deleted file mode 100644 index 2961a71dd..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/dto/SystemNoticeRequest.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pct.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springaicommunity.mcp.annotation.McpToolParam; - -@Data -public class SystemNoticeRequest { - @McpToolParam(description = "공지 분류", required = false) - private String category; - - @McpToolParam(description = "계약심사번호", required = false) - @Schema(pattern = "^[A-Za-z0-9_-]{3,30}$") - private String contractId; -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/dto/SystemNoticeResponse.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/dto/SystemNoticeResponse.java deleted file mode 100644 index ddb164b6b..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/dto/SystemNoticeResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pct.dto; - -import java.util.List; -import lombok.Data; - -@Data -public class SystemNoticeResponse { - private List notices; - - @Data - public static class Notice { - private String title; - private String priority; - private String publishedDate; - } -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/MaintenanceInquiryUseCase.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/MaintenanceInquiryUseCase.java deleted file mode 100644 index 75496eed9..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/MaintenanceInquiryUseCase.java +++ /dev/null @@ -1,116 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pct.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeRequest; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface MaintenanceInquiryUseCase { - @McpTool(name = "pct_maintenance_schedule", title = "점검 일정 조회", description = "모의 점검 일정 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_MAINTENANCE_SCHEDULE", - functionDescription = "모의 점검 일정을 조회합니다.", - whenToUse = "점검 일정 확인이 필요할 때 사용합니다.", - whenNotToUse = "점검 변경에는 사용하지 않습니다.", - ioLimits = "모의 데이터만 반환합니다.", - displayDescription = "점검 일정을 조회합니다.", - exampleQueries = {"점검 일정을 보여줘", "정기 점검이 언제야?", "이번 주 점검을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "점검", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getPrimary(SystemNoticeRequest request); - - @McpTool(name = "pct_maintenance_history", title = "점검 이력 조회", description = "모의 점검 이력 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_MAINTENANCE_HISTORY", - functionDescription = "모의 점검 이력 조회 정보를 조회합니다.", - whenToUse = "점검 이력 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "점검 이력 조회 정보를 조회합니다.", - exampleQueries = {"점검 이력 조회 정보를 보여줘", "점검 이력 조회을 확인해줘", "현재 점검 이력 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getList(SystemNoticeRequest request); - - @McpTool(name = "pct_maintenance_scope", title = "점검 범위 조회", description = "모의 점검 범위 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_MAINTENANCE_SCOPE", - functionDescription = "모의 점검 범위 조회 정보를 조회합니다.", - whenToUse = "점검 범위 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "점검 범위 조회 정보를 조회합니다.", - exampleQueries = {"점검 범위 조회 정보를 보여줘", "점검 범위 조회을 확인해줘", "현재 점검 범위 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getStatus(SystemNoticeRequest request); - - @McpTool(name = "pct_maintenance_status", title = "점검 진행 상태 조회", description = "모의 점검 진행 상태 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_MAINTENANCE_STATUS", - functionDescription = "모의 점검 진행 상태 조회 정보를 조회합니다.", - whenToUse = "점검 진행 상태 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "점검 진행 상태 조회 정보를 조회합니다.", - exampleQueries = {"점검 진행 상태 조회 정보를 보여줘", "점검 진행 상태 조회을 확인해줘", "현재 점검 진행 상태 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getDetail(SystemNoticeRequest request); - - @McpTool(name = "pct_maintenance_contact", title = "점검 담당자 조회", description = "모의 점검 담당자 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_MAINTENANCE_CONTACT", - functionDescription = "모의 점검 담당자 조회 정보를 조회합니다.", - whenToUse = "점검 담당자 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "점검 담당자 조회 정보를 조회합니다.", - exampleQueries = {"점검 담당자 조회 정보를 보여줘", "점검 담당자 조회을 확인해줘", "현재 점검 담당자 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getContact(SystemNoticeRequest request); - - @McpTool(name = "pct_maintenance_result", title = "점검 결과 조회", description = "모의 점검 결과 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_MAINTENANCE_RESULT", - functionDescription = "모의 점검 결과 조회 정보를 조회합니다.", - whenToUse = "점검 결과 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "점검 결과 조회 정보를 조회합니다.", - exampleQueries = {"점검 결과 조회 정보를 보여줘", "점검 결과 조회을 확인해줘", "현재 점검 결과 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getResult(SystemNoticeRequest request); -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/ReleaseInquiryUseCase.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/ReleaseInquiryUseCase.java deleted file mode 100644 index f1d0fdfce..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/ReleaseInquiryUseCase.java +++ /dev/null @@ -1,116 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pct.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeRequest; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface ReleaseInquiryUseCase { - @McpTool(name = "pct_release_notice", title = "배포 공지 조회", description = "모의 배포 공지 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_RELEASE_NOTICE", - functionDescription = "모의 배포 공지를 조회합니다.", - whenToUse = "배포 공지 확인이 필요할 때 사용합니다.", - whenNotToUse = "배포 변경에는 사용하지 않습니다.", - ioLimits = "모의 데이터만 반환합니다.", - displayDescription = "배포 공지를 조회합니다.", - exampleQueries = {"배포 공지를 보여줘", "최근 배포 내용을 알려줘", "개발 배포 공지가 있나요?"}, - destructive = false, - idempotent = true, - tags = {"pct", "배포", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getPrimary(SystemNoticeRequest request); - - @McpTool(name = "pct_release_history", title = "배포 이력 조회", description = "모의 배포 이력 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_RELEASE_HISTORY", - functionDescription = "모의 배포 이력 조회 정보를 조회합니다.", - whenToUse = "배포 이력 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "배포 이력 조회 정보를 조회합니다.", - exampleQueries = {"배포 이력 조회 정보를 보여줘", "배포 이력 조회을 확인해줘", "현재 배포 이력 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getList(SystemNoticeRequest request); - - @McpTool(name = "pct_release_schedule", title = "배포 일정 조회", description = "모의 배포 일정 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_RELEASE_SCHEDULE", - functionDescription = "모의 배포 일정 조회 정보를 조회합니다.", - whenToUse = "배포 일정 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "배포 일정 조회 정보를 조회합니다.", - exampleQueries = {"배포 일정 조회 정보를 보여줘", "배포 일정 조회을 확인해줘", "현재 배포 일정 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getStatus(SystemNoticeRequest request); - - @McpTool(name = "pct_release_version", title = "배포 버전 조회", description = "모의 배포 버전 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_RELEASE_VERSION", - functionDescription = "모의 배포 버전 조회 정보를 조회합니다.", - whenToUse = "배포 버전 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "배포 버전 조회 정보를 조회합니다.", - exampleQueries = {"배포 버전 조회 정보를 보여줘", "배포 버전 조회을 확인해줘", "현재 배포 버전 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getDetail(SystemNoticeRequest request); - - @McpTool(name = "pct_release_scope", title = "배포 범위 조회", description = "모의 배포 범위 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_RELEASE_SCOPE", - functionDescription = "모의 배포 범위 조회 정보를 조회합니다.", - whenToUse = "배포 범위 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "배포 범위 조회 정보를 조회합니다.", - exampleQueries = {"배포 범위 조회 정보를 보여줘", "배포 범위 조회을 확인해줘", "현재 배포 범위 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getContact(SystemNoticeRequest request); - - @McpTool(name = "pct_release_status", title = "배포 상태 조회", description = "모의 배포 상태 조회 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_RELEASE_STATUS", - functionDescription = "모의 배포 상태 조회 정보를 조회합니다.", - whenToUse = "배포 상태 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "배포 상태 조회 정보를 조회합니다.", - exampleQueries = {"배포 상태 조회 정보를 보여줘", "배포 상태 조회을 확인해줘", "현재 배포 상태 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getResult(SystemNoticeRequest request); -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/SystemNoticeUseCase.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/SystemNoticeUseCase.java deleted file mode 100644 index fde3f293b..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/SystemNoticeUseCase.java +++ /dev/null @@ -1,188 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pct.usecase; - -import io.shinhanlife.dat.lib.annotation.GrowToolHint; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeRequest; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeResponse; -import org.springaicommunity.mcp.annotation.McpTool; - -public interface SystemNoticeUseCase { - @McpTool(name = "pct_notice_list", title = "시스템 공지 조회", description = "모의 시스템 공지 목록을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_NOTICE_LIST", - functionDescription = "모의 시스템 공지를 조회합니다.", - whenToUse = "공지 확인이 필요할 때 사용합니다.", - whenNotToUse = "공지 변경에는 사용하지 않습니다.", - ioLimits = "모의 데이터만 반환합니다.", - displayDescription = "시스템 공지를 조회합니다.", - exampleQueries = {"시스템 공지를 보여줘", "현재 공지를 알려줘", "개발 공지가 있나요?"}, - destructive = false, - idempotent = true, - tags = {"pct", "공지", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getSystemNotices(SystemNoticeRequest request); - - @McpTool(name = "pct_notice_search", title = "공지 검색 조회", description = "모의 공지 검색 결과를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_NOTICE_SEARCH", - functionDescription = "모의 공지 검색 결과를 조회합니다.", - whenToUse = "공지를 검색할 때 사용합니다.", - whenNotToUse = "공지 변경에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환합니다.", - displayDescription = "공지 검색 결과를 조회합니다.", - exampleQueries = {"점검 공지를 검색해줘", "배포 공지를 찾아줘", "공지 내용을 조회해줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "공지", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getNoticeSearch(SystemNoticeRequest request); - - @McpTool(name = "pct_notice_recent", title = "최신 공지 조회", description = "모의 최신 공지를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_NOTICE_RECENT", - functionDescription = "모의 최신 공지를 조회합니다.", - whenToUse = "최신 공지 확인이 필요할 때 사용합니다.", - whenNotToUse = "공지 변경에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환합니다.", - displayDescription = "최신 공지를 조회합니다.", - exampleQueries = {"최신 공지를 보여줘", "오늘 공지가 있나요?", "최근 시스템 공지를 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "공지", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getRecentNotices(SystemNoticeRequest request); - - @McpTool(name = "pct_alert_list", title = "알림 목록 조회", description = "모의 알림 목록을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_ALERT_LIST", - functionDescription = "모의 알림 목록을 조회합니다.", - whenToUse = "알림 확인이 필요할 때 사용합니다.", - whenNotToUse = "알림 변경에는 사용하지 않습니다.", - ioLimits = "모의 데이터만 반환합니다.", - displayDescription = "알림 목록을 조회합니다.", - exampleQueries = {"알림 목록을 보여줘", "현재 알림을 알려줘", "점검 알림이 있나요?"}, - destructive = false, - idempotent = true, - tags = {"pct", "알림", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getAlertList(SystemNoticeRequest request); - - @McpTool(name = "pct_notice_detail", title = "공지 상세 조회", description = "모의 공지 상세 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_NOTICE_DETAIL", - functionDescription = "모의 공지 상세 조회 정보를 조회합니다.", - whenToUse = "공지 상세 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "공지 상세 조회 정보를 조회합니다.", - exampleQueries = {"공지 상세 조회 정보를 보여줘", "공지 상세 조회을 확인해줘", "현재 공지 상세 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getNoticeDetail(SystemNoticeRequest request); - - @McpTool(name = "pct_notice_category", title = "공지 분류 조회", description = "모의 공지 분류 정보를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_NOTICE_CATEGORY", - functionDescription = "모의 공지 분류 조회 정보를 조회합니다.", - whenToUse = "공지 분류 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "공지 분류 조회 정보를 조회합니다.", - exampleQueries = {"공지 분류 조회 정보를 보여줘", "공지 분류 조회을 확인해줘", "현재 공지 분류 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getNoticeCategory(SystemNoticeRequest request); - - @McpTool(name = "pct_incident_list", title = "장애 목록 조회", description = "모의 장애 목록을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_INCIDENT_LIST", - functionDescription = "모의 장애 목록 조회 정보를 조회합니다.", - whenToUse = "장애 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "장애 목록 조회 정보를 조회합니다.", - exampleQueries = {"장애 목록 조회 정보를 보여줘", "장애 목록 조회을 확인해줘", "현재 장애 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getIncidentList(SystemNoticeRequest request); - - @McpTool(name = "pct_change_request_list", title = "변경 요청 목록 조회", description = "모의 변경 요청 목록을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_CHANGE_REQUEST_LIST", - functionDescription = "모의 변경 요청 목록 조회 정보를 조회합니다.", - whenToUse = "변경 요청 목록 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "변경 요청 목록 조회 정보를 조회합니다.", - exampleQueries = {"변경 요청 목록 조회 정보를 보여줘", "변경 요청 목록 조회을 확인해줘", "현재 변경 요청 목록 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getChangeRequestList(SystemNoticeRequest request); - - @McpTool(name = "pct_operation_calendar", title = "운영 일정 조회", description = "모의 운영 일정을 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_OPERATION_CALENDAR", - functionDescription = "모의 운영 일정 조회 정보를 조회합니다.", - whenToUse = "운영 일정 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "운영 일정 조회 정보를 조회합니다.", - exampleQueries = {"운영 일정 조회 정보를 보여줘", "운영 일정 조회을 확인해줘", "현재 운영 일정 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getOperationCalendar(SystemNoticeRequest request); - - @McpTool(name = "pct_service_request_status", title = "서비스 요청 상태 조회", description = "모의 서비스 요청 상태를 조회합니다.") - @GrowToolHint( - requiresApproval = false, - categoryKey = "pct", - mappingId = "DIRECT_PCT_SERVICE_REQUEST_STATUS", - functionDescription = "모의 서비스 요청 상태 조회 정보를 조회합니다.", - whenToUse = "서비스 요청 상태 조회 확인이 필요할 때 사용합니다.", - whenNotToUse = "정보 변경이나 실행 작업에는 사용하지 않습니다.", - ioLimits = "모의 조회 데이터만 반환하며 외부 시스템을 변경하지 않습니다.", - displayDescription = "서비스 요청 상태 조회 정보를 조회합니다.", - exampleQueries = {"서비스 요청 상태 조회 정보를 보여줘", "서비스 요청 상태 조회을 확인해줘", "현재 서비스 요청 상태 조회을 알려줘"}, - destructive = false, - idempotent = true, - tags = {"pct", "조회", "mock"}, - ownerOrg = "MCP_TOOL" - ) - SystemNoticeResponse getServiceRequestStatus(SystemNoticeRequest request); -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/MaintenanceInquiryUseCaseImpl.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/MaintenanceInquiryUseCaseImpl.java deleted file mode 100644 index d8b8da8b3..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/MaintenanceInquiryUseCaseImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pct.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeRequest; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeResponse; -import io.shinhanlife.dat.mcc.biz.pct.usecase.MaintenanceInquiryUseCase; -import org.springframework.stereotype.Service; - -@Service -public class MaintenanceInquiryUseCaseImpl implements MaintenanceInquiryUseCase { - @Override - public SystemNoticeResponse getPrimary(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getList(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getStatus(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getDetail(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getContact(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getResult(SystemNoticeRequest request) { - return mockResponse(request); - } - - private SystemNoticeResponse mockResponse(SystemNoticeRequest request) { - SystemNoticeResponse.Notice notice = new SystemNoticeResponse.Notice(); - notice.setTitle("점검 정보 조회"); - notice.setPriority("보통"); - notice.setPublishedDate("2026-08-13"); - SystemNoticeResponse response = new SystemNoticeResponse(); - response.setNotices(java.util.List.of(notice)); - return response; - } -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/ReleaseInquiryUseCaseImpl.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/ReleaseInquiryUseCaseImpl.java deleted file mode 100644 index 3a2087719..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/ReleaseInquiryUseCaseImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pct.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeRequest; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeResponse; -import io.shinhanlife.dat.mcc.biz.pct.usecase.ReleaseInquiryUseCase; -import org.springframework.stereotype.Service; - -@Service -public class ReleaseInquiryUseCaseImpl implements ReleaseInquiryUseCase { - @Override - public SystemNoticeResponse getPrimary(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getList(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getStatus(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getDetail(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getContact(SystemNoticeRequest request) { - return mockResponse(request); - } - - @Override - public SystemNoticeResponse getResult(SystemNoticeRequest request) { - return mockResponse(request); - } - - private SystemNoticeResponse mockResponse(SystemNoticeRequest request) { - SystemNoticeResponse.Notice notice = new SystemNoticeResponse.Notice(); - notice.setTitle("배포 정보 조회"); - notice.setPriority("보통"); - notice.setPublishedDate("2026-08-13"); - SystemNoticeResponse response = new SystemNoticeResponse(); - response.setNotices(java.util.List.of(notice)); - return response; - } -} diff --git a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/SystemNoticeUseCaseImpl.java b/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/SystemNoticeUseCaseImpl.java deleted file mode 100644 index fbc96b9a5..000000000 --- a/dat-was-sys/src/main/java/io/shinhanlife/dat/mcc/biz/pct/usecase/impl/SystemNoticeUseCaseImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.shinhanlife.dat.mcc.biz.pct.usecase.impl; - -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeRequest; -import io.shinhanlife.dat.mcc.biz.pct.dto.SystemNoticeResponse; -import io.shinhanlife.dat.mcc.biz.pct.usecase.SystemNoticeUseCase; -import java.util.List; -import org.springframework.stereotype.Service; - -@Service -public class SystemNoticeUseCaseImpl implements SystemNoticeUseCase { - @Override public SystemNoticeResponse getSystemNotices(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getNoticeSearch(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getRecentNotices(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getAlertList(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getNoticeDetail(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getNoticeCategory(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getIncidentList(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getChangeRequestList(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getOperationCalendar(SystemNoticeRequest request) { return mockResponse(); } - @Override public SystemNoticeResponse getServiceRequestStatus(SystemNoticeRequest request) { return mockResponse(); } - - private SystemNoticeResponse mockResponse() { - SystemNoticeResponse.Notice notice = new SystemNoticeResponse.Notice(); - notice.setTitle("정기 점검 안내"); - notice.setPriority("보통"); - notice.setPublishedDate("2026-08-13"); - SystemNoticeResponse response = new SystemNoticeResponse(); - response.setNotices(List.of(notice)); - return response; - } -} diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 009d09132..3ceba2146 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -1,56 +1,6 @@ -name: ax_hub_mcp_test +name: dat-was-sys services: - was-sal: - build: - context: . - dockerfile: dat-was-sal/Dockerfile - ports: - - "127.0.0.1:9382:8082" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sal.${TOOL_NAMESPACE:-axhub-datmt-test}.svc.cluster.local:8082 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:-http://mci-mock} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:-http://localhost:8082} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:-http://localhost:8082} - - SPRING_PROFILES_ACTIVE=test - - TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key} - restart: unless-stopped - - was-cus: - build: - context: . - dockerfile: dat-was-cus/Dockerfile - ports: - - "127.0.0.1:9384:8084" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-cus.${TOOL_NAMESPACE:-axhub-datmt-test}.svc.cluster.local:8084 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:-http://mci-mock} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:-http://localhost:8084} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:-http://localhost:8084} - - SPRING_PROFILES_ACTIVE=test - - TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key} - restart: unless-stopped - - was-pro: - build: - context: . - dockerfile: dat-was-pro/Dockerfile - ports: - - "127.0.0.1:9385:8085" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-pro.${TOOL_NAMESPACE:-axhub-datmt-test}.svc.cluster.local:8085 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:-http://mci-mock} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:-http://localhost:8085} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:-http://localhost:8085} - - SPRING_PROFILES_ACTIVE=test - - TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key} - restart: unless-stopped was-sys: build: @@ -60,7 +10,7 @@ services: - "127.0.0.1:9386:8086" environment: - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-datmt-test}.svc.cluster.local:8086 + - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-sys-test}.svc.cluster.local:8086 - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:-http://mci-mock} - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:-http://localhost:8086} diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 03b389a74..69d43debc 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,56 +1,6 @@ -name: ax_hub_mcp_dev +name: dat-was-sys services: - was-sal: - build: - context: . - dockerfile: dat-was-sal/Dockerfile - ports: - - "127.0.0.1:9282:8082" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sal.${TOOL_NAMESPACE:-axhub-datmt-dev}.svc.cluster.local:8082 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:-http://mci-mock} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:-http://localhost:8082} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:-http://localhost:8082} - - SPRING_PROFILES_ACTIVE=dev - - TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key} - restart: unless-stopped - - was-cus: - build: - context: . - dockerfile: dat-was-cus/Dockerfile - ports: - - "127.0.0.1:9284:8084" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-cus.${TOOL_NAMESPACE:-axhub-datmt-dev}.svc.cluster.local:8084 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:-http://mci-mock} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:-http://localhost:8084} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:-http://localhost:8084} - - SPRING_PROFILES_ACTIVE=dev - - TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key} - restart: unless-stopped - - was-pro: - build: - context: . - dockerfile: dat-was-pro/Dockerfile - ports: - - "127.0.0.1:9285:8085" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-pro.${TOOL_NAMESPACE:-axhub-datmt-dev}.svc.cluster.local:8085 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:-http://mci-mock} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:-http://localhost:8085} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:-http://localhost:8085} - - SPRING_PROFILES_ACTIVE=dev - - TOOL_SERVER_API_KEY=${TOOL_SERVER_API_KEY:-tool-server-key} - restart: unless-stopped was-sys: build: @@ -60,7 +10,7 @@ services: - "127.0.0.1:9286:8086" environment: - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-datmt-dev}.svc.cluster.local:8086 + - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-sys-dev}.svc.cluster.local:8086 - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:-http://mci-mock} - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:-http://localhost:8086} diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 03c7b283c..2ab2b42f9 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -1,50 +1,6 @@ -name: ax_hub_mcp_local +name: dat-was-sys services: - was-sal: - build: - context: . - dockerfile: dat-was-sal/Dockerfile - ports: - - "8282:8082" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sal.${TOOL_NAMESPACE:-axhub-datmt-local}.svc.cluster.local:8082 - - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - - GLOW_COMMUNICATION_MCI_PORT=8080 - - AXHUB_MEMO_HTTP_DOMAIN=http://localhost:8082 - - AXHUB_INSURANCE_HTTP_DOMAIN=http://localhost:8082 - - SPRING_PROFILES_ACTIVE=local - - was-cus: - build: - context: . - dockerfile: dat-was-cus/Dockerfile - ports: - - "8284:8084" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-cus.${TOOL_NAMESPACE:-axhub-datmt-local}.svc.cluster.local:8084 - - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - - GLOW_COMMUNICATION_MCI_PORT=8080 - - AXHUB_MEMO_HTTP_DOMAIN=http://localhost:8084 - - AXHUB_INSURANCE_HTTP_DOMAIN=http://localhost:8084 - - SPRING_PROFILES_ACTIVE=local - - was-pro: - build: - context: . - dockerfile: dat-was-pro/Dockerfile - ports: - - "8285:8085" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-pro.${TOOL_NAMESPACE:-axhub-datmt-local}.svc.cluster.local:8085 - - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - - GLOW_COMMUNICATION_MCI_PORT=8080 - - AXHUB_MEMO_HTTP_DOMAIN=http://localhost:8085 - - AXHUB_INSURANCE_HTTP_DOMAIN=http://localhost:8085 - - SPRING_PROFILES_ACTIVE=local was-sys: build: @@ -54,7 +10,7 @@ services: - "8286:8086" environment: - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-datmt-local}.svc.cluster.local:8086 + - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-sys-local}.svc.cluster.local:8086 - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - GLOW_COMMUNICATION_MCI_PORT=8080 - AXHUB_MEMO_HTTP_DOMAIN=http://localhost:8086 diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 2c5614ecc..89e6f89a7 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,53 +1,6 @@ -name: ax_hub_mcp_prod +name: dat-was-sys services: - was-sal: - build: - context: . - dockerfile: dat-was-sal/Dockerfile - ports: - - "127.0.0.1:9282:8082" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sal.${TOOL_NAMESPACE:-axhub-datmt-prod}.svc.cluster.local:8082 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:?GLOW_COMMUNICATION_MCI_HOST is required} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:?AXHUB_MEMO_HTTP_DOMAIN is required} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:?AXHUB_INSURANCE_HTTP_DOMAIN is required} - - SPRING_PROFILES_ACTIVE=dev - restart: always - - was-cus: - build: - context: . - dockerfile: dat-was-cus/Dockerfile - ports: - - "127.0.0.1:9284:8084" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-cus.${TOOL_NAMESPACE:-axhub-datmt-prod}.svc.cluster.local:8084 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:?GLOW_COMMUNICATION_MCI_HOST is required} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:?AXHUB_MEMO_HTTP_DOMAIN is required} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:?AXHUB_INSURANCE_HTTP_DOMAIN is required} - - SPRING_PROFILES_ACTIVE=dev - restart: always - - was-pro: - build: - context: . - dockerfile: dat-was-pro/Dockerfile - ports: - - "127.0.0.1:9285:8085" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-pro.${TOOL_NAMESPACE:-axhub-datmt-prod}.svc.cluster.local:8085 - - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:?GLOW_COMMUNICATION_MCI_HOST is required} - - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:?AXHUB_MEMO_HTTP_DOMAIN is required} - - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:?AXHUB_INSURANCE_HTTP_DOMAIN is required} - - SPRING_PROFILES_ACTIVE=dev - restart: always was-sys: build: @@ -57,11 +10,10 @@ services: - "127.0.0.1:9286:8086" environment: - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-datmt-prod}.svc.cluster.local:8086 + - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-sys-prod}.svc.cluster.local:8086 - GLOW_COMMUNICATION_MCI_HOST=${GLOW_COMMUNICATION_MCI_HOST:?GLOW_COMMUNICATION_MCI_HOST is required} - GLOW_COMMUNICATION_MCI_PORT=${GLOW_COMMUNICATION_MCI_PORT:-8080} - AXHUB_MEMO_HTTP_DOMAIN=${AXHUB_MEMO_HTTP_DOMAIN:?AXHUB_MEMO_HTTP_DOMAIN is required} - AXHUB_INSURANCE_HTTP_DOMAIN=${AXHUB_INSURANCE_HTTP_DOMAIN:?AXHUB_INSURANCE_HTTP_DOMAIN is required} - SPRING_PROFILES_ACTIVE=dev restart: always - diff --git a/docker-compose.yml b/docker-compose.yml index 7e6422245..4c5d93be6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,54 +1,7 @@ -name: ax_hub_mcp_tool +name: dat-was-sys services: - was-sal: - build: - context: . - dockerfile: dat-was-sal/Dockerfile - ports: - - "8282:8082" - environment: - - TZ=Asia/Seoul - - AXHUB_SOURCE_DIR=/src - - AXHUB_TOOL_URL=http://was-sal.${TOOL_NAMESPACE:-axhub-dasmt-local}.svc.cluster.local:8082 - - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - - GLOW_COMMUNICATION_MCI_PORT=8080 - - AXHUB_MEMO_HTTP_DOMAIN=http://localhost:8082 - - AXHUB_INSURANCE_HTTP_DOMAIN=http://localhost:8082 - - SPRING_PROFILES_ACTIVE=${ACTIVE_PROFILE:-local} - - was-cus: - build: - context: . - dockerfile: dat-was-cus/Dockerfile - ports: - - "8284:8084" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-cus.${TOOL_NAMESPACE:-axhub-dasmt-local}.svc.cluster.local:8084 - - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - - GLOW_COMMUNICATION_MCI_PORT=8080 - - AXHUB_MEMO_HTTP_DOMAIN=http://localhost:8084 - - AXHUB_INSURANCE_HTTP_DOMAIN=http://localhost:8084 - - SPRING_PROFILES_ACTIVE=${ACTIVE_PROFILE:-local} - - - was-pro: - build: - context: . - dockerfile: dat-was-pro/Dockerfile - ports: - - "8285:8085" - environment: - - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-pro.${TOOL_NAMESPACE:-axhub-dasmt-local}.svc.cluster.local:8085 - - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - - GLOW_COMMUNICATION_MCI_PORT=8080 - - AXHUB_MEMO_HTTP_DOMAIN=http://localhost:8085 - - AXHUB_INSURANCE_HTTP_DOMAIN=http://localhost:8085 - - SPRING_PROFILES_ACTIVE=${ACTIVE_PROFILE:-local} - was-sys: build: context: . @@ -57,10 +10,9 @@ services: - "8286:8086" environment: - TZ=Asia/Seoul - - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-dasmt-local}.svc.cluster.local:8086 + - AXHUB_TOOL_URL=http://was-sys.${TOOL_NAMESPACE:-axhub-sys-local}.svc.cluster.local:8086 - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock - GLOW_COMMUNICATION_MCI_PORT=8080 - AXHUB_MEMO_HTTP_DOMAIN=http://localhost:8086 - AXHUB_INSURANCE_HTTP_DOMAIN=http://localhost:8086 - SPRING_PROFILES_ACTIVE=${ACTIVE_PROFILE:-local} - diff --git a/k8s/base/configmap.yaml b/k8s/base/configmap.yaml index 30abef31b..538669d99 100644 --- a/k8s/base/configmap.yaml +++ b/k8s/base/configmap.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: ConfigMap metadata: - name: datmt-runtime-config + name: datsy-runtime-config labels: - app.kubernetes.io/part-of: axhub-datmt + app.kubernetes.io/part-of: axhub-sys data: SPRING_PROFILES_ACTIVE: dev TZ: Asia/Seoul diff --git a/k8s/base/deployments.yaml b/k8s/base/deployments.yaml index ad41744ae..2576e0162 100644 --- a/k8s/base/deployments.yaml +++ b/k8s/base/deployments.yaml @@ -1,205 +1,10 @@ apiVersion: apps/v1 kind: Deployment -metadata: - name: was-sal - labels: - app.kubernetes.io/name: was-sal - app.kubernetes.io/part-of: axhub-datmt -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: was-sal - template: - metadata: - labels: - app.kubernetes.io/name: was-sal - app.kubernetes.io/part-of: axhub-datmt - spec: - containers: - - name: was-sal - image: was-sal:latest - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8082 - envFrom: - - configMapRef: - name: datmt-runtime-config - - secretRef: - name: datmt-runtime-secrets - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: AXHUB_TOOL_URL - value: http://was-sal.$(POD_NAMESPACE).svc.cluster.local:8082 - resources: - requests: - cpu: 250m - memory: 512Mi - limits: - cpu: "1" - memory: 1Gi - readinessProbe: - tcpSocket: - port: http - initialDelaySeconds: 20 - periodSeconds: 10 - timeoutSeconds: 3 - failureThreshold: 6 - livenessProbe: - tcpSocket: - port: http - initialDelaySeconds: 60 - periodSeconds: 20 - timeoutSeconds: 3 - failureThreshold: 3 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - seccompProfile: - type: RuntimeDefault ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: was-cus - labels: - app.kubernetes.io/name: was-cus - app.kubernetes.io/part-of: axhub-datmt -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: was-cus - template: - metadata: - labels: - app.kubernetes.io/name: was-cus - app.kubernetes.io/part-of: axhub-datmt - spec: - containers: - - name: was-cus - image: was-cus:latest - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8084 - envFrom: - - configMapRef: - name: datmt-runtime-config - - secretRef: - name: datmt-runtime-secrets - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: AXHUB_TOOL_URL - value: http://was-cus.$(POD_NAMESPACE).svc.cluster.local:8084 - resources: - requests: - cpu: 250m - memory: 512Mi - limits: - cpu: "1" - memory: 1Gi - readinessProbe: - tcpSocket: - port: http - initialDelaySeconds: 20 - periodSeconds: 10 - timeoutSeconds: 3 - failureThreshold: 6 - livenessProbe: - tcpSocket: - port: http - initialDelaySeconds: 60 - periodSeconds: 20 - timeoutSeconds: 3 - failureThreshold: 3 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - seccompProfile: - type: RuntimeDefault ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: was-pro - labels: - app.kubernetes.io/name: was-pro - app.kubernetes.io/part-of: axhub-datmt -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: was-pro - template: - metadata: - labels: - app.kubernetes.io/name: was-pro - app.kubernetes.io/part-of: axhub-datmt - spec: - containers: - - name: was-pro - image: was-pro:latest - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8085 - envFrom: - - configMapRef: - name: datmt-runtime-config - - secretRef: - name: datmt-runtime-secrets - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: AXHUB_TOOL_URL - value: http://was-pro.$(POD_NAMESPACE).svc.cluster.local:8085 - resources: - requests: - cpu: 250m - memory: 512Mi - limits: - cpu: "1" - memory: 1Gi - readinessProbe: - tcpSocket: - port: http - initialDelaySeconds: 20 - periodSeconds: 10 - timeoutSeconds: 3 - failureThreshold: 6 - livenessProbe: - tcpSocket: - port: http - initialDelaySeconds: 60 - periodSeconds: 20 - timeoutSeconds: 3 - failureThreshold: 3 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - seccompProfile: - type: RuntimeDefault ---- -apiVersion: apps/v1 -kind: Deployment metadata: name: was-sys labels: app.kubernetes.io/name: was-sys - app.kubernetes.io/part-of: axhub-datmt + app.kubernetes.io/part-of: axhub-sys spec: replicas: 1 selector: @@ -209,7 +14,7 @@ spec: metadata: labels: app.kubernetes.io/name: was-sys - app.kubernetes.io/part-of: axhub-datmt + app.kubernetes.io/part-of: axhub-sys spec: containers: - name: was-sys @@ -220,9 +25,9 @@ spec: containerPort: 8086 envFrom: - configMapRef: - name: datmt-runtime-config + name: datsy-runtime-config - secretRef: - name: datmt-runtime-secrets + name: datsy-runtime-secrets env: - name: POD_NAMESPACE valueFrom: diff --git a/k8s/base/services.yaml b/k8s/base/services.yaml index 746a35b05..547425829 100644 --- a/k8s/base/services.yaml +++ b/k8s/base/services.yaml @@ -1,58 +1,10 @@ apiVersion: v1 kind: Service -metadata: - name: was-sal - labels: - app.kubernetes.io/name: was-sal - app.kubernetes.io/part-of: axhub-datmt -spec: - type: ClusterIP - selector: - app.kubernetes.io/name: was-sal - ports: - - name: http - port: 8082 - targetPort: http ---- -apiVersion: v1 -kind: Service -metadata: - name: was-cus - labels: - app.kubernetes.io/name: was-cus - app.kubernetes.io/part-of: axhub-datmt -spec: - type: ClusterIP - selector: - app.kubernetes.io/name: was-cus - ports: - - name: http - port: 8084 - targetPort: http ---- -apiVersion: v1 -kind: Service -metadata: - name: was-pro - labels: - app.kubernetes.io/name: was-pro - app.kubernetes.io/part-of: axhub-datmt -spec: - type: ClusterIP - selector: - app.kubernetes.io/name: was-pro - ports: - - name: http - port: 8085 - targetPort: http ---- -apiVersion: v1 -kind: Service metadata: name: was-sys labels: app.kubernetes.io/name: was-sys - app.kubernetes.io/part-of: axhub-datmt + app.kubernetes.io/part-of: axhub-sys spec: type: ClusterIP selector: diff --git a/k8s/overlays/dev/kustomization.yaml b/k8s/overlays/dev/kustomization.yaml index 58121185f..fc26c518c 100644 --- a/k8s/overlays/dev/kustomization.yaml +++ b/k8s/overlays/dev/kustomization.yaml @@ -1,7 +1,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -namespace: axhub-datmt-dev +namespace: axhub-sys-dev resources: - ../../base @@ -12,15 +12,6 @@ labels: includeSelectors: true images: - - name: was-sal - newName: image-registry.openshift-image-registry.svc:5000/axhub-datmt-dev/was-sal - newTag: latest - - name: was-cus - newName: image-registry.openshift-image-registry.svc:5000/axhub-datmt-dev/was-cus - newTag: latest - - name: was-pro - newName: image-registry.openshift-image-registry.svc:5000/axhub-datmt-dev/was-pro - newTag: latest - name: was-sys - newName: image-registry.openshift-image-registry.svc:5000/axhub-datmt-dev/was-sys + newName: image-registry.openshift-image-registry.svc:5000/axhub-sys-dev/was-sys newTag: latest diff --git a/settings.gradle b/settings.gradle index f85da64cf..622cb3607 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,5 @@ -rootProject.name = 'dat-was-datmt' +rootProject.name = 'dat-was-sys' -include 'dat-was-lib' -include 'dat-was-cus' -include 'dat-was-sal' -include 'dat-was-pro' -include 'dat-was-sys' \ No newline at end of file +// dat-lib-datmt 소스를 직접 연결하는 includeBuild는 사용하지 않습니다. +// 공통 라이브러리는 Maven 좌표(io.shinhanlife:dat-lib-datmt)로만 가져옵니다. +include 'dat-was-sys'