feat: VMIS-interface 통합 (Spring Boot 2.7 호환)
- VMIS-interface 전체 코드 이식 (33개 Java 파일, 2개 XML)
- 패키지 변경: com.vmis.interfaceapp → go.kr.project.vmis
- Spring Boot 3 → 2 호환: jakarta → javax
- Java 17 → 8 호환: Text Blocks, List.of() 제거
- HttpClient 5 → 4 변환
- GPKI 라이브러리 추가
- application.yml에 VMIS 설정 통합
- MyBatis 매퍼 경로 추가
빌드 성공 확인 ✅
Co-Authored-By: Claude <noreply@anthropic.com>
internalApi
parent
6c677f7f69
commit
ebcd17a6f0
@ -0,0 +1,21 @@
|
||||
package go.kr.project.vmis.client;
|
||||
|
||||
import go.kr.project.vmis.model.basic.BasicRequest;
|
||||
import go.kr.project.vmis.model.basic.BasicResponse;
|
||||
import go.kr.project.vmis.model.common.Envelope;
|
||||
import go.kr.project.vmis.model.ledger.LedgerRequest;
|
||||
import go.kr.project.vmis.model.ledger.LedgerResponse;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* 정부 시스템 연계 API 추상화 인터페이스.
|
||||
*
|
||||
* <p>외부 정부 시스템과의 통신 계약을 명확히 하여 테스트 용이성과
|
||||
* 추후 교체 가능성을 높입니다.</p>
|
||||
*/
|
||||
public interface GovernmentApi {
|
||||
|
||||
ResponseEntity<Envelope<BasicResponse>> callBasic(Envelope<BasicRequest> envelope);
|
||||
|
||||
ResponseEntity<Envelope<LedgerResponse>> callLedger(Envelope<LedgerRequest> envelope);
|
||||
}
|
||||
@ -0,0 +1,627 @@
|
||||
package go.kr.project.vmis.client;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import go.kr.project.vmis.config.properties.VmisProperties;
|
||||
import go.kr.project.vmis.gpki.GpkiService;
|
||||
import go.kr.project.vmis.model.basic.BasicRequest;
|
||||
import go.kr.project.vmis.model.basic.BasicResponse;
|
||||
import go.kr.project.vmis.model.common.Envelope;
|
||||
import go.kr.project.vmis.model.ledger.LedgerRequest;
|
||||
import go.kr.project.vmis.model.ledger.LedgerResponse;
|
||||
import go.kr.project.vmis.util.TxIdUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 정부 시스템 API 클라이언트
|
||||
*
|
||||
* <p>이 클래스는 시군구연계 자동차 정보 조회를 위해 정부 시스템의 API를 호출하는
|
||||
* 클라이언트 역할을 수행합니다. HTTP 통신, 암호화, 에러 처리 등 정부 API와의
|
||||
* 모든 상호작용을 캡슐화합니다.</p>
|
||||
*
|
||||
* <h3>주요 책임:</h3>
|
||||
* <ul>
|
||||
* <li>정부 API 엔드포인트로 HTTP 요청 전송</li>
|
||||
* <li>GPKI(행정전자서명) 암호화/복호화 처리</li>
|
||||
* <li>필수 HTTP 헤더 구성 및 관리</li>
|
||||
* <li>요청/응답 데이터의 JSON 직렬화/역직렬화</li>
|
||||
* <li>트랜잭션 ID(tx_id) 생성 및 추적</li>
|
||||
* <li>네트워크 오류 및 HTTP 에러 처리</li>
|
||||
* <li>상세한 로깅을 통한 디버깅 지원</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>아키텍처 패턴:</h3>
|
||||
* <ul>
|
||||
* <li>Adapter 패턴: 외부 정부 시스템 API를 내부 인터페이스로 변환</li>
|
||||
* <li>Template Method 패턴: callModel 메서드가 공통 흐름을 정의</li>
|
||||
* <li>Dependency Injection: 생성자를 통한 의존성 주입</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>보안 특성:</h3>
|
||||
* <ul>
|
||||
* <li>GPKI 암호화를 통한 데이터 보안 (선택적 활성화)</li>
|
||||
* <li>API 키 기반 인증</li>
|
||||
* <li>기관 식별 정보(INFO_SYS_ID, REGION_CODE 등)를 헤더에 포함</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see RestTemplate
|
||||
* @see GpkiService
|
||||
* @see VmisProperties
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class GovernmentApiClient implements GovernmentApi {
|
||||
|
||||
/**
|
||||
* Spring RestTemplate
|
||||
*
|
||||
* <p>HTTP 클라이언트로서 실제 네트워크 통신을 수행합니다.
|
||||
* 이 객체는 Spring Bean으로 주입되며, 설정에 따라 다음을 포함할 수 있습니다:</p>
|
||||
* <ul>
|
||||
* <li>Connection Timeout 설정</li>
|
||||
* <li>Read Timeout 설정</li>
|
||||
* <li>Connection Pool 관리</li>
|
||||
* <li>메시지 컨버터 (Jackson for JSON)</li>
|
||||
* <li>인터셉터 (로깅, 헤더 추가 등)</li>
|
||||
* </ul>
|
||||
*/
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
/**
|
||||
* VMIS 설정 속성
|
||||
*
|
||||
* <p>application.yml 또는 application.properties에서 로드된 설정값들입니다.
|
||||
* 포함되는 주요 설정:</p>
|
||||
* <ul>
|
||||
* <li>정부 API URL (호스트, 포트, 경로)</li>
|
||||
* <li>API 키 및 인증 정보</li>
|
||||
* <li>시스템 식별 정보 (INFO_SYS_ID, REGION_CODE 등)</li>
|
||||
* <li>GPKI 설정 (인증서 서버 ID 등)</li>
|
||||
* </ul>
|
||||
*/
|
||||
private final VmisProperties props;
|
||||
|
||||
/**
|
||||
* GPKI(행정전자서명) 서비스
|
||||
*
|
||||
* <p>정부24 등 공공기관 간 통신에 사용되는 암호화 서비스입니다.
|
||||
* 주요 기능:</p>
|
||||
* <ul>
|
||||
* <li>요청 데이터 암호화 (공개키 암호화)</li>
|
||||
* <li>응답 데이터 복호화 (개인키 복호화)</li>
|
||||
* <li>전자서명 생성 및 검증</li>
|
||||
* <li>암호화 활성화 여부 확인</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>암호화가 비활성화된 경우 평문(Plain Text)으로 통신합니다.</p>
|
||||
*/
|
||||
private final GpkiService gpkiService;
|
||||
|
||||
/**
|
||||
* Jackson ObjectMapper
|
||||
*
|
||||
* <p>Java 객체와 JSON 문자열 간의 변환을 담당합니다.
|
||||
* 주요 역할:</p>
|
||||
* <ul>
|
||||
* <li>요청 객체를 JSON 문자열로 직렬화 (Serialization)</li>
|
||||
* <li>응답 JSON을 Java 객체로 역직렬화 (Deserialization)</li>
|
||||
* <li>제네릭 타입 처리 (TypeReference 사용)</li>
|
||||
* <li>날짜/시간 포맷 변환</li>
|
||||
* <li>null 값 처리</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Spring Boot가 자동 구성한 ObjectMapper를 주입받아 사용하므로
|
||||
* 전역 설정(날짜 포맷, 네이밍 전략 등)이 일관되게 적용됩니다.</p>
|
||||
*/
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
|
||||
/**
|
||||
* 서비스 타입 열거형
|
||||
*
|
||||
* <p>정부 API 서비스의 종류를 구분하는 열거형입니다.
|
||||
* 각 서비스 타입은 서로 다른 엔드포인트와 API 키를 가집니다.</p>
|
||||
*
|
||||
* <h4>서비스 타입:</h4>
|
||||
* <ul>
|
||||
* <li>BASIC: 자동차 기본사항 조회 서비스
|
||||
* <ul>
|
||||
* <li>차량번호로 기본 정보(소유자, 차종, 용도 등) 조회</li>
|
||||
* <li>비교적 간단한 정보 제공</li>
|
||||
* <li>응답 속도가 빠름</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>LEDGER: 자동차 등록원부(갑) 조회 서비스
|
||||
* <ul>
|
||||
* <li>상세한 등록 정보 및 법적 권리관계 조회</li>
|
||||
* <li>저당권, 압류, 소유권 이전 이력 등 포함</li>
|
||||
* <li>민감 정보를 포함하여 권한 검증이 엄격함</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
public enum ServiceType {
|
||||
/**
|
||||
* Basic service type.
|
||||
*/
|
||||
BASIC,
|
||||
/**
|
||||
* Ledger service type.
|
||||
*/
|
||||
LEDGER }
|
||||
|
||||
/**
|
||||
* HTTP 헤더 구성
|
||||
*
|
||||
* <p>정부 API 호출에 필요한 모든 HTTP 헤더를 구성하는 private 메서드입니다.
|
||||
* 정부 시스템은 엄격한 헤더 검증을 수행하므로 모든 필수 헤더가 정확히 포함되어야 합니다.</p>
|
||||
*
|
||||
* <h3>헤더 구성 항목:</h3>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th>헤더명</th>
|
||||
* <th>설명</th>
|
||||
* <th>예시값</th>
|
||||
* <th>필수여부</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Content-Type</td>
|
||||
* <td>요청 바디의 미디어 타입 및 문자 인코딩</td>
|
||||
* <td>application/json; charset=UTF-8</td>
|
||||
* <td>필수</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Accept</td>
|
||||
* <td>클라이언트가 수용 가능한 응답 형식</td>
|
||||
* <td>application/json</td>
|
||||
* <td>필수</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>gpki_yn</td>
|
||||
* <td>GPKI 암호화 사용 여부 (Y/N)</td>
|
||||
* <td>Y</td>
|
||||
* <td>필수</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>tx_id</td>
|
||||
* <td>트랜잭션 고유 ID (요청 추적용)</td>
|
||||
* <td>20250104123045_abc123</td>
|
||||
* <td>필수</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>cert_server_id</td>
|
||||
* <td>인증서 서버 식별자</td>
|
||||
* <td>VMIS_SERVER_01</td>
|
||||
* <td>필수</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>api_key</td>
|
||||
* <td>서비스별 API 인증 키</td>
|
||||
* <td>abc123def456...</td>
|
||||
* <td>필수</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>cvmis_apikey</td>
|
||||
* <td>CVMIS 시스템 API 키</td>
|
||||
* <td>xyz789uvw012...</td>
|
||||
* <td>필수</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>INFO_SYS_ID</td>
|
||||
* <td>정보시스템 식별자</td>
|
||||
* <td>VMIS_SEOUL</td>
|
||||
* <td>필수</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* <h3>문자 인코딩:</h3>
|
||||
* <ul>
|
||||
* <li>Content-Type에 UTF-8 인코딩을 명시적으로 지정</li>
|
||||
* <li>한글 데이터 처리를 위해 필수</li>
|
||||
* <li>정부 시스템이 다양한 클라이언트와 호환되도록 표준 인코딩 사용</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>보안 고려사항:</h3>
|
||||
* <ul>
|
||||
* <li>API 키는 설정 파일에서 안전하게 관리</li>
|
||||
* <li>로그에 API 키가 노출되지 않도록 주의</li>
|
||||
* <li>각 서비스(BASIC, LEDGER)마다 별도의 API 키 사용 가능</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param svc 서비스 설정 객체 (API 키, 경로 등 포함)
|
||||
* @param txId 이번 요청의 트랜잭션 ID
|
||||
* @return HttpHeaders 구성된 HTTP 헤더 객체
|
||||
*/
|
||||
private HttpHeaders buildHeaders(VmisProperties.GovProps.Service svc, String txId) {
|
||||
// 1. 빈 HttpHeaders 객체 생성
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
// 2. Content-Type 설정
|
||||
// UTF-8 인코딩을 명시하여 한글 데이터가 올바르게 전송되도록 함
|
||||
headers.setContentType(new MediaType("application", "json", StandardCharsets.UTF_8));
|
||||
|
||||
// 3. Accept 헤더 설정
|
||||
// 서버에게 JSON 형식의 응답을 요청함
|
||||
headers.setAccept(java.util.Collections.singletonList(MediaType.APPLICATION_JSON));
|
||||
|
||||
// 4. GPKI 암호화 사용 여부
|
||||
// 정부 서버가 요청 바디 복호화 여부를 결정하는 데 사용
|
||||
headers.add("gpki_yn", gpkiService.isEnabled() ? "Y" : "N");
|
||||
|
||||
// 5. 트랜잭션 ID
|
||||
// 요청 추적, 로그 연관, 문제 해결 시 사용
|
||||
headers.add("tx_id", txId);
|
||||
|
||||
// 6. 인증서 서버 ID
|
||||
// GPKI 인증서를 발급받은 서버의 식별자
|
||||
headers.add("cert_server_id", props.getGpki().getCertServerId());
|
||||
|
||||
// 7. API 인증 키
|
||||
// 서비스별로 다른 API 키 사용 가능 (BASIC과 LEDGER 각각)
|
||||
headers.add("api_key", svc.getApiKey());
|
||||
|
||||
// 8. CVMIS API 키
|
||||
// CVMIS(Car Vehicle Management Information System) 전용 API 키
|
||||
headers.add("cvmis_apikey", svc.getCvmisApikey());
|
||||
|
||||
// 구성 완료된 헤더 반환
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동차 기본사항 조회 API 호출
|
||||
*
|
||||
* <p>타입 안전성이 보장되는 자동차 기본사항 조회 메서드입니다.
|
||||
* 내부적으로 {@link #callModel}을 호출하여 실제 통신을 수행합니다.</p>
|
||||
*
|
||||
* <h3>특징:</h3>
|
||||
* <ul>
|
||||
* <li>제네릭 타입으로 컴파일 타입 타입 체크</li>
|
||||
* <li>요청/응답 객체가 Envelope로 감싸져 있음</li>
|
||||
* <li>Jackson TypeReference를 사용한 제네릭 역직렬화</li>
|
||||
* <li>API 호출 전후로 DB에 로그성 데이터 저장</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>처리 흐름:</h3>
|
||||
* <ol>
|
||||
* <li>요청 정보를 DB에 INSERT (로그 저장)</li>
|
||||
* <li>정부 API 호출</li>
|
||||
* <li>응답 정보를 DB에 UPDATE</li>
|
||||
* <li>에러 발생 시 에러 정보도 DB에 UPDATE</li>
|
||||
* </ol>
|
||||
*
|
||||
* <h3>사용 예시:</h3>
|
||||
* <pre>
|
||||
* BasicRequest request = new BasicRequest();
|
||||
* request.setVehicleNo("12가3456");
|
||||
*
|
||||
* Envelope<BasicRequest> envelope = new Envelope<>();
|
||||
* envelope.setData(request);
|
||||
*
|
||||
* ResponseEntity<Envelope<BasicResponse>> response = govClient.callBasic(envelope);
|
||||
* BasicResponse data = response.getBody().getData();
|
||||
* </pre>
|
||||
*
|
||||
* @param envelope 자동차 기본사항 조회 요청을 담은 Envelope
|
||||
* @return ResponseEntity<Envelope<BasicResponse>> 조회 결과를 담은 응답
|
||||
*/
|
||||
public ResponseEntity<Envelope<BasicResponse>> callBasic(Envelope<BasicRequest> envelope) {
|
||||
// 순수한 전송 책임만 수행: DB 로깅은 서비스 레이어에서 처리
|
||||
return callModel(ServiceType.BASIC, envelope, new TypeReference<Envelope<BasicResponse>>(){});
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동차 등록원부(갑) 조회 API 호출
|
||||
*
|
||||
* <p>타입 안전성이 보장되는 자동차 등록원부 조회 메서드입니다.
|
||||
* 내부적으로 {@link #callModel}을 호출하여 실제 통신을 수행합니다.</p>
|
||||
*
|
||||
* <h3>특징:</h3>
|
||||
* <ul>
|
||||
* <li>제네릭 타입으로 컴파일 타임 타입 체크</li>
|
||||
* <li>요청/응답 객체가 Envelope로 감싸져 있음</li>
|
||||
* <li>Jackson TypeReference를 사용한 제네릭 역직렬화</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>사용 예시:</h3>
|
||||
* <pre>
|
||||
* LedgerRequest request = new LedgerRequest();
|
||||
* request.setVehicleNo("12가3456");
|
||||
* request.setOwnerName("홍길동");
|
||||
*
|
||||
* Envelope<LedgerRequest> envelope = new Envelope<>();
|
||||
* envelope.setData(request);
|
||||
*
|
||||
* ResponseEntity<Envelope<LedgerResponse>> response = govClient.callLedger(envelope);
|
||||
* LedgerResponse data = response.getBody().getData();
|
||||
* </pre>
|
||||
*
|
||||
* @param envelope 자동차 등록원부 조회 요청을 담은 Envelope
|
||||
* @return ResponseEntity<Envelope<LedgerResponse>> 조회 결과를 담은 응답
|
||||
*/
|
||||
public ResponseEntity<Envelope<LedgerResponse>> callLedger(Envelope<LedgerRequest> envelope) {
|
||||
// TypeReference를 사용하여 제네릭 타입 정보 전달
|
||||
// 익명 클래스를 생성하여 타입 소거(Type Erasure) 문제 해결
|
||||
return callModel(ServiceType.LEDGER, envelope, new TypeReference<Envelope<LedgerResponse>>(){});
|
||||
}
|
||||
|
||||
/**
|
||||
* 정부 API 호출 (타입 안전 모델 기반)
|
||||
*
|
||||
* <p>이 메서드는 정부 API 호출의 핵심 로직을 담고 있는 제네릭 메서드입니다.
|
||||
* Java 객체를 받아 JSON으로 변환하고, 암호화하여 전송한 후, 응답을 복호화하여
|
||||
* 다시 Java 객체로 변환하는 전체 파이프라인을 처리합니다.</p>
|
||||
*
|
||||
* <h3>Template Method 패턴:</h3>
|
||||
* <ul>
|
||||
* <li>이 메서드는 Template Method 패턴의 템플릿 역할</li>
|
||||
* <li>공통 처리 흐름을 정의하고 서비스별 차이는 파라미터로 처리</li>
|
||||
* <li>코드 중복을 제거하고 일관성을 보장</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>제네릭 타입 파라미터:</h3>
|
||||
* <ul>
|
||||
* <li><TReq>: 요청 데이터 타입 (BasicRequest 또는 LedgerRequest)</li>
|
||||
* <li><TResp>: 응답 데이터 타입 (BasicResponse 또는 LedgerResponse)</li>
|
||||
* <li>타입 안전성을 보장하여 런타임 에러 방지</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>처리 흐름 (상세):</h3>
|
||||
* <ol>
|
||||
* <li><b>설정 로드:</b>
|
||||
* <ul><li>서비스 타입에 따라 BASIC 또는 LEDGER 설정 선택</li></ul>
|
||||
* </li>
|
||||
* <li><b>URL 및 트랜잭션 ID 구성:</b>
|
||||
* <ul><li>완전한 API URL 생성</li>
|
||||
* <li>고유 트랜잭션 ID 생성</li></ul>
|
||||
* </li>
|
||||
* <li><b>직렬화 (Serialization):</b>
|
||||
* <ul><li>Java 객체(Envelope<TReq>)를 JSON 문자열로 변환</li>
|
||||
* <li>ObjectMapper.writeValueAsString() 사용</li></ul>
|
||||
* </li>
|
||||
* <li><b>헤더 구성:</b>
|
||||
* <ul><li>buildHeaders() 메서드 호출</li>
|
||||
* <li>모든 필수 헤더 추가</li></ul>
|
||||
* </li>
|
||||
* <li><b>GPKI 암호화 (선택적):</b>
|
||||
* <ul><li>GPKI가 활성화된 경우 JSON을 암호화</li>
|
||||
* <li>gpkiEncrypt() 메서드 호출</li></ul>
|
||||
* </li>
|
||||
* <li><b>HTTP 요청 전송:</b>
|
||||
* <ul><li>RestTemplate.exchange()로 POST 요청</li>
|
||||
* <li>요청 로그 기록</li></ul>
|
||||
* </li>
|
||||
* <li><b>GPKI 복호화 (선택적):</b>
|
||||
* <ul><li>성공 응답(2xx)이고 GPKI가 활성화된 경우</li>
|
||||
* <li>gpkiDecrypt() 메서드 호출</li></ul>
|
||||
* </li>
|
||||
* <li><b>역직렬화 (Deserialization):</b>
|
||||
* <ul><li>JSON 문자열을 Java 객체(Envelope<TResp>)로 변환</li>
|
||||
* <li>TypeReference를 사용하여 제네릭 타입 정보 보존</li></ul>
|
||||
* </li>
|
||||
* <li><b>응답 반환:</b>
|
||||
* <ul><li>ResponseEntity로 감싸서 HTTP 정보 포함</li></ul>
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* <h3>에러 처리 전략 (3단계):</h3>
|
||||
* <ol>
|
||||
* <li><b>HttpStatusCodeException (HTTP 에러):</b>
|
||||
* <ul>
|
||||
* <li>정부 API가 4xx 또는 5xx 상태 코드를 반환한 경우</li>
|
||||
* <li>에러 응답 바디를 파싱하여 Envelope 객체로 변환 시도</li>
|
||||
* <li>파싱 실패 시 빈 Envelope 객체 반환</li>
|
||||
* <li>에러 로그 기록 (WARN 레벨)</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li><b>JSON 파싱 에러:</b>
|
||||
* <ul>
|
||||
* <li>응답 JSON이 예상한 형식과 다른 경우</li>
|
||||
* <li>RuntimeException으로 래핑하여 상위로 전파</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li><b>기타 예외:</b>
|
||||
* <ul>
|
||||
* <li>네트워크 타임아웃, 연결 실패 등</li>
|
||||
* <li>RuntimeException으로 래핑하여 상위로 전파</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* <h3>TypeReference의 필요성:</h3>
|
||||
* <p>Java의 제네릭은 런타임에 타입 소거(Type Erasure)가 발생하여
|
||||
* {@code objectMapper.readValue(json, Envelope<TResp>.class)}와 같은 코드는
|
||||
* 컴파일되지 않습니다. TypeReference는 익명 클래스를 사용하여 컴파일 타임의
|
||||
* 제네릭 타입 정보를 런타임에 전달하는 Jackson의 메커니즘입니다.</p>
|
||||
*
|
||||
* <h3>로깅 정보:</h3>
|
||||
* <ul>
|
||||
* <li>요청 로그: [GOV-REQ] url, tx_id, gpki, length</li>
|
||||
* <li>에러 로그: [GOV-ERR] status, body</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <TReq> 요청 데이터의 제네릭 타입
|
||||
* @param <TResp> 응답 데이터의 제네릭 타입
|
||||
* @param type 서비스 타입 (BASIC 또는 LEDGER)
|
||||
* @param envelope 요청 데이터를 담은 Envelope 객체
|
||||
* @param respType 응답 타입에 대한 TypeReference (제네릭 타입 정보 보존용)
|
||||
* @return ResponseEntity<Envelope<TResp>> 응답 데이터를 담은 ResponseEntity
|
||||
* @throws RuntimeException JSON 직렬화/역직렬화 실패, 네트워크 오류, GPKI 암복호화 실패 등
|
||||
*/
|
||||
private <TReq, TResp> ResponseEntity<Envelope<TResp>> callModel(ServiceType type,
|
||||
Envelope<TReq> envelope,
|
||||
TypeReference<Envelope<TResp>> respType) {
|
||||
// 1. 서비스 타입에 따른 설정 로드
|
||||
VmisProperties.GovProps gov = props.getGov();
|
||||
VmisProperties.GovProps.Service svc = (type == ServiceType.BASIC)
|
||||
? gov.getServices().getBasic()
|
||||
: gov.getServices().getLedger();
|
||||
|
||||
// 2. URL 및 트랜잭션 ID 생성
|
||||
String url = gov.buildServiceUrl(svc.getPath());
|
||||
String txId = TxIdUtil.generate();
|
||||
|
||||
try {
|
||||
// 3. 직렬화: Java 객체 → JSON 문자열
|
||||
// ObjectMapper가 Envelope 객체를 JSON으로 변환
|
||||
// 날짜, null 값 등의 처리는 ObjectMapper 설정에 따름
|
||||
String jsonBody = objectMapper.writeValueAsString(envelope);
|
||||
|
||||
// 4. HTTP 헤더 구성
|
||||
HttpHeaders headers = buildHeaders(svc, txId);
|
||||
|
||||
// 5. GPKI 암호화 처리
|
||||
String bodyToSend = jsonBody;
|
||||
if (gpkiService.isEnabled()) {
|
||||
// JSON 평문을 암호화된 문자열로 변환
|
||||
bodyToSend = gpkiEncrypt(jsonBody);
|
||||
}
|
||||
|
||||
// 6. HTTP 엔티티 생성 (헤더 + 바디)
|
||||
HttpEntity<String> request = new HttpEntity<>(bodyToSend, headers);
|
||||
|
||||
// 7. 요청 로그 기록
|
||||
log.info("[GOV-REQ] url={}, tx_id={}, gpki={}, length={}", url, txId, gpkiService.isEnabled(), bodyToSend != null ? bodyToSend.length() : 0);
|
||||
|
||||
// 8. 실제 HTTP POST 요청 전송
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
|
||||
String respBody = response.getBody();
|
||||
|
||||
// 9. GPKI 복호화 처리 (성공 응답인 경우만)
|
||||
if (gpkiService.isEnabled() && response.getStatusCode().is2xxSuccessful()) {
|
||||
// 암호화된 응답을 평문 JSON으로 복호화
|
||||
respBody = gpkiDecrypt(respBody);
|
||||
}
|
||||
|
||||
// 10. 역직렬화: JSON 문자열 → Java 객체
|
||||
// TypeReference를 사용하여 제네릭 타입 정보 전달
|
||||
Envelope<TResp> mapped = objectMapper.readValue(respBody, respType);
|
||||
|
||||
// 11. 응답 반환 (상태 코드, 헤더, 바디 모두 포함)
|
||||
return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(mapped);
|
||||
|
||||
} catch (HttpStatusCodeException ex) {
|
||||
// HTTP 에러 처리 (4xx, 5xx)
|
||||
log.warn("[GOV-ERR] status={}, body={}", ex.getStatusCode(), ex.getResponseBodyAsString());
|
||||
|
||||
// 에러 응답 바디 파싱 시도
|
||||
// 정부 API는 에러 응답도 Envelope 형식으로 반환할 수 있음
|
||||
Envelope<TResp> empty = new Envelope<>();
|
||||
try {
|
||||
// 에러 응답을 Envelope 객체로 파싱
|
||||
Envelope<TResp> parsed = objectMapper.readValue(ex.getResponseBodyAsString(), respType);
|
||||
return ResponseEntity.status(ex.getStatusCode()).headers(ex.getResponseHeaders() != null ? ex.getResponseHeaders() : new HttpHeaders()).body(parsed);
|
||||
} catch (Exception parseEx) {
|
||||
// 파싱 실패 시 빈 Envelope 반환
|
||||
// 호출자는 HTTP 상태 코드로 에러 판단 가능
|
||||
return ResponseEntity.status(ex.getStatusCode()).headers(ex.getResponseHeaders() != null ? ex.getResponseHeaders() : new HttpHeaders()).body(empty);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 기타 모든 예외 (네트워크 오류, JSON 파싱 오류, GPKI 오류 등)
|
||||
// RuntimeException으로 래핑하여 상위로 전파
|
||||
// Spring의 @ExceptionHandler에서 처리 가능
|
||||
throw new RuntimeException("정부 API 호출 중 오류", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GPKI 암호화 처리
|
||||
*
|
||||
* <p>평문 JSON 문자열을 GPKI(행정전자서명) 알고리즘으로 암호화하는 헬퍼 메서드입니다.
|
||||
* 암호화 실패 시 명확한 에러 메시지와 함께 RuntimeException을 발생시킵니다.</p>
|
||||
*
|
||||
* <h3>암호화 과정:</h3>
|
||||
* <ol>
|
||||
* <li>평문 JSON 문자열을 바이트 배열로 변환</li>
|
||||
* <li>정부 시스템의 공개키를 사용하여 암호화</li>
|
||||
* <li>암호화된 바이트 배열을 Base64로 인코딩</li>
|
||||
* <li>Base64 문자열 반환</li>
|
||||
* </ol>
|
||||
*
|
||||
* <h3>에러 처리:</h3>
|
||||
* <ul>
|
||||
* <li>인증서 오류: GPKI 인증서가 유효하지 않거나 만료된 경우</li>
|
||||
* <li>암호화 오류: 암호화 알고리즘 실행 중 오류 발생</li>
|
||||
* <li>인코딩 오류: Base64 인코딩 실패</li>
|
||||
* <li>모든 예외는 RuntimeException으로 래핑하여 즉시 중단</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>보안 고려사항:</h3>
|
||||
* <ul>
|
||||
* <li>공개키 암호화 방식 사용 (비대칭키)</li>
|
||||
* <li>정부 시스템만 개인키로 복호화 가능</li>
|
||||
* <li>민감한 개인정보 보호</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param jsonBody 암호화할 평문 JSON 문자열
|
||||
* @return String Base64로 인코딩된 암호화 문자열
|
||||
* @throws RuntimeException GPKI 암호화 실패 시
|
||||
*/
|
||||
private String gpkiEncrypt(String jsonBody) {
|
||||
try {
|
||||
// GpkiService에 암호화 위임
|
||||
// 실제 암호화 로직은 GpkiService가 캡슐화
|
||||
return gpkiService.encrypt(jsonBody);
|
||||
} catch (Exception e) {
|
||||
// 암호화 실패는 치명적 오류
|
||||
// 평문 데이터를 전송할 수 없으므로 즉시 중단
|
||||
throw new RuntimeException("GPKI 암호화 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GPKI 복호화 처리
|
||||
*
|
||||
* <p>암호화된 응답 문자열을 GPKI(행정전자서명) 알고리즘으로 복호화하는 헬퍼 메서드입니다.
|
||||
* 복호화 실패 시 명확한 에러 메시지와 함께 RuntimeException을 발생시킵니다.</p>
|
||||
*
|
||||
* <h3>복호화 과정:</h3>
|
||||
* <ol>
|
||||
* <li>Base64로 인코딩된 암호문을 바이트 배열로 디코딩</li>
|
||||
* <li>우리 시스템의 개인키를 사용하여 복호화</li>
|
||||
* <li>복호화된 바이트 배열을 UTF-8 문자열로 변환</li>
|
||||
* <li>평문 JSON 문자열 반환</li>
|
||||
* </ol>
|
||||
*
|
||||
* <h3>에러 처리:</h3>
|
||||
* <ul>
|
||||
* <li>인증서 오류: GPKI 인증서가 유효하지 않거나 만료된 경우</li>
|
||||
* <li>복호화 오류: 암호문이 손상되었거나 올바르지 않은 경우</li>
|
||||
* <li>디코딩 오류: Base64 디코딩 실패</li>
|
||||
* <li>문자 인코딩 오류: UTF-8 변환 실패</li>
|
||||
* <li>모든 예외는 RuntimeException으로 래핑하여 즉시 중단</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>보안 고려사항:</h3>
|
||||
* <ul>
|
||||
* <li>개인키 암호화 방식 사용 (비대칭키)</li>
|
||||
* <li>개인키는 안전하게 저장 및 관리 필요</li>
|
||||
* <li>복호화 실패 시 상세 에러 정보 로깅 주의 (정보 유출 방지)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param cipher Base64로 인코딩된 암호화 문자열
|
||||
* @return String 복호화된 평문 JSON 문자열
|
||||
* @throws RuntimeException GPKI 복호화 실패 시
|
||||
*/
|
||||
private String gpkiDecrypt(String cipher) {
|
||||
try {
|
||||
// GpkiService에 복호화 위임
|
||||
// 실제 복호화 로직은 GpkiService가 캡슐화
|
||||
return gpkiService.decrypt(cipher);
|
||||
} catch (Exception e) {
|
||||
// 복호화 실패는 치명적 오류
|
||||
// 암호문을 해석할 수 없으므로 즉시 중단
|
||||
throw new RuntimeException("GPKI 복호화 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package go.kr.project.vmis.config;
|
||||
|
||||
/**
|
||||
* 애플리케이션 전역에서 사용되는 상수를 관리하는 클래스
|
||||
*
|
||||
* <p>이 클래스는 하드코딩된 값들을 상수로 관리하여 유지보수성을 향상시킵니다.</p>
|
||||
* <ul>
|
||||
* <li>연계 결과 코드</li>
|
||||
* <li>시스템 상수</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class ApiConstant {
|
||||
|
||||
// ===== 연계 결과 코드 (CNTC_RESULT_CODE) =====
|
||||
|
||||
/**
|
||||
* 연계 결과 코드: 정상
|
||||
* <p>외부 API 호출이 정상적으로 처리되었음을 나타냅니다.</p>
|
||||
*/
|
||||
public static final String CNTC_RESULT_CODE_SUCCESS = "00";
|
||||
|
||||
/**
|
||||
* 연계 결과 코드: 정보 없음
|
||||
* <p>외부 API 호출이 정상적으로 처리되었으나 정보없음을 나타냅니다.</p>
|
||||
*/
|
||||
public static final String CNTC_RESULT_CODE_NO_DATA = "99";
|
||||
|
||||
/**
|
||||
* 연계 결과 코드: 에러
|
||||
* <p>외부 API 호출 중 에러가 발생했음을 나타냅니다.</p>
|
||||
*/
|
||||
public static final String CNTC_RESULT_CODE_ERROR = "ERROR";
|
||||
|
||||
// ===== 시스템 상수 =====
|
||||
|
||||
/**
|
||||
* 기본 등록자: SYSTEM
|
||||
* <p>시스템에서 자동으로 등록한 레코드의 등록자명입니다.</p>
|
||||
*/
|
||||
public static final String DEFAULT_REGISTRANT = "SYSTEM";
|
||||
|
||||
// ===== Private Constructor =====
|
||||
|
||||
/**
|
||||
* 유틸리티 클래스이므로 인스턴스 생성을 방지합니다.
|
||||
*/
|
||||
private ApiConstant() {
|
||||
throw new AssertionError("Constraint 클래스는 인스턴스화할 수 없습니다.");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package go.kr.project.vmis.config;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* 데이터베이스 및 트랜잭션 설정
|
||||
*
|
||||
* <p>이 클래스는 데이터베이스 연결과 트랜잭션 관리를 위한 설정을 제공합니다.
|
||||
* Spring Boot의 자동 설정을 활용하되, 명시적인 트랜잭션 관리를 위해
|
||||
* TransactionManager를 직접 설정합니다.</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>DataSource: application.yml에서 자동 설정</li>
|
||||
* <li>SqlSessionFactory: MyBatis Spring Boot Starter가 자동 생성</li>
|
||||
* <li>TransactionManager: 명시적으로 설정하여 트랜잭션 관리</li>
|
||||
* <li>MapperScan: go.kr.project.vmis.mapper 패키지의 Mapper 인터페이스 자동 스캔</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
@MapperScan("go.kr.project.vmis.mapper")
|
||||
public class DatabaseConfig {
|
||||
|
||||
/**
|
||||
* 트랜잭션 관리자를 설정합니다.
|
||||
*
|
||||
* <p>DataSourceTransactionManager는 JDBC 기반의 트랜잭션을 관리합니다.
|
||||
* @Transactional 어노테이션을 사용하여 선언적 트랜잭션 관리가 가능합니다.</p>
|
||||
*
|
||||
* <p>트랜잭션 전파(Propagation), 격리 수준(Isolation), 타임아웃 등의
|
||||
* 세부 설정은 @Transactional 어노테이션의 속성으로 지정할 수 있습니다.</p>
|
||||
*
|
||||
* <p>예제:</p>
|
||||
* <pre>
|
||||
* {@code
|
||||
* @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
|
||||
* public void saveData() {
|
||||
* // 트랜잭션 처리가 필요한 로직
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param dataSource Spring Boot가 자동 생성한 DataSource 빈
|
||||
* @return PlatformTransactionManager 트랜잭션 관리자 인스턴스
|
||||
*/
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(DataSource dataSource) {
|
||||
return new DataSourceTransactionManager(dataSource);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package go.kr.project.vmis.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 전역 상수 및 설정값을 관리하는 클래스
|
||||
*
|
||||
* <p>이 클래스는 애플리케이션 전반에서 사용되는 전역 설정값을 제공합니다.
|
||||
* Spring의 @Value를 통해 application.yml의 설정값을 주입받아 사용합니다.</p>
|
||||
*/
|
||||
@Component
|
||||
public class Globals {
|
||||
|
||||
/**
|
||||
* 데이터베이스 타입 (예: mariadb, oracle, mysql 등)
|
||||
*
|
||||
* <p>MyBatis Mapper XML 파일 선택 시 사용됩니다.
|
||||
* mapper-locations: classpath:mybatis/mapper/**\/*_${Globals.DbType}.xml</p>
|
||||
*
|
||||
* <p>예시:
|
||||
* <ul>
|
||||
* <li>DbType = "mariadb" → user_mariadb.xml 매핑</li>
|
||||
* <li>DbType = "oracle" → user_oracle.xml 매핑</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public static String DbType;
|
||||
|
||||
/**
|
||||
* application.yml에서 Globals.DbType 값을 주입합니다.
|
||||
*
|
||||
* @param dbType 데이터베이스 타입 (기본값: mariadb)
|
||||
*/
|
||||
@Value("${Globals.DbType:mariadb}")
|
||||
public void setDbType(String dbType) {
|
||||
Globals.DbType = dbType;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package go.kr.project.vmis.config;
|
||||
|
||||
import go.kr.project.vmis.config.properties.VmisProperties;
|
||||
import go.kr.project.vmis.gpki.GpkiService;
|
||||
import go.kr.project.vmis.gpki.NoopGpkiService;
|
||||
import go.kr.project.vmis.gpki.RealGpkiService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class GpkiConfig {
|
||||
|
||||
@Bean
|
||||
public GpkiService gpkiService(VmisProperties properties) {
|
||||
if (properties.getGpki().isEnabledFlag()) {
|
||||
return new RealGpkiService(properties);
|
||||
}
|
||||
return new NoopGpkiService();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package go.kr.project.vmis.config;
|
||||
|
||||
import go.kr.project.vmis.config.properties.VmisProperties;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* HttpClient 설정
|
||||
* Apache HttpClient 4를 사용한 RestTemplate 구성
|
||||
*/
|
||||
@Configuration
|
||||
public class HttpClientConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(VmisProperties props, RestTemplateBuilder builder) {
|
||||
VmisProperties.GovProps gov = props.getGov();
|
||||
int connectTimeout = gov.getConnectTimeoutMillis();
|
||||
int readTimeout = gov.getReadTimeoutMillis();
|
||||
|
||||
// HttpClient 4 방식의 RequestConfig
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(connectTimeout)
|
||||
.setSocketTimeout(readTimeout)
|
||||
.setConnectionRequestTimeout(connectTimeout)
|
||||
.build();
|
||||
|
||||
// 커넥션 풀 설정
|
||||
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
|
||||
cm.setMaxTotal(100);
|
||||
cm.setDefaultMaxPerRoute(20);
|
||||
|
||||
// HttpClient 생성
|
||||
CloseableHttpClient httpClient = HttpClientBuilder.create()
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setConnectionManager(cm)
|
||||
.build();
|
||||
|
||||
// RestTemplate에 HttpClient 적용
|
||||
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
return builder
|
||||
.requestFactory(() -> requestFactory)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package go.kr.project.vmis.config;
|
||||
|
||||
import io.swagger.v3.oas.models.ExternalDocumentation;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class OpenApiConfig {
|
||||
|
||||
@Bean
|
||||
public OpenAPI vmisOpenAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("VMIS Interface API")
|
||||
.description("시군구연계 자동차 정보 인터페이스 API (자망연계)")
|
||||
.version("v0.1.0")
|
||||
.contact(new Contact().name("VMIS").email("support@example.com"))
|
||||
.license(new License().name("Apache 2.0").url("https://www.apache.org/licenses/LICENSE-2.0.html")))
|
||||
.externalDocs(new ExternalDocumentation()
|
||||
.description("Reference")
|
||||
.url(""));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package go.kr.project.vmis.config;
|
||||
|
||||
import go.kr.project.vmis.config.properties.VmisProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(VmisProperties.class)
|
||||
public class PropertiesConfig {
|
||||
}
|
||||
@ -0,0 +1,180 @@
|
||||
package go.kr.project.vmis.config.properties;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@ConfigurationProperties(prefix = "vmis")
|
||||
@Validated
|
||||
public class VmisProperties {
|
||||
|
||||
@NotNull
|
||||
private SystemProps system = new SystemProps();
|
||||
@NotNull
|
||||
private GpkiProps gpki = new GpkiProps();
|
||||
@NotNull
|
||||
private GovProps gov = new GovProps();
|
||||
|
||||
public SystemProps getSystem() { return system; }
|
||||
public void setSystem(SystemProps system) { this.system = system; }
|
||||
public GpkiProps getGpki() { return gpki; }
|
||||
public void setGpki(GpkiProps gpki) { this.gpki = gpki; }
|
||||
public GovProps getGov() { return gov; }
|
||||
public void setGov(GovProps gov) { this.gov = gov; }
|
||||
|
||||
public static class SystemProps {
|
||||
@NotBlank
|
||||
private String infoSysId;
|
||||
/** INFO_SYS_IP */
|
||||
private String infoSysIp;
|
||||
/** 시군구코드 (SIGUNGU_CODE) */
|
||||
private String regionCode;
|
||||
private String departmentCode;
|
||||
// 담당자 정보
|
||||
private String chargerId;
|
||||
private String chargerIp;
|
||||
private String chargerNm;
|
||||
|
||||
public String getInfoSysId() { return infoSysId; }
|
||||
public void setInfoSysId(String infoSysId) { this.infoSysId = infoSysId; }
|
||||
public String getInfoSysIp() { return infoSysIp; }
|
||||
public void setInfoSysIp(String infoSysIp) { this.infoSysIp = infoSysIp; }
|
||||
public String getRegionCode() { return regionCode; }
|
||||
public void setRegionCode(String regionCode) { this.regionCode = regionCode; }
|
||||
public String getDepartmentCode() { return departmentCode; }
|
||||
public void setDepartmentCode(String departmentCode) { this.departmentCode = departmentCode; }
|
||||
public String getChargerId() { return chargerId; }
|
||||
public void setChargerId(String chargerId) { this.chargerId = chargerId; }
|
||||
public String getChargerIp() { return chargerIp; }
|
||||
public void setChargerIp(String chargerIp) { this.chargerIp = chargerIp; }
|
||||
public String getChargerNm() { return chargerNm; }
|
||||
public void setChargerNm(String chargerNm) { this.chargerNm = chargerNm; }
|
||||
}
|
||||
|
||||
public static class GpkiProps {
|
||||
/** "Y" 또는 "N" */
|
||||
@NotBlank
|
||||
private String enabled = "N";
|
||||
private boolean useSign = true;
|
||||
@NotBlank
|
||||
private String charset = "UTF-8";
|
||||
@NotBlank
|
||||
private String certServerId;
|
||||
@NotBlank
|
||||
private String targetServerId;
|
||||
// Optional advanced config for native GPKI util
|
||||
private Boolean ldap; // null -> util default
|
||||
private String gpkiLicPath; // e.g., C:/gpki2/gpkisecureweb/conf
|
||||
private String certFilePath; // directory for target cert files when LDAP=false
|
||||
private String envCertFilePathName; // ..._env.cer
|
||||
private String envPrivateKeyFilePathName; // ..._env.key
|
||||
private String envPrivateKeyPasswd;
|
||||
private String sigCertFilePathName; // ..._sig.cer
|
||||
private String sigPrivateKeyFilePathName; // ..._sig.key
|
||||
private String sigPrivateKeyPasswd;
|
||||
|
||||
public String getEnabled() { return enabled; }
|
||||
public void setEnabled(String enabled) { this.enabled = enabled; }
|
||||
public boolean isUseSign() { return useSign; }
|
||||
public void setUseSign(boolean useSign) { this.useSign = useSign; }
|
||||
public String getCharset() { return charset; }
|
||||
public void setCharset(String charset) { this.charset = charset; }
|
||||
public String getCertServerId() { return certServerId; }
|
||||
public void setCertServerId(String certServerId) { this.certServerId = certServerId; }
|
||||
public String getTargetServerId() { return targetServerId; }
|
||||
public void setTargetServerId(String targetServerId) { this.targetServerId = targetServerId; }
|
||||
public Boolean getLdap() { return ldap; }
|
||||
public void setLdap(Boolean ldap) { this.ldap = ldap; }
|
||||
public String getGpkiLicPath() { return gpkiLicPath; }
|
||||
public void setGpkiLicPath(String gpkiLicPath) { this.gpkiLicPath = gpkiLicPath; }
|
||||
public String getCertFilePath() { return certFilePath; }
|
||||
public void setCertFilePath(String certFilePath) { this.certFilePath = certFilePath; }
|
||||
public String getEnvCertFilePathName() { return envCertFilePathName; }
|
||||
public void setEnvCertFilePathName(String envCertFilePathName) { this.envCertFilePathName = envCertFilePathName; }
|
||||
public String getEnvPrivateKeyFilePathName() { return envPrivateKeyFilePathName; }
|
||||
public void setEnvPrivateKeyFilePathName(String envPrivateKeyFilePathName) { this.envPrivateKeyFilePathName = envPrivateKeyFilePathName; }
|
||||
public String getEnvPrivateKeyPasswd() { return envPrivateKeyPasswd; }
|
||||
public void setEnvPrivateKeyPasswd(String envPrivateKeyPasswd) { this.envPrivateKeyPasswd = envPrivateKeyPasswd; }
|
||||
public String getSigCertFilePathName() { return sigCertFilePathName; }
|
||||
public void setSigCertFilePathName(String sigCertFilePathName) { this.sigCertFilePathName = sigCertFilePathName; }
|
||||
public String getSigPrivateKeyFilePathName() { return sigPrivateKeyFilePathName; }
|
||||
public void setSigPrivateKeyFilePathName(String sigPrivateKeyFilePathName) { this.sigPrivateKeyFilePathName = sigPrivateKeyFilePathName; }
|
||||
public String getSigPrivateKeyPasswd() { return sigPrivateKeyPasswd; }
|
||||
public void setSigPrivateKeyPasswd(String sigPrivateKeyPasswd) { this.sigPrivateKeyPasswd = sigPrivateKeyPasswd; }
|
||||
|
||||
public boolean isEnabledFlag() { return "Y".equalsIgnoreCase(enabled); }
|
||||
}
|
||||
|
||||
public static class GovProps {
|
||||
@NotBlank
|
||||
private String scheme = "http";
|
||||
@NotBlank
|
||||
private String host;
|
||||
@NotBlank
|
||||
private String basePath;
|
||||
private int connectTimeoutMillis = 5000;
|
||||
private int readTimeoutMillis = 10000;
|
||||
@NotNull
|
||||
private Services services = new Services();
|
||||
|
||||
public String getScheme() { return scheme; }
|
||||
public void setScheme(String scheme) { this.scheme = scheme; }
|
||||
public String getHost() { return host; }
|
||||
public void setHost(String host) { this.host = host; }
|
||||
public String getBasePath() { return basePath; }
|
||||
public void setBasePath(String basePath) { this.basePath = basePath; }
|
||||
public int getConnectTimeoutMillis() { return connectTimeoutMillis; }
|
||||
public void setConnectTimeoutMillis(int connectTimeoutMillis) { this.connectTimeoutMillis = connectTimeoutMillis; }
|
||||
public int getReadTimeoutMillis() { return readTimeoutMillis; }
|
||||
public void setReadTimeoutMillis(int readTimeoutMillis) { this.readTimeoutMillis = readTimeoutMillis; }
|
||||
public Services getServices() { return services; }
|
||||
public void setServices(Services services) { this.services = services; }
|
||||
|
||||
public String buildServiceUrl(String path) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(scheme).append("://").append(host);
|
||||
if (basePath != null && !basePath.isEmpty()) {
|
||||
if (!basePath.startsWith("/")) sb.append('/');
|
||||
sb.append(basePath);
|
||||
}
|
||||
if (path != null && !path.isEmpty()) {
|
||||
if (!path.startsWith("/")) sb.append('/');
|
||||
sb.append(path);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static class Services {
|
||||
@NotNull
|
||||
private Service basic = new Service();
|
||||
@NotNull
|
||||
private Service ledger = new Service();
|
||||
|
||||
public Service getBasic() { return basic; }
|
||||
public void setBasic(Service basic) { this.basic = basic; }
|
||||
public Service getLedger() { return ledger; }
|
||||
public void setLedger(Service ledger) { this.ledger = ledger; }
|
||||
}
|
||||
|
||||
public static class Service {
|
||||
@NotBlank
|
||||
private String path;
|
||||
@NotBlank
|
||||
private String cntcInfoCode;
|
||||
@NotBlank
|
||||
private String apiKey;
|
||||
@NotBlank
|
||||
private String cvmisApikey;
|
||||
|
||||
public String getPath() { return path; }
|
||||
public void setPath(String path) { this.path = path; }
|
||||
public String getCntcInfoCode() { return cntcInfoCode; }
|
||||
public void setCntcInfoCode(String cntcInfoCode) { this.cntcInfoCode = cntcInfoCode; }
|
||||
public String getApiKey() { return apiKey; }
|
||||
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
|
||||
public String getCvmisApikey() { return cvmisApikey; }
|
||||
public void setCvmisApikey(String cvmisApikey) { this.cvmisApikey = cvmisApikey; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
package go.kr.project.vmis.controller;
|
||||
|
||||
import go.kr.project.vmis.model.basic.BasicRequest;
|
||||
import go.kr.project.vmis.model.basic.BasicResponse;
|
||||
import go.kr.project.vmis.model.common.Envelope;
|
||||
import go.kr.project.vmis.model.ledger.LedgerRequest;
|
||||
import go.kr.project.vmis.model.ledger.LedgerResponse;
|
||||
import go.kr.project.vmis.service.CarBassMatterInqireService;
|
||||
import go.kr.project.vmis.service.CarLedgerFrmbkService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 자동차 정보 연계 REST 컨트롤러
|
||||
*
|
||||
* <p>이 컨트롤러는 시군구연계 자동차 정보 조회 API의 진입점(Entry Point)을 제공합니다.
|
||||
* 클라이언트로부터 HTTP 요청을 받아 정부 시스템과의 통신을 중계하는 역할을 담당합니다.</p>
|
||||
*
|
||||
* <h3>주요 기능:</h3>
|
||||
* <ul>
|
||||
* <li>자동차 기본사항 조회 API 제공</li>
|
||||
* <li>자동차 등록원부(갑) 조회 API 제공</li>
|
||||
* <li>요청 데이터를 Envelope로 감싸서 정부 시스템으로 전달</li>
|
||||
* <li>정부 시스템의 응답을 클라이언트에게 반환</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>API 경로:</h3>
|
||||
* <ul>
|
||||
* <li>기본 경로: /api/v1/vehicles</li>
|
||||
* <li>자동차 기본사항 조회: POST /api/v1/vehicles/basic</li>
|
||||
* <li>자동차 등록원부 조회: POST /api/v1/vehicles/ledger</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>아키텍처 설계:</h3>
|
||||
* <ul>
|
||||
* <li>컨트롤러는 얇은(Thin) 레이어로 설계되어 비즈니스 로직을 포함하지 않음</li>
|
||||
* <li>모든 실제 처리는 각 도메인별 서비스에 위임</li>
|
||||
* <li>Swagger/OpenAPI 문서 자동 생성을 위한 어노테이션 포함</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see Envelope
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/vehicles")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Tag(name = "Vehicle Interfaces", description = "시군구연계 자동차 정보 연계 API")
|
||||
public class VehicleInterfaceController {
|
||||
|
||||
private final CarBassMatterInqireService carBassMatterInqireService;
|
||||
private final CarLedgerFrmbkService carLedgerFrmbkService;
|
||||
|
||||
|
||||
/**
|
||||
* 자동차 기본사항 조회 API
|
||||
*
|
||||
* <p>차량번호, 소유자 정보 등 자동차의 기본적인 정보를 조회하는 API입니다.</p>
|
||||
*
|
||||
* <h3>요청 처리 흐름:</h3>
|
||||
* <ol>
|
||||
* <li>클라이언트로부터 JSON 형식의 요청 수신</li>
|
||||
* <li>요청 바디가 Envelope<BasicRequest> 객체로 역직렬화됨</li>
|
||||
* <li>Spring의 @RequestBody 어노테이션이 자동으로 JSON을 객체로 변환</li>
|
||||
* <li>변환된 요청 객체를 GovernmentApiClient로 전달</li>
|
||||
* <li>GovernmentApiClient가 정부 시스템과 통신 수행</li>
|
||||
* <li>응답 데이터를 Envelope<BasicResponse>로 감싸서 반환</li>
|
||||
* <li>Spring이 자동으로 응답 객체를 JSON으로 직렬화하여 클라이언트에게 전송</li>
|
||||
* </ol>
|
||||
*
|
||||
* <h3>HTTP 메서드 및 경로:</h3>
|
||||
* <ul>
|
||||
* <li>메서드: POST</li>
|
||||
* <li>경로: /api/v1/vehicles/basic</li>
|
||||
* <li>Content-Type: application/json (요청)</li>
|
||||
* <li>Accept: application/json (응답)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>데이터 구조:</h3>
|
||||
* <ul>
|
||||
* <li>Envelope: 공통 헤더 정보(전문 ID, 전송 시각 등)와 실제 데이터를 감싸는 래퍼 객체</li>
|
||||
* <li>BasicRequest: 자동차 기본사항 조회를 위한 요청 데이터 (차량번호 등)</li>
|
||||
* <li>BasicResponse: 자동차 기본사항 조회 결과 데이터 (소유자, 차종, 연식 등)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>에러 처리:</h3>
|
||||
* <ul>
|
||||
* <li>JSON 파싱 오류 시 400 Bad Request 반환 (Spring 자동 처리)</li>
|
||||
* <li>정부 API 호출 실패 시 해당 HTTP 상태 코드 그대로 반환</li>
|
||||
* <li>네트워크 오류나 기타 예외 발생 시 RuntimeException으로 래핑되어 처리</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param envelope 자동차 기본사항 조회 요청을 담은 Envelope 객체 공통 헤더(header)와 실제 요청 데이터(data)로 구성됨
|
||||
* @return ResponseEntity<Envelope<BasicResponse>> 조회 결과를 담은 응답 객체 HTTP 상태 코드, 헤더, 응답 바디를 포함
|
||||
*/
|
||||
@PostMapping(value = "/basic", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Operation(
|
||||
summary = "자동차기본사항조회",
|
||||
description = "시군구연계 자동차기본사항조회 인터페이스. 요청 바디를 모델로 받아 정부시스템으로 전달합니다.",
|
||||
requestBody = @RequestBody(
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE,
|
||||
examples = @ExampleObject(
|
||||
name = "기본사항조회 예제",
|
||||
value = "{\"data\": [{\"INFO_SYS_ID\": \"41-345\", \"VHRNO\": \"12가3456\"}]}"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
public ResponseEntity<Envelope<BasicResponse>> basic(
|
||||
@org.springframework.web.bind.annotation.RequestBody Envelope<BasicRequest> envelope
|
||||
) {
|
||||
// 서비스에서 요청 보강/로깅/호출을 모두 오케스트레이션
|
||||
return carBassMatterInqireService.basic(envelope);
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동차 등록원부(갑) 조회 API
|
||||
*
|
||||
* <p>자동차 등록원부의 갑지에 기재된 상세한 등록 정보를 조회하는 API입니다.
|
||||
* 등록원부는 자동차의 법적 소유관계와 등록사항을 증명하는 공적 장부입니다.</p>
|
||||
*
|
||||
* <h3>요청 처리 흐름:</h3>
|
||||
* <ol>
|
||||
* <li>클라이언트로부터 JSON 형식의 요청 수신</li>
|
||||
* <li>요청 바디가 Envelope<LedgerRequest> 객체로 역직렬화됨</li>
|
||||
* <li>Spring의 @RequestBody 어노테이션이 자동으로 JSON을 객체로 변환</li>
|
||||
* <li>변환된 요청 객체를 GovernmentApiClient로 전달</li>
|
||||
* <li>GovernmentApiClient가 정부 시스템과 통신 수행</li>
|
||||
* <li>응답 데이터를 Envelope<LedgerResponse>로 감싸서 반환</li>
|
||||
* <li>Spring이 자동으로 응답 객체를 JSON으로 직렬화하여 클라이언트에게 전송</li>
|
||||
* </ol>
|
||||
*
|
||||
* <h3>HTTP 메서드 및 경로:</h3>
|
||||
* <ul>
|
||||
* <li>메서드: POST</li>
|
||||
* <li>경로: /api/v1/vehicles/ledger</li>
|
||||
* <li>Content-Type: application/json (요청)</li>
|
||||
* <li>Accept: application/json (응답)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>데이터 구조:</h3>
|
||||
* <ul>
|
||||
* <li>Envelope: 공통 헤더 정보(전문 ID, 전송 시각 등)와 실제 데이터를 감싸는 래퍼 객체</li>
|
||||
* <li>LedgerRequest: 자동차 등록원부 조회를 위한 요청 데이터 (차량번호, 소유자 정보 등)</li>
|
||||
* <li>LedgerResponse: 자동차 등록원부 조회 결과 데이터 (상세한 등록사항, 소유권 이력 등)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>등록원부(갑)에 포함되는 정보:</h3>
|
||||
* <ul>
|
||||
* <li>차량 기본 정보 (차명, 차종, 용도 등)</li>
|
||||
* <li>소유자 정보 (성명, 주소, 주민등록번호 등)</li>
|
||||
* <li>등록 이력 (최초 등록일, 변경 이력 등)</li>
|
||||
* <li>저당권 및 압류 정보</li>
|
||||
* <li>기타 법적 권리관계</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>에러 처리:</h3>
|
||||
* <ul>
|
||||
* <li>JSON 파싱 오류 시 400 Bad Request 반환 (Spring 자동 처리)</li>
|
||||
* <li>정부 API 호출 실패 시 해당 HTTP 상태 코드 그대로 반환</li>
|
||||
* <li>네트워크 오류나 기타 예외 발생 시 RuntimeException으로 래핑되어 처리</li>
|
||||
* <li>권한 부족 시 403 Forbidden 또는 401 Unauthorized 반환 가능</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>보안 및 개인정보:</h3>
|
||||
* <ul>
|
||||
* <li>민감한 개인정보를 포함하므로 적절한 인증/인가 필요</li>
|
||||
* <li>GPKI 암호화를 통한 데이터 보안 강화</li>
|
||||
* <li>로그 기록 시 개인정보 마스킹 필요</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param envelope 자동차 등록원부 조회 요청을 담은 Envelope 객체 공통 헤더(header)와 실제 요청 데이터(data)로 구성됨
|
||||
* @return ResponseEntity<Envelope<LedgerResponse>> 등록원부 조회 결과를 담은 응답 객체 HTTP 상태 코드, 헤더, 응답 바디를 포함
|
||||
*/
|
||||
@PostMapping(value = "/ledger", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@Operation(
|
||||
summary = "자동차등록원부(갑)",
|
||||
description = "시군구연계 자동차등록원부(갑) 인터페이스. 요청 바디를 모델로 받아 정부시스템으로 전달합니다.",
|
||||
requestBody = @RequestBody(
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON_VALUE,
|
||||
examples = @ExampleObject(
|
||||
name = "등록원부 조회 예제",
|
||||
value = "{\"data\": [{\"INFO_SYS_ID\": \"41-345\", \"VHRNO\": \"12가3456\"}]}"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
public ResponseEntity<Envelope<LedgerResponse>> ledger(
|
||||
@org.springframework.web.bind.annotation.RequestBody Envelope<LedgerRequest> envelope
|
||||
) {
|
||||
// 서비스에서 요청 보강/호출을 오케스트레이션
|
||||
return carLedgerFrmbkService.ledger(envelope);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package go.kr.project.vmis.gpki;
|
||||
|
||||
public interface GpkiService {
|
||||
String encrypt(String plain) throws Exception;
|
||||
String decrypt(String cipher) throws Exception;
|
||||
boolean isEnabled();
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package go.kr.project.vmis.gpki;
|
||||
|
||||
public class NoopGpkiService implements GpkiService {
|
||||
@Override
|
||||
public String encrypt(String plain) {
|
||||
return plain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String cipher) {
|
||||
return cipher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package go.kr.project.vmis.gpki;
|
||||
|
||||
import go.kr.project.vmis.config.properties.VmisProperties;
|
||||
import go.kr.project.vmis.util.GpkiCryptoUtil;
|
||||
|
||||
/**
|
||||
* Real GPKI service backed by native GPKI JNI via legacy NewGpkiUtil wrapper.
|
||||
* Uses YAML-configured paths and options in {@link VmisProperties.GpkiProps}.
|
||||
*/
|
||||
public class RealGpkiService implements GpkiService {
|
||||
|
||||
private final VmisProperties props;
|
||||
private final GpkiCryptoUtil crypto;
|
||||
|
||||
public RealGpkiService(VmisProperties props) {
|
||||
this.props = props;
|
||||
try {
|
||||
this.crypto = GpkiCryptoUtil.from(props.getGpki());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to initialize GPKI (JNI) util. Check YAML paths/passwords and license.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encrypt(String plain) throws Exception {
|
||||
String charset = props.getGpki().getCharset();
|
||||
String targetId = props.getGpki().getTargetServerId();
|
||||
return crypto.encryptToBase64(plain, targetId, charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String cipher) throws Exception {
|
||||
String charset = props.getGpki().getCharset();
|
||||
return crypto.decryptFromBase64(cipher, charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package go.kr.project.vmis.mapper;
|
||||
|
||||
import go.kr.project.vmis.model.basic.CarBassMatterInqireVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 자동차 기본 사항 조회 Mapper
|
||||
*
|
||||
* <p>API 호출 정보를 관리하는 Mapper 인터페이스입니다.</p>
|
||||
* <ul>
|
||||
* <li>최초 요청 시: insertCarBassMatterInqire() 호출</li>
|
||||
* <li>결과 수신 시: updateCarBassMatterInqire() 호출</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Mapper
|
||||
public interface CarBassMatterInqireMapper {
|
||||
|
||||
/**
|
||||
* 시퀀스로 새로운 자동차 기본 사항 조회 ID를 생성합니다.
|
||||
*
|
||||
* <p>형식: CBMI000000000001</p>
|
||||
*
|
||||
* @return 생성된 ID
|
||||
*/
|
||||
String selectNextCarBassMatterInqireId();
|
||||
|
||||
/**
|
||||
* 최초 API 요청 정보를 등록합니다.
|
||||
*
|
||||
* <p>요청 시점의 정보만 저장하며, 응답 정보는 null 상태입니다.</p>
|
||||
*
|
||||
* @param carBassMatterInqireVO 요청 정보
|
||||
* @return 등록된 행 수
|
||||
*/
|
||||
int insertCarBassMatterInqire(CarBassMatterInqireVO carBassMatterInqireVO);
|
||||
|
||||
/**
|
||||
* API 응답 결과를 업데이트합니다.
|
||||
*
|
||||
* <p>응답 받은 데이터를 기존 레코드에 업데이트합니다.</p>
|
||||
*
|
||||
* @param carBassMatterInqireVO 응답 정보 (carBassMatterInqire 필드는 필수)
|
||||
* @return 업데이트된 행 수
|
||||
*/
|
||||
int updateCarBassMatterInqire(CarBassMatterInqireVO carBassMatterInqireVO);
|
||||
|
||||
/**
|
||||
* ID로 조회 정보를 조회합니다.
|
||||
*
|
||||
* @param carBassMatterInqire 자동차 기본 사항 조회 ID
|
||||
* @return 조회된 정보
|
||||
*/
|
||||
CarBassMatterInqireVO selectCarBassMatterInqireById(String carBassMatterInqire);
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package go.kr.project.vmis.mapper;
|
||||
|
||||
import go.kr.project.vmis.model.ledger.CarLedgerFrmbkDtlVO;
|
||||
import go.kr.project.vmis.model.ledger.CarLedgerFrmbkVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 자동차 등록 원부(갑) Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface CarLedgerFrmbkMapper {
|
||||
|
||||
// ID 시퀀스
|
||||
String selectNextCarLedgerFrmbkId();
|
||||
String selectNextCarLedgerFrmbkDtlId();
|
||||
|
||||
// 마스터 INSERT/UPDATE/SELECT
|
||||
int insertCarLedgerFrmbk(CarLedgerFrmbkVO vo);
|
||||
int updateCarLedgerFrmbk(CarLedgerFrmbkVO vo);
|
||||
CarLedgerFrmbkVO selectCarLedgerFrmbkById(String carLedgerFrmbkId);
|
||||
|
||||
// 상세 INSERT (단건)
|
||||
int insertCarLedgerFrmbkDtl(CarLedgerFrmbkDtlVO vo);
|
||||
|
||||
// 편의: 상세 일괄 (MyBatis foreach를 XML에서 사용할 수도 있으나, 여기서는 단건 호출을 반복)
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package go.kr.project.vmis.model.basic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "자동차기본사항조회 요청 항목")
|
||||
public class BasicRequest {
|
||||
|
||||
// 본문 공통 메타
|
||||
@Schema(description = "정보시스템ID", example = "41-345")
|
||||
@JsonProperty("INFO_SYS_ID")
|
||||
private String infoSysId;
|
||||
|
||||
@Schema(description = "정보시스템IP", example = "105.19.10.135")
|
||||
@JsonProperty("INFO_SYS_IP")
|
||||
private String infoSysIp;
|
||||
|
||||
@Schema(description = "시군구코드", example = "41460")
|
||||
@JsonProperty("SIGUNGU_CODE")
|
||||
private String sigunguCode;
|
||||
|
||||
// 서비스별 필드
|
||||
@Schema(description = "연계정보코드", example = "AC1_FD11_01")
|
||||
@JsonProperty("CNTC_INFO_CODE")
|
||||
private String cntcInfoCode;
|
||||
|
||||
@Schema(description = "담당자ID", example = "")
|
||||
@JsonProperty("CHARGER_ID")
|
||||
private String chargerId;
|
||||
|
||||
@Schema(description = "담당자IP", example = "")
|
||||
@JsonProperty("CHARGER_IP")
|
||||
private String chargerIp;
|
||||
|
||||
@Schema(description = "담당자명(사용자)", example = "")
|
||||
@JsonProperty("CHARGER_NM")
|
||||
private String chargerNm;
|
||||
|
||||
@Schema(description = "부과기준일", example = "20250101")
|
||||
@JsonProperty("LEVY_STDDE")
|
||||
private String levyStdde;
|
||||
|
||||
@Schema(description = "조회구분코드")
|
||||
@JsonProperty("INQIRE_SE_CODE")
|
||||
private String inqireSeCode;
|
||||
|
||||
@Schema(description = "자동차등록번호", example = "12가3456")
|
||||
@JsonProperty("VHRNO")
|
||||
private String vhrno;
|
||||
|
||||
@Schema(description = "차대번호", example = "KMHAB812345678901")
|
||||
@JsonProperty("VIN")
|
||||
private String vin;
|
||||
|
||||
/*
|
||||
// 추가 항목 (명세 샘플 기준)
|
||||
@Schema(description = "개인정보공개", example = "Y")
|
||||
@JsonProperty("ONES_INFORMATION_OPEN")
|
||||
private String onesInformationOpen;
|
||||
|
||||
@Schema(description = "민원인성명")
|
||||
@JsonProperty("CPTTR_NM")
|
||||
private String cpttrNm;
|
||||
|
||||
@Schema(description = "민원인주민번호")
|
||||
@JsonProperty("CPTTR_IHIDNUM")
|
||||
@Size(max = 13)
|
||||
private String cpttrIhidnum;
|
||||
|
||||
@Schema(description = "민원인법정동코드")
|
||||
@JsonProperty("CPTTR_LEGALDONG_CODE")
|
||||
private String cpttrLegaldongCode;
|
||||
|
||||
@Schema(description = "경로구분코드")
|
||||
@JsonProperty("ROUTE_SE_CODE")
|
||||
private String routeSeCode;
|
||||
|
||||
@Schema(description = "내역표시")
|
||||
@JsonProperty("DETAIL_EXPRESSION")
|
||||
private String detailExpression;
|
||||
*/
|
||||
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
package go.kr.project.vmis.model.basic;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Schema(description = "자동차기본사항조회 응답 모델")
|
||||
@Getter
|
||||
@Setter
|
||||
public class BasicResponse {
|
||||
|
||||
@JsonProperty("CNTC_RESULT_CODE")
|
||||
private String cntcResultCode;
|
||||
|
||||
@JsonProperty("CNTC_RESULT_DTLS")
|
||||
private String cntcResultDtls;
|
||||
|
||||
@JsonProperty("record")
|
||||
private List<Record> record;
|
||||
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Schema(description = "기본사항 record 항목")
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Record {
|
||||
@JsonProperty("PRYE") private String prye;
|
||||
@JsonProperty("REGIST_DE") private String registDe;
|
||||
@JsonProperty("ERSR_REGIST_SE_CODE") private String ersrRegistSeCode;
|
||||
@JsonProperty("ERSR_REGIST_SE_NM") private String ersrRegistSeNm;
|
||||
@JsonProperty("ERSR_REGIST_DE") private String ersrRegistDe;
|
||||
@JsonProperty("REGIST_DETAIL_CODE") private String registDetailCode;
|
||||
@JsonProperty("DSPLVL") private String dsplvl;
|
||||
@JsonProperty("USE_STRNGHLD_LEGALDONG_CODE") private String useStrnghldLegaldongCode;
|
||||
@JsonProperty("USE_STRNGHLD_ADSTRD_CODE") private String useStrnghldAdstrdCode;
|
||||
@JsonProperty("USE_STRNGHLD_MNTN") private String useStrnghldMntn;
|
||||
@JsonProperty("USE_STRNGHLD_LNBR") private String useStrnghldLnbr;
|
||||
@JsonProperty("USE_STRNGHLD_HO") private String useStrnghldHo;
|
||||
@JsonProperty("USE_STRNGHLD_ADRES_NM") private String useStrnghldAdresNm;
|
||||
@JsonProperty("USE_STRNGHLD_ROAD_NM_CODE") private String useStrnghldRoadNmCode;
|
||||
@JsonProperty("USGSRHLD_UNDGRND_BULD_SE_CODE") private String usgsrhldUndgrndBuldSeCode;
|
||||
@JsonProperty("USE_STRNGHLD_BULD_MAIN_NO") private String useStrnghldBuldMainNo;
|
||||
@JsonProperty("USE_STRNGHLD_BULD_SUB_NO") private String useStrnghldBuldSubNo;
|
||||
@JsonProperty("USGSRHLD_ADRES_FULL") private String usgsrhldAdresFull;
|
||||
@JsonProperty("MBER_SE_CODE") private String mberSeCode;
|
||||
@JsonProperty("MBER_NM") private String mberNm;
|
||||
@JsonProperty("MBER_SE_NO") private String mberSeNo;
|
||||
@JsonProperty("TELNO") private String telno;
|
||||
@JsonProperty("OWNER_LEGALDONG_CODE") private String ownerLegaldongCode;
|
||||
@JsonProperty("OWNER_ADSTRD_CODE") private String ownerAdstrdCode;
|
||||
@JsonProperty("OWNER_MNTN") private String ownerMntn;
|
||||
@JsonProperty("OWNER_LNBR") private String ownerLnbr;
|
||||
@JsonProperty("OWNER_HO") private String ownerHo;
|
||||
@JsonProperty("OWNER_ADRES_NM") private String ownerAdresNm;
|
||||
@JsonProperty("OWNER_ROAD_NM_CODE") private String ownerRoadNmCode;
|
||||
@JsonProperty("OWNER_UNDGRND_BULD_SE_CODE") private String ownerUndgrndBuldSeCode;
|
||||
@JsonProperty("OWNER_BULD_MAIN_NO") private String ownerBuldMainNo;
|
||||
@JsonProperty("OWNER_BULD_SUB_NO") private String ownerBuldSubNo;
|
||||
@JsonProperty("OWNER_ADRES_FULL") private String ownerAdresFull;
|
||||
@JsonProperty("AFTR_VHRNO") private String aftrVhrno;
|
||||
@JsonProperty("USE_FUEL_CODE") private String useFuelCode;
|
||||
@JsonProperty("PRPOS_SE_CODE") private String prposSeCode;
|
||||
@JsonProperty("MTRS_FOM_NM") private String mtrsFomNm;
|
||||
@JsonProperty("FRNT_VHRNO") private String frntVhrno;
|
||||
@JsonProperty("VHRNO") private String vhrno;
|
||||
@JsonProperty("VIN") private String vin;
|
||||
@JsonProperty("CNM") private String cnm;
|
||||
@JsonProperty("VHCLE_TOT_WT") private String vhcleTotWt;
|
||||
@JsonProperty("CAAG_ENDDE") private String caagEndde;
|
||||
@JsonProperty("CHANGE_DE") private String changeDe;
|
||||
@JsonProperty("VHCTY_ASORT_CODE") private String vhctyAsortCode;
|
||||
@JsonProperty("VHCTY_TY_CODE") private String vhctyTyCode;
|
||||
@JsonProperty("VHCTY_SE_CODE") private String vhctySeCode;
|
||||
@JsonProperty("MXMM_LDG") private String mxmmLdg;
|
||||
@JsonProperty("VHCTY_ASORT_NM") private String vhctyAsortNm;
|
||||
@JsonProperty("VHCTY_TY_NM") private String vhctyTyNm;
|
||||
@JsonProperty("VHCTY_SE_NM") private String vhctySeNm;
|
||||
@JsonProperty("FRST_REGIST_DE") private String frstRegistDe;
|
||||
@JsonProperty("FOM_NM") private String fomNm;
|
||||
@JsonProperty("ACQS_DE") private String acqsDe;
|
||||
@JsonProperty("ACQS_END_DE") private String acqsEndDe;
|
||||
@JsonProperty("YBL_MD") private String yblMd;
|
||||
@JsonProperty("TRANSR_REGIST_DE") private String transrRegistDe;
|
||||
@JsonProperty("SPCF_REGIST_STTUS_CODE") private String spcfRegistSttusCode;
|
||||
@JsonProperty("COLOR_NM") private String colorNm;
|
||||
@JsonProperty("MRTG_CO") private String mrtgCo;
|
||||
@JsonProperty("SEIZR_CO") private String seizrCo;
|
||||
@JsonProperty("STMD_CO") private String stmdCo;
|
||||
@JsonProperty("NMPL_CSDY_AT") private String nmplCsdyAt;
|
||||
@JsonProperty("NMPL_CSDY_REMNR_DE") private String nmplCsdyRemnrDe;
|
||||
@JsonProperty("ORIGIN_SE_CODE") private String originSeCode;
|
||||
@JsonProperty("NMPL_STNDRD_CODE") private String nmplStndrdCode;
|
||||
@JsonProperty("ACQS_AMOUNT") private String acqsAmount;
|
||||
@JsonProperty("INSPT_VALID_PD_BGNDE") private String insptValidPdBgnde;
|
||||
@JsonProperty("INSPT_VALID_PD_ENDDE") private String insptValidPdEndde;
|
||||
@JsonProperty("USE_STRNGHLD_GRC_CODE") private String useStrnghldGrcCode;
|
||||
@JsonProperty("TKCAR_PSCAP_CO") private String tkcarPscapCo;
|
||||
@JsonProperty("SPMNNO") private String spmnno;
|
||||
@JsonProperty("TRVL_DSTNC") private String trvlDstnc;
|
||||
@JsonProperty("FRST_REGIST_RQRCNO") private String frstRegistRqrcno;
|
||||
@JsonProperty("VLNT_ERSR_PRVNTC_NTICE_DE") private String vlntErsrPrvntcNticeDe;
|
||||
@JsonProperty("REGIST_INSTT_NM") private String registInsttNm;
|
||||
@JsonProperty("PROCESS_IMPRTY_RESN_CODE") private String processImprtyResnCode;
|
||||
@JsonProperty("PROCESS_IMPRTY_RESN_DTLS") private String processImprtyResnDtls;
|
||||
@JsonProperty("CBD_LT") private String cbdLt;
|
||||
@JsonProperty("CBD_BT") private String cbdBt;
|
||||
@JsonProperty("CBD_HG") private String cbdHg;
|
||||
@JsonProperty("FRST_MXMM_LDG") private String frstMxmmLdg;
|
||||
@JsonProperty("FUEL_CNSMP_RT") private String fuelCnsmpRt;
|
||||
@JsonProperty("ELCTY_CMPND_FUEL_CNSMP_RT") private String elctyCmpndFuelCnsmpRt;
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,646 @@
|
||||
package go.kr.project.vmis.model.basic;
|
||||
|
||||
import go.kr.project.vmis.config.ApiConstant;
|
||||
import go.kr.project.vmis.model.common.Envelope;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 자동차 기본 사항 조회 엔티티
|
||||
*
|
||||
* <p>API 호출 정보를 저장하는 테이블 매핑 클래스입니다.
|
||||
* 최초 요청 시 INSERT, 결과 수신 시 UPDATE 형태로 사용됩니다.</p>
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CarBassMatterInqireVO {
|
||||
|
||||
// ==== Static factory/mapping methods (moved from Service) ====
|
||||
public static CarBassMatterInqireVO fromRequest(BasicRequest request) {
|
||||
return CarBassMatterInqireVO.builder()
|
||||
.infoSysId(request.getInfoSysId())
|
||||
.infoSysIp(request.getInfoSysIp())
|
||||
.sigunguCode(request.getSigunguCode())
|
||||
.cntcInfoCode(request.getCntcInfoCode())
|
||||
.chargerId(request.getChargerId())
|
||||
.chargerIp(request.getChargerIp())
|
||||
.chargerNm(request.getChargerNm())
|
||||
.dmndLevyStdde(request.getLevyStdde())
|
||||
.dmndInqireSeCode(request.getInqireSeCode())
|
||||
.dmndVhrno(request.getVhrno())
|
||||
.dmndVin(request.getVin())
|
||||
.rgtr(ApiConstant.DEFAULT_REGISTRANT)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static CarBassMatterInqireVO fromResponse(String id, Envelope<BasicResponse> envelope) {
|
||||
if (envelope == null || envelope.getData() == null || envelope.getData().isEmpty()) return null;
|
||||
BasicResponse response = envelope.getData().get(0);
|
||||
CarBassMatterInqireVO.CarBassMatterInqireVOBuilder builder = CarBassMatterInqireVO.builder()
|
||||
.carBassMatterInqire(id)
|
||||
.cntcResultCode(response.getCntcResultCode())
|
||||
.cntcResultDtls(response.getCntcResultDtls());
|
||||
if (response.getRecord() != null && !response.getRecord().isEmpty()) {
|
||||
BasicResponse.Record record = response.getRecord().get(0);
|
||||
applyRecord(builder, record);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static void applyRecord(CarBassMatterInqireVO.CarBassMatterInqireVOBuilder builder, BasicResponse.Record record) {
|
||||
builder
|
||||
.prye(record.getPrye()) // 연식
|
||||
.registDe(record.getRegistDe()) // 등록일
|
||||
.ersrRegistSeCode(record.getErsrRegistSeCode()) // 말소 등록 구분 코드
|
||||
.ersrRegistSeNm(record.getErsrRegistSeNm()) // 말소 등록 구분명
|
||||
.ersrRegistDe(record.getErsrRegistDe()) // 말소 등록일
|
||||
.registDetailCode(record.getRegistDetailCode()) // 등록 상세 코드
|
||||
.dsplvl(record.getDsplvl()) // 배기량
|
||||
.useStrnghldLegaldongCode(record.getUseStrnghldLegaldongCode()) // 사용 본거지 법정동 코드
|
||||
.useStrnghldAdstrdCode(record.getUseStrnghldAdstrdCode()) // 사용 본거지 행정동 코드
|
||||
.useStrnghldMntn(record.getUseStrnghldMntn()) // 사용 본거지 산
|
||||
.useStrnghldLnbr(record.getUseStrnghldLnbr()) // 사용 본거지 번지
|
||||
.useStrnghldHo(record.getUseStrnghldHo()) // 사용 본거지 호
|
||||
.useStrnghldAdresNm(record.getUseStrnghldAdresNm()) // 사용 본거지 상세주소
|
||||
.useStrnghldRoadNmCode(record.getUseStrnghldRoadNmCode()) // 사용 본거지 도로명 코드
|
||||
.usgsrhldUndgrndBuldSeCode(record.getUsgsrhldUndgrndBuldSeCode()) // 사용 본거지 지하 건물 구분 코드
|
||||
.useStrnghldBuldMainNo(record.getUseStrnghldBuldMainNo()) // 사용 본거지 건물 주요 번호
|
||||
.useStrnghldBuldSubNo(record.getUseStrnghldBuldSubNo()) // 사용 본거지 건물 부 번호
|
||||
.usgsrhldAdresFull(record.getUsgsrhldAdresFull()) // 사용 본거지 전체주소
|
||||
.mberSeCode(record.getMberSeCode()) // 대표소유자 회원 구분 코드
|
||||
.mberSeNo(record.getMberSeNo()) // 대표소유자 회원 번호
|
||||
.mberNm(record.getMberNm()) // 대표소유자 성명
|
||||
.telno(record.getTelno()) // 대표소유자 전화번호
|
||||
.ownerLegaldongCode(record.getOwnerLegaldongCode()) // 소유자 법정동 코드
|
||||
.ownerAdstrdCode(record.getOwnerAdstrdCode()) // 소유자 행정동 코드
|
||||
.ownerMntn(record.getOwnerMntn()) // 소유자 산
|
||||
.ownerLnbr(record.getOwnerLnbr()) // 소유자 번지
|
||||
.ownerHo(record.getOwnerHo()) // 소유자 호
|
||||
.ownerAdresNm(record.getOwnerAdresNm()) // 소유자 상세주소
|
||||
.ownerRoadNmCode(record.getOwnerRoadNmCode()) // 소유자 도로명 코드
|
||||
.ownerUndgrndBuldSeCode(record.getOwnerUndgrndBuldSeCode()) // 소유자 지하건물 구분 코드
|
||||
.ownerBuldMainNo(record.getOwnerBuldMainNo()) // 소유자 건물 주요 번호
|
||||
.ownerBuldSubNo(record.getOwnerBuldSubNo()) // 소유자 건물 부 번호
|
||||
.ownrWholaddr(record.getOwnerAdresFull()) // 소유자 전체주소
|
||||
.aftrVhrno(record.getAftrVhrno()) // 신 차량번호
|
||||
.useFuelCode(record.getUseFuelCode()) // 사용 연료 코드
|
||||
.prposSeCode(record.getPrposSeCode()) // 용도 구분 코드
|
||||
.mtrsFomNm(record.getMtrsFomNm()) // 원동기 형식명
|
||||
.frntVhrno(record.getFrntVhrno()) // 이전 차량번호
|
||||
.vhclno(record.getVhrno()) // 차량번호
|
||||
.vin(record.getVin()) // 차대번호
|
||||
.cnm(record.getCnm()) // 차명
|
||||
.vhcleTotWt(record.getVhcleTotWt()) // 차량 총 중량
|
||||
.caagEndde(record.getCaagEndde()) // 차령 만료일자
|
||||
.changeDe(record.getChangeDe()) // 차번호 변경시기
|
||||
.vhctyAsortCode(record.getVhctyAsortCode()) // 차종 종별 코드
|
||||
.vhctyTyCode(record.getVhctyTyCode()) // 차종 유형 코드
|
||||
.vhctySeCode(record.getVhctySeCode()) // 차종 분류 코드
|
||||
.mxmmLdg(record.getMxmmLdg()) // 최대 적재량
|
||||
.vhctyAsortNm(record.getVhctyAsortNm()) // 차종 종별명
|
||||
.vhctyTyNm(record.getVhctyTyNm()) // 차종 유형명
|
||||
.vhctySeNm(record.getVhctySeNm()) // 차종 분류명
|
||||
.frstRegistDe(record.getFrstRegistDe()) // 최초 등록일
|
||||
.fomNm(record.getFomNm()) // 형식
|
||||
.acqsDe(record.getAcqsDe()) // 취득 일자
|
||||
.acqsEndDe(record.getAcqsEndDe()) // 취득 종료일자
|
||||
.yblMd(record.getYblMd()) // 제작 년월일
|
||||
.transrRegistDe(record.getTransrRegistDe()) // 이전 등록일
|
||||
.spcfRegistSttusCode(record.getSpcfRegistSttusCode()) // 제원 등록 상태 코드
|
||||
.colorNm(record.getColorNm()) // 색상명
|
||||
.mrtgCo(record.getMrtgCo()) // 저당수
|
||||
.seizrCo(record.getSeizrCo()) // 압류건수
|
||||
.stmdCo(record.getStmdCo()) // 구조변경수
|
||||
.nmplCsdyAt(record.getNmplCsdyAt()) // 번호판 영치 여부
|
||||
.nmplCsdyRemnrDe(record.getNmplCsdyRemnrDe()) // 번호판 영치 최고일
|
||||
.originSeCode(record.getOriginSeCode()) // 출처 구분 코드
|
||||
.nmplStndrdCode(record.getNmplStndrdCode()) // 번호판 규격 코드
|
||||
.acqsAmount(record.getAcqsAmount()) // 취득 금액
|
||||
.insptValidPdBgnde(record.getInsptValidPdBgnde()) // 검사 유효 기간 시작일
|
||||
.insptValidPdEndde(record.getInsptValidPdEndde()) // 검사 유효 기간 종료일
|
||||
.useStrnghldGrcCode(record.getUseStrnghldGrcCode()) // 사용 본거지 관청 코드
|
||||
.tkcarPscapCo(record.getTkcarPscapCo()) // 승차정원수
|
||||
.spmnno(record.getSpmnno()) // 제원관리번호
|
||||
.trvlDstnc(record.getTrvlDstnc()) // 주행거리
|
||||
.frstRegistRqrcno(record.getFrstRegistRqrcno()) // 최초 등록 접수번호
|
||||
.vlntErsrPrvntcNticeDe(record.getVlntErsrPrvntcNticeDe()) // 예고통지일
|
||||
.registInsttNm(record.getRegistInsttNm()) // 등록 기관명
|
||||
.processImprtyResnCode(record.getProcessImprtyResnCode()) // 처리 불가 사유 코드
|
||||
.processImprtyResnDtls(record.getProcessImprtyResnDtls()) // 처리 불가 사유 명세
|
||||
.cbdLt(record.getCbdLt()) // 차체 길이
|
||||
.cbdBt(record.getCbdBt()) // 차체 너비
|
||||
.cbdHg(record.getCbdHg()) // 차체 높이
|
||||
.frstMxmmLdg(record.getFrstMxmmLdg()) // 최초 최대 적재량
|
||||
.fuelCnsmpRt(record.getFuelCnsmpRt()) // 연료 소비율
|
||||
.elctyCmpndFuelCnsmpRt(record.getElctyCmpndFuelCnsmpRt()); // 전기 복합 연료 소비율
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동차 기본 사항 조회 ID (PK)
|
||||
* 형식: CBMI000000000001
|
||||
*/
|
||||
private String carBassMatterInqire;
|
||||
|
||||
// ===== 요청 정보 =====
|
||||
/**
|
||||
* 정보 시스템 ID
|
||||
*/
|
||||
private String infoSysId;
|
||||
|
||||
/**
|
||||
* 정보 시스템 IP
|
||||
*/
|
||||
private String infoSysIp;
|
||||
|
||||
/**
|
||||
* 시군구 코드
|
||||
*/
|
||||
private String sigunguCode;
|
||||
|
||||
/**
|
||||
* 연계 정보 코드
|
||||
*/
|
||||
private String cntcInfoCode;
|
||||
|
||||
/**
|
||||
* 담당자 ID
|
||||
*/
|
||||
private String chargerId;
|
||||
|
||||
/**
|
||||
* 담당자 IP
|
||||
*/
|
||||
private String chargerIp;
|
||||
|
||||
/**
|
||||
* 담당자명
|
||||
*/
|
||||
private String chargerNm;
|
||||
|
||||
/**
|
||||
* 요청 부과 기준일
|
||||
*/
|
||||
private String dmndLevyStdde;
|
||||
|
||||
/**
|
||||
* 요청 조회 구분 코드
|
||||
*/
|
||||
private String dmndInqireSeCode;
|
||||
|
||||
/**
|
||||
* 요청 자동차등록번호
|
||||
*/
|
||||
private String dmndVhrno;
|
||||
|
||||
/**
|
||||
* 요청 차대번호
|
||||
*/
|
||||
private String dmndVin;
|
||||
|
||||
// ===== 응답 정보 (결과 수신 시 UPDATE) =====
|
||||
/**
|
||||
* 연계 결과 코드
|
||||
*/
|
||||
private String cntcResultCode;
|
||||
|
||||
/**
|
||||
* 연계 결과 상세
|
||||
*/
|
||||
private String cntcResultDtls;
|
||||
|
||||
/**
|
||||
* 연식
|
||||
*/
|
||||
private String prye;
|
||||
|
||||
/**
|
||||
* 등록일
|
||||
*/
|
||||
private String registDe;
|
||||
|
||||
/**
|
||||
* 말소 등록 구분 코드
|
||||
*/
|
||||
private String ersrRegistSeCode;
|
||||
|
||||
/**
|
||||
* 말소 등록 구분명
|
||||
*/
|
||||
private String ersrRegistSeNm;
|
||||
|
||||
/**
|
||||
* 말소 등록일
|
||||
*/
|
||||
private String ersrRegistDe;
|
||||
|
||||
/**
|
||||
* 등록 상세 코드
|
||||
*/
|
||||
private String registDetailCode;
|
||||
|
||||
/**
|
||||
* 배기량
|
||||
*/
|
||||
private String dsplvl;
|
||||
|
||||
/**
|
||||
* 사용 본거지 법정동 코드
|
||||
*/
|
||||
private String useStrnghldLegaldongCode;
|
||||
|
||||
/**
|
||||
* 사용 본거지 행정동 코드
|
||||
*/
|
||||
private String useStrnghldAdstrdCode;
|
||||
|
||||
/**
|
||||
* 사용 본거지 산
|
||||
*/
|
||||
private String useStrnghldMntn;
|
||||
|
||||
/**
|
||||
* 사용 본거지 번지
|
||||
*/
|
||||
private String useStrnghldLnbr;
|
||||
|
||||
/**
|
||||
* 사용 본거지 호
|
||||
*/
|
||||
private String useStrnghldHo;
|
||||
|
||||
/**
|
||||
* 사용 본거지 상세주소
|
||||
*/
|
||||
private String useStrnghldAdresNm;
|
||||
|
||||
/**
|
||||
* 사용 본거지 도로명 코드
|
||||
*/
|
||||
private String useStrnghldRoadNmCode;
|
||||
|
||||
/**
|
||||
* 사용 본거지 지하 건물 구분 코드
|
||||
*/
|
||||
private String usgsrhldUndgrndBuldSeCode;
|
||||
|
||||
/**
|
||||
* 사용 본거지 건물 주요 번호
|
||||
*/
|
||||
private String useStrnghldBuldMainNo;
|
||||
|
||||
/**
|
||||
* 사용 본거지 건물 부 번호
|
||||
*/
|
||||
private String useStrnghldBuldSubNo;
|
||||
|
||||
/**
|
||||
* 사용 본거지 전체주소
|
||||
*/
|
||||
private String usgsrhldAdresFull;
|
||||
|
||||
/**
|
||||
* 대표소유자 회원 구분 코드
|
||||
*/
|
||||
private String mberSeCode;
|
||||
|
||||
/**
|
||||
* 대표소유자 회원 번호
|
||||
*/
|
||||
private String mberSeNo;
|
||||
|
||||
/**
|
||||
* 대표소유자 전화번호
|
||||
*/
|
||||
private String telno;
|
||||
|
||||
/**
|
||||
* 소유자 법정동 코드
|
||||
*/
|
||||
private String ownerLegaldongCode;
|
||||
|
||||
/**
|
||||
* 소유자 행정동 코드
|
||||
*/
|
||||
private String ownerAdstrdCode;
|
||||
|
||||
/**
|
||||
* 소유자 산
|
||||
*/
|
||||
private String ownerMntn;
|
||||
|
||||
/**
|
||||
* 소유자 번지
|
||||
*/
|
||||
private String ownerLnbr;
|
||||
|
||||
/**
|
||||
* 소유자 호
|
||||
*/
|
||||
private String ownerHo;
|
||||
|
||||
/**
|
||||
* 소유자 상세주소
|
||||
*/
|
||||
private String ownerAdresNm;
|
||||
|
||||
/**
|
||||
* 소유자 도로명 코드
|
||||
*/
|
||||
private String ownerRoadNmCode;
|
||||
|
||||
/**
|
||||
* 소유자 지하건물 구분 코드
|
||||
*/
|
||||
private String ownerUndgrndBuldSeCode;
|
||||
|
||||
/**
|
||||
* 소유자 건물 주요 번호
|
||||
*/
|
||||
private String ownerBuldMainNo;
|
||||
|
||||
/**
|
||||
* 소유자 건물 부 번호
|
||||
*/
|
||||
private String ownerBuldSubNo;
|
||||
|
||||
/**
|
||||
* 소유자 전체주소
|
||||
*/
|
||||
private String ownrWholaddr;
|
||||
|
||||
/**
|
||||
* 신 차량번호
|
||||
*/
|
||||
private String aftrVhrno;
|
||||
|
||||
/**
|
||||
* 사용 연료 코드
|
||||
*/
|
||||
private String useFuelCode;
|
||||
|
||||
/**
|
||||
* 용도 구분 코드
|
||||
*/
|
||||
private String prposSeCode;
|
||||
|
||||
/**
|
||||
* 원동기 형식명
|
||||
*/
|
||||
private String mtrsFomNm;
|
||||
|
||||
/**
|
||||
* 이전 차량번호
|
||||
*/
|
||||
private String frntVhrno;
|
||||
|
||||
/**
|
||||
* 차량번호
|
||||
*/
|
||||
private String vhclno;
|
||||
|
||||
/**
|
||||
* 차대번호
|
||||
*/
|
||||
private String vin;
|
||||
|
||||
/**
|
||||
* 차명
|
||||
*/
|
||||
private String cnm;
|
||||
|
||||
/**
|
||||
* 차량 총 중량
|
||||
*/
|
||||
private String vhcleTotWt;
|
||||
|
||||
/**
|
||||
* 차령 만료일자
|
||||
*/
|
||||
private String caagEndde;
|
||||
|
||||
/**
|
||||
* 차번호 변경시기
|
||||
*/
|
||||
private String changeDe;
|
||||
|
||||
/**
|
||||
* 차종 종별 코드
|
||||
*/
|
||||
private String vhctyAsortCode;
|
||||
|
||||
/**
|
||||
* 차종 유형 코드
|
||||
*/
|
||||
private String vhctyTyCode;
|
||||
|
||||
/**
|
||||
* 차종 분류 코드
|
||||
*/
|
||||
private String vhctySeCode;
|
||||
|
||||
/**
|
||||
* 최대 적재량
|
||||
*/
|
||||
private String mxmmLdg;
|
||||
|
||||
/**
|
||||
* 차종 종별명
|
||||
*/
|
||||
private String vhctyAsortNm;
|
||||
|
||||
/**
|
||||
* 차종 유형명
|
||||
*/
|
||||
private String vhctyTyNm;
|
||||
|
||||
/**
|
||||
* 차종 분류명
|
||||
*/
|
||||
private String vhctySeNm;
|
||||
|
||||
/**
|
||||
* 최초 등록일
|
||||
*/
|
||||
private String frstRegistDe;
|
||||
|
||||
/**
|
||||
* 형식
|
||||
*/
|
||||
private String fomNm;
|
||||
|
||||
/**
|
||||
* 취득 일자
|
||||
*/
|
||||
private String acqsDe;
|
||||
|
||||
/**
|
||||
* 취득 종료일자
|
||||
*/
|
||||
private String acqsEndDe;
|
||||
|
||||
/**
|
||||
* 제작 년월일
|
||||
*/
|
||||
private String yblMd;
|
||||
|
||||
/**
|
||||
* 이전 등록일
|
||||
*/
|
||||
private String transrRegistDe;
|
||||
|
||||
/**
|
||||
* 제원 등록 상태 코드
|
||||
*/
|
||||
private String spcfRegistSttusCode;
|
||||
|
||||
/**
|
||||
* 색상명
|
||||
*/
|
||||
private String colorNm;
|
||||
|
||||
/**
|
||||
* 저당수
|
||||
*/
|
||||
private String mrtgCo;
|
||||
|
||||
/**
|
||||
* 압류건수
|
||||
*/
|
||||
private String seizrCo;
|
||||
|
||||
/**
|
||||
* 구조변경수
|
||||
*/
|
||||
private String stmdCo;
|
||||
|
||||
/**
|
||||
* 번호판 영치 여부
|
||||
*/
|
||||
private String nmplCsdyAt;
|
||||
|
||||
/**
|
||||
* 번호판 영치 최고일
|
||||
*/
|
||||
private String nmplCsdyRemnrDe;
|
||||
|
||||
/**
|
||||
* 출처 구분 코드
|
||||
*/
|
||||
private String originSeCode;
|
||||
|
||||
/**
|
||||
* 번호판 규격 코드
|
||||
*/
|
||||
private String nmplStndrdCode;
|
||||
|
||||
/**
|
||||
* 취득 금액
|
||||
*/
|
||||
private String acqsAmount;
|
||||
|
||||
/**
|
||||
* 검사 유효 기간 시작일
|
||||
*/
|
||||
private String insptValidPdBgnde;
|
||||
|
||||
/**
|
||||
* 검사 유효 기간 종료일
|
||||
*/
|
||||
private String insptValidPdEndde;
|
||||
|
||||
/**
|
||||
* 사용 본거지 관청 코드
|
||||
*/
|
||||
private String useStrnghldGrcCode;
|
||||
|
||||
/**
|
||||
* 승차정원수
|
||||
*/
|
||||
private String tkcarPscapCo;
|
||||
|
||||
/**
|
||||
* 제원관리번호
|
||||
*/
|
||||
private String spmnno;
|
||||
|
||||
/**
|
||||
* 주행거리
|
||||
*/
|
||||
private String trvlDstnc;
|
||||
|
||||
/**
|
||||
* 최초 등록 접수번호
|
||||
*/
|
||||
private String frstRegistRqrcno;
|
||||
|
||||
/**
|
||||
* 예고통지일
|
||||
*/
|
||||
private String vlntErsrPrvntcNticeDe;
|
||||
|
||||
/**
|
||||
* 등록 기관명
|
||||
*/
|
||||
private String registInsttNm;
|
||||
|
||||
/**
|
||||
* 처리 불가 사유 코드
|
||||
*/
|
||||
private String processImprtyResnCode;
|
||||
|
||||
/**
|
||||
* 처리 불가 사유 명세
|
||||
*/
|
||||
private String processImprtyResnDtls;
|
||||
|
||||
/**
|
||||
* 차체 길이
|
||||
*/
|
||||
private String cbdLt;
|
||||
|
||||
/**
|
||||
* 차체 너비
|
||||
*/
|
||||
private String cbdBt;
|
||||
|
||||
/**
|
||||
* 차체 높이
|
||||
*/
|
||||
private String cbdHg;
|
||||
|
||||
/**
|
||||
* 최초 최대 적재량
|
||||
*/
|
||||
private String frstMxmmLdg;
|
||||
|
||||
/**
|
||||
* 연료 소비율
|
||||
*/
|
||||
private String fuelCnsmpRt;
|
||||
|
||||
/**
|
||||
* 전기 복합 연료 소비율
|
||||
*/
|
||||
private String elctyCmpndFuelCnsmpRt;
|
||||
|
||||
/**
|
||||
* 등록 일시
|
||||
*/
|
||||
private LocalDateTime regDt;
|
||||
|
||||
/**
|
||||
* 등록자
|
||||
*/
|
||||
private String rgtr;
|
||||
|
||||
/**
|
||||
* 대표소유자 성명
|
||||
*/
|
||||
private String mberNm;
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package go.kr.project.vmis.model.common;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 공통 래퍼: { "data": [ ... ] }
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Getter
|
||||
@Setter
|
||||
public class Envelope<T> {
|
||||
|
||||
@JsonProperty("data")
|
||||
private List<T> data = new ArrayList<>();
|
||||
|
||||
public Envelope() {}
|
||||
|
||||
public Envelope(T single) {
|
||||
if (single != null) this.data.add(single);
|
||||
}
|
||||
|
||||
public Envelope(List<T> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package go.kr.project.vmis.model.ledger;
|
||||
|
||||
import go.kr.project.vmis.config.ApiConstant;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 자동차 등록 원부(갑) 상세 엔티티 VO
|
||||
*
|
||||
* <p>tb_car_ledger_frmbk_dtl 테이블 매핑</p>
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CarLedgerFrmbkDtlVO {
|
||||
|
||||
// ==== Static factory/mapping methods (moved from Service) ====
|
||||
public static List<CarLedgerFrmbkDtlVO> listFromResponse(LedgerResponse res, String masterId) {
|
||||
List<CarLedgerFrmbkDtlVO> list = new ArrayList<>();
|
||||
if (res == null || res.getRecord() == null) return list;
|
||||
for (LedgerResponse.Record r : res.getRecord()) {
|
||||
CarLedgerFrmbkDtlVO vo = CarLedgerFrmbkDtlVO.builder()
|
||||
.carLedgerFrmbkId(masterId) // 원부 마스터 ID (FK)
|
||||
.mainchk(r.getMainchk()) // 주요 체크
|
||||
.changeJobSeCode(r.getChangeJobSeCode()) // 변경 작업 구분 코드
|
||||
.mainno(r.getMainno()) // 주번호
|
||||
.subno(r.getSubno()) // 부번호
|
||||
.dtls(r.getDtls()) // 상세 내역
|
||||
.rqrcno(r.getRqrcno()) // 접수번호
|
||||
.vhmno(r.getVhmno()) // 차량관리번호
|
||||
.ledgerGroupNo(r.getLedgerGroupNo()) // 원부 그룹 번호
|
||||
.ledgerIndvdlzNo(r.getLedgerIndvdlzNo()) // 원부 개별화 번호
|
||||
.gubunNm(r.getGubunNm()) // 구분명
|
||||
.changeDe(r.getChangeDe()) // 변경일자
|
||||
.detailSn(r.getDetailSn()) // 상세 순번
|
||||
.flag(r.getFlag()) // 플래그
|
||||
.rgtr(ApiConstant.DEFAULT_REGISTRANT) // 등록자
|
||||
.build();
|
||||
list.add(vo);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// PK
|
||||
private String carLedgerFrmbkDtlId;
|
||||
|
||||
// FK
|
||||
private String carLedgerFrmbkId;
|
||||
|
||||
// 본문
|
||||
private String mainchk;
|
||||
private String changeJobSeCode;
|
||||
private String mainno;
|
||||
private String subno;
|
||||
private String dtls;
|
||||
private String rqrcno;
|
||||
private String vhmno;
|
||||
private String ledgerGroupNo;
|
||||
private String ledgerIndvdlzNo;
|
||||
private String gubunNm;
|
||||
private String changeDe;
|
||||
private String detailSn;
|
||||
private String flag;
|
||||
|
||||
// 감사
|
||||
private String rgtr;
|
||||
}
|
||||
@ -0,0 +1,192 @@
|
||||
package go.kr.project.vmis.model.ledger;
|
||||
|
||||
import go.kr.project.vmis.config.ApiConstant;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 자동차 등록 원부(갑) 마스터 엔티티 VO
|
||||
*
|
||||
* <p>tb_car_ledger_frmbk 테이블 매핑</p>
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CarLedgerFrmbkVO {
|
||||
|
||||
// ==== Static factory/mapping methods (moved from Service) ====
|
||||
public static CarLedgerFrmbkVO fromRequest(go.kr.project.vmis.model.ledger.LedgerRequest request) {
|
||||
return CarLedgerFrmbkVO.builder()
|
||||
.infoSysId(request.getInfoSysId())
|
||||
.infoSysIp(request.getInfoSysIp())
|
||||
.sigunguCode(request.getSigunguCode())
|
||||
.cntcInfoCode(request.getCntcInfoCode())
|
||||
.chargerId(request.getChargerId())
|
||||
.chargerIp(request.getChargerIp())
|
||||
.chargerNm(request.getChargerNm())
|
||||
.dmndVhrno(request.getVhrno())
|
||||
.rgtr(ApiConstant.DEFAULT_REGISTRANT)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static CarLedgerFrmbkVO fromResponseMaster(String id, go.kr.project.vmis.model.ledger.LedgerResponse res) {
|
||||
return CarLedgerFrmbkVO.builder()
|
||||
.carLedgerFrmbkId(id) // 등록원부 ID
|
||||
.cntcResultCode(res.getCntcResultCode()) // 연계 결과 코드
|
||||
.cntcResultDtls(res.getCntcResultDtls()) // 연계 결과 상세
|
||||
.ledgerGroupNo(res.getLedgerGroupNo()) // 원부 그룹 번호
|
||||
.ledgerIndvdlzNo(res.getLedgerIndvdlzNo()) // 원부 개별화 번호
|
||||
.vhmno(res.getVhmno()) // 차량관리번호
|
||||
.vhrno(res.getVhrno()) // 차량번호
|
||||
.vin(res.getVin()) // 차대번호
|
||||
.vhctyAsortCode(res.getVhctyAsortCode()) // 차종 종별 코드
|
||||
.vhctyAsortNm(res.getVhctyAsortNm()) // 차종 종별명
|
||||
.cnm(res.getCnm()) // 차명
|
||||
.colorCode(res.getColorCode()) // 색상 코드
|
||||
.colorNm(res.getColorNm()) // 색상명
|
||||
.nmplStndrdCode(res.getNmplStndrdCode()) // 번호판 규격 코드
|
||||
.nmplStndrdNm(res.getNmplStndrdNm()) // 번호판 규격명
|
||||
.prposSeCode(res.getPrposSeCode()) // 용도 구분 코드
|
||||
.prposSeNm(res.getPrposSeNm()) // 용도 구분명
|
||||
.mtrsFomNm(res.getMtrsFomNm()) // 원동기 형식명
|
||||
.fomNm(res.getFomNm()) // 형식명
|
||||
.acqsAmount(res.getAcqsAmount()) // 취득 금액
|
||||
.registDetailCode(res.getRegistDetailCode()) // 등록 상세 코드
|
||||
.registDetailNm(res.getRegistDetailNm()) // 등록 상세명
|
||||
.frstRegistDe(res.getFrstRegistDe()) // 최초 등록일
|
||||
.caagEndde(res.getCaagEndde()) // 차령 만료일자
|
||||
.prye(res.getPrye()) // 연식
|
||||
.spmnno1(res.getSpmnno1()) // 제원관리번호1
|
||||
.spmnno2(res.getSpmnno2()) // 제원관리번호2
|
||||
.yblMd(res.getYblMd()) // 제작 년월일
|
||||
.trvlDstnc(res.getTrvlDstnc()) // 주행거리
|
||||
.insptValidPdBgnde(res.getInsptValidPdBgnde()) // 검사 유효 기간 시작일
|
||||
.insptValidPdEndde(res.getInsptValidPdEndde()) // 검사 유효 기간 종료일
|
||||
.chckValidPdBgnde(res.getChckValidPdBgnde()) // 점검 유효 기간 시작일
|
||||
.chckValidPdEndde(res.getChckValidPdEndde()) // 점검 유효 기간 종료일
|
||||
.registReqstSeNm(res.getRegistReqstSeNm()) // 등록 청구 구분명
|
||||
.frstRegistRqrcno(res.getFrstRegistRqrcno()) // 최초 등록 접수번호
|
||||
.nmplCsdyRemnrDe(res.getNmplCsdyRemnrDe()) // 번호판 영치 최고일
|
||||
.nmplCsdyAt(res.getNmplCsdyAt()) // 번호판 영치 여부
|
||||
.bssUsePd(res.getBssUsePd()) // 사업용 기간
|
||||
.octhtErsrPrvntcNticeDe(res.getOcthtErsrPrvntcNticeDe()) // 직권말소 예고통지일
|
||||
.ersrRegistDe(res.getErsrRegistDe()) // 말소 등록일
|
||||
.ersrRegistSeCode(res.getErsrRegistSeCode()) // 말소 등록 구분 코드
|
||||
.ersrRegistSeNm(res.getErsrRegistSeNm()) // 말소 등록 구분명
|
||||
.mrtgcnt(res.getMrtgcnt()) // 저당건수
|
||||
.vhclecnt(res.getVhclecnt()) // 압류건수
|
||||
.stmdcnt(res.getStmdcnt()) // 구조변경건수
|
||||
.adres1(res.getAdres1()) // 주소1
|
||||
.adresNm1(res.getAdresNm1()) // 주소명1
|
||||
.adres(res.getAdres()) // 주소
|
||||
.adresNm(res.getAdresNm()) // 주소명
|
||||
.indvdlBsnmAt(res.getIndvdlBsnmAt()) // 개인사업자 여부
|
||||
.telno(res.getTelno()) // 전화번호
|
||||
.mberNm(res.getMberNm()) // 소유자명
|
||||
.mberSeCode(res.getMberSeCode()) // 소유자 구분 코드
|
||||
.mberSeNo(res.getMberSeNo()) // 소유자 번호
|
||||
.taxxmptTrgterSeCode(res.getTaxxmptTrgterSeCode()) // 면세 대상 구분 코드
|
||||
.taxxmptTrgterSeCodeNm(res.getTaxxmptTrgterSeCodeNm()) // 면세 대상 구분명
|
||||
.cntMatter(res.getCntMatter()) // 내용 사항
|
||||
.emdNm(res.getEmdNm()) // 읍면동명
|
||||
.prvntccnt(res.getPrvntccnt()) // 예방건수
|
||||
.xportFlflAtSttemntDe(res.getXportFlflAtSttemntDe()) // 수출이행신고일
|
||||
.partnRqrcno(res.getPartnRqrcno()) // 협력업체 접수번호
|
||||
.build();
|
||||
}
|
||||
|
||||
// PK
|
||||
private String carLedgerFrmbkId;
|
||||
|
||||
// 요청(헤더/본문) 정보
|
||||
private String infoSysId;
|
||||
private String infoSysIp;
|
||||
private String sigunguCode;
|
||||
private String cntcInfoCode;
|
||||
private String chargerId;
|
||||
private String chargerIp;
|
||||
private String chargerNm;
|
||||
|
||||
// 요청 본문
|
||||
private String dmndVhrno;
|
||||
private String dmndOnesInformationOpen;
|
||||
private String dmndCpttrNm;
|
||||
private String dmndCpttrIhidnum;
|
||||
private String dmndCpttrLegaldongCode;
|
||||
private String dmndRouteSeCode;
|
||||
private String dmndDetailExpression;
|
||||
private String dmndInqireSeCode;
|
||||
|
||||
// 응답 요약 정보
|
||||
private String cntcResultCode;
|
||||
private String cntcResultDtls;
|
||||
|
||||
// 응답 본문(마스터)
|
||||
private String ledgerGroupNo;
|
||||
private String ledgerIndvdlzNo;
|
||||
private String vhmno;
|
||||
private String vhrno;
|
||||
private String vin;
|
||||
private String vhctyAsortCode;
|
||||
private String vhctyAsortNm;
|
||||
private String cnm;
|
||||
private String colorCode;
|
||||
private String colorNm;
|
||||
private String nmplStndrdCode;
|
||||
private String nmplStndrdNm;
|
||||
private String prposSeCode;
|
||||
private String prposSeNm;
|
||||
private String mtrsFomNm;
|
||||
private String fomNm;
|
||||
private String acqsAmount;
|
||||
private String registDetailCode;
|
||||
private String registDetailNm;
|
||||
private String frstRegistDe;
|
||||
private String caagEndde;
|
||||
private String prye;
|
||||
private String spmnno1;
|
||||
private String spmnno2;
|
||||
private String yblMd;
|
||||
private String trvlDstnc;
|
||||
private String insptValidPdBgnde;
|
||||
private String insptValidPdEndde;
|
||||
private String chckValidPdBgnde;
|
||||
private String chckValidPdEndde;
|
||||
private String registReqstSeNm;
|
||||
private String frstRegistRqrcno;
|
||||
private String nmplCsdyRemnrDe;
|
||||
private String nmplCsdyAt;
|
||||
private String bssUsePd;
|
||||
private String octhtErsrPrvntcNticeDe;
|
||||
private String ersrRegistDe;
|
||||
private String ersrRegistSeCode;
|
||||
private String ersrRegistSeNm;
|
||||
private String mrtgcnt;
|
||||
private String vhclecnt;
|
||||
private String stmdcnt;
|
||||
private String adres1;
|
||||
private String adresNm1;
|
||||
private String adres;
|
||||
private String adresNm;
|
||||
private String indvdlBsnmAt;
|
||||
private String telno;
|
||||
private String mberNm;
|
||||
private String mberSeCode;
|
||||
private String mberSeNo;
|
||||
private String taxxmptTrgterSeCode;
|
||||
private String taxxmptTrgterSeCodeNm;
|
||||
private String cntMatter;
|
||||
private String emdNm;
|
||||
private String prvntccnt;
|
||||
private String xportFlflAtSttemntDe;
|
||||
private String partnRqrcno;
|
||||
private String frstTrnsfrDe;
|
||||
private String processImprtyResnCode;
|
||||
private String processImprtyResnDtls;
|
||||
|
||||
// 감사
|
||||
private String rgtr;
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package go.kr.project.vmis.model.ledger;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Schema(description = "자동차등록원부(갑) 요청 항목")
|
||||
@Getter
|
||||
@Setter
|
||||
public class LedgerRequest {
|
||||
|
||||
// 본문 공통 메타
|
||||
@Schema(description = "정보시스템ID", example = "41-345")
|
||||
@JsonProperty("INFO_SYS_ID")
|
||||
private String infoSysId;
|
||||
|
||||
@Schema(description = "정보시스템IP", example = "105.19.10.135")
|
||||
@JsonProperty("INFO_SYS_IP")
|
||||
private String infoSysIp;
|
||||
|
||||
@Schema(description = "시군구코드", example = "41460")
|
||||
@JsonProperty("SIGUNGU_CODE")
|
||||
private String sigunguCode;
|
||||
|
||||
// 서비스별 필드
|
||||
@Schema(description = "연계정보코드", example = "AC1_FD11_02")
|
||||
@JsonProperty("CNTC_INFO_CODE")
|
||||
private String cntcInfoCode;
|
||||
|
||||
@Schema(description = "담당자ID", example = "")
|
||||
@JsonProperty("CHARGER_ID")
|
||||
private String chargerId;
|
||||
|
||||
@Schema(description = "담당자IP", example = "")
|
||||
@JsonProperty("CHARGER_IP")
|
||||
private String chargerIp;
|
||||
|
||||
@Schema(description = "담당자명(사용자)", example = "")
|
||||
@JsonProperty("CHARGER_NM")
|
||||
private String chargerNm;
|
||||
|
||||
@Schema(description = "자동차등록번호")
|
||||
@JsonProperty("VHRNO")
|
||||
private String vhrno;
|
||||
|
||||
/*
|
||||
// 추가 항목 (명세 샘플 기준)
|
||||
@Schema(description = "개인정보공개")
|
||||
@JsonProperty("ONES_INFORMATION_OPEN")
|
||||
private String onesInformationOpen;
|
||||
|
||||
@Schema(description = "민원인성명")
|
||||
@JsonProperty("CPTTR_NM")
|
||||
private String cpttrNm;
|
||||
|
||||
@Schema(description = "민원인주민번호")
|
||||
@JsonProperty("CPTTR_IHIDNUM")
|
||||
@Size(max = 13)
|
||||
private String cpttrIhidnum;
|
||||
|
||||
@Schema(description = "민원인법정동코드")
|
||||
@JsonProperty("CPTTR_LEGALDONG_CODE")
|
||||
private String cpttrLegaldongCode;
|
||||
|
||||
@Schema(description = "경로구분코드")
|
||||
@JsonProperty("ROUTE_SE_CODE")
|
||||
private String routeSeCode;
|
||||
|
||||
@Schema(description = "내역표시")
|
||||
@JsonProperty("DETAIL_EXPRESSION")
|
||||
private String detailExpression;
|
||||
|
||||
@Schema(description = "조회구분코드")
|
||||
@JsonProperty("INQIRE_SE_CODE")
|
||||
private String inqireSeCode;
|
||||
*/
|
||||
|
||||
}
|
||||
@ -0,0 +1,250 @@
|
||||
package go.kr.project.vmis.model.ledger;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Schema(description = "자동차등록원부(갑) 응답 모델")
|
||||
@Getter
|
||||
@Setter
|
||||
public class LedgerResponse {
|
||||
|
||||
@JsonProperty("CNTC_RESULT_CODE")
|
||||
private String cntcResultCode;
|
||||
|
||||
@JsonProperty("CNTC_RESULT_DTLS")
|
||||
private String cntcResultDtls;
|
||||
|
||||
@JsonProperty("LEDGER_GROUP_NO")
|
||||
private String ledgerGroupNo;
|
||||
|
||||
@JsonProperty("LEDGER_INDVDLZ_NO")
|
||||
private String ledgerIndvdlzNo;
|
||||
|
||||
@JsonProperty("VHMNO")
|
||||
private String vhmno;
|
||||
|
||||
@JsonProperty("VHRNO")
|
||||
private String vhrno;
|
||||
|
||||
@JsonProperty("VIN")
|
||||
private String vin;
|
||||
|
||||
@JsonProperty("VHCTY_ASORT_CODE")
|
||||
private String vhctyAsortCode;
|
||||
|
||||
@JsonProperty("VHCTY_ASORT_NM")
|
||||
private String vhctyAsortNm;
|
||||
|
||||
@JsonProperty("CNM")
|
||||
private String cnm;
|
||||
|
||||
@JsonProperty("COLOR_CODE")
|
||||
private String colorCode;
|
||||
|
||||
@JsonProperty("COLOR_NM")
|
||||
private String colorNm;
|
||||
|
||||
@JsonProperty("NMPL_STNDRD_CODE")
|
||||
private String nmplStndrdCode;
|
||||
|
||||
@JsonProperty("NMPL_STNDRD_NM")
|
||||
private String nmplStndrdNm;
|
||||
|
||||
@JsonProperty("PRPOS_SE_CODE")
|
||||
private String prposSeCode;
|
||||
|
||||
@JsonProperty("PRPOS_SE_NM")
|
||||
private String prposSeNm;
|
||||
|
||||
@JsonProperty("MTRS_FOM_NM")
|
||||
private String mtrsFomNm;
|
||||
|
||||
@JsonProperty("FOM_NM")
|
||||
private String fomNm;
|
||||
|
||||
@JsonProperty("ACQS_AMOUNT")
|
||||
private String acqsAmount;
|
||||
|
||||
@JsonProperty("REGIST_DETAIL_CODE")
|
||||
private String registDetailCode;
|
||||
|
||||
@JsonProperty("REGIST_DETAIL_NM")
|
||||
private String registDetailNm;
|
||||
|
||||
@JsonProperty("FRST_REGIST_DE")
|
||||
private String frstRegistDe;
|
||||
|
||||
@JsonProperty("CAAG_ENDDE")
|
||||
private String caagEndde;
|
||||
|
||||
@JsonProperty("PRYE")
|
||||
private String prye;
|
||||
|
||||
@JsonProperty("SPMNNO1")
|
||||
private String spmnno1;
|
||||
|
||||
@JsonProperty("SPMNNO2")
|
||||
private String spmnno2;
|
||||
|
||||
@JsonProperty("YBL_MD")
|
||||
private String yblMd;
|
||||
|
||||
@JsonProperty("TRVL_DSTNC")
|
||||
private String trvlDstnc;
|
||||
|
||||
@JsonProperty("INSPT_VALID_PD_BGNDE")
|
||||
private String insptValidPdBgnde;
|
||||
|
||||
@JsonProperty("INSPT_VALID_PD_ENDDE")
|
||||
private String insptValidPdEndde;
|
||||
|
||||
@JsonProperty("CHCK_VALID_PD_BGNDE")
|
||||
private String chckValidPdBgnde;
|
||||
|
||||
@JsonProperty("CHCK_VALID_PD_ENDDE")
|
||||
private String chckValidPdEndde;
|
||||
|
||||
@JsonProperty("REGIST_REQST_SE_NM")
|
||||
private String registReqstSeNm;
|
||||
|
||||
@JsonProperty("FRST_REGIST_RQRCNO")
|
||||
private String frstRegistRqrcno;
|
||||
|
||||
@JsonProperty("NMPL_CSDY_REMNR_DE")
|
||||
private String nmplCsdyRemnrDe;
|
||||
|
||||
@JsonProperty("NMPL_CSDY_AT")
|
||||
private String nmplCsdyAt;
|
||||
|
||||
@JsonProperty("BSS_USE_PD")
|
||||
private String bssUsePd;
|
||||
|
||||
@JsonProperty("OCTHT_ERSR_PRVNTC_NTICE_DE")
|
||||
private String octhtErsrPrvntcNticeDe;
|
||||
|
||||
@JsonProperty("ERSR_REGIST_DE")
|
||||
private String ersrRegistDe;
|
||||
|
||||
@JsonProperty("ERSR_REGIST_SE_CODE")
|
||||
private String ersrRegistSeCode;
|
||||
|
||||
@JsonProperty("ERSR_REGIST_SE_NM")
|
||||
private String ersrRegistSeNm;
|
||||
|
||||
@JsonProperty("MRTGCNT")
|
||||
private String mrtgcnt;
|
||||
|
||||
@JsonProperty("VHCLECNT")
|
||||
private String vhclecnt;
|
||||
|
||||
@JsonProperty("STMDCNT")
|
||||
private String stmdcnt;
|
||||
|
||||
@JsonProperty("ADRES1")
|
||||
private String adres1;
|
||||
|
||||
@JsonProperty("ADRES_NM1")
|
||||
private String adresNm1;
|
||||
|
||||
@JsonProperty("ADRES")
|
||||
private String adres;
|
||||
|
||||
@JsonProperty("ADRES_NM")
|
||||
private String adresNm;
|
||||
|
||||
@JsonProperty("INDVDL_BSNM_AT")
|
||||
private String indvdlBsnmAt;
|
||||
|
||||
@JsonProperty("TELNO")
|
||||
private String telno;
|
||||
|
||||
@JsonProperty("MBER_NM")
|
||||
private String mberNm;
|
||||
|
||||
@JsonProperty("MBER_SE_CODE")
|
||||
private String mberSeCode;
|
||||
|
||||
@JsonProperty("MBER_SE_NO")
|
||||
private String mberSeNo;
|
||||
|
||||
@JsonProperty("TAXXMPT_TRGTER_SE_CODE")
|
||||
private String taxxmptTrgterSeCode;
|
||||
|
||||
@JsonProperty("TAXXMPT_TRGTER_SE_CODE_NM")
|
||||
private String taxxmptTrgterSeCodeNm;
|
||||
|
||||
@JsonProperty("CNT_MATTER")
|
||||
private String cntMatter;
|
||||
|
||||
@JsonProperty("EMD_NM")
|
||||
private String emdNm;
|
||||
|
||||
@JsonProperty("PRVNTCCNT")
|
||||
private String prvntccnt;
|
||||
|
||||
@JsonProperty("XPORT_FLFL_AT_STTEMNT_DE")
|
||||
private String xportFlflAtSttemntDe;
|
||||
|
||||
@JsonProperty("PARTN_RQRCNO")
|
||||
private String partnRqrcno;
|
||||
|
||||
@JsonProperty("record")
|
||||
private List<Record> record;
|
||||
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Schema(description = "원부 변경내역 record")
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Record {
|
||||
@JsonProperty("MAINCHK") private String mainchk;
|
||||
@JsonProperty("CHANGE_JOB_SE_CODE") private String changeJobSeCode;
|
||||
@JsonProperty("MAINNO") private String mainno;
|
||||
@JsonProperty("SUBNO") private String subno;
|
||||
@JsonProperty("DTLS") private String dtls;
|
||||
@JsonProperty("RQRCNO") private String rqrcno;
|
||||
@JsonProperty("VHMNO") private String vhmno;
|
||||
@JsonProperty("LEDGER_GROUP_NO") private String ledgerGroupNo;
|
||||
@JsonProperty("LEDGER_INDVDLZ_NO") private String ledgerIndvdlzNo;
|
||||
@JsonProperty("GUBUN_NM") private String gubunNm;
|
||||
@JsonProperty("CHANGE_DE") private String changeDe;
|
||||
@JsonProperty("DETAIL_SN") private String detailSn;
|
||||
@JsonProperty("FLAG") private String flag;
|
||||
|
||||
public String getMainchk() { return mainchk; }
|
||||
public void setMainchk(String mainchk) { this.mainchk = mainchk; }
|
||||
public String getChangeJobSeCode() { return changeJobSeCode; }
|
||||
public void setChangeJobSeCode(String changeJobSeCode) { this.changeJobSeCode = changeJobSeCode; }
|
||||
public String getMainno() { return mainno; }
|
||||
public void setMainno(String mainno) { this.mainno = mainno; }
|
||||
public String getSubno() { return subno; }
|
||||
public void setSubno(String subno) { this.subno = subno; }
|
||||
public String getDtls() { return dtls; }
|
||||
public void setDtls(String dtls) { this.dtls = dtls; }
|
||||
public String getRqrcno() { return rqrcno; }
|
||||
public void setRqrcno(String rqrcno) { this.rqrcno = rqrcno; }
|
||||
public String getVhmno() { return vhmno; }
|
||||
public void setVhmno(String vhmno) { this.vhmno = vhmno; }
|
||||
public String getLedgerGroupNo() { return ledgerGroupNo; }
|
||||
public void setLedgerGroupNo(String ledgerGroupNo) { this.ledgerGroupNo = ledgerGroupNo; }
|
||||
public String getLedgerIndvdlzNo() { return ledgerIndvdlzNo; }
|
||||
public void setLedgerIndvdlzNo(String ledgerIndvdlzNo) { this.ledgerIndvdlzNo = ledgerIndvdlzNo; }
|
||||
public String getGubunNm() { return gubunNm; }
|
||||
public void setGubunNm(String gubunNm) { this.gubunNm = gubunNm; }
|
||||
public String getChangeDe() { return changeDe; }
|
||||
public void setChangeDe(String changeDe) { this.changeDe = changeDe; }
|
||||
public String getDetailSn() { return detailSn; }
|
||||
public void setDetailSn(String detailSn) { this.detailSn = detailSn; }
|
||||
public String getFlag() { return flag; }
|
||||
public void setFlag(String flag) { this.flag = flag; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package go.kr.project.vmis.service;
|
||||
|
||||
import go.kr.project.vmis.mapper.CarBassMatterInqireMapper;
|
||||
import go.kr.project.vmis.model.basic.CarBassMatterInqireVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 자동차 기본사항 조회 로그 전용 서비스.
|
||||
*
|
||||
* <p>로그 적재만 별도 트랜잭션(REQUIRES_NEW)으로 처리하여,
|
||||
* 외부 호출 실패나 상위 트랜잭션 롤백 상황에서도 로그는 영속화되도록 보장한다.</p>
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CarBassMatterInqireLogService {
|
||||
|
||||
private final CarBassMatterInqireMapper carBassMatterInqireMapper;
|
||||
|
||||
/**
|
||||
* 최초 API 요청 정보를 등록한다. (REQUIRES_NEW)
|
||||
* @param request 요청 정보
|
||||
* @return 생성된 ID
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public String createInitialRequestNewTx(CarBassMatterInqireVO request) {
|
||||
String generatedId = carBassMatterInqireMapper.selectNextCarBassMatterInqireId();
|
||||
request.setCarBassMatterInqire(generatedId);
|
||||
int result = carBassMatterInqireMapper.insertCarBassMatterInqire(request);
|
||||
if (result != 1) {
|
||||
throw new RuntimeException("자동차 기본 사항 조회 정보 등록 실패");
|
||||
}
|
||||
log.info("[BASIC-REQ-LOG] 요청 정보 저장 완료(별도TX) - ID: {}, 차량번호: {}", generatedId, request.getDmndVhrno());
|
||||
return generatedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 응답/에러 결과를 업데이트한다. (REQUIRES_NEW)
|
||||
* @param response 업데이트 내용
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void updateResponseNewTx(CarBassMatterInqireVO response) {
|
||||
if (response.getCarBassMatterInqire() == null) {
|
||||
throw new IllegalArgumentException("자동차 기본 사항 조회 ID는 필수입니다.");
|
||||
}
|
||||
int result = carBassMatterInqireMapper.updateCarBassMatterInqire(response);
|
||||
if (result != 1) {
|
||||
throw new RuntimeException("자동차 기본 사항 조회 정보 업데이트 실패 - ID: " + response.getCarBassMatterInqire());
|
||||
}
|
||||
log.info("[BASIC-RES-LOG] 응답/에러 정보 저장 완료(별도TX) - ID: {}, 결과코드: {}", response.getCarBassMatterInqire(), response.getCntcResultCode());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
package go.kr.project.vmis.service;
|
||||
|
||||
import go.kr.project.vmis.client.GovernmentApi;
|
||||
import go.kr.project.vmis.config.ApiConstant;
|
||||
import go.kr.project.vmis.util.ExceptionDetailUtil;
|
||||
import go.kr.project.vmis.model.basic.BasicRequest;
|
||||
import go.kr.project.vmis.model.basic.BasicResponse;
|
||||
import go.kr.project.vmis.model.basic.CarBassMatterInqireVO;
|
||||
import go.kr.project.vmis.model.common.Envelope;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 자동차 기본 사항 조회 서비스
|
||||
*
|
||||
* <p>API 호출 정보를 관리하는 서비스 클래스입니다.</p>
|
||||
* <ul>
|
||||
* <li>최초 요청: createInitialRequest() - 시퀀스로 ID 생성 후 INSERT</li>
|
||||
* <li>결과 업데이트: updateResponse() - 응답 데이터 UPDATE</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CarBassMatterInqireService {
|
||||
|
||||
private final GovernmentApi governmentApi;
|
||||
private final RequestEnricher enricher;
|
||||
private final CarBassMatterInqireLogService logService;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 자동차 기본사항 조회: 보강 -> 최초요청로그 -> 외부호출 -> 응답로그.
|
||||
*/
|
||||
@Transactional
|
||||
public ResponseEntity<Envelope<BasicResponse>> basic(Envelope<BasicRequest> envelope) {
|
||||
// 1) 요청 보강
|
||||
enricher.enrichBasic(envelope);
|
||||
|
||||
String generatedId = null;
|
||||
try {
|
||||
// 2) 최초 요청 로그 저장 (첫 번째 데이터 기준)
|
||||
if (envelope.getData() != null && !envelope.getData().isEmpty()) {
|
||||
BasicRequest req = envelope.getData().get(0);
|
||||
CarBassMatterInqireVO logEntity = CarBassMatterInqireVO.fromRequest(req);
|
||||
generatedId = logService.createInitialRequestNewTx(logEntity);
|
||||
}
|
||||
|
||||
// 3) 외부 API 호출
|
||||
ResponseEntity<Envelope<BasicResponse>> response = governmentApi.callBasic(envelope);
|
||||
|
||||
// 4) 응답 로그 업데이트
|
||||
// 원본 소스, 정상적인 호출, 리턴(에러 리턴포함) 일 경우에만 에러 로그 남김
|
||||
if (generatedId != null && response.getBody() != null) {
|
||||
CarBassMatterInqireVO update = CarBassMatterInqireVO.fromResponse(generatedId, response.getBody());
|
||||
if (update != null) {
|
||||
logService.updateResponseNewTx(update);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
// 5) 오류 로그 업데이트
|
||||
if (generatedId != null) {
|
||||
try {
|
||||
String detail = ExceptionDetailUtil.buildForLog(e);
|
||||
CarBassMatterInqireVO errorLog = CarBassMatterInqireVO.builder()
|
||||
.carBassMatterInqire(generatedId) // 자동차기본사항조회 ID (PK)
|
||||
.cntcResultCode(ApiConstant.CNTC_RESULT_CODE_ERROR) // 연계결과코드 (에러)
|
||||
.cntcResultDtls(detail) // 연계결과상세 (에러 메시지)
|
||||
.build();
|
||||
logService.updateResponseNewTx(errorLog);
|
||||
log.error("[BASIC-ERR-LOG] API 호출 에러 정보 저장 완료(별도TX) - ID: {}, detail: {}", generatedId, detail, e);
|
||||
} catch (Exception ignore) {
|
||||
log.error("[BASIC-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package go.kr.project.vmis.service;
|
||||
|
||||
import go.kr.project.vmis.mapper.CarLedgerFrmbkMapper;
|
||||
import go.kr.project.vmis.model.ledger.CarLedgerFrmbkDtlVO;
|
||||
import go.kr.project.vmis.model.ledger.CarLedgerFrmbkVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 자동차 등록 원부(갑) 로그 전용 서비스.
|
||||
* - 모든 로깅(write)은 별도 트랜잭션(REQUIRES_NEW)으로 수행하여 상위 트랜잭션 롤백의 영향을 받지 않도록 한다.
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CarLedgerFrmbkLogService {
|
||||
|
||||
private final CarLedgerFrmbkMapper mapper;
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public String createInitialRequestNewTx(CarLedgerFrmbkVO request) {
|
||||
String id = mapper.selectNextCarLedgerFrmbkId();
|
||||
request.setCarLedgerFrmbkId(id);
|
||||
int result = mapper.insertCarLedgerFrmbk(request);
|
||||
if (result != 1) {
|
||||
throw new RuntimeException("자동차 등록 원부(갑) 최초요청 등록 실패");
|
||||
}
|
||||
log.info("[LEDGER-REQ-LOG] 최초 요청 저장(별도TX) - ID: {}, 차량번호: {}", id, request.getDmndVhrno());
|
||||
return id;
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void updateResponseNewTx(CarLedgerFrmbkVO response) {
|
||||
if (response.getCarLedgerFrmbkId() == null) {
|
||||
throw new IllegalArgumentException("자동차 등록 원부(갑) ID는 필수입니다.");
|
||||
}
|
||||
int updated = mapper.updateCarLedgerFrmbk(response);
|
||||
if (updated != 1) {
|
||||
throw new RuntimeException("자동차 등록 원부(갑) 정보 업데이트 실패 - ID: " + response.getCarLedgerFrmbkId());
|
||||
}
|
||||
log.info("[LEDGER-RES-LOG] 마스터 응답 업데이트(별도TX) - ID: {}, 결과코드: {}",
|
||||
response.getCarLedgerFrmbkId(), response.getCntcResultCode());
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void saveDetailsNewTx(String masterId, List<CarLedgerFrmbkDtlVO> details) {
|
||||
if (details == null || details.isEmpty()) return;
|
||||
for (CarLedgerFrmbkDtlVO dtl : details) {
|
||||
String dtlId = mapper.selectNextCarLedgerFrmbkDtlId();
|
||||
dtl.setCarLedgerFrmbkDtlId(dtlId);
|
||||
dtl.setCarLedgerFrmbkId(masterId);
|
||||
mapper.insertCarLedgerFrmbkDtl(dtl);
|
||||
}
|
||||
log.info("[LEDGER-RES-LOG] 상세 {}건 저장(별도TX) - ID: {}", details.size(), masterId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package go.kr.project.vmis.service;
|
||||
|
||||
import go.kr.project.vmis.client.GovernmentApi;
|
||||
import go.kr.project.vmis.config.ApiConstant;
|
||||
import go.kr.project.vmis.model.common.Envelope;
|
||||
import go.kr.project.vmis.model.ledger.CarLedgerFrmbkDtlVO;
|
||||
import go.kr.project.vmis.model.ledger.CarLedgerFrmbkVO;
|
||||
import go.kr.project.vmis.model.ledger.LedgerRequest;
|
||||
import go.kr.project.vmis.model.ledger.LedgerResponse;
|
||||
import go.kr.project.vmis.util.ExceptionDetailUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 자동차 등록 원부(갑) 서비스 (오케스트레이션)
|
||||
* - 요청 보강, 외부 API 호출, 로그 서비스 위임
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CarLedgerFrmbkService {
|
||||
|
||||
private final GovernmentApi governmentApi;
|
||||
private final RequestEnricher enricher;
|
||||
private final CarLedgerFrmbkLogService logService;
|
||||
|
||||
/**
|
||||
* 자동차 등록원부(갑) 조회: 보강 -> 최초요청로그(별도TX) -> 외부호출 -> 응답로그(마스터/상세, 별도TX) -> 오류 시 에러로그(별도TX).
|
||||
*/
|
||||
@Transactional
|
||||
public ResponseEntity<Envelope<LedgerResponse>> ledger(Envelope<LedgerRequest> envelope) {
|
||||
// 1) 요청 보강
|
||||
enricher.enrichLedger(envelope);
|
||||
|
||||
String generatedId = null;
|
||||
try {
|
||||
// 2) 최초 요청 로그 저장 (첫 번째 데이터 기준)
|
||||
if (envelope.getData() != null && !envelope.getData().isEmpty()) {
|
||||
LedgerRequest req = envelope.getData().get(0);
|
||||
CarLedgerFrmbkVO init = CarLedgerFrmbkVO.fromRequest(req);
|
||||
generatedId = logService.createInitialRequestNewTx(init);
|
||||
}
|
||||
|
||||
// 3) 외부 API 호출
|
||||
ResponseEntity<Envelope<LedgerResponse>> response = governmentApi.callLedger(envelope);
|
||||
|
||||
// 4) 응답 로그 업데이트 (마스터 + 상세)
|
||||
if (generatedId != null && response.getBody() != null &&
|
||||
response.getBody().getData() != null && !response.getBody().getData().isEmpty()) {
|
||||
LedgerResponse body = response.getBody().getData().get(0);
|
||||
CarLedgerFrmbkVO masterUpdate = CarLedgerFrmbkVO.fromResponseMaster(generatedId, body);
|
||||
logService.updateResponseNewTx(masterUpdate);
|
||||
|
||||
List<CarLedgerFrmbkDtlVO> details = CarLedgerFrmbkDtlVO.listFromResponse(body, generatedId);
|
||||
if (details != null && !details.isEmpty()) {
|
||||
logService.saveDetailsNewTx(generatedId, details);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
// 5) 오류 로그 업데이트
|
||||
if (generatedId != null) {
|
||||
try {
|
||||
String detail = ExceptionDetailUtil.buildForLog(e);
|
||||
CarLedgerFrmbkVO errorLog = CarLedgerFrmbkVO.builder()
|
||||
.carLedgerFrmbkId(generatedId)
|
||||
.cntcResultCode(ApiConstant.CNTC_RESULT_CODE_ERROR)
|
||||
.cntcResultDtls(detail)
|
||||
.build();
|
||||
logService.updateResponseNewTx(errorLog);
|
||||
log.error("[LEDGER-ERR-LOG] API 호출 에러 정보 저장 완료(별도TX) - ID: {}, detail: {}", generatedId, detail, e);
|
||||
} catch (Exception ignore) {
|
||||
log.error("[LEDGER-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
package go.kr.project.vmis.service;
|
||||
|
||||
import go.kr.project.vmis.config.properties.VmisProperties;
|
||||
import go.kr.project.vmis.model.basic.BasicRequest;
|
||||
import go.kr.project.vmis.model.common.Envelope;
|
||||
import go.kr.project.vmis.model.ledger.LedgerRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Populates incoming request models with values from YAML configuration.
|
||||
* Unconditionally overwrites the listed fields per requirement:
|
||||
* - INFO_SYS_ID, INFO_SYS_IP, SIGUNGU_CODE
|
||||
* - CNTC_INFO_CODE (service specific)
|
||||
* - CHARGER_ID, CHARGER_IP, CHARGER_NM
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RequestEnricher {
|
||||
|
||||
private final VmisProperties props;
|
||||
|
||||
public RequestEnricher(VmisProperties props) {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public void enrichBasic(Envelope<BasicRequest> envelope) {
|
||||
if (envelope == null || envelope.getData() == null) return;
|
||||
VmisProperties.SystemProps sys = props.getSystem();
|
||||
String cntc = props.getGov().getServices().getBasic().getCntcInfoCode();
|
||||
for (BasicRequest req : envelope.getData()) {
|
||||
if (req == null) continue;
|
||||
req.setInfoSysId(sys.getInfoSysId());
|
||||
req.setInfoSysIp(sys.getInfoSysIp());
|
||||
req.setSigunguCode(sys.getRegionCode());
|
||||
req.setCntcInfoCode(cntc);
|
||||
req.setChargerId(sys.getChargerId());
|
||||
req.setChargerIp(sys.getChargerIp());
|
||||
req.setChargerNm(sys.getChargerNm());
|
||||
}
|
||||
log.debug("[ENRICH] basic: applied INFO_SYS_ID={}, INFO_SYS_IP={}, SIGUNGU_CODE={}, CNTC_INFO_CODE={}",
|
||||
sys.getInfoSysId(), sys.getInfoSysIp(), sys.getRegionCode(), cntc);
|
||||
}
|
||||
|
||||
public void enrichLedger(Envelope<LedgerRequest> envelope) {
|
||||
if (envelope == null || envelope.getData() == null) return;
|
||||
VmisProperties.SystemProps sys = props.getSystem();
|
||||
String cntc = props.getGov().getServices().getLedger().getCntcInfoCode();
|
||||
for (LedgerRequest req : envelope.getData()) {
|
||||
if (req == null) continue;
|
||||
req.setInfoSysId(sys.getInfoSysId());
|
||||
req.setInfoSysIp(sys.getInfoSysIp());
|
||||
req.setSigunguCode(sys.getRegionCode());
|
||||
req.setCntcInfoCode(cntc);
|
||||
req.setChargerId(sys.getChargerId());
|
||||
req.setChargerIp(sys.getChargerIp());
|
||||
req.setChargerNm(sys.getChargerNm());
|
||||
}
|
||||
log.debug("[ENRICH] ledger: applied INFO_SYS_ID={}, INFO_SYS_IP={}, SIGUNGU_CODE={}, CNTC_INFO_CODE={}",
|
||||
sys.getInfoSysId(), sys.getInfoSysIp(), sys.getRegionCode(), cntc);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package go.kr.project.vmis.util;
|
||||
|
||||
/**
|
||||
* Common helper to extract root-cause message and truncate to DB column limit (default 4000 chars).
|
||||
*/
|
||||
public final class ExceptionDetailUtil {
|
||||
|
||||
private ExceptionDetailUtil() {}
|
||||
|
||||
public static String buildForLog(Throwable t) {
|
||||
return buildForLog(t, 4000);
|
||||
}
|
||||
|
||||
public static String buildForLog(Throwable t, int maxLen) {
|
||||
if (t == null) return "오류: unknown";
|
||||
Throwable root = t;
|
||||
int guard = 0;
|
||||
while (root.getCause() != null && root.getCause() != root && guard++ < 20) {
|
||||
root = root.getCause();
|
||||
}
|
||||
String className = root.getClass().getName();
|
||||
String message = root.getMessage();
|
||||
String detail = (message != null && !message.isEmpty()) ? (className + ": " + message) : root.toString();
|
||||
if (detail != null && detail.length() > maxLen) {
|
||||
detail = detail.substring(0, maxLen);
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package go.kr.project.vmis.util;
|
||||
|
||||
import go.kr.project.vmis.config.properties.VmisProperties;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Wrapper utility around legacy {@link NewGpkiUtil} using configuration from YAML.
|
||||
*
|
||||
* Notes:
|
||||
* - Place this class under src/main/java/util as requested.
|
||||
* - Uses Lombok for getters/setters.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class GpkiCryptoUtil {
|
||||
|
||||
private String gpkiLicPath;
|
||||
private Boolean ldap; // null -> legacy default
|
||||
private String certFilePath;
|
||||
private String envCertFilePathName;
|
||||
private String envPrivateKeyFilePathName;
|
||||
private String envPrivateKeyPasswd;
|
||||
private String sigCertFilePathName;
|
||||
private String sigPrivateKeyFilePathName;
|
||||
private String sigPrivateKeyPasswd;
|
||||
private String myServerId; // equals to certServerId (INFO system server cert id)
|
||||
private String targetServerIdList; // comma joined list (can be single id)
|
||||
|
||||
private transient NewGpkiUtil delegate;
|
||||
|
||||
public static GpkiCryptoUtil from(VmisProperties.GpkiProps props) throws Exception {
|
||||
GpkiCryptoUtil util = new GpkiCryptoUtil();
|
||||
util.setGpkiLicPath(props.getGpkiLicPath());
|
||||
util.setLdap(props.getLdap());
|
||||
util.setCertFilePath(props.getCertFilePath());
|
||||
util.setEnvCertFilePathName(props.getEnvCertFilePathName());
|
||||
util.setEnvPrivateKeyFilePathName(props.getEnvPrivateKeyFilePathName());
|
||||
util.setEnvPrivateKeyPasswd(props.getEnvPrivateKeyPasswd());
|
||||
util.setSigCertFilePathName(props.getSigCertFilePathName());
|
||||
util.setSigPrivateKeyFilePathName(props.getSigPrivateKeyFilePathName());
|
||||
util.setSigPrivateKeyPasswd(props.getSigPrivateKeyPasswd());
|
||||
util.setMyServerId(props.getCertServerId());
|
||||
// Accept single targetServerId but allow list if provided by YAML in future
|
||||
util.setTargetServerIdList(props.getTargetServerId());
|
||||
util.initialize();
|
||||
return util;
|
||||
}
|
||||
|
||||
public void initialize() throws Exception {
|
||||
NewGpkiUtil g = new NewGpkiUtil();
|
||||
if (gpkiLicPath != null) g.setGpkiLicPath(gpkiLicPath);
|
||||
if (ldap != null) g.setIsLDAP(ldap);
|
||||
if (certFilePath != null) g.setCertFilePath(certFilePath);
|
||||
if (envCertFilePathName != null) g.setEnvCertFilePathName(envCertFilePathName);
|
||||
if (envPrivateKeyFilePathName != null) g.setEnvPrivateKeyFilePathName(envPrivateKeyFilePathName);
|
||||
if (envPrivateKeyPasswd != null) g.setEnvPrivateKeyPasswd(envPrivateKeyPasswd);
|
||||
if (sigCertFilePathName != null) g.setSigCertFilePathName(sigCertFilePathName);
|
||||
if (sigPrivateKeyFilePathName != null) g.setSigPrivateKeyFilePathName(sigPrivateKeyFilePathName);
|
||||
if (sigPrivateKeyPasswd != null) g.setSigPrivateKeyPasswd(sigPrivateKeyPasswd);
|
||||
if (myServerId != null) g.setMyServerId(myServerId);
|
||||
if (targetServerIdList != null) g.setTargetServerIdList(targetServerIdList);
|
||||
g.init();
|
||||
this.delegate = g;
|
||||
}
|
||||
|
||||
public String encryptToBase64(String plain, String targetServerId, String charset) throws Exception {
|
||||
ensureInit();
|
||||
byte[] enc = delegate.encrypt(plain.getBytes(charset), targetServerId, true);
|
||||
return delegate.encode(enc);
|
||||
}
|
||||
|
||||
public String decryptFromBase64(String base64, String charset) throws Exception {
|
||||
ensureInit();
|
||||
byte[] bin = delegate.decode(base64);
|
||||
byte[] dec = delegate.decrypt(bin);
|
||||
return new String(dec, charset);
|
||||
}
|
||||
|
||||
public String signToBase64(String plain, String charset) throws Exception {
|
||||
ensureInit();
|
||||
byte[] sig = delegate.sign(plain.getBytes(charset));
|
||||
return delegate.encode(sig);
|
||||
}
|
||||
|
||||
public String verifyAndExtractBase64(String signedBase64, String charset) throws Exception {
|
||||
ensureInit();
|
||||
byte[] signed = delegate.decode(signedBase64);
|
||||
byte[] data = delegate.validate(signed);
|
||||
return new String(data, charset);
|
||||
}
|
||||
|
||||
private void ensureInit() {
|
||||
if (delegate == null) {
|
||||
throw new IllegalStateException("GpkiCryptoUtil is not initialized. Call initialize() or from(props).");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,383 @@
|
||||
package go.kr.project.vmis.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.gpki.gpkiapi_jni;
|
||||
import com.gpki.gpkiapi.GpkiApi;
|
||||
import com.gpki.gpkiapi.cert.X509Certificate;
|
||||
import com.gpki.gpkiapi.crypto.PrivateKey;
|
||||
import com.gpki.gpkiapi.storage.Disk;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class NewGpkiUtil {
|
||||
byte[] myEnvCert, myEnvKey, mySigCert, mySigKey;
|
||||
private Map<String, X509Certificate> targetServerCertMap = new HashMap<String, X509Certificate>();
|
||||
|
||||
// properties
|
||||
private String myServerId;
|
||||
private String targetServerIdList;
|
||||
private String envCertFilePathName;
|
||||
private String envPrivateKeyFilePathName;
|
||||
private String envPrivateKeyPasswd;
|
||||
private String sigCertFilePathName;
|
||||
private String sigPrivateKeyFilePathName;
|
||||
private String sigPrivateKeyPasswd;
|
||||
private String certFilePath;
|
||||
private String gpkiLicPath = ".";
|
||||
private boolean isLDAP;
|
||||
private boolean testGPKI = false;
|
||||
|
||||
|
||||
public void init() throws Exception {
|
||||
GpkiApi.init(gpkiLicPath);
|
||||
gpkiapi_jni gpki = this.getGPKI();
|
||||
if(log.isDebugEnabled()){
|
||||
if(gpki.API_GetInfo()==0)
|
||||
log.debug(gpki.sReturnString);
|
||||
else
|
||||
log.error(gpki.sDetailErrorString);
|
||||
}
|
||||
if(targetServerIdList!=null){
|
||||
String certIdList[] = targetServerIdList.split(",");
|
||||
for(int i = 0 ; i < certIdList.length ; i++){
|
||||
String certId = certIdList[i].trim();
|
||||
if(!certId.equals("")){
|
||||
load(gpki, certId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Loading gpki certificate : myServerId="
|
||||
+ this.getMyServerId());
|
||||
|
||||
X509Certificate _myEnvCert = Disk.readCert(this
|
||||
.getEnvCertFilePathName());
|
||||
myEnvCert = _myEnvCert.getCert();
|
||||
|
||||
PrivateKey _myEnvKey = Disk.readPriKey(this
|
||||
.getEnvPrivateKeyFilePathName(), this.getEnvPrivateKeyPasswd());
|
||||
myEnvKey = _myEnvKey.getKey();
|
||||
|
||||
X509Certificate _mySigCert = Disk.readCert(this
|
||||
.getSigCertFilePathName());
|
||||
mySigCert = _mySigCert.getCert();
|
||||
|
||||
PrivateKey _mySigKey = Disk.readPriKey(this
|
||||
.getSigPrivateKeyFilePathName(), this.getSigPrivateKeyPasswd());
|
||||
mySigKey = _mySigKey.getKey();
|
||||
|
||||
//test my cert GPKI
|
||||
if(testGPKI){
|
||||
load(gpki, this.getMyServerId());
|
||||
testGpki(gpki);
|
||||
}
|
||||
this.finish(gpki);
|
||||
log.info("GpkiUtil initialized");
|
||||
}
|
||||
|
||||
private void load(gpkiapi_jni gpki, String certId) throws Exception {
|
||||
|
||||
log.debug("Loading gpki certificate : targetServerId="+ certId);
|
||||
|
||||
X509Certificate cert = targetServerCertMap.get(certId);
|
||||
if (cert != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLDAP) {
|
||||
// String ldapUrl = "ldap://10.1.7.140:389/cn=";
|
||||
// String ldapUrl = "ldap://ldap.gcc.go.kr:389/cn=";
|
||||
String ldapUrl = "ldap://10.1.7.118:389/cn="; // 행정망인 경우
|
||||
// String ldapUrl = "ldap://152.99.57.127:389/cn="; // 인터넷망인 경우
|
||||
String ldapUri;
|
||||
if (certId.charAt(3) > '9') {
|
||||
ldapUri = ",ou=Group of Server,o=Public of Korea,c=KR";
|
||||
} else {
|
||||
ldapUri = ",ou=Group of Server,o=Government of Korea,c=KR";
|
||||
}
|
||||
|
||||
int ret = gpki.LDAP_GetAnyDataByURL("userCertificate;binary", ldapUrl + certId + ldapUri);
|
||||
this.checkResult(ret, gpki);
|
||||
cert = new X509Certificate(gpki.baReturnArray);
|
||||
} else {
|
||||
if(certFilePath != null){
|
||||
cert = Disk.readCert(certFilePath + File.separator + certId + ".cer");
|
||||
}else{
|
||||
log.debug("not certFilePath");
|
||||
}
|
||||
}
|
||||
|
||||
targetServerCertMap.put(certId, cert);
|
||||
}
|
||||
|
||||
private gpkiapi_jni getGPKI(){
|
||||
gpkiapi_jni gpki = new gpkiapi_jni();
|
||||
if(gpki.API_Init(gpkiLicPath) != 0){
|
||||
log.error(gpki.sDetailErrorString);
|
||||
}
|
||||
return gpki;
|
||||
}
|
||||
private void finish(gpkiapi_jni gpki){
|
||||
if(gpki.API_Finish() != 0){
|
||||
log.error(gpki.sDetailErrorString);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] encrypt(byte[] plain, String certId , boolean load) throws Exception {
|
||||
X509Certificate targetEnvCert = targetServerCertMap.get(certId);
|
||||
if (targetEnvCert == null) {
|
||||
throw new Exception("Certificate not found : targetServerId=" + certId);
|
||||
}
|
||||
|
||||
gpkiapi_jni gpki = this.getGPKI();
|
||||
try{
|
||||
int result = gpki.CMS_MakeEnvelopedData(targetEnvCert.getCert(), plain,
|
||||
gpkiapi_jni.SYM_ALG_NEAT_CBC);
|
||||
checkResult(result, "Fail to encrypt message", gpki);
|
||||
|
||||
return gpki.baReturnArray;
|
||||
}catch(Exception ex){
|
||||
throw ex;
|
||||
}finally{
|
||||
finish(gpki);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] encrypt(byte[] plain, String certId) throws Exception {
|
||||
return encrypt(plain,certId , false);
|
||||
}
|
||||
|
||||
public byte[] decrypt(byte[] encrypted) throws Exception {
|
||||
|
||||
gpkiapi_jni gpki = this.getGPKI();
|
||||
try{
|
||||
int result = gpki.CMS_ProcessEnvelopedData(myEnvCert, myEnvKey,
|
||||
encrypted);
|
||||
checkResult(result, "Fail to decrpyt message", gpki);
|
||||
|
||||
return gpki.baReturnArray;
|
||||
}catch(Exception ex){
|
||||
throw ex;
|
||||
}finally{
|
||||
finish(gpki);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] sign(byte[] plain) throws Exception {
|
||||
|
||||
gpkiapi_jni gpki = this.getGPKI();
|
||||
try{
|
||||
int result = gpki.CMS_MakeSignedData(mySigCert, mySigKey, plain, null);
|
||||
checkResult(result, "Fail to sign message", gpki);
|
||||
|
||||
return gpki.baReturnArray;
|
||||
}catch(Exception ex){
|
||||
throw ex;
|
||||
}finally{
|
||||
finish(gpki);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] validate(byte[] signed) throws Exception {
|
||||
|
||||
gpkiapi_jni gpki = this.getGPKI();
|
||||
try{
|
||||
int result = gpki.CMS_ProcessSignedData(signed);
|
||||
checkResult(result, "Fail to validate signed message", gpki);
|
||||
return gpki.baData;
|
||||
}catch(Exception ex){
|
||||
throw ex;
|
||||
}finally{
|
||||
finish(gpki);
|
||||
}
|
||||
}
|
||||
|
||||
public String encode(byte[] plain) throws Exception {
|
||||
|
||||
gpkiapi_jni gpki = this.getGPKI();
|
||||
try{
|
||||
int result = gpki.BASE64_Encode(plain);
|
||||
checkResult(result, "Fail to encode message", gpki);
|
||||
|
||||
return gpki.sReturnString;
|
||||
}catch(Exception ex){
|
||||
throw ex;
|
||||
}finally{
|
||||
finish(gpki);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public byte[] decode(String base64) throws Exception {
|
||||
|
||||
gpkiapi_jni gpki = this.getGPKI();
|
||||
try{
|
||||
int result = gpki.BASE64_Decode(base64);
|
||||
checkResult(result, "Fail to decode base64 message", gpki);
|
||||
|
||||
return gpki.baReturnArray;
|
||||
}catch(Exception ex){
|
||||
throw ex;
|
||||
}finally{
|
||||
finish(gpki);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkResult(int result, gpkiapi_jni gpki)throws Exception{
|
||||
this.checkResult(result, null, gpki);
|
||||
}
|
||||
|
||||
private void checkResult(int result ,String message, gpkiapi_jni gpki)throws Exception{
|
||||
if( 0 != result){
|
||||
if(null != gpki){
|
||||
throw new Exception(message + " : gpkiErrorMessage=" + gpki.sDetailErrorString);
|
||||
}else{
|
||||
throw new Exception(message + " : gpkiErrorCode=" + result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testGpki(gpkiapi_jni gpki) throws Exception{
|
||||
//gpki test eng
|
||||
log.info("=======================================================");
|
||||
log.info("================ TEST GPKI START ======================");
|
||||
log.info("=======================================================");
|
||||
String original_Eng = "abc";
|
||||
log.info("=== TEST ENG STRING: "+ original_Eng);
|
||||
try {
|
||||
byte[] encrypted = encrypt(original_Eng.getBytes(), myServerId);
|
||||
log.info("=== TEST ENG ENCRYPT STRING: "+ encode(encrypted));
|
||||
String decrypted = new String(decrypt(encrypted));
|
||||
log.info("=== TEST ENG DECRYPT STRING: "+decrypted);
|
||||
|
||||
if (!original_Eng.equals(decrypted)) {
|
||||
throw new Exception("GpkiUtil not initialized properly(english)");
|
||||
}
|
||||
log.info("=== TEST ENG: OK");
|
||||
} catch (Exception e) {
|
||||
log.warn("Gpki Test error(english)", e);
|
||||
throw e;
|
||||
}
|
||||
//gpki test kor
|
||||
String original = "한글테스트";
|
||||
log.info("=== TEST KOR STRING: "+ original);
|
||||
try {
|
||||
byte[] encrypted = encrypt(original.getBytes(), myServerId);
|
||||
log.info("=== TEST KOR ENCRYPT STRING: "+ encode(encrypted));
|
||||
String decrypted = new String(decrypt(encrypted));
|
||||
log.info("=== TEST KOR DECRYPT STRING: "+decrypted);
|
||||
if (!original.equals(decrypted)) {
|
||||
throw new Exception("GpkiUtil not initialized properly(korean)");
|
||||
}
|
||||
log.info("=== TEST KOR: OK");
|
||||
} catch (Exception e) {
|
||||
log.warn("Gpki Test error(korean)", e);
|
||||
throw e;
|
||||
}finally{
|
||||
log.info("=======================================================");
|
||||
log.info("================ TEST GPKI END ========================");
|
||||
log.info("=======================================================");
|
||||
}
|
||||
}
|
||||
|
||||
public String getMyServerId() {
|
||||
return myServerId;
|
||||
}
|
||||
|
||||
public void setMyServerId(String myServerId) {
|
||||
this.myServerId = myServerId.trim();
|
||||
}
|
||||
|
||||
public String getEnvCertFilePathName() {
|
||||
return envCertFilePathName;
|
||||
}
|
||||
|
||||
public void setEnvCertFilePathName(String envCertFilePathName) {
|
||||
this.envCertFilePathName = envCertFilePathName.trim();
|
||||
}
|
||||
|
||||
public String getEnvPrivateKeyFilePathName() {
|
||||
return envPrivateKeyFilePathName;
|
||||
}
|
||||
|
||||
public void setEnvPrivateKeyFilePathName(String envPrivateKeyFilePathName) {
|
||||
this.envPrivateKeyFilePathName = envPrivateKeyFilePathName.trim();
|
||||
}
|
||||
|
||||
public String getEnvPrivateKeyPasswd() {
|
||||
return envPrivateKeyPasswd;
|
||||
}
|
||||
|
||||
public void setEnvPrivateKeyPasswd(String envPrivateKeyPasswd) {
|
||||
this.envPrivateKeyPasswd = envPrivateKeyPasswd.trim();
|
||||
}
|
||||
|
||||
public String getSigPrivateKeyPasswd() {
|
||||
return sigPrivateKeyPasswd;
|
||||
}
|
||||
|
||||
public void setSigPrivateKeyPasswd(String sigPrivateKeyPasswd) {
|
||||
this.sigPrivateKeyPasswd = sigPrivateKeyPasswd.trim();
|
||||
}
|
||||
|
||||
public String getSigCertFilePathName() {
|
||||
return sigCertFilePathName;
|
||||
}
|
||||
|
||||
public void setSigCertFilePathName(String sigCertFilePathName) {
|
||||
this.sigCertFilePathName = sigCertFilePathName.trim();
|
||||
}
|
||||
|
||||
public String getSigPrivateKeyFilePathName() {
|
||||
return sigPrivateKeyFilePathName;
|
||||
}
|
||||
|
||||
public void setSigPrivateKeyFilePathName(String sigPrivateKeyFilePathName) {
|
||||
this.sigPrivateKeyFilePathName = sigPrivateKeyFilePathName.trim();
|
||||
}
|
||||
|
||||
public boolean getIsLDAP() {
|
||||
return isLDAP;
|
||||
}
|
||||
|
||||
public void setIsLDAP(boolean isLDAP) {
|
||||
this.isLDAP = isLDAP;
|
||||
}
|
||||
|
||||
public String getCertFilePath() {
|
||||
return certFilePath;
|
||||
}
|
||||
|
||||
public void setCertFilePath(String certFilePath) {
|
||||
this.certFilePath = certFilePath.trim();
|
||||
}
|
||||
|
||||
public String getTargetServerIdList() {
|
||||
return targetServerIdList;
|
||||
}
|
||||
|
||||
public void setTargetServerIdList(String targetServerIdList) {
|
||||
this.targetServerIdList = targetServerIdList;
|
||||
}
|
||||
|
||||
public String getGpkiLicPath() {
|
||||
return gpkiLicPath;
|
||||
}
|
||||
|
||||
public void setGpkiLicPath(String gpkiLicPath) {
|
||||
this.gpkiLicPath = gpkiLicPath;
|
||||
}
|
||||
|
||||
public boolean getTestGPKI() {
|
||||
return testGPKI;
|
||||
}
|
||||
|
||||
public void setTestGPKI(boolean testGPKI) {
|
||||
this.testGPKI = testGPKI;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package go.kr.project.vmis.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
|
||||
public final class TxIdUtil {
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private TxIdUtil() {}
|
||||
|
||||
public static String generate() {
|
||||
String time = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.KOREA).format(new Date());
|
||||
int random = 100000 + RANDOM.nextInt(900000);
|
||||
return time + "_" + random;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="go.kr.project.vmis.mapper.CarBassMatterInqireMapper">
|
||||
|
||||
<!-- 시퀀스로 새로운 ID 생성 -->
|
||||
<select id="selectNextCarBassMatterInqireId" resultType="String">
|
||||
SELECT CONCAT('CBMI', LPAD(NEXTVAL(seq_car_bass_matter_inqire), 16, '0')) AS id
|
||||
</select>
|
||||
|
||||
<!-- 최초 요청 정보 INSERT -->
|
||||
<insert id="insertCarBassMatterInqire" parameterType="CarBassMatterInqireVO">
|
||||
INSERT INTO tb_car_bass_matter_inqire (
|
||||
CAR_BASS_MATTER_INQIRE,
|
||||
INFO_SYS_ID,
|
||||
INFO_SYS_IP,
|
||||
SIGUNGU_CODE,
|
||||
CNTC_INFO_CODE,
|
||||
CHARGER_ID,
|
||||
CHARGER_IP,
|
||||
CHARGER_NM,
|
||||
DMND_LEVY_STDDE,
|
||||
DMND_INQIRE_SE_CODE,
|
||||
DMND_VHRNO,
|
||||
DMND_VIN,
|
||||
REG_DT,
|
||||
RGTR
|
||||
) VALUES (
|
||||
#{carBassMatterInqire},
|
||||
#{infoSysId},
|
||||
#{infoSysIp},
|
||||
#{sigunguCode},
|
||||
#{cntcInfoCode},
|
||||
#{chargerId},
|
||||
#{chargerIp},
|
||||
#{chargerNm},
|
||||
#{dmndLevyStdde},
|
||||
#{dmndInqireSeCode},
|
||||
#{dmndVhrno},
|
||||
#{dmndVin},
|
||||
NOW(),
|
||||
#{rgtr}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 응답 결과 UPDATE -->
|
||||
<update id="updateCarBassMatterInqire" parameterType="CarBassMatterInqireVO">
|
||||
UPDATE tb_car_bass_matter_inqire
|
||||
<set>
|
||||
<if test="cntcResultCode != null">CNTC_RESULT_CODE = #{cntcResultCode},</if>
|
||||
<if test="cntcResultDtls != null">CNTC_RESULT_DTLS = #{cntcResultDtls},</if>
|
||||
<if test="prye != null">PRYE = #{prye},</if>
|
||||
<if test="registDe != null">REGIST_DE = #{registDe},</if>
|
||||
<if test="ersrRegistSeCode != null">ERSR_REGIST_SE_CODE = #{ersrRegistSeCode},</if>
|
||||
<if test="ersrRegistSeNm != null">ERSR_REGIST_SE_NM = #{ersrRegistSeNm},</if>
|
||||
<if test="ersrRegistDe != null">ERSR_REGIST_DE = #{ersrRegistDe},</if>
|
||||
<if test="registDetailCode != null">REGIST_DETAIL_CODE = #{registDetailCode},</if>
|
||||
<if test="dsplvl != null">DSPLVL = #{dsplvl},</if>
|
||||
<if test="useStrnghldLegaldongCode != null">USE_STRNGHLD_LEGALDONG_CODE = #{useStrnghldLegaldongCode},</if>
|
||||
<if test="useStrnghldAdstrdCode != null">USE_STRNGHLD_ADSTRD_CODE = #{useStrnghldAdstrdCode},</if>
|
||||
<if test="useStrnghldMntn != null">USE_STRNGHLD_MNTN = #{useStrnghldMntn},</if>
|
||||
<if test="useStrnghldLnbr != null">USE_STRNGHLD_LNBR = #{useStrnghldLnbr},</if>
|
||||
<if test="useStrnghldHo != null">USE_STRNGHLD_HO = #{useStrnghldHo},</if>
|
||||
<if test="useStrnghldAdresNm != null">USE_STRNGHLD_ADRES_NM = #{useStrnghldAdresNm},</if>
|
||||
<if test="useStrnghldRoadNmCode != null">USE_STRNGHLD_ROAD_NM_CODE = #{useStrnghldRoadNmCode},</if>
|
||||
<if test="usgsrhldUndgrndBuldSeCode != null">USGSRHLD_UNDGRND_BULD_SE_CODE = #{usgsrhldUndgrndBuldSeCode},</if>
|
||||
<if test="useStrnghldBuldMainNo != null">USE_STRNGHLD_BULD_MAIN_NO = #{useStrnghldBuldMainNo},</if>
|
||||
<if test="useStrnghldBuldSubNo != null">USE_STRNGHLD_BULD_SUB_NO = #{useStrnghldBuldSubNo},</if>
|
||||
<if test="usgsrhldAdresFull != null">USGSRHLD_ADRES_FULL = #{usgsrhldAdresFull},</if>
|
||||
<if test="mberSeCode != null">MBER_SE_CODE = #{mberSeCode},</if>
|
||||
<if test="mberSeNo != null">MBER_SE_NO = #{mberSeNo},</if>
|
||||
<if test="telno != null">TELNO = #{telno},</if>
|
||||
<if test="ownerLegaldongCode != null">OWNER_LEGALDONG_CODE = #{ownerLegaldongCode},</if>
|
||||
<if test="ownerAdstrdCode != null">OWNER_ADSTRD_CODE = #{ownerAdstrdCode},</if>
|
||||
<if test="ownerMntn != null">OWNER_MNTN = #{ownerMntn},</if>
|
||||
<if test="ownerLnbr != null">OWNER_LNBR = #{ownerLnbr},</if>
|
||||
<if test="ownerHo != null">OWNER_HO = #{ownerHo},</if>
|
||||
<if test="ownerAdresNm != null">OWNER_ADRES_NM = #{ownerAdresNm},</if>
|
||||
<if test="ownerRoadNmCode != null">OWNER_ROAD_NM_CODE = #{ownerRoadNmCode},</if>
|
||||
<if test="ownerUndgrndBuldSeCode != null">OWNER_UNDGRND_BULD_SE_CODE = #{ownerUndgrndBuldSeCode},</if>
|
||||
<if test="ownerBuldMainNo != null">OWNER_BULD_MAIN_NO = #{ownerBuldMainNo},</if>
|
||||
<if test="ownerBuldSubNo != null">OWNER_BULD_SUB_NO = #{ownerBuldSubNo},</if>
|
||||
<if test="ownrWholaddr != null">OWNR_WHOLADDR = #{ownrWholaddr},</if>
|
||||
<if test="aftrVhrno != null">AFTR_VHRNO = #{aftrVhrno},</if>
|
||||
<if test="useFuelCode != null">USE_FUEL_CODE = #{useFuelCode},</if>
|
||||
<if test="prposSeCode != null">PRPOS_SE_CODE = #{prposSeCode},</if>
|
||||
<if test="mtrsFomNm != null">MTRS_FOM_NM = #{mtrsFomNm},</if>
|
||||
<if test="frntVhrno != null">FRNT_VHRNO = #{frntVhrno},</if>
|
||||
<if test="vhclno != null">VHCLNO = #{vhclno},</if>
|
||||
<if test="vin != null">VIN = #{vin},</if>
|
||||
<if test="cnm != null">CNM = #{cnm},</if>
|
||||
<if test="vhcleTotWt != null">VHCLE_TOT_WT = #{vhcleTotWt},</if>
|
||||
<if test="caagEndde != null">CAAG_ENDDE = #{caagEndde},</if>
|
||||
<if test="changeDe != null">CHANGE_DE = #{changeDe},</if>
|
||||
<if test="vhctyAsortCode != null">VHCTY_ASORT_CODE = #{vhctyAsortCode},</if>
|
||||
<if test="vhctyTyCode != null">VHCTY_TY_CODE = #{vhctyTyCode},</if>
|
||||
<if test="vhctySeCode != null">VHCTY_SE_CODE = #{vhctySeCode},</if>
|
||||
<if test="mxmmLdg != null">MXMM_LDG = #{mxmmLdg},</if>
|
||||
<if test="vhctyAsortNm != null">VHCTY_ASORT_NM = #{vhctyAsortNm},</if>
|
||||
<if test="vhctyTyNm != null">VHCTY_TY_NM = #{vhctyTyNm},</if>
|
||||
<if test="vhctySeNm != null">VHCTY_SE_NM = #{vhctySeNm},</if>
|
||||
<if test="frstRegistDe != null">FRST_REGIST_DE = #{frstRegistDe},</if>
|
||||
<if test="fomNm != null">FOM_NM = #{fomNm},</if>
|
||||
<if test="acqsDe != null">ACQS_DE = #{acqsDe},</if>
|
||||
<if test="acqsEndDe != null">ACQS_END_DE = #{acqsEndDe},</if>
|
||||
<if test="yblMd != null">YBL_MD = #{yblMd},</if>
|
||||
<if test="transrRegistDe != null">TRANSR_REGIST_DE = #{transrRegistDe},</if>
|
||||
<if test="spcfRegistSttusCode != null">SPCF_REGIST_STTUS_CODE = #{spcfRegistSttusCode},</if>
|
||||
<if test="colorNm != null">COLOR_NM = #{colorNm},</if>
|
||||
<if test="mrtgCo != null">MRTG_CO = #{mrtgCo},</if>
|
||||
<if test="seizrCo != null">SEIZR_CO = #{seizrCo},</if>
|
||||
<if test="stmdCo != null">STMD_CO = #{stmdCo},</if>
|
||||
<if test="nmplCsdyAt != null">NMPL_CSDY_AT = #{nmplCsdyAt},</if>
|
||||
<if test="nmplCsdyRemnrDe != null">NMPL_CSDY_REMNR_DE = #{nmplCsdyRemnrDe},</if>
|
||||
<if test="originSeCode != null">ORIGIN_SE_CODE = #{originSeCode},</if>
|
||||
<if test="nmplStndrdCode != null">NMPL_STNDRD_CODE = #{nmplStndrdCode},</if>
|
||||
<if test="acqsAmount != null">ACQS_AMOUNT = #{acqsAmount},</if>
|
||||
<if test="insptValidPdBgnde != null">INSPT_VALID_PD_BGNDE = #{insptValidPdBgnde},</if>
|
||||
<if test="insptValidPdEndde != null">INSPT_VALID_PD_ENDDE = #{insptValidPdEndde},</if>
|
||||
<if test="useStrnghldGrcCode != null">USE_STRNGHLD_GRC_CODE = #{useStrnghldGrcCode},</if>
|
||||
<if test="tkcarPscapCo != null">TKCAR_PSCAP_CO = #{tkcarPscapCo},</if>
|
||||
<if test="spmnno != null">SPMNNO = #{spmnno},</if>
|
||||
<if test="trvlDstnc != null">TRVL_DSTNC = #{trvlDstnc},</if>
|
||||
<if test="frstRegistRqrcno != null">FRST_REGIST_RQRCNO = #{frstRegistRqrcno},</if>
|
||||
<if test="vlntErsrPrvntcNticeDe != null">VLNT_ERSR_PRVNTC_NTICE_DE = #{vlntErsrPrvntcNticeDe},</if>
|
||||
<if test="registInsttNm != null">REGIST_INSTT_NM = #{registInsttNm},</if>
|
||||
<if test="processImprtyResnCode != null">PROCESS_IMPRTY_RESN_CODE = #{processImprtyResnCode},</if>
|
||||
<if test="processImprtyResnDtls != null">PROCESS_IMPRTY_RESN_DTLS = #{processImprtyResnDtls},</if>
|
||||
<if test="cbdLt != null">CBD_LT = #{cbdLt},</if>
|
||||
<if test="cbdBt != null">CBD_BT = #{cbdBt},</if>
|
||||
<if test="cbdHg != null">CBD_HG = #{cbdHg},</if>
|
||||
<if test="frstMxmmLdg != null">FRST_MXMM_LDG = #{frstMxmmLdg},</if>
|
||||
<if test="fuelCnsmpRt != null">FUEL_CNSMP_RT = #{fuelCnsmpRt},</if>
|
||||
<if test="elctyCmpndFuelCnsmpRt != null">ELCTY_CMPND_FUEL_CNSMP_RT = #{elctyCmpndFuelCnsmpRt},</if>
|
||||
<if test="mberNm != null">MBER_NM = #{mberNm},</if>
|
||||
</set>
|
||||
WHERE CAR_BASS_MATTER_INQIRE = #{carBassMatterInqire}
|
||||
</update>
|
||||
|
||||
<!-- ID로 조회 -->
|
||||
<select id="selectCarBassMatterInqireById" parameterType="String" resultType="CarBassMatterInqireVO">
|
||||
SELECT *
|
||||
FROM tb_car_bass_matter_inqire
|
||||
WHERE CAR_BASS_MATTER_INQIRE = #{carBassMatterInqire}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,178 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="go.kr.project.vmis.mapper.CarLedgerFrmbkMapper">
|
||||
|
||||
<!-- 시퀀스로 새로운 마스터/상세 ID 생성 -->
|
||||
<select id="selectNextCarLedgerFrmbkId" resultType="String">
|
||||
SELECT CONCAT('CLFB', LPAD(NEXTVAL(seq_car_ledger_frmbk), 16, '0')) AS id
|
||||
</select>
|
||||
|
||||
<select id="selectNextCarLedgerFrmbkDtlId" resultType="String">
|
||||
SELECT CONCAT('CLFD', LPAD(NEXTVAL(seq_car_ledger_frmbk_dtl), 16, '0')) AS id
|
||||
</select>
|
||||
|
||||
<!-- 최초 요청 정보 INSERT (마스터) -->
|
||||
<insert id="insertCarLedgerFrmbk" parameterType="CarLedgerFrmbkVO">
|
||||
INSERT INTO tb_car_ledger_frmbk (
|
||||
CAR_LEDGER_FRMBK_ID,
|
||||
INFO_SYS_ID,
|
||||
INFO_SYS_IP,
|
||||
SIGUNGU_CODE,
|
||||
CNTC_INFO_CODE,
|
||||
CHARGER_ID,
|
||||
CHARGER_IP,
|
||||
CHARGER_NM,
|
||||
DMND_VHRNO,
|
||||
DMND_ONES_INFORMATION_OPEN,
|
||||
DMND_CPTTR_NM,
|
||||
DMND_CPTTR_IHIDNUM,
|
||||
DMND_CPTTR_LEGALDONG_CODE,
|
||||
DMND_ROUTE_SE_CODE,
|
||||
DMND_DETAIL_EXPRESSION,
|
||||
DMND_INQIRE_SE_CODE,
|
||||
REG_DT,
|
||||
RGTR
|
||||
) VALUES (
|
||||
#{carLedgerFrmbkId},
|
||||
#{infoSysId},
|
||||
#{infoSysIp},
|
||||
#{sigunguCode},
|
||||
#{cntcInfoCode},
|
||||
#{chargerId},
|
||||
#{chargerIp},
|
||||
#{chargerNm},
|
||||
#{dmndVhrno},
|
||||
#{dmndOnesInformationOpen},
|
||||
#{dmndCpttrNm},
|
||||
#{dmndCpttrIhidnum},
|
||||
#{dmndCpttrLegaldongCode},
|
||||
#{dmndRouteSeCode},
|
||||
#{dmndDetailExpression},
|
||||
#{dmndInqireSeCode},
|
||||
NOW(),
|
||||
#{rgtr}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 응답 결과 UPDATE (마스터) -->
|
||||
<update id="updateCarLedgerFrmbk" parameterType="CarLedgerFrmbkVO">
|
||||
UPDATE tb_car_ledger_frmbk
|
||||
<set>
|
||||
<if test="cntcResultCode != null">CNTC_RESULT_CODE = #{cntcResultCode},</if>
|
||||
<if test="cntcResultDtls != null">CNTC_RESULT_DTLS = #{cntcResultDtls},</if>
|
||||
<if test="ledgerGroupNo != null">LEDGER_GROUP_NO = #{ledgerGroupNo},</if>
|
||||
<if test="ledgerIndvdlzNo != null">LEDGER_INDVDLZ_NO = #{ledgerIndvdlzNo},</if>
|
||||
<if test="vhmno != null">VHMNO = #{vhmno},</if>
|
||||
<if test="vhrno != null">VHRNO = #{vhrno},</if>
|
||||
<if test="vin != null">VIN = #{vin},</if>
|
||||
<if test="vhctyAsortCode != null">VHCTY_ASORT_CODE = #{vhctyAsortCode},</if>
|
||||
<if test="vhctyAsortNm != null">VHCTY_ASORT_NM = #{vhctyAsortNm},</if>
|
||||
<if test="cnm != null">CNM = #{cnm},</if>
|
||||
<if test="colorCode != null">COLOR_CODE = #{colorCode},</if>
|
||||
<if test="colorNm != null">COLOR_NM = #{colorNm},</if>
|
||||
<if test="nmplStndrdCode != null">NMPL_STNDRD_CODE = #{nmplStndrdCode},</if>
|
||||
<if test="nmplStndrdNm != null">NMPL_STNDRD_NM = #{nmplStndrdNm},</if>
|
||||
<if test="prposSeCode != null">PRPOS_SE_CODE = #{prposSeCode},</if>
|
||||
<if test="prposSeNm != null">PRPOS_SE_NM = #{prposSeNm},</if>
|
||||
<if test="mtrsFomNm != null">MTRS_FOM_NM = #{mtrsFomNm},</if>
|
||||
<if test="fomNm != null">FOM_NM = #{fomNm},</if>
|
||||
<if test="acqsAmount != null">ACQS_AMOUNT = #{acqsAmount},</if>
|
||||
<if test="registDetailCode != null">REGIST_DETAIL_CODE = #{registDetailCode},</if>
|
||||
<if test="registDetailNm != null">REGIST_DETAIL_NM = #{registDetailNm},</if>
|
||||
<if test="frstRegistDe != null">FRST_REGIST_DE = #{frstRegistDe},</if>
|
||||
<if test="caagEndde != null">CAAG_ENDDE = #{caagEndde},</if>
|
||||
<if test="prye != null">PRYE = #{prye},</if>
|
||||
<if test="spmnno1 != null">SPMNNO1 = #{spmnno1},</if>
|
||||
<if test="spmnno2 != null">SPMNNO2 = #{spmnno2},</if>
|
||||
<if test="yblMd != null">YBL_MD = #{yblMd},</if>
|
||||
<if test="trvlDstnc != null">TRVL_DSTNC = #{trvlDstnc},</if>
|
||||
<if test="insptValidPdBgnde != null">INSPT_VALID_PD_BGNDE = #{insptValidPdBgnde},</if>
|
||||
<if test="insptValidPdEndde != null">INSPT_VALID_PD_ENDDE = #{insptValidPdEndde},</if>
|
||||
<if test="chckValidPdBgnde != null">CHCK_VALID_PD_BGNDE = #{chckValidPdBgnde},</if>
|
||||
<if test="chckValidPdEndde != null">CHCK_VALID_PD_ENDDE = #{chckValidPdEndde},</if>
|
||||
<if test="registReqstSeNm != null">REGIST_REQST_SE_NM = #{registReqstSeNm},</if>
|
||||
<if test="frstRegistRqrcno != null">FRST_REGIST_RQRCNO = #{frstRegistRqrcno},</if>
|
||||
<if test="nmplCsdyRemnrDe != null">NMPL_CSDY_REMNR_DE = #{nmplCsdyRemnrDe},</if>
|
||||
<if test="nmplCsdyAt != null">NMPL_CSDY_AT = #{nmplCsdyAt},</if>
|
||||
<if test="bssUsePd != null">BSS_USE_PD = #{bssUsePd},</if>
|
||||
<if test="octhtErsrPrvntcNticeDe != null">OCTHT_ERSR_PRVNTC_NTICE_DE = #{octhtErsrPrvntcNticeDe},</if>
|
||||
<if test="ersrRegistDe != null">ERSR_REGIST_DE = #{ersrRegistDe},</if>
|
||||
<if test="ersrRegistSeCode != null">ERSR_REGIST_SE_CODE = #{ersrRegistSeCode},</if>
|
||||
<if test="ersrRegistSeNm != null">ERSR_REGIST_SE_NM = #{ersrRegistSeNm},</if>
|
||||
<if test="mrtgcnt != null">MRTGCNT = #{mrtgcnt},</if>
|
||||
<if test="vhclecnt != null">VHCLECNT = #{vhclecnt},</if>
|
||||
<if test="stmdcnt != null">STMDCNT = #{stmdcnt},</if>
|
||||
<if test="adres1 != null">ADRES1 = #{adres1},</if>
|
||||
<if test="adresNm1 != null">ADRES_NM1 = #{adresNm1},</if>
|
||||
<if test="adres != null">ADRES = #{adres},</if>
|
||||
<if test="adresNm != null">ADRES_NM = #{adresNm},</if>
|
||||
<if test="indvdlBsnmAt != null">INDVDL_BSNM_AT = #{indvdlBsnmAt},</if>
|
||||
<if test="telno != null">TELNO = #{telno},</if>
|
||||
<if test="mberNm != null">MBER_NM = #{mberNm},</if>
|
||||
<if test="mberSeCode != null">MBER_SE_CODE = #{mberSeCode},</if>
|
||||
<if test="mberSeNo != null">MBER_SE_NO = #{mberSeNo},</if>
|
||||
<if test="taxxmptTrgterSeCode != null">TAXXMPT_TRGTER_SE_CODE = #{taxxmptTrgterSeCode},</if>
|
||||
<if test="taxxmptTrgterSeCodeNm != null">TAXXMPT_TRGTER_SE_CODE_NM = #{taxxmptTrgterSeCodeNm},</if>
|
||||
<if test="cntMatter != null">CNT_MATTER = #{cntMatter},</if>
|
||||
<if test="emdNm != null">EMD_NM = #{emdNm},</if>
|
||||
<if test="prvntccnt != null">PRVNTCCNT = #{prvntccnt},</if>
|
||||
<if test="xportFlflAtSttemntDe != null">XPORT_FLFL_AT_STTEMNT_DE = #{xportFlflAtSttemntDe},</if>
|
||||
<if test="partnRqrcno != null">PARTN_RQRCNO = #{partnRqrcno},</if>
|
||||
<if test="frstTrnsfrDe != null">FRST_TRNSFR_DE = #{frstTrnsfrDe},</if>
|
||||
<if test="processImprtyResnCode != null">PROCESS_IMPRTY_RESN_CODE = #{processImprtyResnCode},</if>
|
||||
<if test="processImprtyResnDtls != null">PROCESS_IMPRTY_RESN_DTLS = #{processImprtyResnDtls},</if>
|
||||
</set>
|
||||
WHERE CAR_LEDGER_FRMBK_ID = #{carLedgerFrmbkId}
|
||||
</update>
|
||||
|
||||
<!-- 상세 INSERT -->
|
||||
<insert id="insertCarLedgerFrmbkDtl" parameterType="CarLedgerFrmbkDtlVO">
|
||||
INSERT INTO tb_car_ledger_frmbk_dtl (
|
||||
CAR_LEDGER_FRMBK_DTL_ID,
|
||||
CAR_LEDGER_FRMBK_ID,
|
||||
MAINCHK,
|
||||
CHANGE_JOB_SE_CODE,
|
||||
MAINNO,
|
||||
SUBNO,
|
||||
DTLS,
|
||||
RQRCNO,
|
||||
VHMNO,
|
||||
LEDGER_GROUP_NO,
|
||||
LEDGER_INDVDLZ_NO,
|
||||
GUBUN_NM,
|
||||
CHANGE_DE,
|
||||
DETAIL_SN,
|
||||
FLAG,
|
||||
REG_DT,
|
||||
RGTR
|
||||
) VALUES (
|
||||
#{carLedgerFrmbkDtlId},
|
||||
#{carLedgerFrmbkId},
|
||||
#{mainchk},
|
||||
#{changeJobSeCode},
|
||||
#{mainno},
|
||||
#{subno},
|
||||
#{dtls},
|
||||
#{rqrcno},
|
||||
#{vhmno},
|
||||
#{ledgerGroupNo},
|
||||
#{ledgerIndvdlzNo},
|
||||
#{gubunNm},
|
||||
#{changeDe},
|
||||
#{detailSn},
|
||||
#{flag},
|
||||
NOW(),
|
||||
#{rgtr}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- ID로 조회 (선택) -->
|
||||
<select id="selectCarLedgerFrmbkById" parameterType="String" resultType="CarLedgerFrmbkVO">
|
||||
SELECT *
|
||||
FROM tb_car_ledger_frmbk
|
||||
WHERE CAR_LEDGER_FRMBK_ID = #{carLedgerFrmbkId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue