### feat: 명의이전일자 및 검사일 비교 로직 수정

- **명의이전일자 ~ 검사일 계산 조건 보완**
  - `명의이전일자 < 검사종료일` 조건을 추가하여 검사종료일에 따른 비교 로직 세분화.
  - 검사일과 명의이전일자 간 기준 일수 비교 조건 명확화:
    - 검사종료일 기준 조건 추가.
    - 명의이전일자가 검사일 내 포함되는지 확인 로직 개선.

- **적용 대상**
  - `OwnerCloseWithin31Checker`
  - `OwnerLevyOver31Checker`
  - `ProductCloseWithin31Checker`
  - `ProductLevyOver31Checker`

- **로그 레벨 조정**
  - `TransferOmChecker` 관련 `log.debug`를 `log.info`로 변경하여 로그 가시성 강화.

- **문서 업데이트**
  - `자동차과태료_비교로직_정리-[지연].md`에 관련 변경사항 반영:
    - 명의이전일자 비교 기준 및 로직 설명 수정.

- **기타**
  - 중복 코드 제거 및 가독성을 위한 리팩토링.
  - 검사일자의 파싱 처리 및 최신 명의이전일자 계산 로직 정리.
main
박성영 4 days ago
parent 28fc8c4b87
commit 9c6f16e4b2

@ -12,7 +12,7 @@
- `OwnerCloseWithin31Checker.java` - 3. 내사종결(순수 명의이전, 31일 이내) : 명의이전(25.8.28.)
- `ProductLevyOver31Checker.java` - 4. 날짜수정후부과(명의이전 이전소유자 상품용, 31일 초과) : 경기도 고양시/ 장준혁, 미수검명의이전(25.8.19.)
- `OwnerLevyOver31Checker.java` - 5. 날짜수정후부과(순수 명의이전, 31일 초과) : 대구광역시 달서구/ 하나캐피탈(주), 미수검명의이전(25.9.3.)
- `TransferCase115DayChecker.java` - 6. 이첩 : 경상남도 창원시/ 현대캐피탈 주식회사, 115일 도래지, 인천광역시 부평구/ (주)우리카드, 검사일사용본거지
- `TransferCase115DayChecker.java` - 6. 이첩 : case 1 = 경상남도 창원시/ 현대캐피탈 주식회사, 115일 도래지 case 2 = 인천광역시 부평구/ (주)우리카드, 검사일사용본거지
### 기본 설정
- 비교로직에 사용되는 API: `ExternalVehicleApiServiceImpl.getBasicInfo`, `getLedgerInfo` 호출

