Compare commits

...

21 Commits

Author SHA1 Message Date
박성영 36679926f0 jdk1.8 version 추가 3 weeks ago
박성영 e9399c1b60 local 테스트 추가 3 weeks ago
박성영 da570c3de7 fix: 프로파일 dev 일때는 mock json data 랜덤 리턴 3 weeks ago
박성영 bf69ee9f69 fix: 프로파일 dev 일때는 mock json data 랜덤 리턴 3 weeks ago
박성영 a8a9693fc1 fix: 조건문 수정 3 weeks ago
박성영 b768398de4 fix: 신규파라미터에 맞춰 swagger param 변경 3 weeks ago
박성영 1fa3bdc835 fix: model 재확인 3 weeks ago
박성영 13b6e8122e fix: 리턴개선 4 weeks ago
박성영 d124860d00 fix: import문 정리 4 weeks ago
박성영 e9fea5a8c3 fix: 최종 return할때 요청정보도 같이 넘겨주도록 저장, txid 등 호출하는 서비스에서 사용하도록 4 weeks ago
박성영 f221441220 fix: 자동설정 보완 4 weeks ago
박성영 7f37c61971 fix: 조회구분코드 자동설정 보완 4 weeks ago
박성영 ac580220eb refactor: JDK1.8 VERSION 리팩토링 4 weeks ago
박성영 aa691e4dc1 feat: MD 파일 재정의 필요 4 weeks ago
박성영 4f8a91003d feat: 신버전, 구버전 설정 분리 및 소스 분리 중 4 weeks ago
박성영 22146d573a feat: 소스정리중 4 weeks ago
박성영 93e62d2053 feat: 서비스 통일 4 weeks ago
박성영 f4064f716c feat: old, new 구분하여 전체 재개발 4 weeks ago
박성영 785528218a feat: 차량기본정보 request 구성 변경 4 weeks ago
박성영 3bad417598 feat: 스웨거 example 깨짐현상 수정 4 weeks ago
박성영 b15825454a feat: DB 정보 없이 진행 4 weeks ago

@ -0,0 +1,206 @@
---
## 설치 및 실행
### 1) 소스 가져오기
```
git clone <repo-url>
cd VMIS-interface
```
### 2) 빌드
```
# 테스트 포함 빌드
./gradlew build
# 테스트 제외 빌드(필요 시)
./gradlew clean build -x test
```
Windows PowerShell에서는 `./gradlew.bat` 사용 가능합니다.
### 3) 실행
#### 방법 1: Gradle bootRun 사용 (개발 권장)
**개발 환경 (포트 8081)**
```bash
# Linux/Mac
./gradlew bootRun --args='--spring.profiles.active=dev'
# Windows
gradlew.bat bootRun --args='--spring.profiles.active=dev'
```
**운영 환경 (포트 8080)**
```bash
# Linux/Mac
./gradlew bootRun --args='--spring.profiles.active=prd'
# Windows
gradlew.bat bootRun --args='--spring.profiles.active=prd'
```
#### 방법 2: JAR 파일 실행 (배포 권장)
**개발 환경**
```bash
# Linux/Mac
java -Dspring.profiles.active=dev -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
# Windows PowerShell
java -Dspring.profiles.active=dev -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
# 또는 환경변수 사용 (Windows PowerShell)
$env:SPRING_PROFILES_ACTIVE = "dev"
java -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
```
**운영 환경 (환경변수 포함)**
```bash
# Linux/Mac
export SPRING_PROFILES_ACTIVE=prd
export VMIS_SYSTEM_INFO_SYS_ID="41-345"
export VMIS_SYSTEM_SIGUNGU_CODE="41460"
java -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
# Windows PowerShell
$env:SPRING_PROFILES_ACTIVE = "prd"
$env:VMIS_SYSTEM_INFO_SYS_ID = "41-345"
$env:VMIS_SYSTEM_SIGUNGU_CODE = "41460"
java -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
```
#### 백그라운드 실행 (Linux 서버)
```bash
# nohup 사용 (기본 java 경로, 콘솔 출력을 app.log에 저장)
nohup java -Dspring.profiles.active=prd -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > app.log 2>&1 &
# Java 경로를 직접 지정하는 방법 (Java 17 이상)
nohup /usr/bin/java -Dspring.profiles.active=prd -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > app.log 2>&1 &
# 또는 사용자 지정 Java 경로 사용
nohup /opt/jdk-17/bin/java -Dspring.profiles.active=prd -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > app.log 2>&1 &
# 콘솔 출력을 남기지 않을 때 (애플리케이션 자체 로그는 logback-spring.xml 설정에 따라 별도 저장됨)
# 개발 환경
nohup /usr/bin/java -Dspring.profiles.active=dev -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 &
# 운영 환경 (애플리케이션 로그: d:/data/VMIS-interface/logs/vmis-interface.log)
nohup /usr/bin/java -Dspring.profiles.active=prd -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 &
# Java 경로 확인 방법
which java
# 또는
readlink -f $(which java)
# 프로세스 확인
ps aux | grep VMIS-interface
# 종료
kill -9 <PID>
```
**참고:** `/dev/null` 사용 시 콘솔 출력만 버려지며, 애플리케이션 자체 로그는 `logging.file.path` 설정에 따라 정상적으로 기록됩니다.
- 개발: `d:/data/VMIS-interface/logs/vmis-interface.log`
- 운영: `d:/data/VMIS-interface/logs/vmis-interface.log`
#### 백그라운드 실행 (Windows 서버)
**PowerShell - 백그라운드 프로세스로 실행**
```powershell
# 개발 환경 (콘솔 출력을 app.log에 저장)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=dev","-jar","build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"
# 운영 환경 (콘솔 출력을 app.log에 저장)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"
# Java 경로를 직접 지정하는 방법
Start-Process -NoNewWindow -FilePath "C:\Program Files\Java\jdk-17\bin\java.exe" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"
# 콘솔 출력을 남기지 않을 때 (NUL로 리다이렉트)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar" -RedirectStandardOutput "NUL" -RedirectStandardError "NUL"
```
**CMD - javaw 사용 (콘솔 창 없이 실행)**
```cmd
# 개발 환경
start /B javaw -Dspring.profiles.active=dev -jar build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar
# 운영 환경
start /B javaw -Dspring.profiles.active=prd -jar build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar
# Java 경로를 직접 지정 (경로에 공백이 있을 경우 빈 창제목 "" 필요)
start "" /B "C:\Program Files\Java\jdk-17\bin\javaw.exe" -Dspring.profiles.active=prd -jar build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar
# 또는 절대 경로 사용
start "" /B "D:\DEV\.jdks\azul-17.0.14\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\workspace\git\VMIS-interface\build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar
cd /d d:
cd D:\VMIS-interface\jdk1.8.0_271\bin
java -Dspring.profiles.active=prd -jar D:\VMIS-interface\VMIS-interface-0.0.1-SNAPSHOT.jar
# local : java -Dspring.profiles.active=prd -jar D:\workspace\git\VIPS\build\libs\VIPS-BOOT.war
java -Dspring.profiles.active=prd -jar D:\VMIS-interface\VMIS-interface-0.0.1-SNAPSHOT.jar
"D:\VMIS-interface\jdk1.8.0_271\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\VMIS-interface\VMIS-interface-0.0.1-SNAPSHOT.jar
start "" /B "D:\VMIS-interface\jdk1.8.0_271\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\VMIS-interface\VMIS-interface-0.0.1-SNAPSHOT.jar
```
**참고:** `start` 명령어는 첫 번째 따옴표 문자열을 창 제목으로 인식하므로, 경로를 따옴표로 묶을 때는 앞에 빈 창제목 `""`을 추가해야 합니다.
**프로세스 관리**
```powershell
# 프로세스 확인
Get-Process | Where-Object {$_.ProcessName -like "*java*"} | Select-Object Id, ProcessName, Path
# 또는 특정 포트로 확인
netstat -ano | findstr :18080
# 프로세스 종료 (PID로)
Stop-Process -Id <PID> -Force
# 또는 taskkill 사용
taskkill /F /PID <PID>
```
**Java 경로 확인 (Windows)**
```powershell
# PowerShell
where.exe java
# 또는
(Get-Command java).Source
# Java 버전 확인
java -version
```
**참고:** Windows 환경에서도 콘솔 출력을 `NUL`로 리다이렉트하면, 애플리케이션 자체 로그는 `d:/data/VMIS-interface/logs/vmis-interface.log`에 정상적으로 기록됩니다.
### 4) API 문서(Swagger UI)
- 개발 환경: `http://localhost:8081/swagger-ui/index.html`
- 운영 환경: `http://localhost:18080/swagger-ui/index.html`
---
## 실행 스크립트/명령(Gradle)
- 테스트 실행: `./gradlew test`
- 애플리케이션 실행(bootRun): `./gradlew bootRun --args='--spring.profiles.active=dev'`
- 의존성 갱신: `./gradlew --refresh-dependencies`
-
---
## GPKI 연계
- 구현: `gpki/GpkiService.java`, `gpki/RealGpkiService.java`, `gpki/NoopGpkiService.java`
- 인증서/키 위치(예): `src/GPKI/certs/*`
- 운영에서는 `RealGpkiService` 사용 및 JNI 라이브러리 로딩 필요(`lib/libgpkiapi_jni_1.5.jar`)
- 유틸: `util/GpkiCryptoUtil.java`, `util/NewGpkiUtil.java`
- 주의사항
- 운영 키/패스워드 노출 금지, 파일 권한/경로 점검
- 네이티브 라이브러리 로딩 경로와 JCE 정책 확인
---

@ -2,6 +2,9 @@
코드 내부 로직을 분석한 결과, **각 속성의 사용 방식이 다릅니다**:
GPKI 절차
송신 : 평문(암호화전) → 암호화 → 전자서명 → BASE64 인코딩
수신 : 복호화 ← 전자서명 검증 ← BASE64 디코딩 ← 암호화문
---
### 📋 경로 설정 규칙

@ -15,366 +15,13 @@
- `Envelope<T>` 공통 래퍼 구조 사용
- 환경 프로파일 분리: `dev`, `prd`
- GPKI 연계: 운영은 실서명(`RealGpkiService`), 개발은 `NoopGpkiService`
- MyBatis + MariaDB를 통한 로깅 및 조회 이력 관리(기본사항)
- 추후 기능 개발 사항
- request data[] 배열과 response data[] 배열을 별도로 처리하는 기능 추가, 현재 1건의 차량번호와 1건의 response 만 처리하도록 되어있음
- response.getRecord().get(0); || response.getBody().getData().get(0); 으로 모두 처리함, 단건이 아닌 배열처리 기능 필요함
- 배열에 따른 로깅기능도 같이 수정되어야함, 배열만큼 rows insert(그룹해줄수 있는 tx id 컬럼 추가) or 마스터->디테일 테이블 구조로 변경
---
## 기술 스택
- 언어: Java 17
- 프레임워크: Spring Boot 3.3.x (Web, Validation, JDBC)
- 퍼시스턴스: MyBatis, MariaDB JDBC
- 언어: Java 1.8
- HTTP 클라이언트: Apache HttpClient5
- 문서화: springdoc-openapi (Swagger UI)
- 로깅: Logback
- 빌드 도구: Gradle(Wrapper 포함)
- GPKI: JNI 라이브러리(`lib/libgpkiapi_jni_1.5.jar`)
---
## 진입점(Entry Points)
- 애플리케이션 메인 클래스
- `src/main/java/com/vmis/interfaceapp/VmisInterfaceApplication.java`
- REST 컨트롤러
- `src/main/java/com/vmis/interfaceapp/controller/VehicleInterfaceController.java`
- 기본 경로: `/api/v1/vehicles`
- 기본사항 조회: `POST /api/v1/vehicles/basic`
- 등록원부(갑) 조회: `POST /api/v1/vehicles/ledger`
---
## 시스템 요구 사항
- JDK 17 이상
- Gradle Wrapper 사용(별도 설치 불필요)
- MariaDB 인스턴스(개발/운영 DB)
- (운영) GPKI 인증서/개인키 및 네이티브 라이브러리 로딩 환경
---
## 설정 파일 및 프로파일
- 기본 설정: `src/main/resources/application.yml`
- 서버 포트 기본값: `8081`
- 전역 DB 타입 변수: `Globals.DbType: maria`
- 개발: `src/main/resources/application-dev.yml`
- 운영: `src/main/resources/application-prd.yml`
프로파일 활성화 방법
- JVM 옵션: `-Dspring.profiles.active=dev` 또는 `prd`
- 환경변수: `SPRING_PROFILES_ACTIVE=dev` 또는 `prd`
중요: 예시 설정 파일에 포함된 계정/키 값은 샘플이거나 개발용입니다. 실제 운영 환경에서는 반드시 환경변수/비밀 관리(예: Vault, KMS)로 주입하고, 파일에는 값을 남기지 마세요.
### 주요 설정 키(요약)
- Spring DataSource (`spring.datasource.*`)
- `url`, `username`, `password`, `hikari.*`
- MyBatis (`mybatis.*`)
- `config-location`, `mapper-locations`, `type-aliases-package`
- 로깅(`logging.*`)
- `config`, `file.path`, `level.*`
- 서비스 도메인(`vmis.*`)
- `vmis.system.*`: 시스템/담당자 식별 정보
- `vmis.gpki.*`: GPKI 사용 여부, 경로, 패스워드 등
- `vmis.gov.*`: 외부 API 엔드포인트, 타임아웃, 서비스 경로/키
### 권장 환경변수 매핑(예)
- `SPRING_PROFILES_ACTIVE=dev`
- `DB_URL`, `DB_USERNAME`, `DB_PASSWORD`
- `VMIS_SYSTEM_INFO_SYS_ID`, `VMIS_SYSTEM_INFO_SYS_IP`, `VMIS_SYSTEM_SIGUNGU_CODE`
- `VMIS_GPKI_ENABLED`, `VMIS_GPKI_ENV_PRIVATE_KEY_PASSWD`, `VMIS_GPKI_SIG_PRIVATE_KEY_PASSWD`
- `VMIS_GOV_HOST`, `VMIS_GOV_SERVICES_BASIC_APIKEY`, `VMIS_GOV_SERVICES_LEDGER_APIKEY`
프로퍼티 바인딩 클래스: `com.vmis.interfaceapp.config.properties.VmisProperties`
---
## 설치 및 실행
### 1) 소스 가져오기
```
git clone <repo-url>
cd VMIS-interface
```
### 2) 빌드
```
# 테스트 포함 빌드
./gradlew build
# 테스트 제외 빌드(필요 시)
./gradlew clean build -x test
```
Windows PowerShell에서는 `./gradlew.bat` 사용 가능합니다.
### 3) 실행
#### 방법 1: Gradle bootRun 사용 (개발 권장)
**개발 환경 (포트 8081)**
```bash
# Linux/Mac
./gradlew bootRun --args='--spring.profiles.active=dev'
# Windows
gradlew.bat bootRun --args='--spring.profiles.active=dev'
```
**운영 환경 (포트 8080)**
```bash
# Linux/Mac
./gradlew bootRun --args='--spring.profiles.active=prd'
# Windows
gradlew.bat bootRun --args='--spring.profiles.active=prd'
```
#### 방법 2: JAR 파일 실행 (배포 권장)
**개발 환경**
```bash
# Linux/Mac
java -Dspring.profiles.active=dev -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
# Windows PowerShell
java -Dspring.profiles.active=dev -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
# 또는 환경변수 사용 (Windows PowerShell)
$env:SPRING_PROFILES_ACTIVE = "dev"
java -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
```
**운영 환경 (환경변수 포함)**
```bash
# Linux/Mac
export SPRING_PROFILES_ACTIVE=prd
export VMIS_SYSTEM_INFO_SYS_ID="41-345"
export VMIS_SYSTEM_SIGUNGU_CODE="41460"
java -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
# Windows PowerShell
$env:SPRING_PROFILES_ACTIVE = "prd"
$env:VMIS_SYSTEM_INFO_SYS_ID = "41-345"
$env:VMIS_SYSTEM_SIGUNGU_CODE = "41460"
java -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar
```
#### 백그라운드 실행 (Linux 서버)
```bash
# nohup 사용 (기본 java 경로, 콘솔 출력을 app.log에 저장)
nohup java -Dspring.profiles.active=prd -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > app.log 2>&1 &
# Java 경로를 직접 지정하는 방법 (Java 17 이상)
nohup /usr/bin/java -Dspring.profiles.active=prd -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > app.log 2>&1 &
# 또는 사용자 지정 Java 경로 사용
nohup /opt/jdk-17/bin/java -Dspring.profiles.active=prd -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > app.log 2>&1 &
# 콘솔 출력을 남기지 않을 때 (애플리케이션 자체 로그는 logback-spring.xml 설정에 따라 별도 저장됨)
# 개발 환경
nohup /usr/bin/java -Dspring.profiles.active=dev -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 &
# 운영 환경 (애플리케이션 로그: d:/data/VMIS-interface/logs/vmis-interface.log)
nohup /usr/bin/java -Dspring.profiles.active=prd -jar build/libs/VMIS-interface-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 &
# Java 경로 확인 방법
which java
# 또는
readlink -f $(which java)
# 프로세스 확인
ps aux | grep VMIS-interface
# 종료
kill -9 <PID>
```
**참고:** `/dev/null` 사용 시 콘솔 출력만 버려지며, 애플리케이션 자체 로그는 `logging.file.path` 설정에 따라 정상적으로 기록됩니다.
- 개발: `d:/data/VMIS-interface/logs/vmis-interface.log`
- 운영: `d:/data/VMIS-interface/logs/vmis-interface.log`
#### 백그라운드 실행 (Windows 서버)
**PowerShell - 백그라운드 프로세스로 실행**
```powershell
# 개발 환경 (콘솔 출력을 app.log에 저장)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=dev","-jar","build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"
# 운영 환경 (콘솔 출력을 app.log에 저장)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"
# Java 경로를 직접 지정하는 방법
Start-Process -NoNewWindow -FilePath "C:\Program Files\Java\jdk-17\bin\java.exe" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar" -RedirectStandardOutput "app.log" -RedirectStandardError "app.log"
# 콘솔 출력을 남기지 않을 때 (NUL로 리다이렉트)
Start-Process -NoNewWindow -FilePath "java" -ArgumentList "-Dspring.profiles.active=prd","-jar","build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar" -RedirectStandardOutput "NUL" -RedirectStandardError "NUL"
```
**CMD - javaw 사용 (콘솔 창 없이 실행)**
```cmd
# 개발 환경
start /B javaw -Dspring.profiles.active=dev -jar build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar
# 운영 환경
start /B javaw -Dspring.profiles.active=prd -jar build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar
# Java 경로를 직접 지정 (경로에 공백이 있을 경우 빈 창제목 "" 필요)
start "" /B "C:\Program Files\Java\jdk-17\bin\javaw.exe" -Dspring.profiles.active=prd -jar build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar
# 또는 절대 경로 사용
start "" /B "D:\DEV\.jdks\azul-17.0.14\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\workspace\git\VMIS-interface\build\libs\VMIS-interface-0.0.1-SNAPSHOT.jar
cd /d d:
D:\VMIS-interface\azul-17.0.14\bin
java -Dspring.profiles.active=prd -jar D:\VMIS-interface\VMIS-interface-0.0.1-SNAPSHOT.jar
"D:\VMIS-interface\azul-17.0.14\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\VMIS-interface\VMIS-interface-0.0.1-SNAPSHOT.jar
start "" /B "D:\VMIS-interface\azul-17.0.14\bin\javaw.exe" -Dspring.profiles.active=prd -jar D:\VMIS-interface\VMIS-interface-0.0.1-SNAPSHOT.jar
```
**참고:** `start` 명령어는 첫 번째 따옴표 문자열을 창 제목으로 인식하므로, 경로를 따옴표로 묶을 때는 앞에 빈 창제목 `""`을 추가해야 합니다.
**프로세스 관리**
```powershell
# 프로세스 확인
Get-Process | Where-Object {$_.ProcessName -like "*java*"} | Select-Object Id, ProcessName, Path
# 또는 특정 포트로 확인
netstat -ano | findstr :18080
# 프로세스 종료 (PID로)
Stop-Process -Id <PID> -Force
# 또는 taskkill 사용
taskkill /F /PID <PID>
```
**Java 경로 확인 (Windows)**
```powershell
# PowerShell
where.exe java
# 또는
(Get-Command java).Source
# Java 버전 확인
java -version
```
**참고:** Windows 환경에서도 콘솔 출력을 `NUL`로 리다이렉트하면, 애플리케이션 자체 로그는 `d:/data/VMIS-interface/logs/vmis-interface.log`에 정상적으로 기록됩니다.
### 4) API 문서(Swagger UI)
- 개발 환경: `http://localhost:8081/swagger-ui/index.html`
- 운영 환경: `http://localhost:8080/swagger-ui/index.html`
---
## 사용 방법(엔드포인트)
- 기본 경로: `/api/v1/vehicles`
### 1) 자동차 기본사항 조회
- `POST /api/v1/vehicles/basic`
- 요청/응답은 `Envelope<BasicRequest|BasicResponse>` 구조(JSON)
- 컨트롤러: `VehicleInterfaceController#basic`
### 2) 자동차 등록원부(갑) 조회
- `POST /api/v1/vehicles/ledger`
- 요청/응답은 `Envelope<LedgerRequest|LedgerResponse>` 구조(JSON)
- 컨트롤러: `VehicleInterfaceController#ledger`
참고: 요청은 서비스 계층에서 `RequestEnricher`를 통해 공통 헤더/추적정보가 보강되며, 외부 시스템 호출은 `GovernmentApiClient`가 수행합니다.
---
## 실행 스크립트/명령(Gradle)
- 테스트 실행: `./gradlew test`
- 애플리케이션 실행(bootRun): `./gradlew bootRun --args='--spring.profiles.active=dev'`
- 의존성 갱신: `./gradlew --refresh-dependencies`
---
## DB/로깅
- 기본사항 조회 최초 요청/응답 로그 적재
- 서비스: `CarBassMatterInqireService`
- 매퍼: `CarBassMatterInqireMapper`, XML: `resources/mybatis/mapper/CarBassMatterInqireMapper_maria.xml`
- DDL: `ddl/vips/tb_car_bass_matter_inqire.sql`, `ddl/vips/seq_car_bass_matter_inqire.sql`
- 로그 태그 예시
- `[BASIC-REQ-LOG]` 최초 요청 저장 완료
- `[BASIC-RES-LOG]` 응답 저장 완료
- `[BASIC-ERR-LOG]` 외부 호출 오류 저장
---
## GPKI 연계
- 구현: `gpki/GpkiService.java`, `gpki/RealGpkiService.java`, `gpki/NoopGpkiService.java`
- 인증서/키 위치(예): `src/GPKI/certs/*`
- 운영에서는 `RealGpkiService` 사용 및 JNI 라이브러리 로딩 필요(`lib/libgpkiapi_jni_1.5.jar`)
- 유틸: `util/GpkiCryptoUtil.java`, `util/NewGpkiUtil.java`
- 주의사항
- 운영 키/패스워드 노출 금지, 파일 권한/경로 점검
- 네이티브 라이브러리 로딩 경로와 JCE 정책 확인
---
## 프로젝트 구조(요약)
```
VMIS-interface/
├─ build.gradle, settings.gradle
├─ gradlew, gradlew.bat, gradle/wrapper/*
├─ lib/libgpkiapi_jni_1.5.jar
├─ ddl/vips/*.sql
├─ src/
│ ├─ main/java/com/vmis/interfaceapp/
│ │ ├─ VmisInterfaceApplication.java
│ │ ├─ controller/VehicleInterfaceController.java
│ │ ├─ service/* (VehicleInterfaceService, RequestEnricher, CarBassMatterInqireService)
│ │ ├─ client/* (GovernmentApi, GovernmentApiClient)
│ │ ├─ gpki/* (GpkiService, RealGpkiService, NoopGpkiService)
│ │ ├─ mapper/* (CarBassMatterInqireMapper)
│ │ ├─ model/* (common/Envelope, basic/*, ledger/*)
│ │ ├─ config/* (DatabaseConfig, HttpClientConfig, OpenApiConfig, PropertiesConfig, ...)
│ │ └─ util/* (TxIdUtil, GpkiCryptoUtil, NewGpkiUtil)
│ ├─ main/resources/
│ │ ├─ application.yml, application-dev.yml, application-prd.yml
│ │ ├─ logback-spring.xml
│ │ └─ mybatis/*
│ └─ GPKI/certs/*
├─ 참고자료/* (규격서/원시 샘플)
└─ README.md
```
---
## 테스트
- JUnit 기반 테스트 의존성 포함
- `testImplementation 'org.springframework.boot:spring-boot-starter-test'`
- 현재 저장소에 도메인별 테스트 코드가 별도로 포함되어 있지 않을 수 있습니다.
- 실행 방법: `./gradlew test`
- TODO: 통합 테스트(외부 API 목업), 서비스 단위 테스트, 매퍼 테스트 추가
---
## 환경 변수 및 보안
- 프로파일: `SPRING_PROFILES_ACTIVE`(`dev`/`prd`)
- DB/외부 API 키/패스워드는 환경변수나 보안 비밀 관리 도구로 주입하세요.
- 저장소/로그에 민감정보를 남기지 말 것(PII 마스킹 권장)
---
## 라이선스
- TODO: 라이선스 파일(`LICENSE`)을 추가하고 본 섹션을 갱신하세요.
---
## 변경 이력
- 2025-11-05: 초기 README 작성 및 최신 설정 반영
---
## 참고
- OpenAPI/Swagger UI는 `springdoc-openapi`를 사용합니다.
- 외부 API 사양은 `참고자료/인터페이스 정의서` 디렉토리의 문서를 참조하세요.

