diff --git a/docs/자동차과태료_비교로직_정리.md b/docs/자동차과태료_비교로직_정리.md index 3314d4e..6cabdad 100644 --- a/docs/자동차과태료_비교로직_정리.md +++ b/docs/자동차과태료_비교로직_정리.md @@ -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개, 이첩) diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonServiceImpl.java b/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonServiceImpl.java index dbe90fa..1975786 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonServiceImpl.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonServiceImpl.java @@ -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 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 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); } }