@ -119,6 +119,7 @@ public class OwnerCloseWithin31Checker extends AbstractComparisonChecker {
LocalDate vldPrdExpryDateMinus90 = vldPrdExpryDate.minusDays(90);
LocalDate inspEndDate = DateUtil.parseDate(inspEndYmd);
LocalDate latestChgDate = null;
LocalDate inspDate = DateUtil.parseDate(inspYmd);
for (NewLedgerResponse.Record record : ledgerRecords) {
String chgYmd = record.getChgYmd();
@ -156,19 +157,28 @@ public class OwnerCloseWithin31Checker extends AbstractComparisonChecker {
log.info("[내사종결-명의이전] 검사기간 내 명의이전 발견! 변경일자: {}, 변경업무: {}", targetChgYmd, targetRecord.getChgTaskSeNm());
// ========== 명의이전일자 ~ 검사일 사이의 일수 계산 ==========
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("[내사종결-명의이전] 명의이전일자가 검사일의 {}일 이내가 아님 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetChgYmd, inspYmd, daysBetween);
return null;
// 명의이전일자 < 검사종료일
long daysBetween = 0;
if (latestChgDate.isBefore(inspEndDate)) {
// 검사일 - 검사종료일
daysBetween = java.time.temporal.ChronoUnit.DAYS.between(inspEndDate, inspDate);
if (daysBetween < 0 || daysBetween > DAYS_THRESHOLD) {
log.debug("[내사종결-명의이전] 명의이전일자가 검사일의 {}일 이내가 아님 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
} else {
// 조건: 가장 마지막 명의이전일자가 검사일의 기준일수 이내인지 확인, (검사일자 - 마지막 명의이전일자)
daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween < 0 || daysBetween > DAYS_THRESHOLD) {
log.debug("[내사종결-명의이전] 명의이전일자가 검사일의 {}일 이내가 아님 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
log.info("[내사종결-명의이전] 명의이전일자가 검사일의 {}일 이내 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
}
log.info("[내사종결-명의이전] 명의이전일자가 검사일의 {}일 이내 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetChgYmd, inspYmd, daysBetween);
// ========== Step 4: 자동차기본정보 조회 (차대번호, 부과일자=CHG_YMD) ==========
LocalDate targetDate = DateUtil.parseDate(targetChgYmd);
NewBasicRequest step4Request = createBasicRequest(null, vin, targetDate.format(DATE_FORMATTER));

@ -119,6 +119,7 @@ public class OwnerLevyOver31Checker extends AbstractComparisonChecker {
LocalDate vldPrdExpryDateMinus90 = vldPrdExpryDate.minusDays(90);
LocalDate inspEndDate = DateUtil.parseDate(inspEndYmd);
LocalDate latestChgDate = null;
LocalDate inspDate = DateUtil.parseDate(inspYmd);
for (NewLedgerResponse.Record record : ledgerRecords) {
String chgYmd = record.getChgYmd();
@ -156,19 +157,28 @@ public class OwnerLevyOver31Checker extends AbstractComparisonChecker {
log.info("[날짜수정후부과-명의이전] 검사기간 내 명의이전 발견! 변경일자: {}, 변경업무: {}", targetChgYmd, targetRecord.getChgTaskSeNm());
// ========== 명의이전일자 ~ 검사일 사이의 일수 계산 ==========
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("[날짜수정후부과-명의이전] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetChgYmd, inspYmd, daysBetween);
return null;
// 명의이전일자 < 검사종료일
long daysBetween = 0;
if (latestChgDate.isBefore(inspEndDate)) {
// 검사일 - 검사종료일
daysBetween = java.time.temporal.ChronoUnit.DAYS.between(inspEndDate, inspDate);
if (daysBetween <= DAYS_THRESHOLD) {
log.debug("[날짜수정후부과-명의이전] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
} else {
// 조건: 가장 마지막 명의이전일자가 검사일의 기준일수 초과인지 확인, (검사일자 - 마지막 명의이전일자)
daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween <= DAYS_THRESHOLD) {
log.debug("[날짜수정후부과-명의이전] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
log.info("[날짜수정후부과-명의이전] 명의이전일자가 검사일의 {}일 초과 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
}
log.info("[날짜수정후부과-명의이전] 명의이전일자가 검사일의 {}일 초과 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetChgYmd, inspYmd, daysBetween);
// ========== Step 4: 자동차기본정보 조회 (차대번호, 부과일자=CHG_YMD) ==========
LocalDate targetDate = DateUtil.parseDate(targetChgYmd);
NewBasicRequest step4Request = createBasicRequest(null, vin, targetDate.format(DATE_FORMATTER));

@ -151,15 +151,28 @@ public class ProductCloseWithin31Checker extends AbstractComparisonChecker {
return null;
}
// 조건: 가장 마지막 명의이전일자가 검사일의 기준일수 이내인지 확인
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween < 0 || daysBetween > DAYS_THRESHOLD) {
log.debug("[내사종결-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내가 아님 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
// 명의이전일자 < 검사종료일
long daysBetween = 0;
LocalDate inspEndDate = DateUtil.parseDate(inspEndYmd);
if (latestChgDate.isBefore(inspEndDate)) {
// 검사일 - 검사종료일
daysBetween = java.time.temporal.ChronoUnit.DAYS.between(inspEndDate, inspDate);
if (daysBetween < 0 || daysBetween > DAYS_THRESHOLD) {
log.debug("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
} else {
// 조건: 가장 마지막 명의이전일자가 검사일의 기준일수 초과인지 확인, (검사일자 - 마지막 명의이전일자)
daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween < 0 || daysBetween > DAYS_THRESHOLD) {
log.debug("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
log.info("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 초과 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
log.info("[내사종결-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
String targetChgYmd = targetRecord.getChgYmd();
log.info("[내사종결-명의이전 상품용] 조건 충족 레코드 선택! 변경일자: {}, 변경업무: {}", targetChgYmd, targetRecord.getChgTaskSeNm());

@ -151,15 +151,28 @@ public class ProductLevyOver31Checker extends AbstractComparisonChecker {
return null;
}
// 조건: 가장 마지막 명의이전일자가 검사일의 기준일수 초과인지 확인
long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween <= DAYS_THRESHOLD) {
log.debug("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
// 명의이전일자 < 검사종료일
long daysBetween = 0;
LocalDate inspEndDate = DateUtil.parseDate(inspEndYmd);
if (latestChgDate.isBefore(inspEndDate)) {
// 검사일 - 검사종료일
daysBetween = java.time.temporal.ChronoUnit.DAYS.between(inspEndDate, inspDate);
if (daysBetween <= DAYS_THRESHOLD) {
log.debug("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
} else {
// 조건: 가장 마지막 명의이전일자가 검사일의 기준일수 초과인지 확인, (검사일자 - 마지막 명의이전일자)
daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate);
if (daysBetween <= DAYS_THRESHOLD) {
log.debug("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
log.info("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 초과 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
return null;
}
log.info("[날짜수정후부과-명의이전 상품용] 명의이전일자가 검사일의 {}일 초과 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일",
DAYS_THRESHOLD, targetRecord.getChgYmd(), inspYmd, daysBetween);
String targetChgYmd = targetRecord.getChgYmd();
log.info("[날짜수정후부과] 조건 충족 레코드 선택! 변경일자: {}, 변경업무: {}", targetChgYmd, targetRecord.getChgTaskSeNm());

@ -60,7 +60,7 @@ public class TransferOmChecker extends AbstractComparisonOmChecker {
}
if (inspVldPrdEnd == null) {
log.debug("[이첩-미필] 검사유효기간 종료일 없음 - 차량번호: {}", vhclno);
log.info("[이첩-미필] 검사유효기간 종료일 없음 - 차량번호: {}", vhclno);
return null;
}
@ -86,7 +86,7 @@ public class TransferOmChecker extends AbstractComparisonOmChecker {
// 소유자명에 "상품용" 포함 여부 체크
if (step1RprsOwnrNm != null && step1RprsOwnrNm.contains("상품용")) {
log.debug("[이첩-미필] 소유자명에 '상품용' 포함 - 차량번호: {}, 소유자명: {}", vhclno, step1RprsOwnrNm);
log.info("[이첩-미필] 소유자명에 '상품용' 포함 - 차량번호: {}, 소유자명: {}", vhclno, step1RprsOwnrNm);
return null;
}
@ -115,7 +115,7 @@ public class TransferOmChecker extends AbstractComparisonOmChecker {
// 2단계 비교: 1단계 소유자 = 2단계 소유자 동일 체크
if (step1RprsvOwnrIdecno != null && !step1RprsvOwnrIdecno.equals(step2RprsvOwnrIdecno)) {
log.debug("[이첩-미필] 1단계와 2단계 소유자 동일 - 차량번호: {}, 소유자: {}", vhclno, step1RprsOwnrNm);
log.info("[이첩-미필] 1단계와 2단계 소유자 동일 - 차량번호: {}, 소유자: {}", vhclno, step1RprsOwnrNm);
return null;
}
@ -140,20 +140,20 @@ public class TransferOmChecker extends AbstractComparisonOmChecker {
// 3단계 비교: 1단계 소유자 = 3단계 소유자 동일 체크
if (step1RprsvOwnrIdecno != null && !step1RprsvOwnrIdecno.equals(step3RprsvOwnrIdecno)) {
log.debug("[이첩-미필] 1단계와 3단계 소유자 동일 - 차량번호: {}, 소유자: {}", vhclno, step1RprsOwnrNm);
log.info("[이첩-미필] 1단계와 3단계 소유자 동일 - 차량번호: {}, 소유자: {}", vhclno, step1RprsOwnrNm);
return null;
}
// 법정동코드 유효성 검사
if (step1UsgsrhldStdgCd == null || step1UsgsrhldStdgCd.length() < 4) {
log.debug("[이첩-미필] 법정동코드 없음 - 차량번호: {}", vhclno);
log.info("[이첩-미필] 법정동코드 없음 - 차량번호: {}", vhclno);
return null;
}
// 세션에서 사용자 정보 조회
LoginUserVO userInfo = SessionUtil.getLoginUser();
if (userInfo == null || userInfo.getOrgCd() == null) {
log.debug("[이첩-미필] 사용자 정보 없음");
log.info("[이첩-미필] 사용자 정보 없음");
return null;
}
@ -163,7 +163,7 @@ public class TransferOmChecker extends AbstractComparisonOmChecker {
String userOrg4 = userOrgCd.length() >= 4 ? userOrgCd.substring(0, 4) : userOrgCd;
if (legalDong4.equals(userOrg4)) {
log.debug("[이첩-미필] 법정동코드 일치 - 차량번호: {}, 법정동: {}, 조직: {}",
log.info("[이첩-미필] 법정동코드 일치 - 차량번호: {}, 법정동: {}, 조직: {}",
vhclno, legalDong4, userOrg4);
return null;
}

Loading…
Cancel
Save