@ -1,16 +1,14 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.4'
id 'io.spring.dependency-management' version '1.1.6'
id 'org.springframework.boot' version '2.7.18'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'com.vmis'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenCentral()
@ -21,27 +19,22 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
// OpenAPI/Swagger UI
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
// Apache HttpClient5 for RestTemplate request factory
implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.3'
// Apache HttpClient 4.x for RestTemplate request factory (Spring 5.x compatible)
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
// GPKI JNI local library
implementation files('lib/libgpkiapi_jni_1.5.jar')
// Database & MyBatis
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3'
// Configuration metadata
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
// Lombok
compileOnly 'org.projectlombok:lombok:1.18.34'
annotationProcessor 'org.projectlombok:lombok:1.18.34'
testCompileOnly 'org.projectlombok:lombok:1.18.34'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.34'
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
testCompileOnly 'org.projectlombok:lombok:1.18.30'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.30'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

@ -1,106 +1,108 @@
create table tb_car_bass_matter_inqire
(
CAR_BASS_MATTER_INQIRE_ID varchar(20) not null comment '자동차 기본 사항 조회 ID'
CAR_BASS_MATTER_INQIRE_ID varchar(20) not null comment '자동차 기본 사항 조회 ID'
primary key,
INFO_SYS_ID varchar(6) null comment '정보 시스템 ID',
INFO_SYS_IP varchar(23) null comment '정보 시스템 IP',
SIGUNGU_CODE varchar(5) null comment '시군구 코드',
CNTC_INFO_CODE varchar(15) null comment '연계 정보 코드',
CHARGER_ID varchar(15) null comment '담당자 ID',
CHARGER_IP varchar(23) null comment '담당자 IP',
CHARGER_NM varchar(75) null comment '담당자명',
DMND_LEVY_STDDE varchar(8) null comment '요청 부과 기준일',
DMND_INQIRE_SE_CODE varchar(1) null comment '요청 조회 구분 코드',
DMND_VHRNO varchar(30) null comment '요청 자동차등록번호',
DMND_VIN varchar(17) null comment '요청 차대번호',
CNTC_RESULT_CODE varchar(8) null comment '연계 결과 코드',
CNTC_RESULT_DTLS varchar(4000) null comment '연계 결과 상세',
PRYE varchar(4) null comment '연식',
REGIST_DE varchar(8) null comment '등록일',
ERSR_REGIST_SE_CODE varchar(4) null comment '말소 등록 구분 코드',
ERSR_REGIST_SE_NM varchar(30) null comment '말소 등록 구분명',
ERSR_REGIST_DE varchar(8) null comment '말소 등록일',
REGIST_DETAIL_CODE varchar(3) null comment '등록 상세 코드',
DSPLVL varchar(6) null comment '배기량',
USE_STRNGHLD_LEGALDONG_CODE varchar(10) null comment '사용 본거지 법정동 코드',
USE_STRNGHLD_ADSTRD_CODE varchar(10) null comment '사용 본거지 행정동 코드',
USE_STRNGHLD_MNTN varchar(2) null comment '사용 본거지 산',
USE_STRNGHLD_LNBR varchar(4) null comment '사용 본거지 번지',
USE_STRNGHLD_HO varchar(4) null comment '사용 본거지 호',
USE_STRNGHLD_ADRES_NM varchar(300) null comment '사용 본거지 상세주소',
USE_STRNGHLD_ROAD_NM_CODE varchar(12) null comment '사용 본거지 도로명 코드',
USGSRHLD_UNDGRND_BULD_SE_CODE varchar(1) null comment '사용 본거지 지하 건물 구분 코드',
USE_STRNGHLD_BULD_MAIN_NO varchar(5) null comment '사용 본거지 건물 주요 번호',
USE_STRNGHLD_BULD_SUB_NO varchar(5) null comment '사용 본거지 건물 부 번호',
USGSRHLD_ADRES_FULL varchar(750) null comment '사용 본거지 전체주소',
MBER_SE_CODE varchar(2) null comment '대표소유자 회원 구분 코드',
MBER_SE_NO varchar(100) null comment '대표소유자 회원 번호',
TELNO varchar(20) null comment '대표소유자 전화번호',
OWNER_LEGALDONG_CODE varchar(10) null comment '소유자 법정동 코드',
OWNER_ADSTRD_CODE varchar(10) null comment '소유자 행정동 코드',
OWNER_MNTN varchar(2) null comment '소유자 산',
OWNER_LNBR varchar(4) null comment '소유자 번지',
OWNER_HO varchar(4) null comment '소유자 호',
OWNER_ADRES_NM varchar(300) null comment '소유자 상세주소',
OWNER_ROAD_NM_CODE varchar(12) null comment '소유자 도로명 코드',
OWNER_UNDGRND_BULD_SE_CODE varchar(1) null comment '소유자 지하건물 구분 코드',
OWNER_BULD_MAIN_NO varchar(5) null comment '소유자 건물 주요 번호',
OWNER_BULD_SUB_NO varchar(5) null comment '소유자 건물 부 번호',
OWNR_WHOLADDR varchar(750) null comment '소유자 전체주소',
AFTR_VHRNO varchar(30) null comment '신 차량번호',
USE_FUEL_CODE varchar(1) null comment '사용 연료 코드',
PRPOS_SE_CODE varchar(2) null comment '용도 구분 코드',
MTRS_FOM_NM varchar(75) null comment '원동기 형식명',
FRNT_VHRNO varchar(30) null comment '이전 차량번호',
VHRNO varchar(30) null comment '차량번호',
VIN varchar(17) null comment '차대번호',
CNM varchar(75) null comment '차명',
VHCLE_TOT_WT varchar(6) null comment '차량 총 중량',
CAAG_ENDDE varchar(8) null comment '차령 만료일자',
CHANGE_DE varchar(8) null comment '차번호 변경시기',
VHCTY_ASORT_CODE varchar(1) null comment '차종 종별 코드',
VHCTY_TY_CODE varchar(1) null comment '차종 유형 코드',
VHCTY_SE_CODE varchar(1) null comment '차종 분류 코드',
MXMM_LDG varchar(10) null comment '최대 적재량',
VHCTY_ASORT_NM varchar(150) null comment '차종 종별명',
VHCTY_TY_NM varchar(150) null comment '차종 유형명',
VHCTY_SE_NM varchar(150) null comment '차종 분류명',
FRST_REGIST_DE varchar(8) null comment '최초 등록일',
FOM_NM varchar(75) null comment '형식',
ACQS_DE varchar(8) null comment '취득 일자',
ACQS_END_DE varchar(8) null comment '취득 종료일자',
YBL_MD varchar(8) null comment '제작 년월일',
TRANSR_REGIST_DE varchar(8) null comment '이전 등록일',
SPCF_REGIST_STTUS_CODE varchar(6) null comment '제원 등록 상태 코드',
COLOR_NM varchar(75) null comment '색상명',
MRTG_CO varchar(9) null comment '저당수',
SEIZR_CO varchar(9) null comment '압류건수',
STMD_CO varchar(9) null comment '구조변경수',
NMPL_CSDY_AT varchar(1) null comment '번호판 영치 여부',
NMPL_CSDY_REMNR_DE varchar(8) null comment '번호판 영치 최고일',
ORIGIN_SE_CODE varchar(1) null comment '출처 구분 코드',
NMPL_STNDRD_CODE varchar(1) null comment '번호판 규격 코드',
ACQS_AMOUNT varchar(18) null comment '취득 금액',
INSPT_VALID_PD_BGNDE varchar(8) null comment '검사 유효 기간 시작일',
INSPT_VALID_PD_ENDDE varchar(8) null comment '검사 유효 기간 종료일',
USE_STRNGHLD_GRC_CODE varchar(4) null comment '사용 본거지 관청 코드',
TKCAR_PSCAP_CO varchar(3) null comment '승차정원수',
SPMNNO varchar(17) null comment '제원관리번호',
TRVL_DSTNC varchar(10) null comment '주행거리',
FRST_REGIST_RQRCNO varchar(20) null comment '최초 등록 접수번호',
VLNT_ERSR_PRVNTC_NTICE_DE varchar(8) null comment '예고통지일',
REGIST_INSTT_NM varchar(150) null comment '등록 기관명',
PROCESS_IMPRTY_RESN_CODE varchar(2) null comment '처리 불가 사유 코드',
PROCESS_IMPRTY_RESN_DTLS varchar(75) null comment '처리 불가 사유 명세',
CBD_LT varchar(10) null comment '차체 길이',
CBD_BT varchar(10) null comment '차체 너비',
CBD_HG varchar(10) null comment '차체 높이',
FRST_MXMM_LDG varchar(10) null comment '최초 최대 적재량',
FUEL_CNSMP_RT varchar(5) null comment '연료 소비율',
ELCTY_CMPND_FUEL_CNSMP_RT varchar(5) null comment '전기 복합 연료 소비율',
REG_DT datetime null comment '등록 일시',
RGTR varchar(11) null comment '등록자',
MBER_NM varchar(75) null comment '대표소유자 성명'
TX_ID varchar(25) null comment '트랜잭션 ID',
INFO_SYS_ID varchar(6) null comment '정보 시스템 ID',
INFO_SYS_IP_ADDR varchar(23) null comment '정보 시스템 IP',
SGG_CD varchar(5) null comment '시군구 코드',
LINK_INFO_CD varchar(15) null comment '연계 정보 코드',
PIC_ID varchar(15) null comment '담당자 ID',
PIC_IP_ADDR varchar(23) null comment '담당자 IP',
PIC_NM varchar(75) null comment '담당자명',
DMND_LEVY_CRTR_YMD varchar(8) null comment '요청 부과 기준일',
DMND_INQ_SE_CD varchar(1) null comment '요청 조회 구분 코드',
DMND_VHRNO varchar(30) null comment '요청 자동차등록번호',
DMND_VIN varchar(17) null comment '요청 차대번호',
LINK_RSLT_CD varchar(8) null comment '연계 결과 코드',
LINK_RSLT_DTL varchar(4000) null comment '연계 결과 상세',
YRIDNW varchar(4) null comment '연식',
REG_YMD varchar(8) null comment '등록일',
ERSR_REG_SE_CD varchar(4) null comment '말소 등록 구분 코드',
ERSR_REG_SE_NM varchar(30) null comment '말소 등록 구분명',
ERSR_REG_YMD varchar(8) null comment '말소 등록일',
REG_DTL_CD varchar(3) null comment '등록 상세 코드',
DSPLVL varchar(6) null comment '배기량',
USGSRHLD_STDG_CD varchar(10) null comment '사용 본거지 법정동 코드',
USGSRHLD_DONG_CD varchar(10) null comment '사용 본거지 행정동 코드',
USGSRHLD_MTN_YN varchar(2) null comment '사용 본거지 산',
USGSRHLD_LNBR varchar(4) null comment '사용 본거지 번지',
USGSRHLD_HO varchar(4) null comment '사용 본거지 호',
USGSRHLD_ADDR_NM varchar(300) null comment '사용 본거지 상세주소',
USGSRHLD_ROAD_NM_CD varchar(12) null comment '사용 본거지 도로명 코드',
USGSRHLD_UDGD_BLDG_SE_CD varchar(1) null comment '사용 본거지 지하 건물 구분 코드',
USGSRHLD_BMNO varchar(5) null comment '사용 본거지 건물 주요 번호',
USGSRHLD_BSNO varchar(5) null comment '사용 본거지 건물 부 번호',
USGSRHLD_WHOL_ADDR varchar(750) null comment '사용 본거지 전체주소',
RPRS_OWNR_MBR_SE_CD varchar(2) null comment '대표소유자 회원 구분 코드',
RPRS_OWNR_NM varchar(75) null comment '대표소유자 성명',
RPRSV_OWNR_IDECNO varchar(100) null comment '대표소유자 회원 번호',
RPRS_OWNR_TELNO varchar(20) null comment '대표소유자 전화번호',
OWNR_STDG_CD varchar(10) null comment '소유자 법정동 코드',
OWNR_DONG_CD varchar(10) null comment '소유자 행정동 코드',
OWNR_MTN_YN varchar(2) null comment '소유자 산',
OWNR_LNBR varchar(4) null comment '소유자 번지',
OWNR_HO varchar(4) null comment '소유자 호',
OWNR_ADDR_NM varchar(300) null comment '소유자 상세주소',
OWNR_ROAD_NM_CD varchar(12) null comment '소유자 도로명 코드',
OWNR_UDGD_BLDG_SE_CD varchar(1) null comment '소유자 지하건물 구분 코드',
OWNR_BMNO varchar(5) null comment '소유자 건물 주요 번호',
OWNR_BSNO varchar(5) null comment '소유자 건물 부 번호',
OWNR_WHOL_ADDR varchar(750) null comment '소유자 전체주소',
REAR_VHRNO varchar(30) null comment '신 차량번호',
USE_FUEL_CD varchar(1) null comment '사용 연료 코드',
USG_SE_CD varchar(2) null comment '용도 구분 코드',
MTRS_FOM_NM varchar(75) null comment '원동기 형식명',
BFR_VHRNO varchar(30) null comment '이전 차량번호',
VHRNO varchar(30) null comment '차량번호',
VIN varchar(17) null comment '차대번호',
ATMB_NM varchar(75) null comment '차명',
VHCL_TOTL_WT varchar(6) null comment '차량 총 중량',
VEAG_END_YMD varchar(8) null comment '차령 만료일자',
CHG_YMD varchar(8) null comment '차번호 변경시기',
CARMDL_ASORT_CD varchar(1) null comment '차종 종별 코드',
CARMDL_TYPE_CD varchar(1) null comment '차종 유형 코드',
CARMDL_SE_CD varchar(1) null comment '차종 분류 코드',
MXMM_LDG varchar(10) null comment '최대 적재량',
CARMDL_ASORT_NM varchar(150) null comment '차종 종별명',
CARMDL_TYPE_NM varchar(150) null comment '차종 유형명',
CARMDL_CLSF_NM varchar(150) null comment '차종 분류명',
FRST_REG_YMD varchar(8) null comment '최초 등록일',
FOM_NM varchar(75) null comment '형식',
ACQS_YMD varchar(8) null comment '취득 일자',
ACQS_END_YMD varchar(8) null comment '취득 종료일자',
FBCTN_YMD varchar(8) null comment '제작 년월일',
TRANSR_REG_YMD varchar(8) null comment '이전 등록일',
SPCF_REG_STTS_CD varchar(6) null comment '제원 등록 상태 코드',
COLOR_NM varchar(75) null comment '색상명',
MRTG_CNT varchar(9) null comment '저당수',
SZR_CNT varchar(9) null comment '압류건수',
STRCT_CHG_CNT varchar(9) null comment '구조변경수',
NOPLT_CSDY_YN varchar(1) null comment '번호판 영치 여부',
NOPLT_CSDY_AVTSMT_YMD varchar(8) null comment '번호판 영치 최고일',
SRC_SE_CD varchar(1) null comment '출처 구분 코드',
NOPLT_SPCFCT_CD varchar(1) null comment '번호판 규격 코드',
ACQS_AMT varchar(18) null comment '취득 금액',
INSP_VLD_PD_BGNG_YMD varchar(8) null comment '검사 유효 기간 시작일',
INSP_VLD_PD_END_YMD varchar(8) null comment '검사 유효 기간 종료일',
USGSRHLD_GRC_CD varchar(4) null comment '사용 본거지 관청 코드',
RDCPCT_CNT varchar(3) null comment '승차정원수',
SPMNNO varchar(17) null comment '제원관리번호',
DRVNG_DSTNC varchar(10) null comment '주행거리',
FRST_REG_APLY_RCPT_NO varchar(20) null comment '최초 등록 접수번호',
VLNT_ERSR_PRVNTC_AVTSMT_YMD varchar(8) null comment '예고통지일',
OGNZ_NM varchar(150) null comment '등록 기관명',
PRCS_IMPRTY_RSN_CD varchar(2) null comment '처리 불가 사유 코드',
PRCS_IMPRTY_RSN_DTLS varchar(75) null comment '처리 불가 사유 명세',
CBD_LT varchar(10) null comment '차체 길이',
CBD_BT varchar(10) null comment '차체 너비',
CBD_HG varchar(10) null comment '차체 높이',
FRST_MXMM_LDG varchar(10) null comment '최초 최대 적재량',
FUEL_CNSMPRT varchar(5) null comment '연료 소비율',
ELCTY_CMPND_FUEL_CNSMPRT varchar(5) null comment '전기 복합 연료 소비율',
CAR_FFNLG_TRGT_ID varchar(20) null comment '자동차 과태료 대상 ID',
REG_DT datetime null comment '등록 일시',
RGTR varchar(11) null comment '등록자'
)
comment '자동차 기본 사항 조회';

