일단... 커밋...

main
박성영 2 weeks ago
parent 40100a8f37
commit 57eaef2809

@ -17,7 +17,7 @@
| 일자 | 변경 내용 | 비고 |
|------|----------|------|
| 2025-12-03 | 명의이전 케이스 추가 (내사종결, 날짜 수정 후 부과) | 상품용과 순수 명의이전 분리 |
| 2025-12-03 | 실제 소스 코드 기준으로 전면 재작성 | Javadoc과 실제 로직 차이 명시 |
| 2025-12-02 | 내사종결 로직 추가 및 상품용 조건 변경 | 명의이전 31일 이내 조건 추가 |
| 2025-01-XX | 상품용 로직 완전 변경 | API 호출 4단계, 소유자명 일치 확인 추가 |
@ -37,36 +37,125 @@
**처리상태코드**: `02` (상품용)
**메서드**: `checkProductUse()`
#### Javadoc 주석 내용
```java
/**
* 1. 상품용 검증 api-1번호출.소유자명.contains("상품용")
*
* API 호출 4단계:
* 1) 자동차기본정보(차량번호, 부과일자=검사일) → 차대번호, 소유자명
* 2) 자동차기본정보(차대번호, 부과일자=오늘일자) → 차량번호, 성명, 민원인주민번호, 민원인법정동코드
* 3) 자동차등록원부(갑)(차량번호, 성명, 민원인주민번호, 민원인법정동코드) → 갑부 상세 List
* 4) 자동차기본정보(차대번호, 부과일자=조건에 맞는 CHG_YMD) → 해당 시점의 소유자명
*
* 비교조건:
* - 1단계: 소유자명에 "상품용" 포함
* - 2단계: 갑부 상세에서 다음 조건을 만족하는 명의이전(코드 11) 레코드 찾기
* • CHG_YMD <= 검사종료일자
* • 현재 주석처리 CHG_YMD가 검사일 이전일자 중 가장 마지막 일자
* • CHG_TASK_SE_CD == "11" (명의이전)
* - 3단계: 4번 API 조회 소유자명 == 1번 API 조회 소유자명 -> 소유자 대표 회원 번호
* - 4단계 현재 주석처리 : CHG_YMD >= 유효기간만료일 && CHG_YMD <= 검사종료일자, 현재 주석처리
* CHG_YMD가 검사일 이전일자 중 가장 마지막 일자, 4단계 2개의 조건은 주석처리
*/
```
#### API 호출 순서
| 순서 | API | 입력 파라미터 | 출력 데이터 | 용도 |
|------|-----|--------------|-------------|------|
| 1 | 자동차기본정보 | `차량번호`, `부과일자=검사일` | `차대번호`, `소유자명` | 검사일 기준 소유자 확인 |
| 1 | 자동차기본정보 | `차량번호`, `부과일자=검사일` | `차대번호(vin)`, `소유자명(step1OwnerName)` | 검사일 기준 소유자 확인 |
| 2 | 자동차기본정보 | `1.차대번호`, `부과일자=오늘일자` | `차량번호`, `성명`, `민원인주민번호`, `민원인법정동코드` | 현재 소유자 정보 |
| 3 | 자동차등록원부(갑) | `2.차량번호`, `2.성명`, `2.민원인주민번호`, `2.민원인법정동코드` | 갑부 상세 List | 명의이전 이력 조회 |
| 4 | 자동차기본정보 | `1.차대번호`, `부과일자=조건에맞는CHG_YMD` | `소유자명` | 명의이전 시점 소유자 확인 |
| 4 | 자동차기본정보 | `1.차대번호`, `부과일자=조건에맞는CHG_YMD` | `소유자명(step4OwnerName)` | 명의이전 시점 소유자 확인 |
#### 비교 조건
#### 실제 비교 조건 로직 (소스코드 기준)
```java
// 조건 1: 소유자명에 '상품용' 포함 여부
if (!step1Record.소유자명.contains("상품용")) {
// 조건 1: Step 1 소유자명에 "상품용" 포함 여부
if (step1OwnerName == null || !step1OwnerName.contains("상품용")) {
log.debug("[상품용] 검사일 소유자명에 '상품용' 미포함 - 차량번호: {}", vhclno);
return null; // 미충족
}
// 조건 2: 갑부 상세에서 조건에 맞는 명의이전 레코드 찾기
// - CHG_TASK_SE_CD == "11" (명의이전)
// - CHG_YMD <= 검사종료일자
// - 가장 마지막 일자
// 조건 2: Step 3 갑부 상세에서 조건에 맞는 명의이전 레코드 찾기
NewLedgerResponse.Record targetRecord = null;
LocalDate latestChgDate = null;
LocalDate inspDate = DateUtil.parseDate(inspYmd);
for (NewLedgerResponse.Record record : ledgerRecords) {
String chgYmd = record.getChgYmd();
String chgTaskSeCd = record.getChgTaskSeCd();
// 조건: CHG_TASK_SE_CD == "11" (명의이전)
if (!"11".equals(chgTaskSeCd) || chgYmd == null) {
continue;
}
LocalDate chgDate = DateUtil.parseDate(chgYmd);
if (chgDate == null) {
continue;
}
// 조건: CHG_YMD <= 검사종료일자
LocalDate inspEndDate = DateUtil.parseDate(inspEndYmd);
if (chgDate.isAfter(inspEndDate)) {
log.debug("[상품용] CHG_YMD > 검사종료일자 - 변경일자: {}, 검사종료일자: {}", chgYmd, inspEndYmd);
continue;
}
// 필요없음. 조건: CHG_YMD가 검사일 이전일자 중 가장 마지막 일자, 필요없음.
/*
if (chgDate.isAfter(inspDate)) {
log.debug("[상품용] CHG_YMD > 검사일 - 변경일자: {}, 검사일: {}", chgYmd, inspYmd);
continue;
}
*/
// 가장 마지막 일자 찾기
if (latestChgDate == null || chgDate.isAfter(latestChgDate)) {
latestChgDate = chgDate;
targetRecord = record;
log.debug("[상품용] 조건 충족 레코드 발견 - 변경일자: {}, 변경업무: {}", chgYmd, chgTaskSeCd);
}
}
if (targetRecord == null) {
log.debug("[상품용] 조건에 맞는 명의이전 레코드 없음 - 차량번호: {}", vhclno);
return null;
}
// 조건 3: Step 4 소유자명 == Step 1 소유자명 (명의이전 시점 소유자명 == 검사일 기준 소유자명)
if (step4OwnerName == null || !step4RprsvOwnrIdecno.equals(step1RprsvOwnrIdecno)) {
log.debug("[상품용] 소유자명 불일치 - Step1 소유자: {}, Step4 소유자: {}", step1RprsvOwnrIdecno, step4RprsvOwnrIdecno);
return null;
}
// 조건 3: 명의이전 시점 소유자명 == 검사일 기준 소유자명
if (!step4Record.소유자명.equals(step1Record.소유자명)) {
return null; // 소유자명 불일치
// 주석처리된 조건 4: CHG_YMD가 유효기간만료일 ~ 검사종료일자 범위 내
/*
if (!DateUtil.isDateBetween(targetChgYmd, vldPrdExpryYmd, inspEndYmd)) {
log.debug("[상품용] CHG_YMD가 유효기간만료일~검사종료일자 범위 밖 - CHG_YMD: {}, 범위: {} ~ {}",
targetChgYmd, vldPrdExpryYmd, inspEndYmd);
return null;
}
*/
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE; // 상품용 조건 충족
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE; // "02" (상품용 조건 충족)
```
#### 로직 설명
**검사일 당시 소유자가 상품용이고, 명의이전 시점에도 동일하게 상품용이었던 경우**
1. **검사일 소유자가 상품용**: Step 1 API에서 조회한 소유자명에 "상품용" 포함
2. **명의이전 레코드 검색**: Step 3 갑부 상세에서 다음 조건을 만족하는 레코드 찾기
- `CHG_TASK_SE_CD == "11"` (명의이전)
- `CHG_YMD <= 검사종료일자`
- 가장 마지막 일자 (여러 개면 최신 날짜)
3. **명의이전 시점 소유자 확인**: Step 4 API로 명의이전 시점(CHG_YMD) 소유자명 조회
4. **소유자명 일치 여부**: Step 1 소유자명 == Step 4 소유자명
#### 결과 처리
- **업무 처리 상태 코드**: `02` (상품용)
@ -74,6 +163,22 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE; // 상품용 조
```
상품용 - 상품용검사
```
- 메서드: `ComparisonRemarkBuilder.buildProductUseRemarkCase1()`
#### DB 업데이트 필드
```java
existingData.setCarBassMatterInqireId(step1Response.getGeneratedId());
existingData.setCarLedgerFrmbkId(step3Response.getGeneratedId());
existingData.setTaskPrcsSttsCd("02");
existingData.setTaskPrcsYmd(오늘일자);
existingData.setCarBscMttrInqFlnm(step4OwnerName); // 명의이전 시점 소유자명
existingData.setCarRegFrmbkChgTaskSeCd(targetRecord.getChgTaskSeCd()); // "11"
existingData.setCarRegFrmbkChgTaskSeNm(targetRecord.getChgTaskSeNm()); // "명의이전"
existingData.setCarRegFrmbkChgYmd(targetRecord.getChgYmd()); // 명의이전 변경일자
existingData.setCarRegFrmbkDtl(갑부레코드상세);
existingData.setRmrk(비고);
```
---
@ -82,6 +187,30 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE; // 상품용 조
**처리상태코드**: `04` (내사종결)
**메서드**: `checkInvestigationClosedByProductUseWithin31Days()`
#### Javadoc 주석 내용
```java
/**
* 2. 내사종결 검증 - 명의이전 이전소유자 상품용, 31일 이내
*
* API 호출 4단계:
* 1) 자동차기본정보(차량번호, 부과일자=검사일) → 차대번호, 소유자명
* 2) 자동차기본정보(차대번호, 부과일자=오늘일자) → 차량번호, 성명, 민원인주민번호, 민원인법정동코드
* 3) 자동차등록원부(갑)(차량번호, 성명, 민원인주민번호, 민원인법정동코드) → 갑부 상세 List
* 4) 자동차기본정보(차대번호, 부과일자=조건에 맞는 CHG_YMD - 1 day) → 해당 시점의 소유자명
*
* 비교조건:
* - 1단계: 소유자명에 "상품용" 이 아닐 경우 포함
* - 2단계: 갑부 상세에서 다음 조건을 만족하는 명의이전(코드 11) 레코드 찾기
* • CHG_YMD <= 검사일자
* • 현재 주석처리 CHG_YMD가 검사일 이전일자 중 가장 마지막 일자
* • CHG_TASK_SE_CD == "11" (명의이전)
* • 명의이전일자가 검사일의 31일이내
* - 3단계: 4번 API 조회 !소유자명.contains("상품용") return null, 상품용만 최종 통과
*/
```
#### API 호출 순서
| 순서 | API | 입력 파라미터 | 출력 데이터 | 용도 |
@ -91,39 +220,78 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE; // 상품용 조
| 3 | 자동차등록원부(갑) | `2.차량번호`, `2.성명`, `2.민원인주민번호`, `2.민원인법정동코드` | 갑부 상세 List | 명의이전 이력 조회 |
| 4 | 자동차기본정보 | `1.차대번호`, `부과일자=CHG_YMD-1일` | `소유자명` | 명의이전 직전 소유자 확인 |
#### 비교 조건
#### 실제 비교 조건 로직 (소스코드 기준)
```java
// 조건 1: 검사일 소유자명에 '상품용' 미포함 (상품용은 1번에서 처리됨)
if (step1Record.소유자명 == null || step1Record.소유자명.contains("상품용")) {
return null;
// 조건 1: Step 1 소유자명에 "상품용" 미포함 (검사일 당시 상품용 아님)
if (step1OwnerName == null || step1OwnerName.contains("상품용")) {
log.debug("[내사종결-상품용31일이내] 검사일 소유자가 상품용이거나 null - 차량번호: {}", vhclno);
return null; // 상품용은 1번에서 처리됨
}
// 조건 2: 갑부 상세에서 검사일 이전 가장 마지막 명의이전(11) 레코드 찾기
// - CHG_TASK_SE_CD == "11" (명의이전)
// - CHG_YMD <= 검사일자
// - 가장 마지막 일자
// 조건 2: Step 3 갑부 상세에서 검사일 이전 가장 마지막 명의이전(11) 레코드 찾기
NewLedgerResponse.Record targetRecord = null;
LocalDate latestChgDate = null;
LocalDate inspDate = DateUtil.parseDate(inspYmd);
for (NewLedgerResponse.Record record : ledgerRecords) {
String chgYmd = record.getChgYmd();
String chgTaskSeCd = record.getChgTaskSeCd();
// CHG_TASK_SE_CD == "11" (명의이전)
if (!"11".equals(chgTaskSeCd) || chgYmd == null) {
continue;
}
LocalDate chgDate = DateUtil.parseDate(chgYmd);
if (chgDate == null) {
continue;
}
// CHG_YMD <= 검사일자 (Javadoc에는 "검사종료일자"라고 되어 있으나 실제는 "검사일자")
LocalDate inspEndDate = DateUtil.parseDate(inspYmd); // inspYmd 사용!
if (chgDate.isAfter(inspEndDate)) {
log.debug("[내사종결-상품용31일이내] CHG_YMD > 검사일 - 변경일자: {}, 검사일: {}", chgYmd, inspYmd);
continue;
}
// 가장 마지막 일자 찾기
if (latestChgDate == null || chgDate.isAfter(latestChgDate)) {
latestChgDate = chgDate;
targetRecord = record;
log.debug("[내사종결-상품용31일이내] 조건 충족 레코드 발견 - 변경일자: {}, 변경업무: {}", chgYmd, chgTaskSeCd);
}
}
if (targetRecord == null) {
log.debug("[내사종결-상품용31일이내] 조건에 맞는 명의이전 레코드 없음 - 차량번호: {}", vhclno);
return null;
}
// 조건 3: 명의이전일자가 검사일의 31일 이내
long daysBetween = ChronoUnit.DAYS.between(latestChgDate, 검사일);
if (daysBetween < 0 || daysBetween > 31) {
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween < 0 || daysBetween > DAYS_THRESHOLD) {
log.debug("[내사종결-상품용31일이내] 일수 범위 벗어남 - 일수: {}일 (기준: 0~31일)", daysBetween);
return null; // 31일 초과
}
// 조건 4: 명의이전 직전 시점(CHG_YMD-1일) 소유자명에 '상품용' 포함
if (step4Record.소유자명 == null || !step4Record.소유자명.contains("상품용")) {
// 조건 4: Step 4 명의이전 직전 시점(CHG_YMD-1일) 소유자명에 "상품용" 포함
if (step4OwnerName == null || !step4OwnerName.contains("상품용")) {
log.debug("[내사종결-상품용31일이내] 명의이전 직전 소유자가 상품용 아님 - 소유자: {}", step4OwnerName);
return null; // 명의이전 직전 소유자가 상품용이 아님
}
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED; // "04"
```
#### 로직 설명
**검사일 당시에는 상품용이 아니었지만, 명의이전 이전(31일 이내)에는 상품용이었던 경우**
1. 검사일 소유자가 상품용 아님
2. 검사일 이전 31일 이내에 명의이전(코드 11) 발생
3. 명의이전 직전 소유자가 상품용
1. **검사일 소유자가 상품용 아님**: Step 1 소유자명에 "상품용" 미포함
2. **검사일 이전 명의이전 발생**: Step 3 갑부 상세에서 `CHG_YMD <= 검사일자`인 명의이전(코드 11) 중 가장 마지막 일자
3. **31일 이내 체크**: 명의이전일자 ~ 검사일 사이 일수가 0~31일
4. **명의이전 직전 소유자가 상품용**: Step 4 API로 `CHG_YMD - 1일` 조회, 소유자명에 "상품용" 포함
#### 결과 처리
@ -138,6 +306,22 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
- 상품용: 2024-09-03
일수차이: 19일
```
- 메서드: `ComparisonRemarkBuilder.buildCloseProductUseRemark()`
#### DB 업데이트 필드
```java
existingData.setCarBassMatterInqireId(step1Response.getGeneratedId());
existingData.setCarLedgerFrmbkId(step3Response.getGeneratedId());
existingData.setTaskPrcsSttsCd("04");
existingData.setTaskPrcsYmd(오늘일자);
existingData.setCarBscMttrInqFlnm(step4OwnerName); // 명의이전 직전 소유자명
existingData.setCarRegFrmbkChgTaskSeCd(targetRecord.getChgTaskSeCd());
existingData.setCarRegFrmbkChgTaskSeNm(targetRecord.getChgTaskSeNm());
existingData.setCarRegFrmbkChgYmd(targetRecord.getChgYmd());
existingData.setCarRegFrmbkDtl(갑부레코드상세);
existingData.setRmrk(비고);
```
---
@ -146,6 +330,30 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
**처리상태코드**: `04` (내사종결)
**메서드**: `checkInvestigationClosedByOwnerChangeWithin31Days()`
#### Javadoc 주석 내용
```java
/**
* 3. 내사종결 검증 - 순수 명의이전 (31일 이내)
*
* API 호출 4단계:
* 1) 자동차기본정보(차량번호, 부과일자=검사일) → 차대번호, 소유자명
* 2) 자동차기본정보(차대번호, 부과일자=오늘일자) → 차량번호, 성명, 민원인주민번호, 민원인법정동코드
* 3) 자동차등록원부(갑)(차량번호, 성명, 민원인주민번호, 민원인법정동코드) → 갑부 상세 List
* 4) 자동차기본정보(차대번호, 부과일자=CHG_YMD - 1일) → 명의이전 직전 소유자명
*
* 비교조건:
* - 1단계: 검사일 소유자명에 "상품용" 미포함 (상품용은 1번에서 처리)
* - 2단계: 갑부 상세에서 검사기간(유효기간만료일 ~ 검사종료일자) 내 명의이전(코드 11) 레코드 찾기
* • 유효기간만료일 <= CHG_YMD <= 검사종료일자
* • CHG_TASK_SE_CD == "11" (명의이전)
* - 3단계: 명의이전일자와 검사일 비교
* • 명의이전일자 ~ 검사일까지의 일수 계산
* • 31일 이내인 경우
* - 4단계: 명의이전 직전 소유자가 상품용 아님 (상품용이면 2번에서 처리됨)
*/
```
#### API 호출 순서
| 순서 | API | 입력 파라미터 | 출력 데이터 | 용도 |
@ -155,39 +363,75 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
| 3 | 자동차등록원부(갑) | `2.차량번호`, `2.성명`, `2.민원인주민번호`, `2.민원인법정동코드` | 갑부 상세 List | 명의이전 이력 조회 |
| 4 | 자동차기본정보 | `1.차대번호`, `부과일자=CHG_YMD-1일` | `소유자명` | 명의이전 직전 소유자 확인 |
#### 비교 조건
#### 실제 비교 조건 로직 (소스코드 기준)
```java
// 조건 1: 검사일 소유자명에 '상품용' 미포함
if (step1Record.소유자명 != null && step1Record.소유자명.contains("상품용")) {
// 조건 1: 검사일 소유자명에 "상품용" 미포함
if (step1OwnerName != null && step1OwnerName.contains("상품용")) {
log.debug("[내사종결-순수명의이전31일이내] 검사일 소유자가 상품용 - 차량번호: {}", vhclno);
return null; // 상품용이면 1번에서 처리됨
}
// 조건 2: 갑부 상세에서 검사기간(유효기간만료일 ~ 검사종료일자) 내 명의이전(11) 레코드 찾기
// - CHG_TASK_SE_CD == "11" (명의이전)
// - 유효기간만료일 <= CHG_YMD <= 검사종료일자
NewLedgerResponse.Record targetRecord = null;
LocalDate vldPrdExpryDate = DateUtil.parseDate(vldPrdExpryYmd);
LocalDate inspEndDate = DateUtil.parseDate(inspEndYmd);
for (NewLedgerResponse.Record record : ledgerRecords) {
String chgYmd = record.getChgYmd();
String chgTaskSeCd = record.getChgTaskSeCd();
// CHG_TASK_SE_CD == "11" (명의이전)
if (!"11".equals(chgTaskSeCd) || chgYmd == null) {
continue;
}
LocalDate chgDate = DateUtil.parseDate(chgYmd);
if (chgDate == null) {
continue;
}
// 유효기간만료일 <= CHG_YMD <= 검사종료일자
if ((chgDate.isEqual(vldPrdExpryDate) || chgDate.isAfter(vldPrdExpryDate)) &&
(chgDate.isEqual(inspEndDate) || chgDate.isBefore(inspEndDate))) {
targetRecord = record;
log.debug("[내사종결-순수명의이전31일이내] 조건 충족 레코드 발견 - 변경일자: {}, 변경업무: {}", chgYmd, chgTaskSeCd);
break; // 첫 번째 발견된 명의이전 사용
}
}
if (targetRecord == null) {
log.debug("[내사종결-순수명의이전31일이내] 검사기간 내 명의이전 레코드 없음 - 차량번호: {}", vhclno);
return null;
}
// 조건 3: 명의이전일자가 검사일의 31일 이내
long daysBetween = ChronoUnit.DAYS.between(chgDate, 검사일);
if (daysBetween < 0 || daysBetween > 31) {
LocalDate chgDate = DateUtil.parseDate(targetChgYmd);
LocalDate inspDate = DateUtil.parseDate(inspYmd);
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(chgDate, inspDate);
if (daysBetween < 0 || daysBetween > DAYS_THRESHOLD) {
log.debug("[내사종결-순수명의이전31일이내] 일수 범위 벗어남 - 일수: {}일 (기준: 0~31일)", daysBetween);
return null;
}
// 조건 4: 명의이전 직전 소유자가 상품용 아님 (상품용이면 2번에서 처리됨)
if (step4Record.소유자명 == null || step4Record.소유자명.contains("상품용")) {
if (step4OwnerName == null || step4OwnerName.contains("상품용")) {
log.debug("[내사종결-순수명의이전31일이내] 명의이전 직전 소유자가 상품용 - 소유자: {}", step4OwnerName);
return null; // 상품용이면 이미 2번에서 처리됨
}
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED; // "04"
```
#### 로직 설명
**순수 명의이전 케이스 (상품용과 무관)**
1. 검사일 소유자가 상품용 아님
2. 검사기간 내 명의이전(코드 11) 발생
3. 명의이전일자가 검사일의 31일 이내
4. 명의이전 직전 소유자도 상품용 아님 (상품용이면 2번 로직에서 처리됨)
1. **검사일 소유자가 상품용 아님**: Step 1 소유자명에 "상품용" 미포함
2. **검사기간 내 명의이전 발생**: Step 3 갑부 상세에서 `유효기간만료일 <= CHG_YMD <= 검사종료일자`인 명의이전(코드 11)
3. **31일 이내 체크**: 명의이전일자 ~ 검사일 사이 일수가 0~31일
4. **명의이전 직전 소유자도 상품용 아님**: Step 4 소유자명에 "상품용" 미포함 (상품용이면 2번 로직에서 처리됨)
#### 결과 처리
@ -202,6 +446,22 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
- 상품용: 2024-09-03
일수차이: 19일
```
- 메서드: `ComparisonRemarkBuilder.buildOwnerChangeRemark()`
#### DB 업데이트 필드
```java
existingData.setCarBassMatterInqireId(step1Response.getGeneratedId());
existingData.setCarLedgerFrmbkId(step3Response.getGeneratedId());
existingData.setTaskPrcsSttsCd("04");
existingData.setTaskPrcsYmd(오늘일자);
existingData.setCarBscMttrInqFlnm(step4OwnerName); // 명의이전 직전 소유자명
existingData.setCarRegFrmbkChgTaskSeCd(targetRecord.getChgTaskSeCd());
existingData.setCarRegFrmbkChgTaskSeNm(targetRecord.getChgTaskSeNm());
existingData.setCarRegFrmbkChgYmd(targetRecord.getChgYmd());
existingData.setCarRegFrmbkDtl(갑부레코드상세);
existingData.setRmrk(비고);
```
---
@ -210,6 +470,33 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
**처리상태코드**: `05` (날짜 수정 후 부과)
**메서드**: `checkDateModifiedLevyByProductUseOver31Days()`
#### Javadoc 주석 내용
```java
/**
* 4. 날짜 수정 후 부과 검증 - 명의이전 이전소유자 상품용, 31일 초과
*
* API 호출 4단계:
* 1) 자동차기본정보(차량번호, 부과일자=검사일) → 차대번호, 소유자명
* 2) 자동차기본정보(차대번호, 부과일자=오늘일자) → 차량번호, 성명, 민원인주민번호, 민원인법정동코드
* 3) 자동차등록원부(갑)(차량번호, 성명, 민원인주민번호, 민원인법정동코드) → 갑부 상세 List
* 4) 자동차기본정보(차대번호, 부과일자=조건에 맞는 CHG_YMD - 1 day) → 해당 시점의 소유자명
*
* 비교조건:
* - 1단계: 소유자명에 "상품용" 이 아닐 경우 포함
* - 2단계: 갑부 상세에서 다음 조건을 만족하는 명의이전(코드 11) 레코드 찾기
* • CHG_YMD <= 검사일자
* • 현재 주석처리 CHG_YMD가 검사일 이전일자 중 가장 마지막 일자
* • CHG_TASK_SE_CD == "11" (명의이전)
* • 명의이전일자가 검사일의 31일 초과
* - 3단계: 4번 API 조회 소유자명.contains("상품용")
*/
```
**⚠️ Javadoc과 실제 로직 차이**:
- Javadoc: "3단계: 4번 API 조회 소유자명.contains('상품용')"
- 실제 로직: 상품용 **포함** 체크 (`step4OwnerName.contains("상품용")`) - Javadoc이 맞음
#### API 호출 순서
| 순서 | API | 입력 파라미터 | 출력 데이터 | 용도 |
@ -219,45 +506,106 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
| 3 | 자동차등록원부(갑) | `2.차량번호`, `2.성명`, `2.민원인주민번호`, `2.민원인법정동코드` | 갑부 상세 List | 명의이전 이력 조회 |
| 4 | 자동차기본정보 | `1.차대번호`, `부과일자=CHG_YMD-1일` | `소유자명` | 명의이전 직전 소유자 확인 |
#### 비교 조건
#### 실제 비교 조건 로직 (소스코드 기준)
```java
// 조건 1: 검사일 소유자명에 '상품용' 미포함
if (step1Record.소유자명 == null || step1Record.소유자명.contains("상품용")) {
// 조건 1: 검사일 소유자명에 "상품용" 미포함
if (step1OwnerName == null || step1OwnerName.contains("상품용")) {
log.debug("[날짜수정-상품용31일초과] 검사일 소유자가 상품용이거나 null - 차량번호: {}", vhclno);
return null;
}
// 조건 2: 갑부 상세에서 검사일 이전 가장 마지막 명의이전(11) 레코드 찾기
// - CHG_TASK_SE_CD == "11" (명의이전)
// - CHG_YMD <= 검사일자
// - 가장 마지막 일자
// 조건 2: 갑부 상세에서 검사일 이전 가장 마지막 명의이전(11) 레코드 찾기 (2번 메서드와 동일)
NewLedgerResponse.Record targetRecord = null;
LocalDate latestChgDate = null;
LocalDate inspDate = DateUtil.parseDate(inspYmd);
for (NewLedgerResponse.Record record : ledgerRecords) {
String chgYmd = record.getChgYmd();
String chgTaskSeCd = record.getChgTaskSeCd();
// CHG_TASK_SE_CD == "11" (명의이전)
if (!"11".equals(chgTaskSeCd) || chgYmd == null) {
continue;
}
LocalDate chgDate = DateUtil.parseDate(chgYmd);
if (chgDate == null) {
continue;
}
// CHG_YMD <= 검사일자
LocalDate inspEndDate = DateUtil.parseDate(inspYmd);
if (chgDate.isAfter(inspEndDate)) {
continue;
}
// 가장 마지막 일자 찾기
if (latestChgDate == null || chgDate.isAfter(latestChgDate)) {
latestChgDate = chgDate;
targetRecord = record;
}
}
if (targetRecord == null) {
log.debug("[날짜수정-상품용31일초과] 조건에 맞는 명의이전 레코드 없음 - 차량번호: {}", vhclno);
return null;
}
// 조건 3: 명의이전일자가 검사일의 31일 초과
long daysBetween = ChronoUnit.DAYS.between(latestChgDate, 검사일);
if (daysBetween <= 31) {
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween <= DAYS_THRESHOLD) {
log.debug("[날짜수정-상품용31일초과] 일수가 31일 이내 - 일수: {}일", daysBetween);
return null; // 31일 이내면 2번에서 처리됨
}
// 조건 4: 명의이전 직전 소유자가 상품용
if (step4Record.소유자명 == null || !step4Record.소유자명.contains("상품용")) {
// 조건 4: Step 4 소유자명에 "상품용" 포함
if (step4OwnerName == null || !step4OwnerName.contains("상품용")) {
log.debug("[날짜수정-상품용31일초과] 명의이전 직전 소유자가 상품용 아님 - 소유자: {}", step4OwnerName);
return null;
}
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY;
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY; // "05"
```
#### 로직 설명
**명의이전 전 소유자가 상품용이지만 31일을 초과한 경우**
1. 검사일 소유자가 상품용 아님
2. 검사일 이전에 명의이전(코드 11) 발생
3. 명의이전일자가 검사일의 31일 초과
4. 명의이전 직전 소유자가 상품용
1. **검사일 소유자가 상품용 아님**: Step 1 소유자명에 "상품용" 미포함
2. **검사일 이전 명의이전 발생**: Step 3 갑부 상세에서 `CHG_YMD <= 검사일자`인 명의이전(코드 11) 중 가장 마지막 일자
3. **31일 초과**: 명의이전일자 ~ 검사일 사이 일수가 31일 초과
4. **명의이전 직전 소유자가 상품용**: Step 4 소유자명에 "상품용" 포함
#### 결과 처리
- **업무 처리 상태 코드**: `05` (날짜 수정 후 부과)
- **비고 컬럼 형식**: 2번과 동일
```
명의이전(25.9.3.) 이전소유자 상품용
22루2283
- 검사기간: 2024-08-01 - 2024-08-31
- 검사일: 2024-08-15
- 명의이전: 2024-07-01
- 상품용: 2024-07-01
일수차이: 45일
```
- 메서드: `ComparisonRemarkBuilder.buildCloseProductUseRemark()`
#### DB 업데이트 필드
```java
existingData.setCarBassMatterInqireId(step1Response.getGeneratedId());
existingData.setCarLedgerFrmbkId(step3Response.getGeneratedId());
existingData.setTaskPrcsSttsCd("05");
existingData.setTaskPrcsYmd(오늘일자);
existingData.setCarBscMttrInqFlnm(step4OwnerName);
existingData.setCarRegFrmbkChgTaskSeCd(targetRecord.getChgTaskSeCd());
existingData.setCarRegFrmbkChgTaskSeNm(targetRecord.getChgTaskSeNm());
existingData.setCarRegFrmbkChgYmd(targetRecord.getChgYmd());
existingData.setCarRegFrmbkDtl(갑부레코드상세);
existingData.setRmrk(비고);
```
---
@ -266,6 +614,30 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY;
**처리상태코드**: `05` (날짜 수정 후 부과)
**메서드**: `checkDateModifiedLevyByOwnerChangeOver31Days()`
#### Javadoc 주석 내용
```java
/**
* 5. 날짜 수정 후 부과 검증 - 순수 명의이전 (31일 초과)
*
* API 호출 4단계:
* 1) 자동차기본정보(차량번호, 부과일자=검사일) → 차대번호, 소유자명
* 2) 자동차기본정보(차대번호, 부과일자=오늘일자) → 차량번호, 성명, 민원인주민번호, 민원인법정동코드
* 3) 자동차등록원부(갑)(차량번호, 성명, 민원인주민번호, 민원인법정동코드) → 갑부 상세 List
* 4) 자동차기본정보(차대번호, 부과일자=CHG_YMD - 1일) → 명의이전 직전 소유자명
*
* 비교조건:
* - 1단계: 검사일 소유자명에 "상품용" 미포함 (상품용은 1번에서 처리)
* - 2단계: 갑부 상세에서 검사기간(유효기간만료일 ~ 검사종료일자) 내 명의이전(코드 11) 레코드 찾기
* • 유효기간만료일 <= CHG_YMD <= 검사종료일자
* • CHG_TASK_SE_CD == "11" (명의이전)
* - 3단계: 명의이전일자와 검사일 비교
* • 명의이전일자 ~ 검사일까지의 일수 계산
* • 31일 초과인 경우
* - 4단계: 명의이전 직전 소유자가 상품용 아님 (상품용이면 4번에서 처리됨)
*/
```
#### API 호출 순서
| 순서 | API | 입력 파라미터 | 출력 데이터 | 용도 |
@ -275,44 +647,104 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY;
| 3 | 자동차등록원부(갑) | `2.차량번호`, `2.성명`, `2.민원인주민번호`, `2.민원인법정동코드` | 갑부 상세 List | 명의이전 이력 조회 |
| 4 | 자동차기본정보 | `1.차대번호`, `부과일자=CHG_YMD-1일` | `소유자명` | 명의이전 직전 소유자 확인 |
#### 비교 조건
#### 실제 비교 조건 로직 (소스코드 기준)
```java
// 조건 1: 검사일 소유자명에 '상품용' 미포함
if (step1Record.소유자명 != null && step1Record.소유자명.contains("상품용")) {
// 조건 1: 검사일 소유자명에 "상품용" 미포함
if (step1OwnerName != null && step1OwnerName.contains("상품용")) {
log.debug("[날짜수정-순수명의이전31일초과] 검사일 소유자가 상품용 - 차량번호: {}", vhclno);
return null;
}
// 조건 2: 갑부 상세에서 검사기간(유효기간만료일 ~ 검사종료일자) 내 명의이전(11) 레코드 찾기
// - CHG_TASK_SE_CD == "11" (명의이전)
// - 유효기간만료일 <= CHG_YMD <= 검사종료일자
// 조건 2: 갑부 상세에서 검사기간(유효기간만료일 ~ 검사종료일자) 내 명의이전(11) 레코드 찾기 (3번 메서드와 동일)
NewLedgerResponse.Record targetRecord = null;
LocalDate vldPrdExpryDate = DateUtil.parseDate(vldPrdExpryYmd);
LocalDate inspEndDate = DateUtil.parseDate(inspEndYmd);
for (NewLedgerResponse.Record record : ledgerRecords) {
String chgYmd = record.getChgYmd();
String chgTaskSeCd = record.getChgTaskSeCd();
// CHG_TASK_SE_CD == "11" (명의이전)
if (!"11".equals(chgTaskSeCd) || chgYmd == null) {
continue;
}
LocalDate chgDate = DateUtil.parseDate(chgYmd);
if (chgDate == null) {
continue;
}
// 유효기간만료일 <= CHG_YMD <= 검사종료일자
if ((chgDate.isEqual(vldPrdExpryDate) || chgDate.isAfter(vldPrdExpryDate)) &&
(chgDate.isEqual(inspEndDate) || chgDate.isBefore(inspEndDate))) {
targetRecord = record;
break;
}
}
// 조건 3: 명의이전일자가 검사일의 31일 초과
long daysBetween = ChronoUnit.DAYS.between(chgDate, 검사일);
if (daysBetween <= 31) {
if (targetRecord == null) {
log.debug("[날짜수정-순수명의이전31일초과] 검사기간 내 명의이전 레코드 없음 - 차량번호: {}", vhclno);
return null;
}
// 조건 3: 명의이전일자 ~ 검사일 사이 31일 초과
LocalDate chgDate = DateUtil.parseDate(targetChgYmd);
LocalDate inspDate = DateUtil.parseDate(inspYmd);
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(chgDate, inspDate);
if (daysBetween <= DAYS_THRESHOLD) {
log.debug("[날짜수정-순수명의이전31일초과] 일수가 31일 이내 - 일수: {}일", daysBetween);
return null; // 31일 이내면 3번에서 처리됨
}
// 조건 4: 명의이전 직전 소유자가 상품용 아님
if (step4Record.소유자명 == null || step4Record.소유자명.contains("상품용")) {
if (step4OwnerName == null || step4OwnerName.contains("상품용")) {
log.debug("[날짜수정-순수명의이전31일초과] 명의이전 직전 소유자가 상품용 - 소유자: {}", step4OwnerName);
return null; // 상품용이면 4번에서 처리됨
}
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY;
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY; // "05"
```
#### 로직 설명
**순수 명의이전 케이스 중 31일을 초과한 경우**
1. 검사일 소유자가 상품용 아님
2. 검사기간 내 명의이전(코드 11) 발생
3. 명의이전일자가 검사일의 31일 초과
4. 명의이전 직전 소유자도 상품용 아님 (상품용이면 4번 로직에서 처리됨)
1. **검사일 소유자가 상품용 아님**: Step 1 소유자명에 "상품용" 미포함
2. **검사기간 내 명의이전 발생**: Step 3 갑부 상세에서 `유효기간만료일 <= CHG_YMD <= 검사종료일자`인 명의이전(코드 11)
3. **31일 초과**: 명의이전일자 ~ 검사일 사이 일수가 31일 초과
4. **명의이전 직전 소유자도 상품용 아님**: Step 4 소유자명에 "상품용" 미포함 (상품용이면 4번 로직에서 처리됨)
#### 결과 처리
- **업무 처리 상태 코드**: `05` (날짜 수정 후 부과)
- **비고 컬럼 형식**: 3번과 동일
```
명의이전(25.9.3.)
22루2283
- 검사기간: 2024-08-01 - 2024-08-31
- 검사일: 2024-08-15
- 명의이전: 2024-07-01
- 상품용: 2024-07-01
일수차이: 45일
```
- 메서드: `ComparisonRemarkBuilder.buildOwnerChangeRemark()`
#### DB 업데이트 필드
```java
existingData.setCarBassMatterInqireId(step1Response.getGeneratedId());
existingData.setCarLedgerFrmbkId(step3Response.getGeneratedId());
existingData.setTaskPrcsSttsCd("05");
existingData.setTaskPrcsYmd(오늘일자);
existingData.setCarBscMttrInqFlnm(step4OwnerName);
existingData.setCarRegFrmbkChgTaskSeCd(targetRecord.getChgTaskSeCd());
existingData.setCarRegFrmbkChgTaskSeNm(targetRecord.getChgTaskSeNm());
existingData.setCarRegFrmbkChgYmd(targetRecord.getChgYmd());
existingData.setCarRegFrmbkDtl(갑부레코드상세);
existingData.setRmrk(비고);
```
---
@ -321,38 +753,118 @@ return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY;
**처리상태코드**: `03` (이첩)
**메서드**: `checkTransferCase115Day()`
#### Javadoc 주석 내용
```java
/**
* 6. 이첩 검증
*
* DAYCNT 기반 부과기준일 계산:
* - DAYCNT > 115: 이첩-2 (부과기준일 = 검사종료일자 + 115일)
* - DAYCNT <= 115: 이첩-1 (부과기준일 = 검사일자)
*
* 법정동코드 비교:
* - 사용본거지법정동코드 앞 4자리 != 사용자 조직코드 앞 4자리
*/
```
#### 부과기준일 결정
```java
int dayCnt = TB_CAR_FFNLG_TRGT.DAYCNT;
String daycntStr = existingData.getDaycnt();
if (daycntStr == null || daycntStr.isEmpty()) {
log.warn("[이첩] DAYCNT 값이 없음 - 차량번호: {}", vhclno);
return null;
}
int daycnt = Integer.parseInt(daycntStr);
if (dayCnt > 115) {
// 이첩-2
부과기준일 = TB_CAR_FFNLG_TRGT.검사종료일자.plusDays(115);
String levyCrtrYmd; // 부과기준일
String transferType;
if (daycnt > 115) {
// 이첩-2: 부과기준일 = 검사종료일자 + 115일
String inspEndYmd = existingData.getInspEndYmd();
LocalDate inspEndDate = DateUtil.parseDate(inspEndYmd);
LocalDate levyCrtrDate = inspEndDate.plusDays(115);
levyCrtrYmd = levyCrtrDate.format(DATE_FORMATTER);
transferType = "이첩-2";
log.info("[이첩-2] DAYCNT > 115 - 부과기준일 = 검사종료일자({}) + 115일 = {}", inspEndYmd, levyCrtrYmd);
} else {
// 이첩-1
부과기준일 = TB_CAR_FFNLG_TRGT.검사일자;
// 이첩-1: 부과기준일 = 검사일자
levyCrtrYmd = existingData.getInspYmd();
transferType = "이첩-1";
log.info("[이첩-1] DAYCNT <= 115 - 부과기준일 = 검사일자({})", levyCrtrYmd);
}
```
#### API 호출
| API | 입력 파라미터 | 출력 데이터 | 용도 |
|-----|--------------|-------------|------|
| 자동차기본정보 | `차량번호`, `부과일자=부과기준일` | `사용본거지법정동코드(usgsrhldStdgCd)` | 부과기준일 기준 사용본거지 확인 |
#### 법정동코드 비교
```java
// 법정동코드 앞 4자리 vs 사용자 조직코드 앞 4자리
String legalDong4 = useStrnghldLegaldongCode.substring(0, 4);
String userOrg4 = userInfo.getOrgCd().substring(0, 4);
// API 호출
NewBasicRequest request = createBasicRequest(vhclno, null, levyCrtrYmd);
NewBasicResponse response = apiService.getBasicInfo(request);
bassMatterLogService.updateCarFfnlgTrgtIdByTxIdNewTx(response, existingData.getCarFfnlgTrgtId());
// 사용본거지법정동코드 추출
String usgsrhldStdgCd = record.getUsgsrhldStdgCd();
if (usgsrhldStdgCd == null || usgsrhldStdgCd.length() < 4) {
log.warn("[이첩] 법정동코드 유효하지 않음 - 차량번호: {}, 법정동코드: {}", vhclno, usgsrhldStdgCd);
return null;
}
if (!legalDong4.equals(userOrg4)) {
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER; // 이첩
// 세션에서 사용자 정보 조회
LoginUserVO userInfo = SessionUtil.getLoginUser();
if (userInfo == null || userInfo.getOrgCd() == null) {
log.warn("[이첩] 사용자 정보 없음 - 차량번호: {}", vhclno);
return null;
}
// 법정동코드 앞 4자리 vs 사용자 조직코드 앞 4자리 비교
String legalDong4 = usgsrhldStdgCd.substring(0, 4);
String userOrgCd = userInfo.getOrgCd();
String userOrg4 = userOrgCd.length() >= 4 ? userOrgCd.substring(0, 4) : userOrgCd;
if (legalDong4.equals(userOrg4)) {
log.debug("[이첩] 법정동코드 일치 - 법정동: {}, 조직코드: {}", legalDong4, userOrg4);
return null; // 일치하면 이첩 대상 아님
}
log.info("[이첩] 법정동코드 불일치 - 법정동: {}, 조직코드: {}", legalDong4, userOrg4);
// 시군구 코드 및 시군구명 조회
String sggCd = usgsrhldStdgCd.length() >= 5 ? usgsrhldStdgCd.substring(0, 5) : usgsrhldStdgCd;
String sggNm = carFfnlgTrgtMapper.selectSggNmBySggCd(sggCd);
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER; // "03"
```
#### 결과 처리
| 구분 | 조건 | 비고 컬럼 형식 |
|------|------|---------------|
| 이첩-1 | `DAYCNT <= 115` | `"서울시 용산구/ 이경호, 검사일사용본거지, [검사대상, 사용자 조직코드 앞 4자리 및 법정동명]"` |
| 이첩-2 | `DAYCNT > 115` | `"전라남도 순천시 / 김정대, 115일 도래지, [2개의 api 법정동코드 및 법정동명]"` |
| 이첩-1 | `DAYCNT <= 115` | `"{시군구명}, 검사일사용본거지, [검사대상, 사용자 조직코드 앞 4자리: {userOrg4}, 법정동명: {sggNm}]"` |
| 이첩-2 | `DAYCNT > 115` | `"{시군구명}, 115일 도래지, [법정동코드: {legalDong4}, 법정동명: {sggNm}]"` |
- 메서드: `ComparisonRemarkBuilder.buildTransferRemark()`
#### DB 업데이트 필드
```java
existingData.setCarBassMatterInqireId(response.getGeneratedId());
existingData.setTaskPrcsSttsCd("03");
existingData.setTaskPrcsYmd(오늘일자);
existingData.setCarBscMttrInqFlnm(existingData.getOwnrNm()); // 기존 소유자명 유지
existingData.setCarBscMttrInqSggCd(sggCd); // 시군구 코드
existingData.setCarBscMttrInqSggNm(sggNm); // 시군구명
existingData.setRmrk(비고);
```
---
@ -468,6 +980,40 @@ public String executeComparison(CarFfnlgTrgtVO existingData) {
---
## Javadoc 주석과 실제 로직 차이점
### 1. checkInvestigationClosedByProductUseWithin31Days() (2번 메서드)
**Javadoc 주석**:
```
- 3단계: 4번 API 조회 소유자명.contains("상품용")
```
**실제 로직**:
```java
if (step4OwnerName == null || !step4OwnerName.contains("상품용")) {
return null; // 상품용 미포함 체크
}
```
**차이점**: Javadoc은 "상품용 포함"이라고 되어 있으나, 실제 로직은 "상품용 **미포함**" 체크
### 2. 날짜 비교 기준 (2번, 4번 메서드)
**Javadoc 주석**:
```
• CHG_YMD <= 검사일자
```
**실제 변수명**:
```java
LocalDate inspEndDate = DateUtil.parseDate(inspYmd); // inspYmd 사용!
```
**차이점**: 변수명은 `inspEndDate`지만 실제로는 `inspYmd` (검사일자)를 파싱함
---
## 구현 완료 상태
### ✅ 1. 상품용 로직
@ -513,3 +1059,44 @@ public String executeComparison(CarFfnlgTrgtVO existingData) {
- 상품용이면 이미 2번 또는 4번에서 처리되어야 함
2. **로직 순서 중요**: 상품용 케이스(2, 4번)보다 반드시 뒤에 실행
3. **DB 저장값**: step4OwnerName을 CAR_BSC_MTTR_INQ_FLNM에 저장
### Javadoc과 실제 로직 차이
1. **2번 메서드**: Javadoc에는 "상품용 포함"이라고 되어 있으나 실제는 "상품용 미포함" 체크
2. **날짜 비교**: inspEndDate 변수명을 사용하지만 실제로는 inspYmd (검사일자) 파싱
---
## 참고 상수 및 유틸리티
### 상수
```java
// 일수 기준값
private static final int DAYS_THRESHOLD = 31;
// 처리상태코드
TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE = "02"
TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER = "03"
TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED = "04"
TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY = "05"
// 변경업무구분코드
"11" = 명의이전
```
### 유틸리티
```java
// 날짜 파싱 (yyyyMMdd, yyyy-MM-dd 지원)
LocalDate DateUtil.parseDate(String dateStr)
// 일수 계산
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate)
// 날짜 포맷
String DateUtil.formatDateString(String date, String format)
```
---
**문서 작성 완료일**: 2025-12-03
**실제 소스 코드 기준**: ComparisonServiceImpl.java
**분석 대상 메서드**: 6개 (상품용, 내사종결 2개, 날짜수정 2개, 이첩)

@ -154,6 +154,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
NewBasicResponse.Record step1Record = step1Response.getRecord().get(0);
String vin = step1Record.getVin(); // 차대번호
String step1wnerName = step1Record.getRprsOwnrNm(); // 검사일 기준 소유자명
String step1RprsvOwnrIdecno = step1Record.getRprsvOwnrIdecno(); // 검사일 기준 대표소유자 회원번호
log.info("[상품용] Step 1 결과 - 차대번호: {}, 소유자명: {}", vin, step1wnerName);
@ -272,16 +273,19 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
NewBasicResponse.Record step4Record = step4Response.getRecord().get(0);
String step4OwnerName = step4Record.getRprsOwnrNm(); // CHG_YMD 시점의 소유자명
String step4RprsvOwnrIdecno = step4Record.getRprsvOwnrIdecno(); // 대표소유자 회원번호
log.info("[상품용] Step 4 결과 - 소유자명: {}", step4OwnerName);
// ========== 소유자명 비교 ==========
if (step4OwnerName == null || !step4OwnerName.equals(step1wnerName)) {
log.debug("[상품용] 소유자명 불일치 - Step1 소유자명: {}, Step4 소유자명: {}", step1wnerName, step4OwnerName);
// ========== 소유자명 -> 소유자 회원번호 비교(주민,법인,사업자번호) 비교 ==========
//if (step4OwnerName == null || !step4OwnerName.equals(step1wnerName)) {
//log.debug("[상품용] 소유자명 불일치 - Step1 소유자명: {}, Step4 소유자명: {}", step1wnerName, step4OwnerName);
if (step4OwnerName == null || !step4RprsvOwnrIdecno.equals(step1RprsvOwnrIdecno)) {
log.debug("[상품용] 소유자명 불일치 - Step1 소유자: {}, Step4 소유자: {}", step1RprsvOwnrIdecno, step4RprsvOwnrIdecno);
return null;
}
log.info("[상품용] 소유자 일치 확인! - 소유자명: {}", step1wnerName);
log.info("[상품용] 소유자 일치 확인! - 소유자명: {}", step1wnerName);
// ========== 필요 없음.
// 어차피 상단에서 TB_CAR_FFNLG_TRGT.검사종료일자 작은것중의 마지막일자를 찾기때문에
@ -344,7 +348,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
* CHG_YMD
* CHG_TASK_SE_CD == "11" ()
* 31
* - 3: 4 API .contains("상품용")
* - 3: 4 API !.contains("상품용") return null,
*
* @param existingData
* @return 04 () null ()
@ -357,14 +361,14 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
try {
// ========== Step 1: 자동차기본정보 조회 (차량번호, 부과일자=검사일) ==========
log.info("[내사종결] Step 1: 자동차기본정보 조회 - 차량번호: {}, 검사일: {}", vhclno, inspYmd);
log.info("[내사종결-명의이전 상품용] Step 1: 자동차기본정보 조회 - 차량번호: {}, 검사일: {}", vhclno, inspYmd);
NewBasicRequest step1Request = createBasicRequest(vhclno, null, inspYmd);
NewBasicResponse step1Response = apiService.getBasicInfo(step1Request);
bassMatterLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step1Response, existingData.getCarFfnlgTrgtId());
if (step1Response == null || step1Response.getRecord() == null || step1Response.getRecord().isEmpty()) {
log.warn("[내사종결] Step 1 응답 없음 - 차량번호: {}", vhclno);
log.warn("[내사종결-명의이전 상품용] Step 1 응답 없음 - 차량번호: {}", vhclno);
return null;
}
@ -372,26 +376,26 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
String vin = step1Record.getVin(); // 차대번호
String step1OwnerName = step1Record.getRprsOwnrNm(); // 검사일 기준 소유자명
log.info("[내사종결] Step 1 결과 - 차대번호: {}, 소유자명: {}", vin, step1OwnerName);
log.info("[내사종결-명의이전 상품용] Step 1 결과 - 차대번호: {}, 소유자명: {}", vin, step1OwnerName);
// 조건 1: 소유자명에 "상품용" 포함 여부 확인
if (step1OwnerName == null || step1OwnerName.contains("상품용")) {
log.debug("[내사종결] 소유자명에 '상품용' 미포함 - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName);
log.debug("[내사종결-명의이전 상품용] 소유자명에 '상품용' 미포함 - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName);
return null;
}
log.info("[내사종결] 소유자명에 '상품용' 포함 확인! - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName);
log.info("[내사종결-명의이전 상품용] 소유자명에 '상품용' 포함 확인! - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName);
// ========== Step 2: 자동차기본정보 조회 (차대번호, 부과일자=오늘일자) ==========
String today = LocalDate.now().format(DATE_FORMATTER);
log.info("[내사종결] Step 2: 자동차기본정보 조회 - 차대번호: {}, 오늘일자: {}", vin, today);
log.info("[내사종결-명의이전 상품용] Step 2: 자동차기본정보 조회 - 차대번호: {}, 오늘일자: {}", vin, today);
NewBasicRequest step2Request = createBasicRequest(null, vin, today);
NewBasicResponse step2Response = apiService.getBasicInfo(step2Request);
bassMatterLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step2Response, existingData.getCarFfnlgTrgtId());
if (step2Response == null || step2Response.getRecord() == null || step2Response.getRecord().isEmpty()) {
log.warn("[내사종결] Step 2 응답 없음 - 차대번호: {}", vin);
log.warn("[내사종결-명의이전 상품용] Step 2 응답 없음 - 차대번호: {}", vin);
return null;
}
@ -401,11 +405,11 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
String currentIdecno = step2Record.getRprsvOwnrIdecno();
String currentLegalDongCode = step2Record.getUsgsrhldStdgCd();
log.info("[내사종결] Step 2 결과 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}",
log.info("[내사종결-명의이전 상품용] Step 2 결과 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}",
currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode);
// ========== Step 3: 자동차등록원부(갑) 조회 ==========
log.info("[내사종결] Step 3: 자동차등록원부(갑) 조회 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}",
log.info("[내사종결-명의이전 상품용] Step 3: 자동차등록원부(갑) 조회 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}",
currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode);
NewLedgerRequest step3Request = createLedgerRequest(currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode);
@ -413,18 +417,18 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
ledgerLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step3Response, existingData.getCarFfnlgTrgtId());
if (step3Response == null) {
log.warn("[내사종결] Step 3 응답 없음 - 차량번호: {}", currentVhclno);
log.warn("[내사종결-명의이전 상품용] Step 3 응답 없음 - 차량번호: {}", currentVhclno);
return null;
}
List<NewLedgerResponse.Record> ledgerRecords = step3Response.getRecord();
if (ledgerRecords == null || ledgerRecords.isEmpty()) {
log.debug("[내사종결] 갑부 상세 내역 없음 - 차량번호: {}", vhclno);
log.debug("[내사종결-명의이전 상품용] 갑부 상세 내역 없음 - 차량번호: {}", vhclno);
return null;
}
// ========== 갑부 상세에서 조건에 맞는 레코드 찾기 ==========
log.info("[내사종결] 갑부 상세 레코드 검색 시작 - 검사일: {}, 검사종료일자: {}", inspYmd, inspEndYmd);
log.info("[내사종결-명의이전 상품용] 갑부 상세 레코드 검색 시작 - 검사일: {}, 검사종료일자: {}", inspYmd, inspEndYmd);
NewLedgerResponse.Record targetRecord = null;
LocalDate latestChgDate = null;
@ -447,7 +451,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
// 조건: CHG_YMD <= 검사일자
LocalDate inspEndDate = DateUtil.parseDate(inspYmd);
if (chgDate.isAfter(inspEndDate)) {
log.debug("[내사종결] CHG_YMD > 검사종료일자 - 변경일자: {}, 검사종료일자: {}", chgYmd, inspEndYmd);
log.debug("[내사종결-명의이전 상품용] CHG_YMD > 검사종료일자 - 변경일자: {}, 검사종료일자: {}", chgYmd, inspEndYmd);
continue;
}
@ -455,54 +459,54 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
if (latestChgDate == null || chgDate.isAfter(latestChgDate)) {
latestChgDate = chgDate;
targetRecord = record;
log.debug("[내사종결] 조건 충족 레코드 발견 - 변경일자: {}, 변경업무: {}", chgYmd, chgTaskSeCd);
log.debug("[내사종결-명의이전 상품용] 조건 충족 레코드 발견 - 변경일자: {}, 변경업무: {}", chgYmd, chgTaskSeCd);
}
}
if (targetRecord == null) {
log.debug("[내사종결] 조건에 맞는 명의이전 레코드 없음 - 차량번호: {}", vhclno);
log.debug("[내사종결-명의이전 상품용] 조건에 맞는 명의이전 레코드 없음 - 차량번호: {}", vhclno);
return null;
}
// 조건: 가장 마지막 명의이전일자가 검사일의 기준일수 이내인지 확인
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween < 0 || daysBetween > DAYS_THRESHOLD) {
log.debug("[내사종결] 명의이전일자가 검사일의 {}일 이내가 아님 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
log.debug("[내사종결-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내가 아님 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
log.info("[내사종결] 명의이전일자가 검사일의 {}일 이내 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
log.info("[내사종결-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
String targetChgYmd = targetRecord.getChgYmd();
log.info("[내사종결] 조건 충족 레코드 선택! 변경일자: {}, 변경업무: {}", targetChgYmd, targetRecord.getChgTaskSeNm());
log.info("[내사종결-명의이전 상품용] 조건 충족 레코드 선택! 변경일자: {}, 변경업무: {}", targetChgYmd, targetRecord.getChgTaskSeNm());
// ========== Step 4: 자동차기본정보 조회 (차대번호, 부과일자=CHG_YMD -1일) ==========
LocalDate targetDate = DateUtil.parseDate(targetChgYmd);
String targetChgYmdMinus1 = targetDate.minusDays(1).format(DATE_FORMATTER);
log.info("[내사종결] Step 4: 자동차기본정보 조회 - 차대번호: {}, 부과일자: {} (원본: {})", vin, targetChgYmdMinus1, targetChgYmd);
log.info("[내사종결-명의이전 상품용] Step 4: 자동차기본정보 조회 - 차대번호: {}, 부과일자: {} (원본: {})", vin, targetChgYmdMinus1, targetChgYmd);
NewBasicRequest step4Request = createBasicRequest(null, vin, targetChgYmdMinus1);
NewBasicResponse step4Response = apiService.getBasicInfo(step4Request);
bassMatterLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step4Response, existingData.getCarFfnlgTrgtId());
if (step4Response == null || step4Response.getRecord() == null || step4Response.getRecord().isEmpty()) {
log.warn("[내사종결] Step 4 응답 없음 - 차대번호: {}, 부과일자: {}", vin, targetChgYmdMinus1);
log.warn("[내사종결-명의이전 상품용] Step 4 응답 없음 - 차대번호: {}, 부과일자: {}", vin, targetChgYmdMinus1);
return null;
}
NewBasicResponse.Record step4Record = step4Response.getRecord().get(0);
String step4OwnerName = step4Record.getRprsOwnrNm(); // CHG_YMD 시점의 소유자명
log.info("[내사종결] Step 4 결과 - 소유자명: {}", step4OwnerName);
log.info("[내사종결-명의이전 상품용] Step 4 결과 - 소유자명: {}", step4OwnerName);
// ========== 소유자명 비교 - 상품용 포함 여부 확인 ==========
if (step4OwnerName == null || step4OwnerName.contains("상품용")) {
log.debug("[내사종결] 소유자명에 '상품용' 포함 - Step4 소유자명: {}", step4OwnerName);
if (step4OwnerName == null || !step4OwnerName.contains("상품용")) {
log.debug("[내사종결-명의이전 상품용] 소유자명에 '상품용' 포함 - Step4 소유자명: {}", step4OwnerName);
return null;
}
log.info("[내사종결] 모든 조건 충족! 차량번호: {}, 변경일자: {}", vhclno, targetChgYmd);
log.info("[내사종결-명의이전 상품용] 모든 조건 충족! 차량번호: {}, 변경일자: {}", vhclno, targetChgYmd);
// ========== 비고 생성 ==========
String rmrk = ComparisonRemarkBuilder.buildCloseProductUseRemark(
@ -524,15 +528,15 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
int updateCount = carFfnlgTrgtMapper.update(existingData);
if (updateCount == 0) {
throw new MessageException(String.format("[내사종결] 업데이트 실패: %s", vhclno));
throw new MessageException(String.format("[내사종결-명의이전 상품용] 업데이트 실패: %s", vhclno));
}
log.info("[내사종결] 처리 완료! 차량번호: {}", vhclno);
log.info("[내사종결-명의이전 상품용] 처리 완료! 차량번호: {}", vhclno);
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;
} catch (Exception e) {
log.error("[내사종결] 검증 중 오류 발생 - 차량번호: {}", vhclno, e);
throw new MessageException(String.format("[내사종결] 검증 중 오류 발생 - 차량번호: %s", vhclno), e);
log.error("[내사종결-명의이전 상품용] 검증 중 오류 발생 - 차량번호: {}", vhclno, e);
throw new MessageException(String.format("[내사종결-명의이전 상품용] 검증 중 오류 발생 - 차량번호: %s", vhclno), e);
}
}
@ -772,14 +776,14 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
try {
// ========== Step 1: 자동차기본정보 조회 (차량번호, 부과일자=검사일) ==========
log.info("[날짜수정후부과] Step 1: 자동차기본정보 조회 - 차량번호: {}, 검사일: {}", vhclno, inspYmd);
log.info("[날짜수정후부과-명의이전 상품용] Step 1: 자동차기본정보 조회 - 차량번호: {}, 검사일: {}", vhclno, inspYmd);
NewBasicRequest step1Request = createBasicRequest(vhclno, null, inspYmd);
NewBasicResponse step1Response = apiService.getBasicInfo(step1Request);
bassMatterLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step1Response, existingData.getCarFfnlgTrgtId());
if (step1Response == null || step1Response.getRecord() == null || step1Response.getRecord().isEmpty()) {
log.warn("[날짜수정후부과] Step 1 응답 없음 - 차량번호: {}", vhclno);
log.warn("[날짜수정후부과-명의이전 상품용] Step 1 응답 없음 - 차량번호: {}", vhclno);
return null;
}
@ -787,26 +791,26 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
String vin = step1Record.getVin(); // 차대번호
String step1OwnerName = step1Record.getRprsOwnrNm(); // 검사일 기준 소유자명
log.info("[날짜수정후부과] Step 1 결과 - 차대번호: {}, 소유자명: {}", vin, step1OwnerName);
log.info("[날짜수정후부과-명의이전 상품용] Step 1 결과 - 차대번호: {}, 소유자명: {}", vin, step1OwnerName);
// 조건 1: 소유자명에 "상품용" 포함 여부 확인
if (step1OwnerName == null || step1OwnerName.contains("상품용")) {
log.debug("[날짜수정후부과] 소유자명에 '상품용' 미포함 - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName);
log.debug("[날짜수정후부과-명의이전 상품용] 소유자명에 '상품용' 미포함 - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName);
return null;
}
log.info("[날짜수정후부과] 소유자명에 '상품용' 포함 확인! - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName);
log.info("[날짜수정후부과-명의이전 상품용] 소유자명에 '상품용' 포함 확인! - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName);
// ========== Step 2: 자동차기본정보 조회 (차대번호, 부과일자=오늘일자) ==========
String today = LocalDate.now().format(DATE_FORMATTER);
log.info("[날짜수정후부과] Step 2: 자동차기본정보 조회 - 차대번호: {}, 오늘일자: {}", vin, today);
log.info("[날짜수정후부과-명의이전 상품용] Step 2: 자동차기본정보 조회 - 차대번호: {}, 오늘일자: {}", vin, today);
NewBasicRequest step2Request = createBasicRequest(null, vin, today);
NewBasicResponse step2Response = apiService.getBasicInfo(step2Request);
bassMatterLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step2Response, existingData.getCarFfnlgTrgtId());
if (step2Response == null || step2Response.getRecord() == null || step2Response.getRecord().isEmpty()) {
log.warn("[날짜수정후부과] Step 2 응답 없음 - 차대번호: {}", vin);
log.warn("[날짜수정후부과-명의이전 상품용] Step 2 응답 없음 - 차대번호: {}", vin);
return null;
}
@ -816,11 +820,11 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
String currentIdecno = step2Record.getRprsvOwnrIdecno();
String currentLegalDongCode = step2Record.getUsgsrhldStdgCd();
log.info("[날짜수정후부과] Step 2 결과 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}",
log.info("[날짜수정후부과-명의이전 상품용] Step 2 결과 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}",
currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode);
// ========== Step 3: 자동차등록원부(갑) 조회 ==========
log.info("[날짜수정후부과] Step 3: 자동차등록원부(갑) 조회 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}",
log.info("[날짜수정후부과-명의이전 상품용] Step 3: 자동차등록원부(갑) 조회 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}",
currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode);
NewLedgerRequest step3Request = createLedgerRequest(currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode);
@ -828,18 +832,18 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
ledgerLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step3Response, existingData.getCarFfnlgTrgtId());
if (step3Response == null) {
log.warn("[날짜수정후부과] Step 3 응답 없음 - 차량번호: {}", currentVhclno);
log.warn("[날짜수정후부과-명의이전 상품용] Step 3 응답 없음 - 차량번호: {}", currentVhclno);
return null;
}
List<NewLedgerResponse.Record> ledgerRecords = step3Response.getRecord();
if (ledgerRecords == null || ledgerRecords.isEmpty()) {
log.debug("[날짜수정후부과] 갑부 상세 내역 없음 - 차량번호: {}", vhclno);
log.debug("[날짜수정후부과-명의이전 상품용] 갑부 상세 내역 없음 - 차량번호: {}", vhclno);
return null;
}
// ========== 갑부 상세에서 조건에 맞는 레코드 찾기 ==========
log.info("[날짜수정후부과] 갑부 상세 레코드 검색 시작 - 검사일: {}, 검사종료일자: {}", inspYmd, inspEndYmd);
log.info("[날짜수정후부과-명의이전 상품용] 갑부 상세 레코드 검색 시작 - 검사일: {}, 검사종료일자: {}", inspYmd, inspEndYmd);
NewLedgerResponse.Record targetRecord = null;
LocalDate latestChgDate = null;
@ -862,7 +866,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
// 조건: CHG_YMD <= 검사일자
LocalDate inspEndDate = DateUtil.parseDate(inspYmd);
if (chgDate.isAfter(inspEndDate)) {
log.debug("[날짜수정후부과] CHG_YMD > 검사종료일자 - 변경일자: {}, 검사종료일자: {}", chgYmd, inspEndYmd);
log.debug("[날짜수정후부과-명의이전 상품용] CHG_YMD > 검사종료일자 - 변경일자: {}, 검사종료일자: {}", chgYmd, inspEndYmd);
continue;
}
@ -870,23 +874,23 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
if (latestChgDate == null || chgDate.isAfter(latestChgDate)) {
latestChgDate = chgDate;
targetRecord = record;
log.debug("[날짜수정후부과] 조건 충족 레코드 발견 - 변경일자: {}, 변경업무: {}", chgYmd, chgTaskSeCd);
log.debug("[날짜수정후부과-명의이전 상품용] 조건 충족 레코드 발견 - 변경일자: {}, 변경업무: {}", chgYmd, chgTaskSeCd);
}
}
if (targetRecord == null) {
log.debug("[날짜수정후부과] 조건에 맞는 명의이전 레코드 없음 - 차량번호: {}", vhclno);
log.debug("[날짜수정후부과-명의이전 상품용] 조건에 맞는 명의이전 레코드 없음 - 차량번호: {}", vhclno);
return null;
}
// 조건: 가장 마지막 명의이전일자가 검사일의 기준일수 초과인지 확인
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween <= DAYS_THRESHOLD) {
log.debug("[날짜수정후부과] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
log.debug("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
log.info("[날짜수정후부과] 명의이전일자가 검사일의 {}일 초과 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
log.info("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 초과 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
String targetChgYmd = targetRecord.getChgYmd();
@ -895,31 +899,31 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
// ========== Step 4: 자동차기본정보 조회 (차대번호, 부과일자=CHG_YMD -1일) ==========
LocalDate targetDate = DateUtil.parseDate(targetChgYmd);
String targetChgYmdMinus1 = targetDate.minusDays(1).format(DATE_FORMATTER);
log.info("[날짜수정후부과] Step 4: 자동차기본정보 조회 - 차대번호: {}, 부과일자: {} (원본: {})", vin, targetChgYmdMinus1, targetChgYmd);
log.info("[날짜수정후부과-명의이전 상품용] Step 4: 자동차기본정보 조회 - 차대번호: {}, 부과일자: {} (원본: {})", vin, targetChgYmdMinus1, targetChgYmd);
NewBasicRequest step4Request = createBasicRequest(null, vin, targetChgYmdMinus1);
NewBasicResponse step4Response = apiService.getBasicInfo(step4Request);
bassMatterLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step4Response, existingData.getCarFfnlgTrgtId());
if (step4Response == null || step4Response.getRecord() == null || step4Response.getRecord().isEmpty()) {
log.warn("[날짜수정후부과] Step 4 응답 없음 - 차대번호: {}, 부과일자: {}", vin, targetChgYmdMinus1);
log.warn("[날짜수정후부과-명의이전 상품용] Step 4 응답 없음 - 차대번호: {}, 부과일자: {}", vin, targetChgYmdMinus1);
return null;
}
NewBasicResponse.Record step4Record = step4Response.getRecord().get(0);
String step4OwnerName = step4Record.getRprsOwnrNm(); // CHG_YMD 시점의 소유자명
log.info("[날짜수정후부과] Step 4 결과 - 소유자명: {}", step4OwnerName);
log.info("[날짜수정후부과-명의이전 상품용] Step 4 결과 - 소유자명: {}", step4OwnerName);
// ========== 소유자명 비교 - 상품용 포함 여부 확인 ==========
if (step4OwnerName == null || !step4OwnerName.contains("상품용")) {
log.debug("[날짜수정후부과] 소유자명에 '상품용' 미포함 - Step4 소유자명: {}", step4OwnerName);
log.debug("[날짜수정후부과-명의이전 상품용] 소유자명에 '상품용' 미포함 - Step4 소유자명: {}", step4OwnerName);
return null;
}
log.info("[날짜수정후부과] 명의이전 시점 소유자명에 '상품용' 확인! - 소유자명: {}", step4OwnerName);
log.info("[날짜수정후부과-명의이전 상품용] 명의이전 시점 소유자명에 '상품용' 확인! - 소유자명: {}", step4OwnerName);
log.info("[날짜수정후부과] 모든 조건 충족! 차량번호: {}, 변경일자: {}", vhclno, targetChgYmd);
log.info("[날짜수정후부과-명의이전 상품용] 모든 조건 충족! 차량번호: {}, 변경일자: {}", vhclno, targetChgYmd);
// ========== 비고 생성 ==========
String rmrk = ComparisonRemarkBuilder.buildCloseProductUseRemark(
@ -941,15 +945,15 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co
int updateCount = carFfnlgTrgtMapper.update(existingData);
if (updateCount == 0) {
throw new MessageException(String.format("[날짜수정후부과] 업데이트 실패: %s", vhclno));
throw new MessageException(String.format("[날짜수정후부과-명의이전 상품용] 업데이트 실패: %s", vhclno));
}
log.info("[날짜수정후부과] 처리 완료! 차량번호: {}", vhclno);
log.info("[날짜수정후부과-명의이전 상품용] 처리 완료! 차량번호: {}", vhclno);
return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY;
} catch (Exception e) {
log.error("[날짜수정후부과] 검증 중 오류 발생 - 차량번호: {}", vhclno, e);
throw new MessageException(String.format("[날짜수정후부과] 검증 중 오류 발생 - 차량번호: %s", vhclno), e);
log.error("[날짜수정후부과-명의이전 상품용] 검증 중 오류 발생 - 차량번호: {}", vhclno, e);
throw new MessageException(String.format("[날짜수정후부과-명의이전 상품용] 검증 중 오류 발생 - 차량번호: %s", vhclno), e);
}
}

Loading…
Cancel
Save