@ -1,87 +1,90 @@
create table tb_car_ledger_frmbk
(
CAR_LEDGER_FRMBK_ID varchar(20) not null comment '자동차 등록 원부 갑 ID'
CAR_LEDGER_FRMBK_ID varchar(20) not null comment '자동차 등록 원부 갑 ID'
primary key,
INFO_SYS_ID varchar(6) null comment '정보 시스템 ID',
INFO_SYS_IP varchar(23) null comment '정보 시스템 IP',
SIGUNGU_CODE varchar(5) null comment '시군구 코드',
CNTC_INFO_CODE varchar(15) null comment '연계 정보 코드',
CHARGER_ID varchar(15) null comment '담당자 ID',
CHARGER_IP varchar(23) null comment '담당자 IP',
CHARGER_NM varchar(75) null comment '담당자명',
DMND_VHRNO varchar(30) null comment '요청 자동차등록번호',
DMND_ONES_INFORMATION_OPEN varchar(1) null comment '요청 개인 정보 공개',
DMND_CPTTR_NM varchar(75) null comment '요청 민원인 성명',
DMND_CPTTR_IHIDNUM varchar(100) null comment '요청 민원인 주민번호',
DMND_CPTTR_LEGALDONG_CODE varchar(10) null comment '요청 민원인 법정동 코드',
DMND_ROUTE_SE_CODE varchar(1) null comment '요청 경로 구분 코드',
DMND_DETAIL_EXPRESSION varchar(1) null comment '요청 내역 표시',
DMND_INQIRE_SE_CODE varchar(1) null comment '요청 조회 구분 코드',
CNTC_RESULT_CODE varchar(8) null comment '연계 결과 코드',
CNTC_RESULT_DTLS varchar(4000) null,
LEDGER_GROUP_NO varchar(6) null comment '원부 그룹 번호',
LEDGER_INDVDLZ_NO varchar(6) null comment '원부 개별 번호',
VHMNO varchar(20) null comment '차량관리번호',
VHRNO varchar(30) null comment '차량등록번호',
VIN varchar(17) null comment '차대번호',
VHCTY_ASORT_CODE varchar(1) null comment '차종 종별 코드',
VHCTY_ASORT_NM varchar(30) null comment '차종 종별명',
CNM varchar(75) null comment '차명',
COLOR_CODE varchar(2) null comment '색상 코드',
COLOR_NM varchar(30) null comment '색상명',
NMPL_STNDRD_CODE varchar(1) null comment '번호판 규격 코드',
NMPL_STNDRD_NM varchar(30) null comment '번호판 규격명',
PRPOS_SE_CODE varchar(2) null comment '용도 구분 코드',
PRPOS_SE_NM varchar(20) null comment '용도 구분명',
MTRS_FOM_NM varchar(75) null comment '원동기 형식명',
FOM_NM varchar(75) null comment '형식명',
ACQS_AMOUNT varchar(50) null comment '취득 금액',
REGIST_DETAIL_CODE varchar(8) null comment '등록 상세 코드',
REGIST_DETAIL_NM varchar(30) null comment '등록 상세명',
FRST_REGIST_DE varchar(8) null comment '최초 등록일',
CAAG_ENDDE varchar(8) null comment '차령 종료일',
PRYE varchar(4) null comment '연식',
SPMNNO1 varchar(3) null comment '제원관리번호1',
SPMNNO2 varchar(14) null comment '제원관리번호2',
YBL_MD varchar(8) null comment '제작 년월일',
TRVL_DSTNC varchar(10) null comment '주행 거리',
INSPT_VALID_PD_BGNDE varchar(8) null comment '검사 유효 기간 시작일',
INSPT_VALID_PD_ENDDE varchar(8) null comment '검사 유효 기간 종료일',
CHCK_VALID_PD_BGNDE varchar(8) null comment '점검 유효 기간 시작일',
CHCK_VALID_PD_ENDDE varchar(8) null comment '점검 유효 기간 종료일',
REGIST_REQST_SE_NM varchar(75) null comment '등록 신청 구분명',
FRST_REGIST_RQRCNO varchar(20) null comment '최초 등록 접수번호',
NMPL_CSDY_REMNR_DE varchar(8) null comment '번호판 영치 최고일',
NMPL_CSDY_AT varchar(1) null comment '번호판 영치 여부',
BSS_USE_PD varchar(30) null comment '사업용 사용 기간',
OCTHT_ERSR_PRVNTC_NTICE_DE varchar(8) null comment '직권 말소 예고 통지일',
ERSR_REGIST_DE varchar(8) null comment '말소 등록일',
ERSR_REGIST_SE_CODE varchar(4) null comment '말소 등록 구분 코드',
ERSR_REGIST_SE_NM varchar(200) null comment '말소 등록 구분명',
MRTGCNT varchar(4) null comment '저당수',
VHCLECNT varchar(4) null comment '압류건수',
STMDCNT varchar(4) null comment '구조변경수',
ADRES1 varchar(750) null comment '사용 본거지 주소',
ADRES_NM1 varchar(300) null comment '사용 본거지 주소상세',
ADRES varchar(750) null comment '소유자 주소',
ADRES_NM varchar(300) null comment '소유자 주소상세',
INDVDL_BSNM_AT varchar(1) null comment '개인 사업자 여부',
TELNO varchar(30) null comment '대표소유자 전화번호',
MBER_NM varchar(75) null comment '대표소유자 성명',
MBER_SE_CODE varchar(2) null comment '대표소유자 회원 구분 코드',
MBER_SE_NO varchar(100) null comment '대표소유자 회원 번호',
TAXXMPT_TRGTER_SE_CODE varchar(2) null comment '비과세 대상 구분 코드',
TAXXMPT_TRGTER_SE_CODE_NM varchar(30) null comment '비과세 대상 구분 코드명',
CNT_MATTER varchar(5) null comment '특기사항 건수',
EMD_NM varchar(75) null comment '사용 본거지 행정동명',
PRVNTCCNT varchar(4) null comment '예고수',
XPORT_FLFL_AT_STTEMNT_DE varchar(8) null comment '수출 이행 여부 신고일',
PARTN_RQRCNO varchar(13) null comment '발급번호',
FRST_TRNSFR_DE varchar(8) null comment '최초 양도일',
PROCESS_IMPRTY_RESN_CODE varchar(2) null comment '처리 불가 사유 코드',
PROCESS_IMPRTY_RESN_DTLS varchar(200) null comment '처리 불가 사유 명세',
REG_DT datetime null comment '등록 일시',
RGTR varchar(11) null comment '등록자'
TX_ID varchar(25) null comment '트랜잭션 ID',
INFO_SYS_ID varchar(6) null comment '정보 시스템 ID',
INFO_SYS_IP_ADDR varchar(23) null comment '정보 시스템 IP',
SGG_CD varchar(5) null comment '시군구 코드',
LINK_INFO_CD varchar(15) null comment '연계 정보 코드',
PIC_ID varchar(15) null comment '담당자 ID',
PIC_IP_ADDR varchar(23) null comment '담당자 IP',
PIC_NM varchar(75) null comment '담당자명',
DMND_VHRNO varchar(30) null comment '요청 자동차등록번호',
DMND_PRVC_RLS varchar(1) null comment '요청 개인 정보 공개',
DMND_CVLPR_NM varchar(75) null comment '요청 민원인 성명',
DMND_CVLPR_IDECNO varchar(100) null comment '요청 민원인 주민번호',
DMND_CVLPR_STDG_CD varchar(10) null comment '요청 민원인 법정동 코드',
DMND_PATH_SE_CD varchar(1) null comment '요청 경로 구분 코드',
DMND_DSCTN_INDCT varchar(1) null comment '요청 내역 표시',
DMND_INQ_SE_CD varchar(1) null comment '요청 조회 구분 코드',
LINK_RSLT_CD varchar(8) null comment '연계 결과 코드',
LINK_RSLT_DTL varchar(4000) null comment '연계 결과 상세',
LEDGER_GROUP_NO varchar(6) null comment '원부 그룹 번호',
LEDGER_INDIV_NO varchar(6) null comment '원부 개별 번호',
VHMNO varchar(20) null comment '차량관리번호',
VHRNO varchar(30) null comment '차량등록번호',
VIN varchar(17) null comment '차대번호',
CARMDL_ASORT_CD varchar(1) null comment '차종 종별 코드',
CARMDL_ASORT_NM varchar(30) null comment '차종 종별명',
ATMB_NM varchar(75) null comment '차명',
COLOR_CD varchar(2) null comment '색상 코드',
COLOR_NM varchar(30) null comment '색상명',
NOPLT_SPCFCT_CD varchar(1) null comment '번호판 규격 코드',
NOPLT_SPCFCT_NM varchar(30) null comment '번호판 규격명',
USG_SE_CD varchar(2) null comment '용도 구분 코드',
USG_SE_NM varchar(20) null comment '용도 구분명',
MTRS_FOM_NM varchar(75) null comment '원동기 형식명',
FOM_NM varchar(75) null comment '형식명',
ACQS_AMT varchar(50) null comment '취득 금액',
REG_DTL_CD varchar(8) null comment '등록 상세 코드',
REG_DTL_NM varchar(30) null comment '등록 상세명',
FRST_REG_YMD varchar(8) null comment '최초 등록일',
VEAG_END_YMD varchar(8) null comment '차령 종료일',
YRIDNW varchar(4) null comment '연식',
SPMNNO_1 varchar(3) null comment '제원관리번호1',
SPMNNO_2 varchar(14) null comment '제원관리번호2',
FBCTN_YMD varchar(8) null comment '제작 년월일',
DRVNG_DSTNC varchar(10) null comment '주행 거리',
INSP_VLD_PD_BGNG_YMD varchar(8) null comment '검사 유효 기간 시작일',
INSP_VLD_PD_END_YMD varchar(8) null comment '검사 유효 기간 종료일',
CHCK_VLD_PD_BGNG_YMD varchar(8) null comment '점검 유효 기간 시작일',
CHCK_VLD_PD_END_YMD varchar(8) null comment '점검 유효 기간 종료일',
REG_APLY_SE_NM varchar(75) null comment '등록 신청 구분명',
FRST_REG_APLY_RCPT_NO varchar(20) null comment '최초 등록 접수번호',
NOPLT_CSDY_AVTSMT_YMD varchar(8) null comment '번호판 영치 최고일',
NOPLT_CSDY_YN varchar(1) null comment '번호판 영치 여부',
BSS_USE_PD_YMD varchar(30) null comment '사업용 사용 기간',
OCTHT_ERSR_PRVNTC_AVTSMT_YMD varchar(8) null comment '직권 말소 예고 통지일',
ERSR_REG_YMD varchar(8) null comment '말소 등록일',
ERSR_REG_SE_CD varchar(4) null comment '말소 등록 구분 코드',
ERSR_REG_SE_NM varchar(200) null comment '말소 등록 구분명',
MRTG_CNT varchar(4) null comment '저당수',
SZR_CNT varchar(4) null comment '압류건수',
STRCT_CHG_CNT varchar(4) null comment '구조변경수',
USGSRHLD_ADDR_1 varchar(750) null comment '사용 본거지 주소',
USGSRHLD_ADDR_DTL_1 varchar(300) null comment '사용 본거지 주소상세',
OWNR_ADDR varchar(750) null comment '소유자 주소',
OWNR_ADDR_DTL varchar(300) null comment '소유자 주소상세',
INDVDL_BZMN_YN varchar(1) null comment '개인 사업자 여부',
RPRS_OWNR_TELNO varchar(30) null comment '대표소유자 전화번호',
RPRS_OWNR_NM varchar(75) null comment '대표소유자 성명',
RPRS_OWNR_MBR_SE_CD varchar(2) null comment '대표소유자 회원 구분 코드',
RPRSV_OWNR_IDECNO varchar(100) null comment '대표소유자 회원 번호',
TAXXMPT_TRPR_SE_CD varchar(2) null comment '비과세 대상 구분 코드',
TAXXMPT_APLCN_SE_CD varchar(30) null comment '비과세 대상 구분 코드명',
SPCABL_MTTR_CNT varchar(5) null comment '특기사항 건수',
USGSRHLD_DONG_NM varchar(75) null comment '사용 본거지 행정동명',
PRVNTC_CNT varchar(4) null comment '예고수',
XPORT_FLFL_YN_DCLR_YMD varchar(8) null comment '수출 이행 여부 신고일',
ISSU_NO varchar(13) null comment '발급번호',
FRST_TRNSFR_YMD varchar(8) null comment '최초 양도일',
DRIV_SRGBTRY_IDNTF_NO varchar(20) null comment '구동축전지 식별번호',
PRCS_IMPRTY_RSN_CD varchar(2) null comment '처리 불가 사유 코드',
PRCS_IMPRTY_RSN_DTLS varchar(200) null comment '처리 불가 사유 명세',
CAR_FFNLG_TRGT_ID varchar(20) null comment '자동차 과태료 대상 ID',
REG_DT datetime null comment '등록 일시',
RGTR varchar(11) null comment '등록자'
)
comment '자동차 등록 원부 갑';

@ -3,18 +3,20 @@ create table tb_car_ledger_frmbk_dtl
CAR_LEDGER_FRMBK_DTL_ID varchar(20) not null comment '자동차 등록 원부 갑 상세 ID'
primary key,
CAR_LEDGER_FRMBK_ID varchar(20) null comment '자동차 등록 원부 갑 ID',
MAINCHK varchar(2) null comment '해제여부',
CHANGE_JOB_SE_CODE varchar(2) null comment '변경 업무 구분 코드',
MAINNO varchar(4) null comment '주번호',
SUBNO varchar(4) null comment '부번호',
DTLS varchar(2000) null comment '사항란',
RQRCNO varchar(20) null comment '접수번호',
SZR_RMV_DTL_SN varchar(2) null comment '해제여부',
CHG_TASK_SE_CD varchar(2) null comment '변경 업무 구분 코드',
MAIN_NO varchar(4) null comment '주번호',
SNO varchar(4) null comment '부번호',
SPCABL_MTTR varchar(2000) null comment '사항란',
HSHLDR_NM varchar(75) null comment '세대주명',
HSHLDR_IDECNO varchar(500) null comment '세대주개인암호화번호',
APLY_RCPT_NO varchar(20) null comment '접수번호',
VHMNO varchar(20) null comment '차량관리번호',
LEDGER_GROUP_NO varchar(6) null comment '원부 그룹 번호',
LEDGER_INDVDLZ_NO varchar(6) null comment '원부 개별 번호',
GUBUN_NM varchar(75) null comment '변경 업무 구분명',
CHANGE_DE varchar(8) null comment '변경 일자',
DETAIL_SN varchar(6) null comment '상세 순번',
LEDGER_INDIV_NO varchar(6) null comment '원부 개별 번호',
CHG_TASK_SE_NM varchar(75) null comment '변경 업무 구분명',
CHG_YMD varchar(8) null comment '변경 일자',
DTL_SN varchar(6) null comment '상세 순번',
FLAG varchar(3) null comment '표기여부',
REG_DT datetime null comment '등록 일시',
RGTR varchar(11) null comment '등록자'

@ -1,10 +1,12 @@
package com.vmis.interfaceapp.client;
import com.vmis.interfaceapp.model.basic.BasicRequest;
import com.vmis.interfaceapp.model.basic.BasicResponse;
import com.vmis.interfaceapp.model.basic.NewBasicRequest;
import com.vmis.interfaceapp.model.basic.NewBasicResponse;
import com.vmis.interfaceapp.model.basic.OldBasicRequest;
import com.vmis.interfaceapp.model.basic.OldBasicResponse;
import com.vmis.interfaceapp.model.common.Envelope;
import com.vmis.interfaceapp.model.ledger.LedgerRequest;
import com.vmis.interfaceapp.model.ledger.LedgerResponse;
import com.vmis.interfaceapp.model.ledger.NewLedgerRequest;
import com.vmis.interfaceapp.model.ledger.NewLedgerResponse;
import org.springframework.http.ResponseEntity;
/**
@ -15,7 +17,12 @@ import org.springframework.http.ResponseEntity;
*/
public interface GovernmentApi {
ResponseEntity<Envelope<BasicResponse>> callBasic(Envelope<BasicRequest> envelope);
// 구버전 자동차기본사항조회
ResponseEntity<Envelope<OldBasicResponse>> callOldBasic(String txId, Envelope<OldBasicRequest> envelope);
ResponseEntity<Envelope<LedgerResponse>> callLedger(Envelope<LedgerRequest> envelope);
// 신버전 자동차기본사항조회
ResponseEntity<Envelope<NewBasicResponse>> callNewBasic(String txId, Envelope<NewBasicRequest> envelope);
// 신버전 자동차등록원부(갑)
ResponseEntity<Envelope<NewLedgerResponse>> callNewLedger(String txId, Envelope<NewLedgerRequest> envelope);
}

@ -1,23 +1,27 @@
package com.vmis.interfaceapp.client;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vmis.interfaceapp.config.properties.VmisProperties;
import com.vmis.interfaceapp.config.properties.GpkiProperties;
import com.vmis.interfaceapp.config.properties.NewVmisProperties;
import com.vmis.interfaceapp.config.properties.OldVmisProperties;
import com.vmis.interfaceapp.gpki.GpkiService;
import com.vmis.interfaceapp.model.basic.BasicRequest;
import com.vmis.interfaceapp.model.basic.BasicResponse;
import com.vmis.interfaceapp.model.basic.NewBasicRequest;
import com.vmis.interfaceapp.model.basic.NewBasicResponse;
import com.vmis.interfaceapp.model.basic.OldBasicRequest;
import com.vmis.interfaceapp.model.basic.OldBasicResponse;
import com.vmis.interfaceapp.model.common.Envelope;
import com.vmis.interfaceapp.model.ledger.LedgerRequest;
import com.vmis.interfaceapp.model.ledger.LedgerResponse;
import com.vmis.interfaceapp.model.ledger.NewLedgerRequest;
import com.vmis.interfaceapp.model.ledger.NewLedgerResponse;
import com.vmis.interfaceapp.util.TxIdUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;
/**
@ -54,7 +58,7 @@ import java.nio.charset.StandardCharsets;
*
* @see RestTemplate
* @see GpkiService
* @see VmisProperties
* @see OldVmisProperties
*/
@Slf4j
@RequiredArgsConstructor
@ -77,7 +81,7 @@ public class GovernmentApiClient implements GovernmentApi {
private final RestTemplate restTemplate;
/**
* VMIS
* VMIS
*
* <p>application.yml application.properties .
* :</p>
@ -85,10 +89,23 @@ public class GovernmentApiClient implements GovernmentApi {
* <li> API URL (, , )</li>
* <li>API </li>
* <li> (INFO_SYS_ID, REGION_CODE )</li>
* <li>GPKI ( ID )</li>
* </ul>
*/
private final VmisProperties props;
private final OldVmisProperties oldProps;
/**
* VMIS
*
* <p> API .</p>
*/
private final NewVmisProperties newProps;
/**
* GPKI
*
* <p>GPKI ID .</p>
*/
private final GpkiProperties gpkiProps;
/**
* GPKI()
@ -151,13 +168,17 @@ public class GovernmentApiClient implements GovernmentApi {
*/
public enum ServiceType {
/**
* Basic service type.
* Old Basic service type ( ).
*/
BASIC,
OLD_BASIC,
/**
* Ledger service type.
* New Basic service type ( ).
*/
LEDGER }
NEW_BASIC,
/**
* New Ledger service type ( ()).
*/
NEW_LEDGER }
/**
* HTTP
@ -237,11 +258,12 @@ public class GovernmentApiClient implements GovernmentApi {
* <li> (BASIC, LEDGER) API </li>
* </ul>
*
* @param svc (API , )
* @param apiKey API
* @param cvmisApikey CVMIS API
* @param txId ID
* @return HttpHeaders HTTP
*/
private HttpHeaders buildHeaders(VmisProperties.GovProps.Service svc, String txId) {
private HttpHeaders buildHeaders(String apiKey, String cvmisApikey, String txId) {
// 1. 빈 HttpHeaders 객체 생성
HttpHeaders headers = new HttpHeaders();
@ -251,7 +273,7 @@ public class GovernmentApiClient implements GovernmentApi {
// 3. Accept 헤더 설정
// 서버에게 JSON 형식의 응답을 요청함
headers.setAccept(java.util.List.of(MediaType.APPLICATION_JSON));
headers.setAccept(java.util.Arrays.asList(MediaType.APPLICATION_JSON));
// 4. GPKI 암호화 사용 여부
// 정부 서버가 요청 바디 복호화 여부를 결정하는 데 사용
@ -263,15 +285,15 @@ public class GovernmentApiClient implements GovernmentApi {
// 6. 인증서 서버 ID
// GPKI 인증서를 발급받은 서버의 식별자
headers.add("cert_server_id", props.getGpki().getCertServerId());
headers.add("cert_server_id", gpkiProps.getCertServerId());
// 7. API 인증 키
// 서비스별로 다른 API 키 사용 가능 (BASIC과 LEDGER 각각)
headers.add("api_key", svc.getApiKey());
headers.add("api_key", apiKey);
// 8. CVMIS API 키
// CVMIS(Car Vehicle Management Information System) 전용 API 키
headers.add("cvmis_apikey", svc.getCvmisApikey());
headers.add("cvmis_apikey", cvmisApikey);
// 구성 완료된 헤더 반환
return headers;
@ -314,9 +336,22 @@ public class GovernmentApiClient implements GovernmentApi {
* @param envelope Envelope
* @return ResponseEntity&lt;Envelope&lt;BasicResponse&gt;&gt;
*/
public ResponseEntity<Envelope<BasicResponse>> callBasic(Envelope<BasicRequest> envelope) {
public ResponseEntity<Envelope<OldBasicResponse>> callOldBasic(String txId, Envelope<OldBasicRequest> envelope) {
// 순수한 전송 책임만 수행: DB 로깅은 서비스 레이어에서 처리
return callModel(ServiceType.BASIC, envelope, new TypeReference<Envelope<BasicResponse>>(){});
return callModel(ServiceType.OLD_BASIC, txId, envelope, new TypeReference<Envelope<OldBasicResponse>>(){});
}
/**
* API
*
* <p> . 릿 ({@link #callModel})
* .</p>
*
* @param envelope () Envelope
* @return ResponseEntity Envelope
*/
public ResponseEntity<Envelope<NewBasicResponse>> callNewBasic(String txId, Envelope<NewBasicRequest> envelope) {
return callModel(ServiceType.NEW_BASIC, txId, envelope, new TypeReference<Envelope<NewBasicResponse>>(){});
}
/**
@ -348,10 +383,10 @@ public class GovernmentApiClient implements GovernmentApi {
* @param envelope Envelope
* @return ResponseEntity&lt;Envelope&lt;LedgerResponse&gt;&gt;
*/
public ResponseEntity<Envelope<LedgerResponse>> callLedger(Envelope<LedgerRequest> envelope) {
public ResponseEntity<Envelope<NewLedgerResponse>> callNewLedger(String txId, Envelope<NewLedgerRequest> envelope) {
// TypeReference를 사용하여 제네릭 타입 정보 전달
// 익명 클래스를 생성하여 타입 소거(Type Erasure) 문제 해결
return callModel(ServiceType.LEDGER, envelope, new TypeReference<Envelope<LedgerResponse>>(){});
return callModel(ServiceType.NEW_LEDGER, txId, envelope, new TypeReference<Envelope<NewLedgerResponse>>(){});
}
/**
@ -452,38 +487,64 @@ public class GovernmentApiClient implements GovernmentApi {
* @param <TReq>
* @param <TResp>
* @param type (BASIC LEDGER)
* @param txId ID ( , )
* @param envelope Envelope
* @param respType TypeReference ( )
* @return ResponseEntity&lt;Envelope&lt;TResp&gt;&gt; ResponseEntity
* @throws RuntimeException JSON / , , GPKI
*/
private <TReq, TResp> ResponseEntity<Envelope<TResp>> callModel(ServiceType type,
String txId,
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();
String url;
String apiKey;
String cvmisApikey;
if (type == ServiceType.OLD_BASIC) {
OldVmisProperties.GovProps gov = oldProps.getGov();
OldVmisProperties.GovProps.Service svc = gov.getServices().getBasic();
url = gov.buildServiceUrl(svc.getPath());
apiKey = svc.getApiKey();
cvmisApikey = svc.getCvmisApikey();
} else if (type == ServiceType.NEW_BASIC) {
NewVmisProperties.GovProps gov = newProps.getGov();
NewVmisProperties.GovProps.Service svc = gov.getServices().getBasic();
url = gov.buildServiceUrl(svc.getPath());
apiKey = svc.getApiKey();
cvmisApikey = svc.getCvmisApikey();
} else if (type == ServiceType.NEW_LEDGER) {
NewVmisProperties.GovProps gov = newProps.getGov();
NewVmisProperties.GovProps.Service svc = gov.getServices().getLedger();
url = gov.buildServiceUrl(svc.getPath());
apiKey = svc.getApiKey();
cvmisApikey = svc.getCvmisApikey();
} else {
throw new UnsupportedOperationException("지원하지 않는 서비스 타입: " + type);
}
// 2. URL 및 트랜잭션 ID 생성
String url = gov.buildServiceUrl(svc.getPath());
String txId = TxIdUtil.generate();
// 2. 트랜잭션 ID 사용 (상위에서 전달받음)
// 기존: String txId = TxIdUtil.generate(); (여기서 생성하면 일관성 문제 발생)
try {
// 3. 직렬화: Java 객체 → JSON 문자열
// ObjectMapper가 Envelope 객체를 JSON으로 변환
// 날짜, null 값 등의 처리는 ObjectMapper 설정에 따름
String jsonBody = objectMapper.writeValueAsString(envelope);
// 상세 진단: 요청 평문 JSON 전체 로깅 (개인 테스트 요청에 따라 상세 출력)
log.info("[GOV-REQ-BODY] tx_id={}, json={}", txId, jsonBody);
// 4. HTTP 헤더 구성
HttpHeaders headers = buildHeaders(svc, txId);
HttpHeaders headers = buildHeaders(apiKey, cvmisApikey, txId);
// 5. GPKI 암호화 처리
String bodyToSend = jsonBody;
if (gpkiService.isEnabled()) {
// JSON 평문을 암호화된 문자열로 변환
bodyToSend = gpkiEncrypt(jsonBody);
// 상세 진단: 암호문 전체 로깅 (개인 테스트 요청에 따라 상세 출력)
log.info("[GOV-REQ-BODY-ENC] tx_id={}, cipher={}", txId, bodyToSend);
}
// 6. HTTP 엔티티 생성 (헤더 + 바디)
@ -491,23 +552,70 @@ public class GovernmentApiClient implements GovernmentApi {
// 7. 요청 로그 기록
log.info("[GOV-REQ] url={}, tx_id={}, gpki={}, length={}", url, txId, gpkiService.isEnabled(), bodyToSend != null ? bodyToSend.length() : 0);
// 상세 진단: 요청 헤더 전체 로깅
log.info("[GOV-REQ-HEADERS] tx_id={}, headers={}", txId, headers);
// 8. 실제 HTTP POST 요청 전송
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
String respBody = response.getBody();
// 상세 진단: 수신 원문 전체 로깅
log.info("[GOV-RES-RAW] tx_id={}, status={}, body={}",
txId, response.getStatusCode(), respBody);
// 상세 진단: 응답 헤더 전체 로깅
log.info("[GOV-RES-HEADERS] tx_id={}, headers={}", txId, response.getHeaders());
// 9-a. 복호화 전 응답 바디 진단 로그(민감정보 노출 방지를 위해 앞부분만 샘플링)
MediaType contentType = response.getHeaders() != null ? response.getHeaders().getContentType() : null;
boolean looksBase64 = looksLikeBase64(respBody);
log.info("[GOV-RES] status={}, contentType={}, len={}, looksBase64={}, head64={}",
response.getStatusCode(),
contentType,
(respBody != null ? respBody.length() : 0),
looksBase64,
head(respBody, 64));
// 9. GPKI 복호화 처리 (성공 응답인 경우만)
if (gpkiService.isEnabled() && response.getStatusCode().is2xxSuccessful()) {
// 암호화된 응답을 평문 JSON으로 복호화
respBody = gpkiDecrypt(respBody);
if (looksBase64) {
try {
// 암호화된 응답을 평문 JSON으로 복호화
respBody = gpkiDecrypt(respBody);
// 상세 진단: 복호화 후 평문 전체 로깅
log.info("[GOV-RES-DEC] tx_id={}, body={}", txId, respBody);
} catch (RuntimeException decEx) {
// 복호화 실패 시 진단 로그(샘플만 노출)
log.warn("[GOV-RES-DECERR] tx_id={}, len={}, sample={}, cause={}",
txId,
(respBody != null ? respBody.length() : 0),
head(respBody, 64),
decEx.getMessage());
throw decEx; // 기존 흐름 유지: 상위에서 전역 예외 처리
}
} else {
// Base64처럼 보이지 않으면 복호화를 생략하고 경고만 남김
log.warn("[GOV-RES] skip decrypt: status2xx=true, gpki=true, looksBase64=false, tx_id={}, ct={}, len={}, head64={}",
txId, contentType, (respBody != null ? respBody.length() : 0), head(respBody, 64));
}
}
// 10. 역직렬화: JSON 문자열 → Java 객체
// TypeReference를 사용하여 제네릭 타입 정보 전달
Envelope<TResp> mapped = objectMapper.readValue(respBody, respType);
try {
Envelope<TResp> mapped = objectMapper.readValue(respBody, respType);
// 정상 매핑 시 반환
return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(mapped);
} catch (JsonProcessingException jpe) {
// 상세 진단: 비JSON/깨진 JSON 전체 출력
log.warn("[GOV-RES-PLAINERR] tx_id={}, ct={}, len={}, body={}",
txId,
contentType,
(respBody != null ? respBody.length() : 0),
respBody);
throw new RuntimeException("정부 API 비정상 응답(비JSON 또는 파싱 실패). tx_id=" + txId + ", ct=" + contentType + ", body=" + respBody, jpe);
}
// 11. 응답 반환 (상태 코드, 헤더, 바디 모두 포함)
return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(mapped);
// 11. 응답 반환 (상태 코드, 헤더, 바디 모두 포함) — 위에서 이미 반환됨
} catch (HttpStatusCodeException ex) {
// HTTP 에러 처리 (4xx, 5xx)
@ -624,4 +732,18 @@ public class GovernmentApiClient implements GovernmentApi {
}
}
// === 내부 유틸리티: 응답 바디 샘플 및 Base64 판별 ===
private static String head(String s, int max) {
if (s == null) return null;
return s.substring(0, Math.min(max, s.length()));
}
private static boolean looksLikeBase64(String s) {
if (s == null || s.isEmpty()) return false;
int len = s.length();
if ((len % 4) != 0) return false; // Base64는 4의 배수 길이
// 허용 문자 집합만 포함하는지 대략 판별(개행 허용)
return s.matches("[A-Za-z0-9+/=\\r\\n]+\\z");
}
}

@ -1,57 +0,0 @@
package com.vmis.interfaceapp.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: com.vmis.interfaceapp.mapper Mapper </li>
* </ul>
*/
@Configuration
@EnableTransactionManagement
@MapperScan("com.vmis.interfaceapp.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);
}
}

@ -1,6 +1,5 @@
package com.vmis.interfaceapp.config;
import com.vmis.interfaceapp.model.common.Envelope;
import com.vmis.interfaceapp.util.ExceptionDetailUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;

@ -1,6 +1,6 @@
package com.vmis.interfaceapp.config;
import com.vmis.interfaceapp.config.properties.VmisProperties;
import com.vmis.interfaceapp.config.properties.GpkiProperties;
import com.vmis.interfaceapp.gpki.GpkiService;
import com.vmis.interfaceapp.gpki.NoopGpkiService;
import com.vmis.interfaceapp.gpki.RealGpkiService;
@ -11,9 +11,9 @@ import org.springframework.context.annotation.Configuration;
public class GpkiConfig {
@Bean
public GpkiService gpkiService(VmisProperties properties) {
if (properties.getGpki().isEnabledFlag()) {
return new RealGpkiService(properties);
public GpkiService gpkiService(GpkiProperties gpkiProperties) {
if (gpkiProperties.isEnabledFlag()) {
return new RealGpkiService(gpkiProperties);
}
return new NoopGpkiService();
}

@ -1,30 +1,32 @@
package com.vmis.interfaceapp.config;
import com.vmis.interfaceapp.config.properties.VmisProperties;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import com.vmis.interfaceapp.config.properties.HttpProperties;
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.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
import java.nio.charset.StandardCharsets;
import java.util.List;
@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();
public RestTemplate restTemplate(HttpProperties httpProps, RestTemplateBuilder builder) {
int connectTimeout = httpProps.getConnectTimeoutMillis();
int readTimeout = httpProps.getReadTimeoutMillis();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeout, java.util.concurrent.TimeUnit.MILLISECONDS)
.setResponseTimeout(readTimeout, java.util.concurrent.TimeUnit.MILLISECONDS)
.setConnectTimeout(connectTimeout)
.setSocketTimeout(readTimeout)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
@ -37,8 +39,16 @@ public class HttpClientConfig {
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
return builder
RestTemplate rt = builder
.requestFactory(() -> requestFactory)
.build();
// 개인 테스트 환경: 응답 한글 깨짐 방지를 위해 String 컨버터를 UTF-8로 강제
// 기본 등록된 StringHttpMessageConverter를 제거하고 UTF-8 컨버터를 최상단에 배치
List<HttpMessageConverter<?>> converters = rt.getMessageConverters();
converters.removeIf(c -> c instanceof StringHttpMessageConverter);
converters.add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return rt;
}
}

@ -1,10 +1,13 @@
package com.vmis.interfaceapp.config;
import com.vmis.interfaceapp.config.properties.VmisProperties;
import com.vmis.interfaceapp.config.properties.GpkiProperties;
import com.vmis.interfaceapp.config.properties.HttpProperties;
import com.vmis.interfaceapp.config.properties.NewVmisProperties;
import com.vmis.interfaceapp.config.properties.OldVmisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(VmisProperties.class)
@EnableConfigurationProperties({OldVmisProperties.class, NewVmisProperties.class, GpkiProperties.class, HttpProperties.class})
public class PropertiesConfig {
}

@ -0,0 +1,37 @@
package com.vmis.interfaceapp.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
@Data
@ConfigurationProperties(prefix = "gpki")
@Validated
public class GpkiProperties {
/** "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 boolean isEnabledFlag() {
return "Y".equalsIgnoreCase(enabled);
}
}

@ -0,0 +1,13 @@
package com.vmis.interfaceapp.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@Data
@ConfigurationProperties(prefix = "http")
@Validated
public class HttpProperties {
private int connectTimeoutMillis = 5000;
private int readTimeoutMillis = 10000;
}

@ -0,0 +1,82 @@
package com.vmis.interfaceapp.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* VMIS (, () )
*/
@Data
@ConfigurationProperties(prefix = "new.vmis")
@Validated
public class NewVmisProperties {
@NotNull
private SystemProps system = new SystemProps();
@NotNull
private GovProps gov = new GovProps();
@Data
public static class SystemProps {
@NotBlank
private String infoSysId;
/** INFO_SYS_IP_ADDR */
private String infoSysIpAddr;
/** 시군구코드 (SGG_CD) */
private String sggCd;
// 담당자 정보
private String picId;
private String picIpAddr;
private String picNm;
}
@Data
public static class GovProps {
@NotBlank
private String scheme = "http";
@NotBlank
private String host;
@NotBlank
private String basePath;
@NotNull
private Services services = new 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();
}
@Data
public static class Services {
@NotNull
private Service basic = new Service();
@NotNull
private Service ledger = new Service();
}
@Data
public static class Service {
@NotBlank
private String path;
@NotBlank
private String linkInfoCd;
@NotBlank
private String apiKey;
@NotBlank
private String cvmisApikey;
}
}
}

@ -1,21 +1,23 @@
package com.vmis.interfaceapp.config.properties;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* VMIS ()
*/
@Data
@ConfigurationProperties(prefix = "vmis")
@ConfigurationProperties(prefix = "old.vmis")
@Validated
public class VmisProperties {
public class OldVmisProperties {
@NotNull
private SystemProps system = new SystemProps();
@NotNull
private GpkiProps gpki = new GpkiProps();
@NotNull
private GovProps gov = new GovProps();
@Data
@ -33,34 +35,6 @@ public class VmisProperties {
private String chargerNm;
}
@Data
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 boolean isEnabledFlag() {
return "Y".equalsIgnoreCase(enabled);
}
}
@Data
public static class GovProps {
@NotBlank
@ -69,8 +43,6 @@ public class VmisProperties {
private String host;
@NotBlank
private String basePath;
private int connectTimeoutMillis = 5000;
private int readTimeoutMillis = 10000;
@NotNull
private Services services = new Services();
@ -92,8 +64,7 @@ public class VmisProperties {
public static class Services {
@NotNull
private Service basic = new Service();
@NotNull
private Service ledger = new Service();
// 구버전에는 ledger(갑부) 서비스 없음
}
@Data

@ -1,12 +1,14 @@
package com.vmis.interfaceapp.controller;
import com.vmis.interfaceapp.model.basic.BasicRequest;
import com.vmis.interfaceapp.model.basic.BasicResponse;
import com.vmis.interfaceapp.model.basic.NewBasicRequest;
import com.vmis.interfaceapp.model.basic.NewBasicResponse;
import com.vmis.interfaceapp.model.basic.OldBasicRequest;
import com.vmis.interfaceapp.model.basic.OldBasicResponse;
import com.vmis.interfaceapp.model.common.ApiResponse;
import com.vmis.interfaceapp.model.common.Envelope;
import com.vmis.interfaceapp.model.ledger.LedgerRequest;
import com.vmis.interfaceapp.model.ledger.LedgerResponse;
import com.vmis.interfaceapp.service.CarBassMatterInqireService;
import com.vmis.interfaceapp.service.CarLedgerFrmbkService;
import com.vmis.interfaceapp.model.ledger.NewLedgerRequest;
import com.vmis.interfaceapp.model.ledger.NewLedgerResponse;
import com.vmis.interfaceapp.service.VehicleInterfaceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
@ -16,38 +18,12 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* 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
@ -55,173 +31,99 @@ import org.springframework.web.bind.annotation.*;
@Tag(name = "Vehicle Interfaces", description = "시군구연계 자동차 정보 연계 API")
public class VehicleInterfaceController {
private final CarBassMatterInqireService carBassMatterInqireService;
private final CarLedgerFrmbkService carLedgerFrmbkService;
private final VehicleInterfaceService service;
/**
* API
*
* <p>, API.</p>
*
* <h3> :</h3>
* <ol>
* <li> JSON </li>
* <li> Envelope&lt;BasicRequest&gt; </li>
* <li>Spring @RequestBody JSON </li>
* <li> GovernmentApiClient </li>
* <li>GovernmentApiClient </li>
* <li> Envelope&lt;BasicResponse&gt; </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&lt;Envelope&lt;BasicResponse&gt;&gt; HTTP , ,
*/
@PostMapping(value = "/basic", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = "/old-basic", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(
summary = "자동차기본사항조회",
summary = "자동차기본사항조회 - 구버전",
description = "시군구연계 자동차기본사항조회 인터페이스. 요청 바디를 모델로 받아 정부시스템으로 전달합니다.",
requestBody = @RequestBody(
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(
name = "기본사항조회 예제",
value = """
{
"data": [
{
"VHRNO": "12가3456",
"LEVY_STDDE": "20250101",
"VIN": "KMHAB812345678901"
}
]
}
"""
value = "{\n" +
" \"data\": [{\n" +
" \"record\": [{\n" +
" \"LEVY_STDDE\": \"20250101\",\n" +
" \"VHRNO\": \"차량번호\",\n" +
" \"VIN\": null\n" +
" }]\n" +
" }]\n" +
"}"
)
)
)
)
public ResponseEntity<Envelope<BasicResponse>> basic(
@jakarta.validation.Valid @org.springframework.web.bind.annotation.RequestBody Envelope<BasicRequest> envelope
public ResponseEntity<ApiResponse<OldBasicRequest, OldBasicResponse>> oldBasic(
@Valid @org.springframework.web.bind.annotation.RequestBody Envelope<OldBasicRequest> envelope
) {
// 서비스에서 요청 보강/로깅/호출을 모두 오케스트레이션
return carBassMatterInqireService.basic(envelope);
return service.oldBasic(envelope);
}
/**
* () API
*
* <p> API.
* .</p>
*
* <h3> :</h3>
* <ol>
* <li> JSON </li>
* <li> Envelope&lt;LedgerRequest&gt; </li>
* <li>Spring @RequestBody JSON </li>
* <li> GovernmentApiClient </li>
* <li>GovernmentApiClient </li>
* <li> Envelope&lt;LedgerResponse&gt; </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&lt;Envelope&lt;LedgerResponse&gt;&gt; HTTP , ,
*/
@PostMapping(value = "/ledger", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = "/new-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 = "{\n" +
" \"data\": [{\n" +
" \"record\": [{\n" +
" \"INQ_SE_CD\": \"자동설정: VHRNO not null → 3:자동차번호, VIN not null → 2:차대번호\",\n" +
" \"LEVY_CRTR_YMD\": \"20250101\",\n" +
" \"VHRNO\": \"차량번호\",\n" +
" \"VIN\": null\n" +
" }]\n" +
" }]\n" +
"}"
)
)
)
)
public ResponseEntity<ApiResponse<NewBasicRequest, NewBasicResponse>> newBasic(
@Valid @org.springframework.web.bind.annotation.RequestBody Envelope<NewBasicRequest> envelope
) {
// 서비스에서 요청 보강/로깅/호출을 모두 오케스트레이션
return service.newBasic(envelope);
}
@PostMapping(value = "/new-ledger", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(
summary = "자동차등록원부(갑)",
summary = "자동차등록원부(갑) - 신버전",
description = "시군구연계 자동차등록원부(갑) 인터페이스. 요청 바디를 모델로 받아 정부시스템으로 전달합니다.",
requestBody = @RequestBody(
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(
name = "등록원부 조회 예제",
value = """
{
"data": [
{
"VHRNO": "12가3456",
"ONES_INFORMATION_OPEN": "1",
"CPTTR_NM": "홍길동",
"CPTTR_IHIDNUM": "8801011234567",
"CPTTR_LEGALDONG_CODE": "1111011700",
"DETAIL_EXPRESSION": "1",
"INQIRE_SE_CODE": "1",
}
]
}
"""
value = "{\n" +
" \"data\": [\n" +
" {\n" +
" \"VHRNO\": \"차량번호\",\n" +
" \"PRVC_RLS\": \"1\",\n" +
" \"CVLPR_NM\": \"홍길동\",\n" +
" \"CVLPR_IDECNO\": \"8801011234567\",\n" +
" \"CVLPR_STDG_CD\": \"1111011700\",\n" +
" \"PATH_SE_CD\": \"3\",\n" +
" \"DSCTN_INDCT\": \"1\",\n" +
" \"INQ_SE_CD\": \"1\"\n" +
" }\n" +
" ]\n" +
"}"
)
)
)
)
public ResponseEntity<Envelope<LedgerResponse>> ledger(
@org.springframework.web.bind.annotation.RequestBody Envelope<LedgerRequest> envelope
public ResponseEntity<ApiResponse<NewLedgerRequest, NewLedgerResponse>> newLedger(
@org.springframework.web.bind.annotation.RequestBody Envelope<NewLedgerRequest> envelope
) {
// 서비스에서 요청 보강/호출을 오케스트레이션
return carLedgerFrmbkService.ledger(envelope);
return service.newLedger(envelope);
}
}

@ -1,21 +1,23 @@
package com.vmis.interfaceapp.gpki;
import com.vmis.interfaceapp.config.properties.VmisProperties;
import com.vmis.interfaceapp.config.properties.GpkiProperties;
import com.vmis.interfaceapp.util.GpkiCryptoUtil;
import lombok.extern.slf4j.Slf4j;
/**
* Real GPKI service backed by native GPKI JNI via legacy NewGpkiUtil wrapper.
* Uses YAML-configured paths and options in {@link VmisProperties.GpkiProps}.
* Uses YAML-configured paths and options in {@link GpkiProperties}.
*/
@Slf4j
public class RealGpkiService implements GpkiService {
private final VmisProperties props;
private final GpkiProperties gpkiProps;
private final GpkiCryptoUtil crypto;
public RealGpkiService(VmisProperties props) {
this.props = props;
public RealGpkiService(GpkiProperties gpkiProps) {
this.gpkiProps = gpkiProps;
try {
this.crypto = GpkiCryptoUtil.from(props.getGpki());
this.crypto = GpkiCryptoUtil.from(gpkiProps);
} catch (Exception e) {
throw new IllegalStateException("Failed to initialize GPKI (JNI) util. Check YAML paths/passwords and license.", e);
}
@ -23,19 +25,55 @@ public class RealGpkiService implements GpkiService {
@Override
public String encrypt(String plain) throws Exception {
String charset = props.getGpki().getCharset();
String targetId = props.getGpki().getTargetServerId();
return crypto.encryptToBase64(plain, targetId, charset);
String charset = gpkiProps.getCharset();
String targetId = gpkiProps.getTargetServerId();
boolean useSign = gpkiProps.isUseSign();
try {
// 샘플 순서 준수: encrypt(bytes) → (서명) → Base64
return crypto.encryptThenSignToBase64(plain, targetId, charset, useSign);
} catch (Exception e) {
String detail = extractGpkiDetail(e);
if (detail != null) {
log.warn("[GPKI-ENC-ERR] targetId={}, detail={}", targetId, detail);
}
throw e;
}
}
@Override
public String decrypt(String cipher) throws Exception {
String charset = props.getGpki().getCharset();
return crypto.decryptFromBase64(cipher, charset);
String charset = gpkiProps.getCharset();
boolean useSign = gpkiProps.isUseSign();
try {
// 샘플 순서 준수: Base64 decode → (서명검증) → decrypt → String 변환
return crypto.decodeValidateThenDecrypt(cipher, charset, useSign);
} catch (Exception e) {
String detail = extractGpkiDetail(e);
if (detail != null) {
log.warn("[GPKI-DEC-ERR] detail={}", detail);
}
throw e;
}
}
@Override
public boolean isEnabled() {
return true;
}
private static String extractGpkiDetail(Throwable t) {
if (t == null) return null;
Throwable root = t;
int guard = 0;
while (root.getCause() != null && root.getCause() != root && guard++ < 20) {
root = root.getCause();
}
String msg = root.getMessage();
if (msg == null) return null;
int idx = msg.indexOf("gpkiErrorMessage=");
if (idx >= 0) {
return msg.substring(idx + "gpkiErrorMessage=".length()).trim();
}
return msg;
}
}

@ -1,54 +0,0 @@
package com.vmis.interfaceapp.mapper;
import com.vmis.interfaceapp.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);
}

@ -1,28 +0,0 @@
package com.vmis.interfaceapp.mapper;
import com.vmis.interfaceapp.model.ledger.CarLedgerFrmbkDtlVO;
import com.vmis.interfaceapp.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에서 사용할 수도 있으나, 여기서는 단건 호출을 반복)
}

@ -1,90 +0,0 @@
package com.vmis.interfaceapp.model.basic;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Setter
@Schema(description = "자동차기본사항조회 요청 항목")
public class BasicRequest {
// 본문 공통 메타 (application.yml에서 자동 설정)
@Schema(description = "정보시스템ID (자동설정: vmis.system.infoSysId)", example = "41-345")
@JsonProperty("INFO_SYS_ID")
private String infoSysId;
@Schema(description = "정보시스템IP (자동설정: vmis.system.infoSysIp)", example = "105.19.10.135")
@JsonProperty("INFO_SYS_IP")
private String infoSysIp;
@Schema(description = "시군구코드 (자동설정: vmis.system.sigunguCode)", example = "41460")
@JsonProperty("SIGUNGU_CODE")
private String sigunguCode;
// 서비스별 필드 (application.yml에서 자동 설정)
@Schema(description = "연계정보코드 (자동설정: vmis.gov.services.basic.cntcInfoCode)", example = "AC1_FD11_01")
@JsonProperty("CNTC_INFO_CODE")
private String cntcInfoCode;
@Schema(description = "담당자ID (자동설정: vmis.system.chargerId)", example = "")
@JsonProperty("CHARGER_ID")
private String chargerId;
@Schema(description = "담당자IP (자동설정: vmis.system.chargerIp)", example = "")
@JsonProperty("CHARGER_IP")
private String chargerIp;
@Schema(description = "담당자명(사용자) (자동설정: vmis.system.chargerNm)", example = "")
@JsonProperty("CHARGER_NM")
private String chargerNm;
@Schema(description = "부과기준일", example = "20250101")
@JsonProperty("LEVY_STDDE")
private String levyStdde;
@Schema(description = "조회구분코드 (자동설정: VHRNO not null → 3:자동차번호, VIN not null → 2:차대번호)")
@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;
*/
}

@ -1,121 +0,0 @@
package com.vmis.interfaceapp.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(name = "BasicRecord", 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;
}
}

@ -1,646 +0,0 @@
package com.vmis.interfaceapp.model.basic;
import com.vmis.interfaceapp.config.ApiConstant;
import com.vmis.interfaceapp.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()
.carBassMatterInqireId(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()) // 이전 차량번호
.vhrno(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 carBassMatterInqireId;
// ===== 요청 정보 =====
/**
* 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 vhrno;
/**
*
*/
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,71 @@
package com.vmis.interfaceapp.model.basic;
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;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Setter
@Schema(description = "자동차기본사항조회 요청 항목 (신버전)")
public class NewBasicRequest { // 총 8개 필드 (외부 클래스 8개, 중첩 Record 클래스 4개)
// 본문 공통 메타 (application.yml에서 자동 설정)
@Schema(description = "정보시스템ID (자동설정: new.vmis.system.infoSysId)", example = "41-345")
@JsonProperty("INFO_SYS_ID")
private String infoSysId;
@Schema(description = "정보시스템IP (자동설정: new.vmis.system.infoSysIpAddr)", example = "105.19.10.135")
@JsonProperty("INFO_SYS_IP_ADDR")
private String infoSysIpAddr;
@Schema(description = "시군구코드 (자동설정: new.vmis.system.sggCd)", example = "41460")
@JsonProperty("SGG_CD")
private String sggCd;
// 서비스별 필드 (application.yml에서 자동 설정)
@Schema(description = "연계정보코드 (자동설정: new.vmis.gov.services.basic.linkInfoCd)", example = "AC1_FD11_01")
@JsonProperty("LINK_INFO_CD")
private String linkInfoCd;
@Schema(description = "담당자ID (자동설정: new.vmis.system.picId)", example = "")
@JsonProperty("PIC_ID")
private String picId;
@Schema(description = "담당자IP (자동설정: new.vmis.system.picIpAddr)", example = "")
@JsonProperty("PIC_IP_ADDR")
private String picIpAddr;
@Schema(description = "담당자명 (자동설정: new.vmis.system.picNm)", example = "")
@JsonProperty("PIC_NM")
private String picNm;
@Schema(description = "조회 대상 record 배열")
@JsonProperty("record")
private java.util.List<Record> record;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Setter
@Schema(name = "NewBasicRequest.Record", description = "기본사항 요청 record 항목 (신버전)")
public static class Record {
@Schema(description = "부과기준일", example = "20250101")
@JsonProperty("LEVY_CRTR_YMD")
private String levyCrtrYmd;
@Schema(description = "조회구분코드 (자동설정: VHRNO not null → 3:자동차번호, VIN not null → 2:차대번호)")
@JsonProperty("INQ_SE_CD")
private String inqSeCd;
@Schema(description = "자동차등록번호", example = "12가3456")
@JsonProperty("VHRNO")
private String vhrno;
@Schema(description = "차대번호", example = "KMHAB812345678901")
@JsonProperty("VIN")
private String vin;
}
}

@ -0,0 +1,130 @@
package com.vmis.interfaceapp.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;
/**
* ()
*
* <p>
* . ,
* record .
* </p>
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Schema(description = "자동차기본사항조회 응답 모델 (신버전)")
@Getter
@Setter
public class NewBasicResponse { // 총 3개 필드 (외부 클래스 3개, 중첩 Record 클래스 84개)
/** 연계결과코드 (성공/실패 코드) */
@JsonProperty("LINK_RSLT_CD")
private String linkRsltCd;
/** 연계결과상세 (에러 메시지 등 상세 사유) */
@JsonProperty("LINK_RSLT_DTL")
private String linkRsltDtl;
/** 응답 레코드 목록 */
@JsonProperty("record")
private List<Record> record;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Schema(name = "NewBasicRecord", description = "기본사항 record 항목 (신버전)")
@Getter
@Setter
public static class Record {
@JsonProperty("VHRNO") private String vhrno; // 차량번호
@JsonProperty("ATMB_NM") private String atmbNm; // 차명
@JsonProperty("RPRS_OWNR_NM") private String rprsOwnrNm; // 대표소유자성명
@JsonProperty("RPRSV_OWNR_IDECNO") private String rprsvOwnrIdecno; // 대표소유자회원번호
@JsonProperty("ERSR_REG_YMD") private String ersrRegYmd; // 말소등록일
@JsonProperty("PRCS_IMPRTY_RSN_CD") private String prcsImprtyRsnCd; // 처리불가사유코드
@JsonProperty("PRCS_IMPRTY_RSN_DTLS") private String prcsImprtyRsnDtls; // 처리불가사유명세
@JsonProperty("YRIDNW") private String yridnw; // 연식
@JsonProperty("VIN") private String vin; // 차대번호
@JsonProperty("CARMDL_ASORT_NM") private String carmdlAsortNm; // 차종종별명
@JsonProperty("FRST_REG_YMD") private String frstRegYmd; // 최초등록일
@JsonProperty("COLOR_NM") private String colorNm; // 색상명
@JsonProperty("STRCT_CHG_CNT") private String strctChgCnt; // 구조변경수
@JsonProperty("NOPLT_CSDY_YN") private String noplTcsdyYn; // 번호판영치여부
@JsonProperty("NOPLT_CSDY_AVTSMT_YMD") private String noplTcsdyAvtsmtYmd; // 번호판영치최고일
@JsonProperty("INSP_VLD_PD_BGNG_YMD") private String inspVldPdBgngYmd; // 검사유효기간시작일
@JsonProperty("INSP_VLD_PD_END_YMD") private String inspVldPdEndYmd; // 검사유효기간종료일
@JsonProperty("SPMNNO") private String spmnno; // 제원관리번호
@JsonProperty("DRVNG_DSTNC") private String drvngDstnc; // 주행거리
@JsonProperty("FOM_NM") private String fomNm; // 형식
@JsonProperty("DSPLVL") private String dsplvl; // 배기량
@JsonProperty("CARMDL_CLSF_NM") private String carmdlClsfNm; // 차종분류명
@JsonProperty("FBCTN_YMD") private String fbctnYmd; // 제작년월일
@JsonProperty("USGSRHLD_ADDR_NM") private String usgsrhldAddrNm; // 사용본거지상세주소
@JsonProperty("MTRS_FOM_NM") private String mtrsFomNm; // 원동기형식명
@JsonProperty("RDCPCT_CNT") private String rdcpctCnt; // 승차정원수
@JsonProperty("FRST_REG_APLY_RCPT_NO") private String frstRegAplyRcptNo; // 최초등록접수번호
@JsonProperty("OGNZ_NM") private String ognzNm; // 등록기관명
@JsonProperty("ERSR_REG_SE_NM") private String ersrRegSeNm; // 말소등록구분명
@JsonProperty("BFR_VHRNO") private String bfrVhrno; // 이전차량번호
@JsonProperty("USE_FUEL_CD") private String useFuelCd; // 사용연료코드
@JsonProperty("RPRS_OWNR_MBR_SE_CD") private String rprsOwnrMbrSeCd; // 대표소유자회원구분코드
@JsonProperty("RPRS_OWNR_TELNO") private String rprsOwnrTelno; // 대표소유자전화번호
@JsonProperty("OWNR_STDG_CD") private String ownrStdgCd; // 소유자법정동코드
@JsonProperty("OWNR_WHOL_ADDR") private String ownrWholAddr; // 소유자전체주소
@JsonProperty("VHCL_TOTL_WT") private String vhclTotlWt; // 차량총중량
@JsonProperty("MXMM_LDG") private String mxmmLdg; // 최대적재량
@JsonProperty("CBD_LT") private String cbdLt; // 차체길이
@JsonProperty("CBD_BT") private String cbdBt; // 차체너비
@JsonProperty("CBD_HG") private String cbdHg; // 차체높이
@JsonProperty("CARMDL_ASORT_CD") private String carmdlAsortCd; // 차종종별코드
@JsonProperty("CARMDL_TYPE_CD") private String carmdlTypeCd; // 차종유형코드
@JsonProperty("FUEL_CNSMPRT") private String fuelCnsmprt; // 연료소비율
@JsonProperty("ERSR_REG_SE_CD") private String ersrRegSeCd; // 말소등록구분코드
@JsonProperty("REG_DTL_CD") private String regDtlCd; // 등록상세코드
@JsonProperty("USGSRHLD_STDG_CD") private String usgsrhldStdgCd; // 사용본거지법정동코드
@JsonProperty("USGSRHLD_DONG_CD") private String usgsrhldDongCd; // 사용본거지행정동코드
@JsonProperty("USGSRHLD_MTN_YN") private String usgsrhldMtnYn; // 사용본거지산 여부
@JsonProperty("USGSRHLD_LNBR") private String usgsrhldLnbr; // 사용본거지번지
@JsonProperty("USGSRHLD_HO") private String usgsrhldHo; // 사용본거지호
@JsonProperty("USGSRHLD_ROAD_NM_CD") private String usgsrhldRoadNmCd; // 사용본거지도로명코드
@JsonProperty("USGSRHLD_UDGD_BLDG_SE_CD") private String usgsrhldUdgdBldgSeCd; // 사용본거지지하건물구분코드
@JsonProperty("USGSRHLD_BMNO") private String usgsrhldBmno; // 사용본거지건물주요번호
@JsonProperty("USGSRHLD_BSNO") private String usgsrhldBsno; // 사용본거지건물부번호
@JsonProperty("OWNR_DONG_CD") private String ownrDongCd; // 소유자행정동코드
@JsonProperty("OWNR_MTN_YN") private String ownrMtnYn; // 소유자산 여부
@JsonProperty("OWNR_LNBR") private String ownrLnbr; // 소유자번지
@JsonProperty("OWNR_HO") private String ownrHo; // 소유자호
@JsonProperty("OWNR_ADDR_NM") private String ownrAddrNm; // 소유자상세주소
@JsonProperty("OWNR_ROAD_NM_CD") private String ownrRoadNmCd; // 소유자도로명코드
@JsonProperty("OWNR_UDGD_BLDG_SE_CD") private String ownrUdgdBldgSeCd; // 소유자지하건물구분코드
@JsonProperty("OWNR_BMNO") private String ownrBmno; // 소유자건물주요번호
@JsonProperty("OWNR_BSNO") private String ownrBsno; // 소유자건물부번호
@JsonProperty("REAR_VHRNO") private String rearVhrno; // 신차량번호
@JsonProperty("USG_SE_CD") private String usgSeCd; // 용도구분코드
@JsonProperty("VEAG_END_YMD") private String veagEndYmd; // 차령만료일자
@JsonProperty("CHG_YMD") private String chgYmd; // 차번호변경시기
@JsonProperty("CARMDL_SE_CD") private String carmdlSeCd; // 차종분류코드
@JsonProperty("CARMDL_TYPE_NM") private String carmdlTypeNm; // 차종유형명
@JsonProperty("ACQS_YMD") private String acqsYmd; // 취득일자
@JsonProperty("ACQS_END_YMD") private String acqsEndYmd; // 취득종료일자
@JsonProperty("TRANSR_REG_YMD") private String transrRegYmd; // 이전등록일(양수일)
@JsonProperty("SPCF_REG_STTS_CD") private String spcfRegSttsCd; // 제원등록상태코드
@JsonProperty("SRC_SE_CD") private String srcSeCd; // 출처구분코드
@JsonProperty("NOPLT_SPCFCT_CD") private String noplTSpcfctCd; // 번호판규격코드
@JsonProperty("ACQS_AMT") private String acqsAmt; // 취득금액
@JsonProperty("USGSRHLD_GRC_CD") private String usgsrhldGrcCd; // 사용본거지관청코드
@JsonProperty("VLNT_ERSR_PRVNTC_AVTSMT_YMD") private String vlntErsrPrvntcAvtsmtYmd; // 예고통지일
@JsonProperty("FRST_MXMM_LDG") private String frstMxmmLdg; // 최초최대적재량
@JsonProperty("REG_YMD") private String regYmd; // 등록일(변경일)
@JsonProperty("ELCTY_CMPND_FUEL_CNSMPRT") private String elctyCmpndFuelCnsmprt; // 전기복합연료소비율
@JsonProperty("USGSRHLD_WHOL_ADDR") private String usgsrhldWholAddr; // 사용본거지전체주소
@JsonProperty("MRTG_CNT") private String mrtgCnt; // 저당수
@JsonProperty("SZR_CNT") private String szrCnt; // 압류건수
}
}

@ -0,0 +1,71 @@
package com.vmis.interfaceapp.model.basic;
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;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Setter
@Schema(description = "자동차기본사항조회 요청 항목 (구버전)")
public class OldBasicRequest { // 총 8개 필드 (외부 클래스 8개, 중첩 Record 클래스 4개)
// 본문 공통 메타 (application.yml에서 자동 설정)
@Schema(description = "정보시스템ID (자동설정: old.vmis.system.infoSysId)", example = "41-345")
@JsonProperty("INFO_SYS_ID")
private String infoSysId;
@Schema(description = "정보시스템IP (자동설정: old.vmis.system.infoSysIp)", example = "105.19.10.135")
@JsonProperty("INFO_SYS_IP")
private String infoSysIp;
@Schema(description = "시군구코드 (자동설정: old.vmis.system.sigunguCode)", example = "41460")
@JsonProperty("SIGUNGU_CODE")
private String sigunguCode;
// 서비스별 필드 (application.yml에서 자동 설정)
@Schema(description = "연계정보코드 (자동설정: old.vmis.gov.services.basic.cntcInfoCode)", example = "AC1_FD11_01")
@JsonProperty("CNTC_INFO_CODE")
private String cntcInfoCode;
@Schema(description = "담당자ID (자동설정: old.vmis.system.chargerId)", example = "")
@JsonProperty("CHARGER_ID")
private String chargerId;
@Schema(description = "담당자IP (자동설정: old.vmis.system.chargerIp)", example = "")
@JsonProperty("CHARGER_IP")
private String chargerIp;
@Schema(description = "담당자명 (자동설정: old.vmis.system.chargerNm)", example = "")
@JsonProperty("CHARGER_NM")
private String chargerNm;
@Schema(description = "조회 대상 record 배열")
@JsonProperty("record")
private java.util.List<Record> record;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Setter
@Schema(name = "OldBasicRequest.Record", description = "기본사항 요청 record 항목 (구버전)")
public static class Record {
@Schema(description = "부과기준일", example = "20250101")
@JsonProperty("LEVY_STDDE")
private String levyStdde;
@Schema(description = "조회구분코드 (자동설정: VHRNO not null → 3:자동차번호, VIN not null → 2:차대번호)")
@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;
}
}

@ -0,0 +1,132 @@
package com.vmis.interfaceapp.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;
/**
* ()
*
* <p>
* (/ ) .
* / , record .
* </p>
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Schema(description = "자동차기본사항조회 응답 모델 (구버전)")
@Getter
@Setter
public class OldBasicResponse { // 총 3개 필드 (외부 클래스 3개, 중첩 Record 클래스 84개)
/** 연계결과코드 */
@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(name = "OldBasicRecord", 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,44 @@
package com.vmis.interfaceapp.model.common;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
/**
* API :
*
* <pre>
* {
* "txId": "...",
* "request": { "data": [...] },
* "response": { "data": [...] }
* }
* </pre>
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Setter
public class ApiResponse<REQ, RES> {
@JsonProperty("txId")
private String txId;
@JsonProperty("request")
private Envelope<REQ> request;
@JsonProperty("response")
private Envelope<RES> response;
public ApiResponse() {}
public ApiResponse(String txId, Envelope<REQ> request, Envelope<RES> response) {
this.txId = txId;
this.request = request;
this.response = response;
}
public static <REQ, RES> ApiResponse<REQ, RES> of(String txId, Envelope<REQ> request, Envelope<RES> response) {
return new ApiResponse<>(txId, request, response);
}
}

@ -0,0 +1,158 @@
package com.vmis.interfaceapp.model.common;
import com.vmis.interfaceapp.config.properties.NewVmisProperties;
import com.vmis.interfaceapp.config.properties.OldVmisProperties;
import com.vmis.interfaceapp.model.basic.NewBasicRequest;
import com.vmis.interfaceapp.model.basic.OldBasicRequest;
import com.vmis.interfaceapp.model.ledger.NewLedgerRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* Populates incoming request models with values from YAML configuration.
*
* <p> (OldBasicRequest):</p>
* <ul>
* <li>INFO_SYS_ID, INFO_SYS_IP, SIGUNGU_CODE</li>
* <li>CNTC_INFO_CODE (service specific)</li>
* <li>CHARGER_ID, CHARGER_IP, CHARGER_NM</li>
* <li>INQIRE_SE_CODE: VHRNO not null 3:, VIN not null 2:</li>
* </ul>
*
* <p> (NewLedgerRequest):</p>
* <ul>
* <li>INFO_SYS_ID, INFO_SYS_IP_ADDR, SGG_CD</li>
* <li>LINK_INFO_CD (service specific)</li>
* <li>PIC_ID, PIC_IP_ADDR, PIC_NM</li>
* <li>INQ_SE_CD: Always 1 ()</li>
* </ul>
*/
@Slf4j
@Component
public class RequestEnricher {
private final OldVmisProperties oldProps;
private final NewVmisProperties newProps;
public RequestEnricher(OldVmisProperties oldProps, NewVmisProperties newProps) {
this.oldProps = oldProps;
this.newProps = newProps;
}
public void enrichOldBasic(Envelope<OldBasicRequest> envelope) {
if (envelope == null || envelope.getData() == null) return;
OldVmisProperties.SystemProps sys = oldProps.getSystem();
String cntc = oldProps.getGov().getServices().getBasic().getCntcInfoCode();
for (OldBasicRequest req : envelope.getData()) {
if (req == null) continue;
// 메타 필드 보강 (data level)
req.setInfoSysId(sys.getInfoSysId());
req.setInfoSysIp(sys.getInfoSysIp());
req.setSigunguCode(sys.getSigunguCode());
req.setCntcInfoCode(cntc);
req.setChargerId(sys.getChargerId());
req.setChargerIp(sys.getChargerIp());
req.setChargerNm(sys.getChargerNm());
// record 배열이 없으면 생성 (최소 1개)
java.util.List<OldBasicRequest.Record> records = req.getRecord();
if (records == null || records.isEmpty()) {
OldBasicRequest.Record r = new OldBasicRequest.Record();
java.util.ArrayList<OldBasicRequest.Record> list = new java.util.ArrayList<>();
list.add(r);
req.setRecord(list);
records = list;
}
// 자동차 기본사항 조회 시 INQIRE_SE_CODE 자동 설정
// VHRNO(차량번호) not null → 3:자동차번호, VIN(차대번호) not null → 2:차대번호
for (OldBasicRequest.Record record : records) {
if( record.getInqireSeCode() == null || record.getInqireSeCode().trim().isEmpty() ){
if (record.getVhrno() != null && !record.getVhrno().trim().isEmpty()) {
record.setInqireSeCode("3");
} else if (record.getVin() != null && !record.getVin().trim().isEmpty()) {
record.setInqireSeCode("2");
}
}
}
}
log.debug("[ENRICH] oldBasic: applied INFO_SYS_ID={}, INFO_SYS_IP={}, SIGUNGU_CODE={}, CNTC_INFO_CODE={}",
sys.getInfoSysId(), sys.getInfoSysIp(), sys.getSigunguCode(), cntc);
}
public void enrichNewBasic(Envelope<NewBasicRequest> envelope) {
if (envelope == null || envelope.getData() == null) return;
NewVmisProperties.SystemProps sys = newProps.getSystem();
String linkInfoCd = oldProps.getGov().getServices().getBasic().getCntcInfoCode();
for (NewBasicRequest req : envelope.getData()) {
if (req == null) continue;
// 메타 필드 보강 (data level)
req.setInfoSysId(sys.getInfoSysId());
req.setInfoSysIpAddr(sys.getInfoSysIpAddr());
req.setSggCd(sys.getSggCd());
req.setLinkInfoCd(linkInfoCd);
req.setPicId(sys.getPicId());
req.setPicIpAddr(sys.getPicIpAddr());
req.setPicNm(sys.getPicNm());
// record 배열이 없으면 생성 (최소 1개)
java.util.List<NewBasicRequest.Record> records = req.getRecord();
if (records == null || records.isEmpty()) {
NewBasicRequest.Record r = new NewBasicRequest.Record();
java.util.ArrayList<NewBasicRequest.Record> list = new java.util.ArrayList<>();
list.add(r);
req.setRecord(list);
records = list;
}
// 자동차 기본사항 조회 시 INQIRE_SE_CODE 자동 설정
// VHRNO(차량번호) not null → 3:자동차번호, VIN(차대번호) not null → 2:차대번호
for (NewBasicRequest.Record record : records) {
if( record.getInqSeCd() == null || record.getInqSeCd().trim().isEmpty() ){
if (record.getVhrno() != null && !record.getVhrno().trim().isEmpty()) {
record.setInqSeCd("3");
} else if (record.getVin() != null && !record.getVin().trim().isEmpty()) {
record.setInqSeCd("2");
}
}
}
}
log.debug("[ENRICH] newBasic: applied INFO_SYS_ID={}, INFO_SYS_IP_ADDR={}, SGG_CD={}, LINK_INFO_CD={}",
sys.getInfoSysId(), sys.getInfoSysIpAddr(), sys.getSggCd(), linkInfoCd);
}
public void enrichNewLedger(Envelope<NewLedgerRequest> envelope) {
if (envelope == null || envelope.getData() == null) return;
NewVmisProperties.SystemProps sys = newProps.getSystem();
String linkInfoCd = newProps.getGov().getServices().getLedger().getLinkInfoCd();
for (NewLedgerRequest req : envelope.getData()) {
if (req == null) continue;
req.setInfoSysId(sys.getInfoSysId());
req.setInfoSysIpAddr(sys.getInfoSysIpAddr());
req.setSggCd(sys.getSggCd());
req.setLinkInfoCd(linkInfoCd);
req.setPicId(sys.getPicId());
req.setPicIpAddr(sys.getPicIpAddr());
req.setPicNm(sys.getPicNm());
// 고정값 설정 (값이 없는 경우에만 설정)
if (req.getPrvcRls() == null || req.getPrvcRls().isEmpty()) {
// 고정값 설정 (값이 없는 경우에만 설정)
if (req.getPrvcRls() == null || req.getPrvcRls().isEmpty()) {
req.setPrvcRls(req.getCvlprStdgCd() != null && !req.getCvlprStdgCd().isEmpty() ? "1" : "4"); // 개인정보공개 (법정동코드 있으면 1, 없으면 4)
}
}
if (req.getPathSeCd() == null || req.getPathSeCd().isEmpty()) {
req.setPathSeCd("3"); // 경로구분코드
}
if (req.getDsctnIndct() == null || req.getDsctnIndct().isEmpty()) {
req.setDsctnIndct("1"); // 내역표시 (전체내역)
}
if (req.getInqSeCd() == null || req.getInqSeCd().isEmpty()) {
req.setInqSeCd("1"); // 조회구분코드 (열람)
}
}
log.debug("[ENRICH] newLedger: applied INFO_SYS_ID={}, INFO_SYS_IP_ADDR={}, SGG_CD={}, LINK_INFO_CD={}",
sys.getInfoSysId(), sys.getInfoSysIpAddr(), sys.getSggCd(), linkInfoCd);
}
}

@ -1,73 +0,0 @@
package com.vmis.interfaceapp.model.ledger;
import com.vmis.interfaceapp.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;
}

@ -1,192 +0,0 @@
package com.vmis.interfaceapp.model.ledger;
import com.vmis.interfaceapp.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(com.vmis.interfaceapp.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, com.vmis.interfaceapp.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;
}

@ -1,79 +0,0 @@
package com.vmis.interfaceapp.model.ledger;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "자동차등록원부(갑) 요청 항목")
@Getter
@Setter
public class LedgerRequest {
// 본문 공통 메타 (application.yml에서 자동 설정)
@Schema(description = "정보시스템ID (자동설정: vmis.system.infoSysId)", example = "41-345")
@JsonProperty("INFO_SYS_ID")
private String infoSysId;
@Schema(description = "정보시스템IP (자동설정: vmis.system.infoSysIp)", example = "105.19.10.135")
@JsonProperty("INFO_SYS_IP")
private String infoSysIp;
@Schema(description = "시군구코드 (자동설정: vmis.system.sigunguCode)", example = "41460")
@JsonProperty("SIGUNGU_CODE")
private String sigunguCode;
// 서비스별 필드 (application.yml에서 자동 설정)
@Schema(description = "연계정보코드 (자동설정: vmis.gov.services.ledger.cntcInfoCode)", example = "AC1_FD11_02")
@JsonProperty("CNTC_INFO_CODE")
private String cntcInfoCode;
@Schema(description = "담당자ID (자동설정: vmis.system.chargerId)", example = "")
@JsonProperty("CHARGER_ID")
private String chargerId;
@Schema(description = "담당자IP (자동설정: vmis.system.chargerIp)", example = "")
@JsonProperty("CHARGER_IP")
private String chargerIp;
@Schema(description = "담당자명(사용자) (자동설정: vmis.system.chargerNm)", example = "")
@JsonProperty("CHARGER_NM")
private String chargerNm;
@Schema(description = "자동차등록번호")
@JsonProperty("VHRNO")
private String vhrno;
@Schema(description = "개인정보공개 {1:소유자공개, 2:비공개, 3:비공개(주민등록번호), 4:비공개(사용본거지)}")
@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 = "경로구분코드 고정코드:3")
@JsonProperty("ROUTE_SE_CODE")
private String routeSeCode;
@Schema(description = "내역표시 {1:전체내역, 2:최종내역}")
@JsonProperty("DETAIL_EXPRESSION")
private String detailExpression;
@Schema(description = "조회구분코드 (자동설정: 1:열람 고정)")
@JsonProperty("INQIRE_SE_CODE")
private String inqireSeCode;
}

@ -1,223 +0,0 @@
package com.vmis.interfaceapp.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(name = "LedgerRecord", 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;
}
}

@ -0,0 +1,80 @@
package com.vmis.interfaceapp.model.ledger;
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 javax.validation.constraints.Size;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "자동차등록원부(갑) 요청 항목 (신버전)")
@Getter
@Setter
public class NewLedgerRequest { // 총 15개 필드
// 본문 공통 메타 (application.yml에서 자동 설정)
@Schema(description = "정보시스템ID (자동설정: new.vmis.system.infoSysId)", example = "41-345")
@JsonProperty("INFO_SYS_ID")
private String infoSysId;
@Schema(description = "정보시스템IP주소 (자동설정: new.vmis.system.infoSysIpAddr)", example = "105.19.10.135")
@JsonProperty("INFO_SYS_IP_ADDR")
private String infoSysIpAddr;
@Schema(description = "시군구코드 (자동설정: new.vmis.system.sggCd)", example = "41460")
@JsonProperty("SGG_CD")
private String sggCd;
// 서비스별 필드 (application.yml에서 자동 설정)
@Schema(description = "연계정보코드 (자동설정: new.vmis.gov.services.ledger.linkInfoCd)", example = "AC1_FD11_02")
@JsonProperty("LINK_INFO_CD")
private String linkInfoCd;
@Schema(description = "담당자ID (자동설정: new.vmis.system.picId)", example = "")
@JsonProperty("PIC_ID")
private String picId;
@Schema(description = "담당자IP주소 (자동설정: new.vmis.system.picIpAddr)", example = "")
@JsonProperty("PIC_IP_ADDR")
private String picIpAddr;
@Schema(description = "담당자명 (자동설정: new.vmis.system.picNm)", example = "")
@JsonProperty("PIC_NM")
private String picNm;
@Schema(description = "자동차등록번호")
@JsonProperty("VHRNO")
private String vhrno;
@Schema(description = "개인정보공개 {1:소유자공개, 2:비공개, 3:비공개(주민등록번호), 4:비공개(사용본거지)}")
@JsonProperty("PRVC_RLS")
private String prvcRls;
@Schema(description = "민원인성명")
@JsonProperty("CVLPR_NM")
private String cvlprNm;
@Schema(description = "민원인주민번호")
@JsonProperty("CVLPR_IDECNO")
@Size(max = 13)
private String cvlprIdecno;
@Schema(description = "민원인법정동코드")
@JsonProperty("CVLPR_STDG_CD")
private String cvlprStdgCd;
@Schema(description = "경로구분코드 고정코드:3")
@JsonProperty("PATH_SE_CD")
private String pathSeCd;
@Schema(description = "내역표시 {1:전체내역, 2:최종내역}")
@JsonProperty("DSCTN_INDCT")
private String dsctnIndct;
@Schema(description = "조회구분코드 (자동설정: 1:열람 고정)")
@JsonProperty("INQ_SE_CD")
private String inqSeCd;
}

@ -0,0 +1,343 @@
package com.vmis.interfaceapp.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 NewLedgerResponse { // 총 64개 필드 (외부 클래스 66개, 중첩 Record 클래스 16개)
@JsonProperty("LINK_RSLT_CD")
private String linkRsltCd;
@JsonProperty("LINK_RSLT_DTL")
private String linkRsltDtl;
/** 원부그룹번호 */
@JsonProperty("LEDGER_GROUP_NO")
private String ledgerGroupNo;
/** 원부개별번호 */
@JsonProperty("LEDGER_INDIV_NO")
private String ledgerIndivNo;
/** 차량관리번호 */
@JsonProperty("VHMNO")
private String vhmno;
/** 차량등록번호 */
@JsonProperty("VHRNO")
private String vhrno;
/** 차대번호 */
@JsonProperty("VIN")
private String vin;
/** 차종종별코드 */
@JsonProperty("CARMDL_ASORT_CD")
private String carmdlAsortCd;
/** 차종종별명 */
@JsonProperty("CARMDL_ASORT_NM")
private String carmdlAsortNm;
/** 차명 */
@JsonProperty("ATMB_NM")
private String atmbNm;
/** 색상코드 */
@JsonProperty("COLOR_CD")
private String colorCd;
/** 색상명 */
@JsonProperty("COLOR_NM")
private String colorNm;
/** 번호판규격코드 */
@JsonProperty("NOPLT_SPCFCT_CD")
private String nopltSpcfctCd;
/** 번호판규격명 */
@JsonProperty("NOPLT_SPCFCT_NM")
private String nopltSpcfctNm;
/** 용도구분코드 */
@JsonProperty("USG_SE_CD")
private String usgSeCd;
/** 용도구분명 */
@JsonProperty("USG_SE_NM")
private String usgSeNm;
/** 원동기형식명 */
@JsonProperty("MTRS_FOM_NM")
private String mtrsFomNm;
/** 형식명 */
@JsonProperty("FOM_NM")
private String fomNm;
/** 취득금액 */
@JsonProperty("ACQS_AMT")
private String acqsAmt;
/** 등록상세코드 */
@JsonProperty("REG_DTL_CD")
private String regDtlCd;
/** 등록상세명 */
@JsonProperty("REG_DTL_NM")
private String regDtlNm;
/** 최초등록일 */
@JsonProperty("FRST_REG_YMD")
private String frstRegYmd;
/** 차령종료일 */
@JsonProperty("VEAG_END_YMD")
private String veagEndYmd;
/** 연식 */
@JsonProperty("YRIDNW")
private String yridnw;
/** 제원관리번호1 */
@JsonProperty("SPMNNO_1")
private String spmnno1;
/** 제원관리번호2 */
@JsonProperty("SPMNNO_2")
private String spmnno2;
/** 제작년월일 */
@JsonProperty("FBCTN_YMD")
private String fbctnYmd;
/** 주행거리 */
@JsonProperty("DRVNG_DSTNC")
private String drvngDstnc;
/** 검사유효기간시작일 */
@JsonProperty("INSP_VLD_PD_BGNG_YMD")
private String inspVldPdBgngYmd;
/** 검사유효기간종료일 */
@JsonProperty("INSP_VLD_PD_END_YMD")
private String inspVldPdEndYmd;
/** 점검유효기간시작일 */
@JsonProperty("CHCK_VLD_PD_BGNG_YMD")
private String chckVldPdBgngYmd;
/** 점검유효기간종료일 */
@JsonProperty("CHCK_VLD_PD_END_YMD")
private String chckVldPdEndYmd;
/** 등록신청구분명 */
@JsonProperty("REG_APLY_SE_NM")
private String regAplySeNm;
/** 최초등록접수번호 */
@JsonProperty("FRST_REG_APLY_RCPT_NO")
private String frstRegAplyRcptNo;
/** 번호판영치최고일 */
@JsonProperty("NOPLT_CSDY_AVTSMT_YMD")
private String nopltCsdyAvtsmtYmd;
/** 번호판영치여부 */
@JsonProperty("NOPLT_CSDY_YN")
private String nopltCsdyYn;
/** 사업용사용기간 */
@JsonProperty("BSS_USE_PD_YMD")
private String bssUsePdYmd;
/** 직권말소예고통지일 */
@JsonProperty("OCTHT_ERSR_PRVNTC_AVTSMT_YMD")
private String octhtErsrPrvntcAvtsmtYmd;
/** 말소등록일 */
@JsonProperty("ERSR_REG_YMD")
private String ersrRegYmd;
/** 말소등록구분코드 */
@JsonProperty("ERSR_REG_SE_CD")
private String ersrRegSeCd;
/** 말소등록구분명 */
@JsonProperty("ERSR_REG_SE_NM")
private String ersrRegSeNm;
/** 저당수 */
@JsonProperty("MRTG_CNT")
private String mrtgCnt;
/** 압류건수 */
@JsonProperty("SZR_CNT")
private String szrCnt;
/** 구조변경수 */
@JsonProperty("STRCT_CHG_CNT")
private String strctChgCnt;
/** 사용본거지주소 */
@JsonProperty("USGSRHLD_ADDR_1")
private String usgsrhldAddr1;
/** 사용본거지주소상세 */
@JsonProperty("USGSRHLD_ADDR_DTL_1")
private String usgsrhldAddrDtl1;
/** 소유자주소 */
@JsonProperty("OWNR_ADDR")
private String ownrAddr;
/** 소유자주소상세 */
@JsonProperty("OWNR_ADDR_DTL")
private String ownrAddrDtl;
/** 개인사업자여부 */
@JsonProperty("INDVDL_BZMN_YN")
private String indvdlBzmnYn;
/** 대표소유자전화번호 */
@JsonProperty("RPRS_OWNR_TELNO")
private String rprsOwnrTelno;
/** 대표소유자성명 */
@JsonProperty("RPRS_OWNR_NM")
private String rprsOwnrNm;
/** 대표소유자회원구분코드 */
@JsonProperty("RPRS_OWNR_MBR_SE_CD")
private String rprsOwnrMbrSeCd;
/** 대표소유자회원번호 */
@JsonProperty("RPRSV_OWNR_IDECNO")
private String rprsvOwnrIdecno;
/** 비과세대상구분코드명 */
@JsonProperty("TAXXMPT_APLCN_SE_CD")
private String taxxmptAplcnSeCd;
/** 비과세대상구분코드 */
@JsonProperty("TAXXMPT_TRPR_SE_CD")
private String taxxmptTrprSeCd;
/** 특기사항건수 */
@JsonProperty("SPCABL_MTTR_CNT")
private String spcablMttrCnt;
/** 사용본거지행정동명 */
@JsonProperty("USGSRHLD_DONG_NM")
private String usgsrhldDongNm;
/** 예고수 */
@JsonProperty("PRVNTC_CNT")
private String prvntcCnt;
/** 수출이행여부신고일 */
@JsonProperty("XPORT_FLFL_YN_DCLR_YMD")
private String xportFlflYnDclrYmd;
/** 발급번호 */
@JsonProperty("ISSU_NO")
private String issuNo;
/** 최초양도일 */
@JsonProperty("FRST_TRNSFR_YMD")
private String frstTrnsfrYmd;
/** 구동축전지식별번호 */
@JsonProperty("DRIV_SRGBTRY_IDNTF_NO")
private String drivSrgbtryIdntfNo;
/** 처리불가사유코드 */
@JsonProperty("PROCESS_IMPRTY_RESN_CODE")
private String processImprtyResnCode;
/** 처리불가사유상세 */
@JsonProperty("PROCESS_IMPRTY_RESN_DTLS")
private String processImprtyResnDtls;
@JsonProperty("record")
private List<Record> record;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Schema(name = "NewLedgerRecord", description = "원부 변경내역 record (신버전)")
@Getter
@Setter
public static class Record {
/** 해제여부 */
@JsonProperty("SZR_RMV_DTL_SN")
private String szrRmvDtlSn;
/** 변경업무구분코드 */
@JsonProperty("CHG_TASK_SE_CD")
private String chgTaskSeCd;
/** 주번호 */
@JsonProperty("MAIN_NO")
private String mainNo;
/** 부번호 */
@JsonProperty("SNO")
private String sno;
/** 사항란 */
@JsonProperty("SPCABL_MTTR")
private String spcablMttr;
/** 세대주명 */
@JsonProperty("HSHLDR_NM")
private String hshldrNm;
/** 세대주개인암호화번호 */
@JsonProperty("HSHLDR_IDECNO")
private String hshldrIdecno;
/** 접수번호 */
@JsonProperty("APLY_RCPT_NO")
private String aplyRcptNo;
/** 차량관리번호 */
@JsonProperty("VHMNO")
private String vhmno;
/** 원부그룹번호 */
@JsonProperty("LEDGER_GROUP_NO")
private String ledgerGroupNo;
/** 원부개별번호 */
@JsonProperty("LEDGER_INDIV_NO")
private String ledgerIndivNo;
/** 변경업무구분명 */
@JsonProperty("CHG_TASK_SE_NM")
private String chgTaskSeNm;
/** 변경일자 */
@JsonProperty("CHG_YMD")
private String chgYmd;
/** 상세순번 */
@JsonProperty("DTL_SN")
private String dtlSn;
/** 표기여부 */
@JsonProperty("FLAG")
private String flag;
}
}

@ -1,56 +0,0 @@
package com.vmis.interfaceapp.service;
import com.vmis.interfaceapp.mapper.CarBassMatterInqireMapper;
import com.vmis.interfaceapp.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.setCarBassMatterInqireId(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.getCarBassMatterInqireId() == null) {
throw new IllegalArgumentException("자동차 기본 사항 조회 ID는 필수입니다.");
}
int result = carBassMatterInqireMapper.updateCarBassMatterInqire(response);
if (result != 1) {
throw new RuntimeException("자동차 기본 사항 조회 정보 업데이트 실패 - ID: " + response.getCarBassMatterInqireId());
}
log.info("[BASIC-RES-LOG] 응답/에러 정보 저장 완료(별도TX) - ID: {}, 결과코드: {}", response.getCarBassMatterInqireId(), response.getCntcResultCode());
}
}

@ -1,87 +0,0 @@
package com.vmis.interfaceapp.service;
import com.vmis.interfaceapp.client.GovernmentApi;
import com.vmis.interfaceapp.config.ApiConstant;
import com.vmis.interfaceapp.util.ExceptionDetailUtil;
import com.vmis.interfaceapp.model.basic.BasicRequest;
import com.vmis.interfaceapp.model.basic.BasicResponse;
import com.vmis.interfaceapp.model.basic.CarBassMatterInqireVO;
import com.vmis.interfaceapp.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()
.carBassMatterInqireId(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;
}
}
}

@ -1,61 +0,0 @@
package com.vmis.interfaceapp.service;
import com.vmis.interfaceapp.mapper.CarLedgerFrmbkMapper;
import com.vmis.interfaceapp.model.ledger.CarLedgerFrmbkDtlVO;
import com.vmis.interfaceapp.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);
}
}

@ -1,85 +0,0 @@
package com.vmis.interfaceapp.service;
import com.vmis.interfaceapp.client.GovernmentApi;
import com.vmis.interfaceapp.config.ApiConstant;
import com.vmis.interfaceapp.model.common.Envelope;
import com.vmis.interfaceapp.model.ledger.CarLedgerFrmbkDtlVO;
import com.vmis.interfaceapp.model.ledger.CarLedgerFrmbkVO;
import com.vmis.interfaceapp.model.ledger.LedgerRequest;
import com.vmis.interfaceapp.model.ledger.LedgerResponse;
import com.vmis.interfaceapp.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;
}
}
}

@ -1,88 +0,0 @@
package com.vmis.interfaceapp.service;
import com.vmis.interfaceapp.config.properties.VmisProperties;
import com.vmis.interfaceapp.model.basic.BasicRequest;
import com.vmis.interfaceapp.model.common.Envelope;
import com.vmis.interfaceapp.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
* - INQIRE_SE_CODE (automatic based on business rules)
* - Basic: VHRNO not null 3:, VIN not null 2:
* - Ledger: Always 1 ()
*/
@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.getSigunguCode());
req.setCntcInfoCode(cntc);
req.setChargerId(sys.getChargerId());
req.setChargerIp(sys.getChargerIp());
req.setChargerNm(sys.getChargerNm());
// 자동차 기본사항 조회 시 INQIRE_SE_CODE 자동 설정
// VHRNO(차량번호) not null → 3:자동차번호, VIN(차대번호) not null → 2:차대번호
if (req.getVhrno() != null && !req.getVhrno().trim().isEmpty()) {
req.setInqireSeCode("3");
} else if (req.getVin() != null && !req.getVin().trim().isEmpty()) {
req.setInqireSeCode("2");
}
}
log.debug("[ENRICH] basic: applied INFO_SYS_ID={}, INFO_SYS_IP={}, SIGUNGU_CODE={}, CNTC_INFO_CODE={}",
sys.getInfoSysId(), sys.getInfoSysIp(), sys.getSigunguCode(), 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.getSigunguCode());
req.setCntcInfoCode(cntc);
req.setChargerId(sys.getChargerId());
req.setChargerIp(sys.getChargerIp());
req.setChargerNm(sys.getChargerNm());
// 고정값 설정 (값이 없는 경우에만 설정)
if (req.getOnesInformationOpen() == null || req.getOnesInformationOpen().isEmpty()) {
req.setOnesInformationOpen("1"); // 개인정보공개 (소유자공개)
}
if (req.getRouteSeCode() == null || req.getRouteSeCode().isEmpty()) {
req.setRouteSeCode("3"); // 경로구분코드
}
if (req.getDetailExpression() == null || req.getDetailExpression().isEmpty()) {
req.setDetailExpression("1"); // 내역표시 (전체내역)
}
if (req.getInqireSeCode() == null || req.getInqireSeCode().isEmpty()) {
req.setInqireSeCode("1"); // 조회구분코드 (열람)
}
}
log.debug("[ENRICH] ledger: applied INFO_SYS_ID={}, INFO_SYS_IP={}, SIGUNGU_CODE={}, CNTC_INFO_CODE={}",
sys.getInfoSysId(), sys.getInfoSysIp(), sys.getSigunguCode(), cntc);
}
}

@ -0,0 +1,104 @@
package com.vmis.interfaceapp.service;
import com.vmis.interfaceapp.client.GovernmentApi;
import com.vmis.interfaceapp.model.basic.NewBasicRequest;
import com.vmis.interfaceapp.model.basic.NewBasicResponse;
import com.vmis.interfaceapp.model.basic.OldBasicRequest;
import com.vmis.interfaceapp.model.basic.OldBasicResponse;
import com.vmis.interfaceapp.model.common.ApiResponse;
import com.vmis.interfaceapp.model.common.Envelope;
import com.vmis.interfaceapp.model.common.RequestEnricher;
import com.vmis.interfaceapp.model.ledger.NewLedgerRequest;
import com.vmis.interfaceapp.model.ledger.NewLedgerResponse;
import com.vmis.interfaceapp.util.MockDataLoader;
import com.vmis.interfaceapp.util.TxIdUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import java.util.Arrays;
/**
* () ()
* - , API ,
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class VehicleInterfaceService {
private final GovernmentApi governmentApi;
private final RequestEnricher enricher;
private final MockDataLoader mockDataLoader;
private final Environment environment;
public ResponseEntity<ApiResponse<OldBasicRequest, OldBasicResponse>> oldBasic(Envelope<OldBasicRequest> envelope) {
// 0) txId 생성 (25자리 숫자: yyyyMMddHHmmssSSS + 랜덤8자리)
String txId = TxIdUtil.generate();
// 1) 요청 보강
enricher.enrichOldBasic(envelope);
// 2) dev 프로파일일 때 Mock 데이터 반환
if (isDevProfile()) {
log.info("[DEV] Returning old-basic mock data instead of calling external API, txId={}", txId);
Envelope<OldBasicResponse> mockResponse = mockDataLoader.getRandomOldBasicMockData();
ApiResponse<OldBasicRequest, OldBasicResponse> apiResponse = ApiResponse.of(txId, envelope, mockResponse);
return ResponseEntity.status(HttpStatus.OK).body(apiResponse);
}
// 3) 외부 API 호출 (txId 전달)
ResponseEntity<Envelope<OldBasicResponse>> response = governmentApi.callOldBasic(txId, envelope);
// 4) 요청/응답 함께 반환
ApiResponse<OldBasicRequest, OldBasicResponse> apiResponse = ApiResponse.of(txId, envelope, response.getBody());
return ResponseEntity.status(response.getStatusCode()).body(apiResponse);
}
public ResponseEntity<ApiResponse<NewBasicRequest, NewBasicResponse>> newBasic(Envelope<NewBasicRequest> envelope) {
// 0) txId 생성 (25자리 숫자: yyyyMMddHHmmssSSS + 랜덤8자리)
String txId = TxIdUtil.generate();
// 1) 요청 보강
enricher.enrichNewBasic(envelope);
// 2) 외부 API 호출 (txId 전달)
ResponseEntity<Envelope<NewBasicResponse>> response = governmentApi.callNewBasic(txId, envelope);
// 3) 요청/응답 함께 반환
ApiResponse<NewBasicRequest, NewBasicResponse> apiResponse = ApiResponse.of(txId, envelope, response.getBody());
return ResponseEntity.status(response.getStatusCode()).body(apiResponse);
}
public ResponseEntity<ApiResponse<NewLedgerRequest, NewLedgerResponse>> newLedger(Envelope<NewLedgerRequest> envelope) {
// 0) txId 생성 (25자리 숫자: yyyyMMddHHmmssSSS + 랜덤8자리)
String txId = TxIdUtil.generate();
// 1) 요청 보강
enricher.enrichNewLedger(envelope);
// 2) dev 프로파일일 때 Mock 데이터 반환
if (isDevProfile()) {
log.info("[DEV] Returning new-ledger mock data instead of calling external API, txId={}", txId);
Envelope<NewLedgerResponse> mockResponse = mockDataLoader.getRandomNewLedgerMockData();
ApiResponse<NewLedgerRequest, NewLedgerResponse> apiResponse = ApiResponse.of(txId, envelope, mockResponse);
return ResponseEntity.status(HttpStatus.OK).body(apiResponse);
}
// 3) 외부 API 호출 (txId 전달)
ResponseEntity<Envelope<NewLedgerResponse>> response = governmentApi.callNewLedger(txId, envelope);
// 4) 요청/응답 함께 반환
ApiResponse<NewLedgerRequest, NewLedgerResponse> apiResponse = ApiResponse.of(txId, envelope, response.getBody());
return ResponseEntity.status(response.getStatusCode()).body(apiResponse);
}
/**
* dev
*/
private boolean isDevProfile() {
return Arrays.asList(environment.getActiveProfiles()).contains("dev");
}
}

@ -1,6 +1,6 @@
package com.vmis.interfaceapp.util;
import com.vmis.interfaceapp.config.properties.VmisProperties;
import com.vmis.interfaceapp.config.properties.GpkiProperties;
import lombok.Getter;
import lombok.Setter;
@ -29,7 +29,7 @@ public class GpkiCryptoUtil {
private NewGpkiUtil delegate;
public static GpkiCryptoUtil from(VmisProperties.GpkiProps props) throws Exception {
public static GpkiCryptoUtil from(GpkiProperties props) throws Exception {
GpkiCryptoUtil util = new GpkiCryptoUtil();
util.setGpkiLicPath(props.getGpkiLicPath());
util.setLdap(props.getLdap());
@ -90,6 +90,33 @@ public class GpkiCryptoUtil {
return new String(data, charset);
}
/**
* Encrypt plain text with target server public key, then optionally sign the encrypted bytes,
* then Base64-encode the resulting payload exactly matching the sample implementation order.
*
* Order: encrypt(bytes) (useSign? sign(encryptedBytes) : encryptedBytes) encode(Base64)
*/
public String encryptThenSignToBase64(String plain, String targetServerId, String charset, boolean useSign) throws Exception {
ensureInit();
byte[] encrypted = delegate.encrypt(plain.getBytes(charset), targetServerId, true);
byte[] payload = useSign ? delegate.sign(encrypted) : encrypted;
return delegate.encode(payload);
}
/**
* Decode Base64, then validate signature (when enabled), then decrypt, then convert to string with charset.
*
* <p> :
* Base64 decode (useSign=true) validate decrypt new String(dec, charset)</p>
*/
public String decodeValidateThenDecrypt(String base64, String charset, boolean useSign) throws Exception {
ensureInit();
byte[] decoded = delegate.decode(base64);
byte[] encrypted = useSign ? delegate.validate(decoded) : decoded;
byte[] plain = delegate.decrypt(encrypted);
return new String(plain, charset);
}
private void ensureInit() {
if (delegate == null) {
throw new IllegalStateException("GpkiCryptoUtil is not initialized. Call initialize() or from(props).");

@ -0,0 +1,141 @@
package com.vmis.interfaceapp.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vmis.interfaceapp.model.basic.OldBasicResponse;
import com.vmis.interfaceapp.model.common.Envelope;
import com.vmis.interfaceapp.model.ledger.NewLedgerResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Mock
* dev API Mock
*/
@Slf4j
@Component
public class MockDataLoader {
private final ObjectMapper objectMapper;
private final ResourcePatternResolver resourcePatternResolver;
private final Random random;
private final List<Envelope<OldBasicResponse>> oldBasicMockData = new ArrayList<>();
private final List<Envelope<NewLedgerResponse>> newLedgerMockData = new ArrayList<>();
public MockDataLoader(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
this.random = new Random();
loadMockData();
}
/**
* Mock
*/
private void loadMockData() {
try {
// old-basic Mock 데이터 로딩
loadOldBasicMockData();
log.info("Loaded {} old-basic mock data files", oldBasicMockData.size());
// new-ledger Mock 데이터 로딩
loadNewLedgerMockData();
log.info("Loaded {} new-ledger mock data files", newLedgerMockData.size());
} catch (Exception e) {
log.error("Failed to load mock data", e);
}
}
/**
* old-basic Mock
*/
private void loadOldBasicMockData() throws IOException {
Resource[] resources = resourcePatternResolver.getResources("classpath:mock/old-basic/*.json");
for (Resource resource : resources) {
try {
Envelope<OldBasicResponse> envelope = objectMapper.readValue(
resource.getInputStream(),
objectMapper.getTypeFactory().constructParametricType(
Envelope.class,
OldBasicResponse.class
)
);
oldBasicMockData.add(envelope);
log.debug("Loaded old-basic mock data: {}", resource.getFilename());
} catch (Exception e) {
log.error("Failed to load old-basic mock data from {}", resource.getFilename(), e);
}
}
}
/**
* new-ledger Mock
*/
private void loadNewLedgerMockData() throws IOException {
Resource[] resources = resourcePatternResolver.getResources("classpath:mock/new-ledger/*.json");
for (Resource resource : resources) {
try {
Envelope<NewLedgerResponse> envelope = objectMapper.readValue(
resource.getInputStream(),
objectMapper.getTypeFactory().constructParametricType(
Envelope.class,
NewLedgerResponse.class
)
);
newLedgerMockData.add(envelope);
log.debug("Loaded new-ledger mock data: {}", resource.getFilename());
} catch (Exception e) {
log.error("Failed to load new-ledger mock data from {}", resource.getFilename(), e);
}
}
}
/**
* old-basic Mock
*/
public Envelope<OldBasicResponse> getRandomOldBasicMockData() {
if (oldBasicMockData.isEmpty()) {
log.warn("No old-basic mock data available");
return new Envelope<>();
}
Envelope<OldBasicResponse> mockData = oldBasicMockData.get(random.nextInt(oldBasicMockData.size()));
log.info("Returning old-basic mock data");
return mockData;
}
/**
* new-ledger Mock
*/
public Envelope<NewLedgerResponse> getRandomNewLedgerMockData() {
if (newLedgerMockData.isEmpty()) {
log.warn("No new-ledger mock data available");
return new Envelope<>();
}
Envelope<NewLedgerResponse> mockData = newLedgerMockData.get(random.nextInt(newLedgerMockData.size()));
log.info("Returning new-ledger mock data");
return mockData;
}
/**
* old-basic Mock
*/
public List<Envelope<OldBasicResponse>> getAllOldBasicMockData() {
return new ArrayList<>(oldBasicMockData);
}
/**
* new-ledger Mock
*/
public List<Envelope<NewLedgerResponse>> getAllNewLedgerMockData() {
return new ArrayList<>(newLedgerMockData);
}
}

@ -1,17 +1,16 @@
package com.vmis.interfaceapp.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 com.gpki.gpkiapi_jni;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class NewGpkiUtil {
byte[] myEnvCert, myEnvKey, mySigCert, mySigKey;

@ -10,9 +10,12 @@ public final class TxIdUtil {
private TxIdUtil() {}
/*
* yyyyMMddHHmmssSSS+8
* */
public static String generate() {
String time = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.KOREA).format(new Date());
int random = 100000 + RANDOM.nextInt(900000);
return time + "_" + random;
int random = 10000000 + RANDOM.nextInt(90000000);
return time + random;
}
}

@ -56,45 +56,73 @@ logging:
org.springframework: INFO # Spring Framework 로거 레벨
org.springframework.web: DEBUG # Spring Web 로거 레벨 - HTTP 요청/응답 로그 포함
# 인터페이스 및 연계 설정 - 개발(DEV) 환경
vmis:
system:
infoSysId: "41-345" # 정보시스템 ID
infoSysIp: "105.19.10.135" # 정보시스템 IP 주소
sigunguCode: "41460" # 시군구 코드 (경기도 용인시)
departmentCode: "" # 부서 코드
chargerId: "" # 담당자 ID
chargerIp: "" # 담당자 IP 주소
chargerNm: "" # 담당자 이름
gpki:
enabled: "N" # GPKI 사용 여부 (개발환경에서는 비활성화)
useSign: true # 서명 사용 여부
charset: "UTF-8" # 문자셋 인코딩
certServerId: "SVR5640020001" # 인증서 서버 ID (요청 시스템)
targetServerId: "SVR1611000006" # 대상 서버 ID (차세대교통안전공단)
ldap: true # LDAP 사용 여부
gpkiLicPath: "C:\\GPKI\\VMIS-Lic" # GPKI 라이선스 파일 경로
certFilePath: "c:\\GPKI\\Certificate\\class1" # 인증서 파일 디렉토리 경로
envCertFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_env.cer" # 암호화용 인증서 파일 경로
envPrivateKeyFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_env.key" # 암호화용 개인키 파일 경로
envPrivateKeyPasswd: "*sbm204221" # 암호화용 개인키 비밀번호
sigCertFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_sig.cer" # 서명용 인증서 파일 경로
sigPrivateKeyFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_sig.key" # 서명용 개인키 파일 경로
sigPrivateKeyPasswd: "*sbm204221" # 서명용 개인키 비밀번호
gov:
scheme: "http" # 프로토콜 (http/https)
host: "10.188.225.94:29001" # 개발(DEV) 행정망 호스트 및 포트
basePath: "/piss/api/molit" # API 기본 경로
connectTimeoutMillis: 5000 # 연결 타임아웃 (밀리초) - 5초
readTimeoutMillis: 10000 # 읽기 타임아웃 (밀리초) - 10초
services:
basic: # 시군구연계 자동차기본사항조회 서비스
path: "/SignguCarBassMatterInqireService" # 서비스 경로
cntcInfoCode: "AC1_FD11_01" # 연계정보코드
apiKey: "05e8d748fb366a0831dce71a32424460746a72d591cf483ccc130534dd51e394" # API 인증키
cvmisApikey: "014F9215-B6D9A3B6-4CED5225-68408C46" # CVMIS API 키
ledger: # 시군구연계 자동차등록원부(갑) 서비스
path: "/SignguCarLedgerFrmbkService" # 서비스 경로
cntcInfoCode: "AC1_FD11_02" # 연계정보코드
apiKey: "1beeb01857c2e7e9b41c002b007ccb9754d9c272f66d4bb64fc45b302c69e529" # API 인증키
cvmisApikey: "63DF159B-7B9C64C5-86CCB15C-5F93E750" # CVMIS API 키
# HTTP 클라이언트 설정 - 공통
http:
connectTimeoutMillis: 5000 # 연결 타임아웃 (밀리초) - 5초
readTimeoutMillis: 10000 # 읽기 타임아웃 (밀리초) - 10초
# GPKI 설정 - 공통
gpki:
enabled: "N" # GPKI 사용 여부 (개발환경에서는 비활성화)
useSign: true # 서명 사용 여부
charset: "UTF-8" # 문자셋 인코딩
certServerId: "SVR5640020001" # 인증서 서버 ID (요청 시스템)
targetServerId: "SVR1611000006" # 대상 서버 ID (차세대교통안전공단)
ldap: true # LDAP 사용 여부
gpkiLicPath: "C:\\GPKI\\VMIS-Lic" # GPKI 라이선스 파일 경로
certFilePath: "c:\\GPKI\\Certificate\\class1" # 인증서 파일 디렉토리 경로
envCertFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_env.cer" # 암호화용 인증서 파일 경로
envPrivateKeyFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_env.key" # 암호화용 개인키 파일 경로
envPrivateKeyPasswd: "*sbm204221" # 암호화용 개인키 비밀번호
sigCertFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_sig.cer" # 서명용 인증서 파일 경로
sigPrivateKeyFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_sig.key" # 서명용 개인키 파일 경로
sigPrivateKeyPasswd: "*sbm204221" # 서명용 개인키 비밀번호
# 구버전 인터페이스 및 연계 설정 - 개발(DEV) 환경
old:
vmis:
system:
infoSysId: "41-345" # 정보시스템 ID
infoSysIp: "105.19.10.135" # 정보시스템 IP 주소
sigunguCode: "41460" # 시군구 코드 (경기도 용인시)
departmentCode: "" # 부서 코드
chargerId: "" # 담당자 ID
chargerIp: "" # 담당자 IP 주소
chargerNm: "" # 담당자 이름
gov:
scheme: "http" # 프로토콜 (http/https)
host: "10.188.225.94:29001" # 개발(DEV) 행정망 호스트 및 포트
basePath: "/piss/api/molit" # API 기본 경로
services:
basic: # 시군구연계 자동차기본사항조회 서비스 (구버전)
path: "/SignguCarBassMatterInqireService" # 서비스 경로
cntcInfoCode: "AC1_FD11_01" # 연계정보코드
apiKey: "05e8d748fb366a0831dce71a32424460746a72d591cf483ccc130534dd51e394" # API 인증키
cvmisApikey: "014F9215-B6D9A3B6-4CED5225-68408C46" # CVMIS API 키
# 시군구연계 자동차등록원부(갑) 서비스 (구버전, 없음.)
# 신버전 인터페이스 및 연계 설정 - 개발(DEV) 환경
new:
vmis:
system:
infoSysId: "41-345" # 정보시스템 ID
infoSysIpAddr: "105.19.10.135" # 정보시스템 IP 주소
sggCd: "41460" # 시군구 코드 (경기도 용인시)
picId: "" # 담당자 ID
picIpAddr: "" # 담당자 IP 주소
picNm: "" # 담당자 이름
gov:
scheme: "http" # 프로토콜 (http/https)
host: "10.188.225.94:29001" # 개발(DEV) 행정망 호스트 및 포트
basePath: "/piss/api/molit" # API 기본 경로
services:
basic: # 시군구연계 자동차기본사항조회 서비스 (신규버전)
path: "/SignguCarBassMatterInqireService" # 서비스 경로
linkInfoCd: "AC1_FD11_01" # 연계정보코드
apiKey: "05e8d748fb366a0831dce71a32424460746a72d591cf483ccc130534dd51e394" # API 인증키
cvmisApikey: "014F9215-B6D9A3B6-4CED5225-68408C46" # CVMIS API 키
ledger: # 시군구연계 자동차등록원부(갑) 서비스 (신규버전)
path: "/SignguCarLedgerFrmbkService" # 서비스 경로
linkInfoCd: "AC1_FD11_02" # 연계정보코드
apiKey: "1beeb01857c2e7e9b41c002b007ccb9754d9c272f66d4bb64fc45b302c69e529" # API 인증키
cvmisApikey: "63DF159B-7B9C64C5-86CCB15C-5F93E750" # CVMIS API 키

@ -2,44 +2,6 @@
server:
port: 18080 # 애플리케이션 구동 포트 (운영환경)
spring:
# DataSource 설정 - MariaDB
datasource:
driver-class-name: org.mariadb.jdbc.Driver # MariaDB JDBC 드라이버
url: jdbc:mariadb://211.119.124.117:53306/vips?characterEncoding=UTF-8&allowMultiQueries=true # DB 접속 URL (UTF-8 인코딩, 다중쿼리 허용)
username: # DB 접속 사용자명
password: # DB 접속 비밀번호
hikari:
# 커넥션 풀 크기 설정 (4코어 32GB 서버 기준)
# 동시에 사용할 수 있는 최대 커넥션 수
# 권장값: (코어수 × 2) + (동시사용자 × 0.1) = (4 × 2) + (300 × 0.1) = 38 → 40
maximum-pool-size: 40
# 풀에서 유지할 최소 유휴 커넥션 수
# 권장값: maximum-pool-size의 25% (40 × 0.25 = 10)
minimum-idle: 10
# 커넥션을 얻기 위한 최대 대기 시간 (밀리초)
# 권장값: 30초 - 네트워크 지연이나 데이터베이스 부하 시 적절한 대기 시간
connection-timeout: 30000
# 커넥션 유효성 검사 타임아웃 (밀리초)
validation-timeout: 60000
# 커넥션의 최대 생명 시간 (밀리초)
# 권장값: 30분 - 데이터베이스 연결이 너무 오래 유지되지 않도록 제한
max-lifetime: 1800000
# 유휴 커넥션을 제거하기 위한 최소 대기 시간 (밀리초)
# 권장값: 10분 - 메모리 절약과 커넥션 재사용의 균형점
idle-timeout: 600000
# auto-commit을 false로 설정하여 명시적 트랜잭션 관리
auto-commit: false
# MyBatis 설정
mybatis:
# MyBatis 전역 설정 파일 위치
config-location: classpath:mybatis/mybatis-config.xml
# Mapper XML 파일 위치 (DbType 변수 사용)
mapper-locations: classpath:mybatis/mapper/**/*_${Globals.DbType}.xml
# 타입 별칭 패키지 (하위 패키지 자동 스캔)
type-aliases-package: com.vmis.interfaceapp.model
# 로그 설정 - 운영(PRD) 환경
logging:
config: classpath:logback-spring.xml # Logback 설정 파일 위치
@ -56,46 +18,73 @@ logging:
org.springframework: WARN # Spring Framework 로거 레벨 - 경고 이상만 출력
org.springframework.web: INFO # Spring Web 로거 레벨 - 요청/응답 기본 정보 출력
# 인터페이스 및 연계 설정 - 운영(PRD) 환경
# 주의: 실제 운영 키/호스트는 배포 환경 변수나 외부 설정(Secret)로 주입 권장
vmis:
system:
infoSysId: "41-345" # 정보시스템 ID
infoSysIp: "105.19.10.135" # 정보시스템 IP 주소
sigunguCode: "41460" # 시군구 코드 (경기도 용인시)
departmentCode: "" # 부서 코드
chargerId: "" # 담당자 ID
chargerIp: "" # 담당자 IP 주소
chargerNm: "" # 담당자 이름
gpki:
enabled: "Y" # GPKI 사용 여부 (운영환경에서는 활성화)
useSign: true # 서명 사용 여부
charset: "UTF-8" # 문자셋 인코딩
certServerId: "SVR5640020001" # 인증서 서버 ID (요청 시스템)
targetServerId: "SVR1611000006" # 대상 서버 ID (차세대교통안전공단)
ldap: true # LDAP 사용 여부
gpkiLicPath: "C:\\GPKI\\VMIS-Lic" # GPKI 라이선스 파일 경로
certFilePath: "c:\\GPKI\\Certificate\\class1" # 인증서 파일 디렉토리 경로
envCertFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_env.cer" # 암호화용 인증서 파일 경로
envPrivateKeyFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_env.key" # 암호화용 개인키 파일 경로
envPrivateKeyPasswd: "*sbm204221" # 암호화용 개인키 비밀번호
sigCertFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_sig.cer" # 서명용 인증서 파일 경로
sigPrivateKeyFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_sig.key" # 서명용 개인키 파일 경로
sigPrivateKeyPasswd: "*sbm204221" # 서명용 개인키 비밀번호
gov:
scheme: "http" # 프로토콜 (http/https)
host: "10.188.225.25:29001" # 운영(PRD) 행정망 호스트 및 포트 (명세에 맞춰 수정)
basePath: "/piss/api/molit" # API 기본 경로
connectTimeoutMillis: 5000 # 연결 타임아웃 (밀리초) - 5초
readTimeoutMillis: 10000 # 읽기 타임아웃 (밀리초) - 10초
services:
basic: # 시군구연계 자동차기본사항조회 서비스
path: "/SignguCarBassMatterInqireService" # 서비스 경로
cntcInfoCode: "AC1_FD11_01" # 연계정보코드
apiKey: "05e8d748fb366a0831dce71a32424460746a72d591cf483ccc130534dd51e394" # API 인증키
cvmisApikey: "014F9215-B6D9A3B6-4CED5225-68408C46" # CVMIS API 키
ledger: # 시군구연계 자동차등록원부(갑) 서비스
path: "/SignguCarLedgerFrmbkService" # 서비스 경로
cntcInfoCode: "AC1_FD11_02" # 연계정보코드
apiKey: "1beeb01857c2e7e9b41c002b007ccb9754d9c272f66d4bb64fc45b302c69e529" # API 인증키
cvmisApikey: "63DF159B-7B9C64C5-86CCB15C-5F93E750" # CVMIS API 키
# HTTP 클라이언트 설정 - 공통
http:
connectTimeoutMillis: 5000 # 연결 타임아웃 (밀리초) - 5초
readTimeoutMillis: 10000 # 읽기 타임아웃 (밀리초) - 10초
# GPKI 설정 - 공통
gpki:
enabled: "Y" # GPKI 사용 여부 (운영환경에서는 활성화)
useSign: true # 서명 사용 여부
charset: "UTF-8" # 문자셋 인코딩
certServerId: "SVR5640020001" # 인증서 서버 ID (요청 시스템)
targetServerId: "SVR1611000006" # 대상 서버 ID (차세대교통안전공단)
ldap: true # LDAP 사용 여부
gpkiLicPath: "C:\\GPKI\\VMIS-Lic" # GPKI 라이선스 파일 경로
certFilePath: "c:\\GPKI\\Certificate\\class1" # 인증서 파일 디렉토리 경로
envCertFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_env.cer" # 암호화용 인증서 파일 경로
envPrivateKeyFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_env.key" # 암호화용 개인키 파일 경로
envPrivateKeyPasswd: "*sbm204221" # 암호화용 개인키 비밀번호
sigCertFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_sig.cer" # 서명용 인증서 파일 경로
sigPrivateKeyFilePathName: "c:\\GPKI\\Certificate\\class1\\SVR5640020001_sig.key" # 서명용 개인키 파일 경로
sigPrivateKeyPasswd: "*sbm204221" # 서명용 개인키 비밀번호
# 구버전 인터페이스 및 연계 설정 - 운영(PRD) 환경
old:
vmis:
system:
infoSysId: "41-345" # 정보시스템 ID
infoSysIp: "105.19.10.135" # 정보시스템 IP 주소
sigunguCode: "41460" # 시군구 코드 (경기도 용인시)
departmentCode: "" # 부서 코드
chargerId: "" # 담당자 ID
chargerIp: "" # 담당자 IP 주소
chargerNm: "" # 담당자 이름
gov:
scheme: "http" # 프로토콜 (http/https)
host: "10.188.225.25:29001" # 운영(PRD) 행정망 호스트 및 포트 (명세에 맞춰 수정)
basePath: "/piss/api/molit" # API 기본 경로
services:
basic: # 시군구연계 자동차기본사항조회 서비스 (구버전)
path: "/SignguCarBassMatterInqireService" # 서비스 경로
cntcInfoCode: "AC1_FD11_01" # 연계정보코드
apiKey: "05e8d748fb366a0831dce71a32424460746a72d591cf483ccc130534dd51e394" # API 인증키
cvmisApikey: "014F9215-B6D9A3B6-4CED5225-68408C46" # CVMIS API 키
# 시군구연계 자동차등록원부(갑) 서비스 (구버전, 없음.)
# 신버전 인터페이스 및 연계 설정 - 운영(PRD) 환경
new:
vmis:
system:
infoSysId: "41-345" # 정보시스템 ID
infoSysIpAddr: "105.19.10.135" # 정보시스템 IP 주소
sggCd: "41460" # 시군구 코드 (경기도 용인시)
picId: "" # 담당자 ID
picIpAddr: "" # 담당자 IP 주소
picNm: "" # 담당자 이름
gov:
scheme: "http" # 프로토콜 (http/https)
host: "10.188.225.25:29001" # 운영(PRD) 행정망 호스트 및 포트
basePath: "/piss/api/molit" # API 기본 경로
services:
basic: # 시군구연계 자동차기본사항조회 서비스 (신규버전)
path: "/SignguCarBassMatterInqireService" # 서비스 경로
linkInfoCd: "AC1_FD11_01" # 연계정보코드
apiKey: "05e8d748fb366a0831dce71a32424460746a72d591cf483ccc130534dd51e394" # API 인증키
cvmisApikey: "014F9215-B6D9A3B6-4CED5225-68408C46" # CVMIS API 키
ledger: # 시군구연계 자동차등록원부(갑) 서비스 (신규버전)
path: "/SignguCarLedgerFrmbkService" # 서비스 경로
linkInfoCd: "AC1_FD11_02" # 연계정보코드
apiKey: "1beeb01857c2e7e9b41c002b007ccb9754d9c272f66d4bb64fc45b302c69e529" # API 인증키
cvmisApikey: "63DF159B-7B9C64C5-86CCB15C-5F93E750" # CVMIS API 키

@ -0,0 +1,67 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "123456",
"LEDGER_INDIV_NO": "01",
"VHMNO": "A1234567890123",
"VHRNO": "12가1234",
"VIN": "KMHXX00XXXX000001",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "소나타",
"COLOR_CD": "W",
"COLOR_NM": "흰색",
"NOPLT_SPCFCT_CD": "1",
"NOPLT_SPCFCT_NM": "일반",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "SMARTSTREAM G2.0",
"FOM_NM": "DN8",
"ACQS_AMT": "25000000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20200315",
"VEAG_END_YMD": "20300314",
"YRIDNW": "2020",
"SPMNNO_1": "202",
"SPMNNO_2": "00012340000000",
"FBCTN_YMD": "20200201",
"DRVNG_DSTNC": "45000",
"INSP_VLD_PD_BGNG_YMD": "20240101",
"INSP_VLD_PD_END_YMD": "20261231",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2020-0315-001234",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "서울특별시 강남구 테헤란로 123",
"USGSRHLD_ADDR_DTL_1": "",
"OWNR_ADDR": "서울특별시 강남구 테헤란로 123",
"OWNR_ADDR_DTL": "",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-1234-5678",
"RPRS_OWNR_NM": "홍길동",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "8801011234567",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "0",
"USGSRHLD_DONG_NM": "역삼동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-001",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": []
}]
}

@ -0,0 +1,67 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "234567",
"LEDGER_INDIV_NO": "01",
"VHMNO": "B2345678901234",
"VHRNO": "34나5678",
"VIN": "KMHXX00XXXX000002",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "그랜저",
"COLOR_CD": "B",
"COLOR_NM": "검정",
"NOPLT_SPCFCT_CD": "1",
"NOPLT_SPCFCT_NM": "일반",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "SMARTSTREAM G2.5",
"FOM_NM": "GN7",
"ACQS_AMT": "35000000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20210520",
"VEAG_END_YMD": "20310519",
"YRIDNW": "2021",
"SPMNNO_1": "202",
"SPMNNO_2": "10023450000000",
"FBCTN_YMD": "20210410",
"DRVNG_DSTNC": "32000",
"INSP_VLD_PD_BGNG_YMD": "20240301",
"INSP_VLD_PD_END_YMD": "20270228",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2021-0520-002345",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "경기도 성남시 분당구 판교역로 235",
"USGSRHLD_ADDR_DTL_1": "101동 1002호",
"OWNR_ADDR": "경기도 성남시 분당구 판교역로 235",
"OWNR_ADDR_DTL": "101동 1002호",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-2345-6789",
"RPRS_OWNR_NM": "김철수",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "8502152345678",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "0",
"USGSRHLD_DONG_NM": "삼평동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-002",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": []
}]
}

@ -0,0 +1,80 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "345678",
"LEDGER_INDIV_NO": "01",
"VHMNO": "C3456789012345",
"VHRNO": "56다7890",
"VIN": "KMHXX00XXXX000003",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "아반떼",
"COLOR_CD": "S",
"COLOR_NM": "은색",
"NOPLT_SPCFCT_CD": "1",
"NOPLT_SPCFCT_NM": "일반",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "SMARTSTREAM G1.6",
"FOM_NM": "AD",
"ACQS_AMT": "18000000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20190801",
"VEAG_END_YMD": "20290731",
"YRIDNW": "2019",
"SPMNNO_1": "201",
"SPMNNO_2": "90034560000000",
"FBCTN_YMD": "20190620",
"DRVNG_DSTNC": "78000",
"INSP_VLD_PD_BGNG_YMD": "20230801",
"INSP_VLD_PD_END_YMD": "20250731",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2019-0801-003456",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "1",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "부산광역시 해운대구 센텀중앙로 79",
"USGSRHLD_ADDR_DTL_1": "센텀아파트 205동 1505호",
"OWNR_ADDR": "부산광역시 해운대구 센텀중앙로 79",
"OWNR_ADDR_DTL": "센텀아파트 205동 1505호",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-3456-7890",
"RPRS_OWNR_NM": "이영희",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "9203253456789",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "1",
"USGSRHLD_DONG_NM": "우동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-003",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": [{
"CHG_TASK_SE_CD": "08",
"CHG_TASK_SE_NM": "저당권설정",
"MAIN_NO": "1",
"SNO": "0",
"SPCABL_MTTR": "KB국민은행 저당권설정",
"CHG_YMD": "20190805",
"DTL_SN": "1",
"FLAG": "Y",
"APLY_RCPT_NO": "2019-0805-001",
"VHMNO": "C3456789012345",
"LEDGER_GROUP_NO": "345678",
"LEDGER_INDIV_NO": "01"
}]
}]
}

@ -0,0 +1,67 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "456789",
"LEDGER_INDIV_NO": "01",
"VHMNO": "D4567890123456",
"VHRNO": "78라1122",
"VIN": "KMHXX00XXXX000004",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "투싼",
"COLOR_CD": "BL",
"COLOR_NM": "파란색",
"NOPLT_SPCFCT_CD": "1",
"NOPLT_SPCFCT_NM": "일반",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "SMARTSTREAM D2.0",
"FOM_NM": "NX4",
"ACQS_AMT": "32000000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20220410",
"VEAG_END_YMD": "20320409",
"YRIDNW": "2022",
"SPMNNO_1": "202",
"SPMNNO_2": "20045670000000",
"FBCTN_YMD": "20220301",
"DRVNG_DSTNC": "18000",
"INSP_VLD_PD_BGNG_YMD": "20240410",
"INSP_VLD_PD_END_YMD": "20280409",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2022-0410-004567",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "인천광역시 연수구 컨벤시아대로 165",
"USGSRHLD_ADDR_DTL_1": "",
"OWNR_ADDR": "인천광역시 연수구 컨벤시아대로 165",
"OWNR_ADDR_DTL": "",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-4567-8901",
"RPRS_OWNR_NM": "박민수",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "8904124567890",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "0",
"USGSRHLD_DONG_NM": "송도동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-004",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": []
}]
}

@ -0,0 +1,67 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "567890",
"LEDGER_INDIV_NO": "01",
"VHMNO": "E5678901234567",
"VHRNO": "90마3344",
"VIN": "KMHXX00XXXX000005",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "카니발",
"COLOR_CD": "PW",
"COLOR_NM": "백색진주",
"NOPLT_SPCFCT_CD": "2",
"NOPLT_SPCFCT_NM": "대형",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "SMARTSTREAM D2.2",
"FOM_NM": "KA4",
"ACQS_AMT": "42000000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20210915",
"VEAG_END_YMD": "20310914",
"YRIDNW": "2021",
"SPMNNO_1": "202",
"SPMNNO_2": "10056780000000",
"FBCTN_YMD": "20210805",
"DRVNG_DSTNC": "52000",
"INSP_VLD_PD_BGNG_YMD": "20230915",
"INSP_VLD_PD_END_YMD": "20270914",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2021-0915-005678",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "대전광역시 유성구 대학로 99",
"USGSRHLD_ADDR_DTL_1": "한빛아파트 301동 502호",
"OWNR_ADDR": "대전광역시 유성구 대학로 99",
"OWNR_ADDR_DTL": "한빛아파트 301동 502호",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-5678-9012",
"RPRS_OWNR_NM": "정수진",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "8705285678901",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "0",
"USGSRHLD_DONG_NM": "궁동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-005",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": []
}]
}

@ -0,0 +1,67 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "678901",
"LEDGER_INDIV_NO": "01",
"VHMNO": "F6789012345678",
"VHRNO": "23바5566",
"VIN": "KMHXX00XXXX000006",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "모닝",
"COLOR_CD": "R",
"COLOR_NM": "빨강",
"NOPLT_SPCFCT_CD": "1",
"NOPLT_SPCFCT_NM": "일반",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "KAPPA G1.0",
"FOM_NM": "TA",
"ACQS_AMT": "9500000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20180607",
"VEAG_END_YMD": "20280606",
"YRIDNW": "2018",
"SPMNNO_1": "201",
"SPMNNO_2": "80067890000000",
"FBCTN_YMD": "20180520",
"DRVNG_DSTNC": "95000",
"INSP_VLD_PD_BGNG_YMD": "20230607",
"INSP_VLD_PD_END_YMD": "20250606",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2018-0607-006789",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "광주광역시 서구 상무대로 1151",
"USGSRHLD_ADDR_DTL_1": "",
"OWNR_ADDR": "광주광역시 서구 상무대로 1151",
"OWNR_ADDR_DTL": "",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-6789-0123",
"RPRS_OWNR_NM": "최지우",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "9511156789012",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "0",
"USGSRHLD_DONG_NM": "치평동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-006",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": []
}]
}

@ -0,0 +1,67 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "789012",
"LEDGER_INDIV_NO": "01",
"VHMNO": "G7890123456789",
"VHRNO": "45사7788",
"VIN": "KMHXX00XXXX000007",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "스포티지",
"COLOR_CD": "GY",
"COLOR_NM": "회색",
"NOPLT_SPCFCT_CD": "1",
"NOPLT_SPCFCT_NM": "일반",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "SMARTSTREAM G1.6 TURBO HYBRID",
"FOM_NM": "NQ5",
"ACQS_AMT": "38000000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20230222",
"VEAG_END_YMD": "20330221",
"YRIDNW": "2023",
"SPMNNO_1": "202",
"SPMNNO_2": "30078900000000",
"FBCTN_YMD": "20230115",
"DRVNG_DSTNC": "8000",
"INSP_VLD_PD_BGNG_YMD": "20250222",
"INSP_VLD_PD_END_YMD": "20290221",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2023-0222-007890",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "울산광역시 남구 삼산로 258",
"USGSRHLD_ADDR_DTL_1": "삼산타워 15층",
"OWNR_ADDR": "울산광역시 남구 삼산로 258",
"OWNR_ADDR_DTL": "삼산타워 15층",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-7890-1234",
"RPRS_OWNR_NM": "강태양",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "9106227890123",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "0",
"USGSRHLD_DONG_NM": "삼산동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-007",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": []
}]
}

@ -0,0 +1,67 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "890123",
"LEDGER_INDIV_NO": "01",
"VHMNO": "H8901234567890",
"VHRNO": "67아9900",
"VIN": "KMHXX00XXXX000008",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "EV6",
"COLOR_CD": "GN",
"COLOR_NM": "녹색",
"NOPLT_SPCFCT_CD": "1",
"NOPLT_SPCFCT_NM": "일반",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "ELECTRIC MOTOR",
"FOM_NM": "CV",
"ACQS_AMT": "52000000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20221105",
"VEAG_END_YMD": "20321104",
"YRIDNW": "2022",
"SPMNNO_1": "202",
"SPMNNO_2": "20089010000000",
"FBCTN_YMD": "20221015",
"DRVNG_DSTNC": "25000",
"INSP_VLD_PD_BGNG_YMD": "20241105",
"INSP_VLD_PD_END_YMD": "20281104",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2022-1105-008901",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "세종특별자치시 한누리대로 2130",
"USGSRHLD_ADDR_DTL_1": "",
"OWNR_ADDR": "세종특별자치시 한누리대로 2130",
"OWNR_ADDR_DTL": "",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-8901-2345",
"RPRS_OWNR_NM": "송하나",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "8809198901234",
"TAXXMPT_APLCN_SE_CD": "02",
"TAXXMPT_TRPR_SE_CD": "02",
"SPCABL_MTTR_CNT": "0",
"USGSRHLD_DONG_NM": "나성동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-008",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "KM8901234567890123",
"record": []
}]
}

@ -0,0 +1,80 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "901234",
"LEDGER_INDIV_NO": "01",
"VHMNO": "I9012345678901",
"VHRNO": "89자1122",
"VIN": "KMHXX00XXXX000009",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "팰리세이드",
"COLOR_CD": "PW",
"COLOR_NM": "진주백",
"NOPLT_SPCFCT_CD": "2",
"NOPLT_SPCFCT_NM": "대형",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "SMARTSTREAM D3.0",
"FOM_NM": "LX2",
"ACQS_AMT": "48000000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20200718",
"VEAG_END_YMD": "20300717",
"YRIDNW": "2020",
"SPMNNO_1": "202",
"SPMNNO_2": "00090120000000",
"FBCTN_YMD": "20200605",
"DRVNG_DSTNC": "62000",
"INSP_VLD_PD_BGNG_YMD": "20240718",
"INSP_VLD_PD_END_YMD": "20260717",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2020-0718-009012",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "1",
"USGSRHLD_ADDR_1": "경기도 수원시 영통구 광교중앙로 140",
"USGSRHLD_ADDR_DTL_1": "광교비즈니스센터 701호",
"OWNR_ADDR": "경기도 수원시 영통구 광교중앙로 140",
"OWNR_ADDR_DTL": "광교비즈니스센터 701호",
"INDVDL_BZMN_YN": "Y",
"RPRS_OWNR_TELNO": "010-9012-3456",
"RPRS_OWNR_NM": "윤두리",
"RPRS_OWNR_MBR_SE_CD": "2",
"RPRSV_OWNR_IDECNO": "1208901234567",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "1",
"USGSRHLD_DONG_NM": "하동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-009",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": [{
"CHG_TASK_SE_CD": "12",
"CHG_TASK_SE_NM": "구조변경",
"MAIN_NO": "1",
"SNO": "0",
"SPCABL_MTTR": "좌석수 변경 (7인승 → 8인승)",
"CHG_YMD": "20210825",
"DTL_SN": "1",
"FLAG": "Y",
"APLY_RCPT_NO": "2021-0825-001",
"VHMNO": "I9012345678901",
"LEDGER_GROUP_NO": "901234",
"LEDGER_INDIV_NO": "01"
}]
}]
}

@ -0,0 +1,67 @@
{
"data": [{
"LINK_RSLT_CD": "00",
"LINK_RSLT_DTL": "정상처리",
"LEDGER_GROUP_NO": "012345",
"LEDGER_INDIV_NO": "01",
"VHMNO": "J0123456789012",
"VHRNO": "11차3344",
"VIN": "KMHXX00XXXX000010",
"CARMDL_ASORT_CD": "1",
"CARMDL_ASORT_NM": "승용자동차",
"ATMB_NM": "레이",
"COLOR_CD": "SB",
"COLOR_NM": "하늘색",
"NOPLT_SPCFCT_CD": "1",
"NOPLT_SPCFCT_NM": "일반",
"USG_SE_CD": "01",
"USG_SE_NM": "자가용",
"MTRS_FOM_NM": "KAPPA G1.0",
"FOM_NM": "TAM",
"ACQS_AMT": "13500000",
"REG_DTL_CD": "01",
"REG_DTL_NM": "신규등록",
"FRST_REG_YMD": "20190925",
"VEAG_END_YMD": "20290924",
"YRIDNW": "2019",
"SPMNNO_1": "201",
"SPMNNO_2": "90001230000000",
"FBCTN_YMD": "20190815",
"DRVNG_DSTNC": "71000",
"INSP_VLD_PD_BGNG_YMD": "20230925",
"INSP_VLD_PD_END_YMD": "20250924",
"CHCK_VLD_PD_BGNG_YMD": "",
"CHCK_VLD_PD_END_YMD": "",
"REG_APLY_SE_NM": "신규등록",
"FRST_REG_APLY_RCPT_NO": "2019-0925-000123",
"NOPLT_CSDY_AVTSMT_YMD": "",
"NOPLT_CSDY_YN": "N",
"BSS_USE_PD_YMD": "",
"OCTHT_ERSR_PRVNTC_AVTSMT_YMD": "",
"ERSR_REG_YMD": "",
"ERSR_REG_SE_CD": "",
"ERSR_REG_SE_NM": "",
"MRTG_CNT": "0",
"SZR_CNT": "0",
"STRCT_CHG_CNT": "0",
"USGSRHLD_ADDR_1": "강원도 춘천시 퇴계로 123",
"USGSRHLD_ADDR_DTL_1": "퇴계빌라 202호",
"OWNR_ADDR": "강원도 춘천시 퇴계로 123",
"OWNR_ADDR_DTL": "퇴계빌라 202호",
"INDVDL_BZMN_YN": "N",
"RPRS_OWNR_TELNO": "010-0123-4567",
"RPRS_OWNR_NM": "임세영",
"RPRS_OWNR_MBR_SE_CD": "1",
"RPRSV_OWNR_IDECNO": "9312010123456",
"TAXXMPT_APLCN_SE_CD": "",
"TAXXMPT_TRPR_SE_CD": "",
"SPCABL_MTTR_CNT": "0",
"USGSRHLD_DONG_NM": "퇴계동",
"PRVNTC_CNT": "0",
"XPORT_FLFL_YN_DCLR_YMD": "",
"ISSU_NO": "2024-010",
"FRST_TRNSFR_YMD": "",
"DRIV_SRGBTRY_IDNTF_NO": "",
"record": []
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "12가1234",
"VIN": "KMHXX00XXXX000001",
"CNM": "소나타",
"PRYE": "2020",
"REGIST_DE": "20200315",
"FRST_REGIST_DE": "20200315",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "소형",
"VHCTY_SE_NM": "일반형",
"DSPLVL": "1999",
"TKCAR_PSCAP_CO": "5",
"MBER_NM": "홍길동",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "서울특별시 강남구 테헤란로 123",
"OWNER_ADRES_FULL": "서울특별시 강남구 테헤란로 123",
"COLOR_NM": "흰색",
"INSPT_VALID_PD_BGNDE": "20240101",
"INSPT_VALID_PD_ENDDE": "20261231",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "45000",
"FOM_NM": "DN8",
"USE_FUEL_CODE": "G",
"ACQS_AMOUNT": "25000000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "34나5678",
"VIN": "KMHXX00XXXX000002",
"CNM": "그랜저",
"PRYE": "2021",
"REGIST_DE": "20210520",
"FRST_REGIST_DE": "20210520",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "중형",
"VHCTY_SE_NM": "일반형",
"DSPLVL": "2497",
"TKCAR_PSCAP_CO": "5",
"MBER_NM": "김철수",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "경기도 성남시 분당구 판교역로 235",
"OWNER_ADRES_FULL": "경기도 성남시 분당구 판교역로 235",
"COLOR_NM": "검정",
"INSPT_VALID_PD_BGNDE": "20240301",
"INSPT_VALID_PD_ENDDE": "20270228",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "32000",
"FOM_NM": "GN7",
"USE_FUEL_CODE": "G",
"ACQS_AMOUNT": "35000000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "56다7890",
"VIN": "KMHXX00XXXX000003",
"CNM": "아반떼",
"PRYE": "2019",
"REGIST_DE": "20190801",
"FRST_REGIST_DE": "20190801",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "소형",
"VHCTY_SE_NM": "일반형",
"DSPLVL": "1591",
"TKCAR_PSCAP_CO": "5",
"MBER_NM": "이영희",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "부산광역시 해운대구 센텀중앙로 79",
"OWNER_ADRES_FULL": "부산광역시 해운대구 센텀중앙로 79",
"COLOR_NM": "은색",
"INSPT_VALID_PD_BGNDE": "20230801",
"INSPT_VALID_PD_ENDDE": "20250731",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "78000",
"FOM_NM": "AD",
"USE_FUEL_CODE": "G",
"ACQS_AMOUNT": "18000000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "78라1122",
"VIN": "KMHXX00XXXX000004",
"CNM": "투싼",
"PRYE": "2022",
"REGIST_DE": "20220410",
"FRST_REGIST_DE": "20220410",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "소형",
"VHCTY_SE_NM": "다목적형",
"DSPLVL": "1999",
"TKCAR_PSCAP_CO": "5",
"MBER_NM": "박민수",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "인천광역시 연수구 컨벤시아대로 165",
"OWNER_ADRES_FULL": "인천광역시 연수구 컨벤시아대로 165",
"COLOR_NM": "파란색",
"INSPT_VALID_PD_BGNDE": "20240410",
"INSPT_VALID_PD_ENDDE": "20280409",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "18000",
"FOM_NM": "NX4",
"USE_FUEL_CODE": "D",
"ACQS_AMOUNT": "32000000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "90마3344",
"VIN": "KMHXX00XXXX000005",
"CNM": "카니발",
"PRYE": "2021",
"REGIST_DE": "20210915",
"FRST_REGIST_DE": "20210915",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "대형",
"VHCTY_SE_NM": "다목적형",
"DSPLVL": "2199",
"TKCAR_PSCAP_CO": "11",
"MBER_NM": "정수진",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "대전광역시 유성구 대학로 99",
"OWNER_ADRES_FULL": "대전광역시 유성구 대학로 99",
"COLOR_NM": "백색진주",
"INSPT_VALID_PD_BGNDE": "20230915",
"INSPT_VALID_PD_ENDDE": "20270914",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "52000",
"FOM_NM": "KA4",
"USE_FUEL_CODE": "D",
"ACQS_AMOUNT": "42000000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "23바5566",
"VIN": "KMHXX00XXXX000006",
"CNM": "모닝",
"PRYE": "2018",
"REGIST_DE": "20180607",
"FRST_REGIST_DE": "20180607",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "경형",
"VHCTY_SE_NM": "일반형",
"DSPLVL": "998",
"TKCAR_PSCAP_CO": "5",
"MBER_NM": "최지우",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "광주광역시 서구 상무대로 1151",
"OWNER_ADRES_FULL": "광주광역시 서구 상무대로 1151",
"COLOR_NM": "빨강",
"INSPT_VALID_PD_BGNDE": "20230607",
"INSPT_VALID_PD_ENDDE": "20250606",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "95000",
"FOM_NM": "TA",
"USE_FUEL_CODE": "G",
"ACQS_AMOUNT": "9500000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "45사7788",
"VIN": "KMHXX00XXXX000007",
"CNM": "스포티지",
"PRYE": "2023",
"REGIST_DE": "20230222",
"FRST_REGIST_DE": "20230222",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "중형",
"VHCTY_SE_NM": "다목적형",
"DSPLVL": "1598",
"TKCAR_PSCAP_CO": "5",
"MBER_NM": "강태양",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "울산광역시 남구 삼산로 258",
"OWNER_ADRES_FULL": "울산광역시 남구 삼산로 258",
"COLOR_NM": "회색",
"INSPT_VALID_PD_BGNDE": "20250222",
"INSPT_VALID_PD_ENDDE": "20290221",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "8000",
"FOM_NM": "NQ5",
"USE_FUEL_CODE": "H",
"ACQS_AMOUNT": "38000000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "67아9900",
"VIN": "KMHXX00XXXX000008",
"CNM": "EV6",
"PRYE": "2022",
"REGIST_DE": "20221105",
"FRST_REGIST_DE": "20221105",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "중형",
"VHCTY_SE_NM": "전기",
"DSPLVL": "0",
"TKCAR_PSCAP_CO": "5",
"MBER_NM": "송하나",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "세종특별자치시 한누리대로 2130",
"OWNER_ADRES_FULL": "세종특별자치시 한누리대로 2130",
"COLOR_NM": "녹색",
"INSPT_VALID_PD_BGNDE": "20241105",
"INSPT_VALID_PD_ENDDE": "20281104",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "25000",
"FOM_NM": "CV",
"USE_FUEL_CODE": "E",
"ACQS_AMOUNT": "52000000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "89자1122",
"VIN": "KMHXX00XXXX000009",
"CNM": "팰리세이드",
"PRYE": "2020",
"REGIST_DE": "20200718",
"FRST_REGIST_DE": "20200718",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "대형",
"VHCTY_SE_NM": "다목적형",
"DSPLVL": "3470",
"TKCAR_PSCAP_CO": "7",
"MBER_NM": "윤두리",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "경기도 수원시 영통구 광교중앙로 140",
"OWNER_ADRES_FULL": "경기도 수원시 영통구 광교중앙로 140",
"COLOR_NM": "진주백",
"INSPT_VALID_PD_BGNDE": "20240718",
"INSPT_VALID_PD_ENDDE": "20260717",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "62000",
"FOM_NM": "LX2",
"USE_FUEL_CODE": "D",
"ACQS_AMOUNT": "48000000"
}]
}]
}

@ -0,0 +1,31 @@
{
"data": [{
"CNTC_RESULT_CODE": "00",
"CNTC_RESULT_DTLS": "정상처리",
"record": [{
"VHRNO": "11차3344",
"VIN": "KMHXX00XXXX000010",
"CNM": "레이",
"PRYE": "2019",
"REGIST_DE": "20190925",
"FRST_REGIST_DE": "20190925",
"VHCTY_ASORT_NM": "승용자동차",
"VHCTY_TY_NM": "경형",
"VHCTY_SE_NM": "다목적형",
"DSPLVL": "998",
"TKCAR_PSCAP_CO": "4",
"MBER_NM": "임세영",
"MBER_SE_CODE": "1",
"USE_STRNGHLD_ADRES_NM": "강원도 춘천시 퇴계로 123",
"OWNER_ADRES_FULL": "강원도 춘천시 퇴계로 123",
"COLOR_NM": "하늘색",
"INSPT_VALID_PD_BGNDE": "20230925",
"INSPT_VALID_PD_ENDDE": "20250924",
"REGIST_DETAIL_CODE": "01",
"TRVL_DSTNC": "71000",
"FOM_NM": "TAM",
"USE_FUEL_CODE": "L",
"ACQS_AMOUNT": "13500000"
}]
}]
}

@ -1,148 +0,0 @@
<?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="com.vmis.interfaceapp.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_ID,
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 (
#{carBassMatterInqireId},
#{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="vhrno != null">VHRNO = #{vhrno},</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_ID = #{carBassMatterInqireId}
</update>
<!-- ID로 조회 -->
<select id="selectCarBassMatterInqireById" parameterType="String" resultType="CarBassMatterInqireVO">
SELECT *
FROM tb_car_bass_matter_inqire
WHERE CAR_BASS_MATTER_INQIRE_ID = #{carBassMatterInqireId}
</select>
</mapper>

@ -1,178 +0,0 @@
<?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="com.vmis.interfaceapp.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>

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 설정 -->
<settings>
<!-- 카멜케이스 자동 매핑 (DB의 snake_case를 Java의 camelCase로 자동 변환) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- JDBC 타입이 null일 때의 처리 (NULL 값 허용) -->
<setting name="jdbcTypeForNull" value="NULL"/>
<!-- 쿼리 실행 시 로그 출력 레벨 설정 -->
<setting name="logImpl" value="SLF4J"/>
<!-- 결과가 null일 때도 객체 생성 -->
<setting name="callSettersOnNulls" value="true"/>
<!-- 지연 로딩 설정 (필요시 변경) -->
<setting name="lazyLoadingEnabled" value="false"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 캐시 사용 설정 -->
<setting name="cacheEnabled" value="false"/>
<!-- 자동 생성 키 사용 -->
<setting name="useGeneratedKeys" value="true"/>
<!-- 디폴트 Executor 타입 (SIMPLE, REUSE, BATCH) -->
<setting name="defaultExecutorType" value="REUSE"/>
<!-- 타임아웃 설정 (초 단위, null이면 무제한) -->
<setting name="defaultStatementTimeout" value="25"/>
</settings>
<!-- 타입 별칭 설정 (선택사항) -->
<!-- type-aliases-package로 자동 스캔되므로 여기서는 생략 가능 -->
<typeAliases>
<!-- 필요시 추가 -->
</typeAliases>
<!-- 타입 핸들러 설정 (선택사항) -->
<typeHandlers>
<!-- 필요시 추가 -->
</typeHandlers>
</configuration>
Loading…
Cancel
Save