You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
VIPS/docs/자동차과태료_비교로직_정리.md

24 KiB

자동차 과태료 비교 로직 명세서

개요

자동차 과태료 부과 대상을 검증하기 위한 비교 로직 정의서입니다.

구현 위치

  • 서비스: ComparisonServiceImpl.java
  • 경로: src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonServiceImpl.java

기본 설정

  • 비교로직에 사용되는 API는 ExternalVehicleApiServiceImpl.getBasicInfo, getLedgerInfo 호출
  • 날짜 유틸리티: DateUtil.parseDate(), DateUtil.isDateBetween(), DateUtil.formatDateString() 사용

문서 이력

일자 변경 내용 비고
2025-12-02 내사종결 로직 추가 및 상품용 조건 변경 명의이전 30일 이내 조건 추가
2025-01-XX 상품용 로직 완전 변경 API 호출 4단계, 소유자명 일치 확인 추가

처리 규칙

중요: 순서가 중요함!

    1. 상품용 검증 (Case1) → 2. 내사종결 상품용 검증 (Case2) → 3. 이첩 검증
  • 조건에 걸리는 순간 다음 차량번호 비교로 진행
  • 각 비교 로직별로 개별 API 호출 수행

비교 로직 상세

1. 상품용 검증 Case1 - 검사일 소유자가 상품용

처리상태코드: 02 (상품용) 메서드: checkProductUseCase1() (ComparisonServiceImpl.java:113~306)

API 호출 순서

순서 API 입력 파라미터 출력 데이터 용도
1 자동차기본정보 차량번호, 부과일자=검사일 차대번호, 소유자명 검사일 기준 소유자 확인
2 자동차기본정보 1.차대번호, 부과일자=오늘일자 차량번호, 성명, 민원인주민번호, 민원인법정동코드 현재 소유자 정보
3 자동차등록원부(갑) 2.차량번호, 2.성명, 2.민원인주민번호, 2.민원인법정동코드 갑부 상세 List 명의이전 이력 조회
4 자동차기본정보 1.차대번호, 부과일자=조건에맞는CHG_YMD 소유자명 명의이전 시점 소유자 확인

비교 조건 (순차 실행)

// 조건 1: 소유자명에 '상품용' 포함 여부 (Line 138-142)
if (!step1Record.소유자명.contains("상품용")) {
    return null;  // 미충족
}

// 조건 2: 갑부 상세에서 조건에 맞는 명의이전 레코드 찾기 (Line 187-234)
LocalDate latestChgDate = null;
NewLedgerResponse.Record targetRecord = null;

for (NewLedgerResponse.Record record : ledgerRecords) {
    // 2-1. 명의이전 코드 확인 (Line 199)
    if (!"11".equals(record.CHG_TASK_SE_CD)) {
        continue;  // 명의이전이 아니면 스킵
    }

    // 2-2. CHG_YMD <= 검사종료일자 (Line 208-213)
    if (record.CHG_YMD > TB_CAR_FFNLG_TRGT.검사종료일자) {
        continue;  // 검사종료일자 이후면 스킵
    }

    // 주석처리됨: CHG_YMD <= 검사일 조건 (Line 215-221)
    // → 검사일 이후 데이터도 포함 (검사종료일자까지만 제한)

    // 2-3. 가장 마지막 일자 찾기 (Line 224-228)
    if (latestChgDate == null || record.CHG_YMD > latestChgDate) {
        latestChgDate = record.CHG_YMD;
        targetRecord = record;
    }
}

if (targetRecord == null) {
    return null;  // 조건에 맞는 레코드 없음
}

// 조건 3: 명의이전 시점 소유자명 == 검사일 기준 소유자명 (Line 257-260)
if (!step4Record.소유자명.equals(step1Record.소유자명)) {
    return null;  // 소유자명 불일치
}

// 주석처리됨: CHG_YMD 기간 범위 확인 (Line 264-273)
// → 기간 범위 체크하지 않음

return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE;  // 상품용 조건 충족

주요 변경사항

  1. 주석처리된 조건:

    • CHG_YMD <= 검사일 (검사일 이전일자 중) → 제거됨
    • CHG_YMD가 유효기간만료일 ~ 검사종료일자 범위 내 → 제거됨
  2. 적용되는 조건:

    • CHG_YMD <= 검사종료일자 (검사종료일자 이전)
    • 명의이전(코드 11) 레코드 중 가장 마지막 일자
    • 소유자명 일치 확인

결과 처리

  • 업무 처리 상태 코드: 02 (상품용)
  • 비고 컬럼 형식: (Line 732-761, 대부분 주석처리됨)
상품용 - 상품용검사

2. 내사종결 상품용 검증 Case2 - 명의이전 후 상품용

처리상태코드: 04 (내사종결) 메서드: checkCloseProductUse() (ComparisonServiceImpl.java:329~515)

API 호출 순서

순서 API 입력 파라미터 출력 데이터 용도
1 자동차기본정보 차량번호, 부과일자=검사일 차대번호, 소유자명 검사일 기준 소유자 확인
2 자동차기본정보 1.차대번호, 부과일자=오늘일자 차량번호, 성명, 민원인주민번호, 민원인법정동코드 현재 소유자 정보
3 자동차등록원부(갑) 2.차량번호, 2.성명, 2.민원인주민번호, 2.민원인법정동코드 갑부 상세 List 명의이전 이력 조회
4 자동차기본정보 1.차대번호, 부과일자=CHG_YMD-1일 소유자명 명의이전 직전 소유자 확인

비교 조건 (순차 실행)

// 조건 1: 소유자명에 '상품용' 미포함 (Line 354-358)
if (step1Record.소유자명 == null || step1Record.소유자명.contains("상품용")) {
    return null;  // 미충족 (검사일 소유자가 상품용이면 Case1에서 처리됨)
}

// 조건 2: 갑부 상세에서 조건에 맞는 명의이전 레코드 찾기 (Line 403-437)
LocalDate latestChgDate = null;
NewLedgerResponse.Record targetRecord = null;

for (NewLedgerResponse.Record record : ledgerRecords) {
    // 2-1. 명의이전 코드 확인 (Line 415)
    if (!"11".equals(record.CHG_TASK_SE_CD)) {
        continue;  // 명의이전이 아니면 스킵
    }

    // 2-2. CHG_YMD <= 검사일자 (Line 424-429)
    if (record.CHG_YMD > TB_CAR_FFNLG_TRGT.검사일자) {
        continue;  // 검사일자 이후면 스킵
    }

    // 2-3. 가장 마지막 일자 찾기 (Line 432-436)
    if (latestChgDate == null || record.CHG_YMD > latestChgDate) {
        latestChgDate = record.CHG_YMD;
        targetRecord = record;
    }
}

if (targetRecord == null) {
    return null;  // 조건에 맞는 레코드 없음
}

// 조건 3: 명의이전일자가 검사일의 30일 이내인지 확인 (Line 444-450)
long daysBetween = ChronoUnit.DAYS.between(latestChgDate, 검사일);
if (daysBetween < 0 || daysBetween > 30) {
    return null;  // 30일 초과
}

// 조건 4: 명의이전 직전 시점(CHG_YMD-1일) 소유자명에 '상품용' 포함 (Line 476-482)
// Step 4 API: 부과일자 = CHG_YMD - 1일 (Line 457-464)
if (step4Record.소유자명 == null || !step4Record.소유자명.contains("상품용")) {
    return null;  // 명의이전 직전 소유자가 상품용이 아님
}

return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED;  // 내사종결 조건 충족

로직 설명

이 로직은 검사일 당시에는 상품용이 아니었지만, 명의이전 이전(30일 이내)에는 상품용이었던 경우를 처리합니다:

  1. 검사일 소유자가 상품용이 아님
  2. 검사일 이전 30일 이내에 명의이전(코드 11) 발생
  3. 명의이전 직전 소유자가 상품용

결과 처리

  • 업무 처리 상태 코드: 04 (내사종결)
  • 비고 컬럼 형식: (Line 784-818)
명의이전(25.9.3.) 이전소유자 상품용
22루2283
 - 검사기간: 2024-08-01 - 2024-08-31
 - 검사일: 2024-08-15
 - 명의이전: 2024-09-03
 - 상품용: 2024-09-03

3. 이첩 검증 (이첩-1, 이첩-2 병합 로직)

처리상태코드: 03 (이첩) 메서드: checkTransferCase115Day() (ComparisonServiceImpl.java:531~648)

필요 API: 자동차기본정보

부과기준일 결정

int dayCnt = TB_CAR_FFNLG_TRGT.DAYCNT;  // textFile 일수

if (dayCnt > 115) {
    // 이첩-2
    부과기준일 = TB_CAR_FFNLG_TRGT.검사종료일자.plusDays(115);
} else {
    // 이첩-1
    부과기준일 = TB_CAR_FFNLG_TRGT.검사일자;
}

API 호출

// 부과기준일 기준으로 자동차기본정보 API 호출
BasicResponse response = 자동차기본정보API.call(부과기준일, 차량번호);

법정동코드 비교 로직 (공통)

/**
 * 이첩 조건: 법정동코드 불일치 검증
 * 사용본거지법정동코드 앞 4자리 != 사용자 조직코드 앞 4자리
 */
private boolean checkTransferCondition_LegalDongMismatch(
    BasicResponse.Record basicInfo,
    String userId,
    String vhclno
) {
    String useStrnghldLegaldongCode = basicInfo.getUseStrnghldLegaldongCode();

    // 1. 법정동코드 유효성 검사
    if (useStrnghldLegaldongCode == null || useStrnghldLegaldongCode.length() < 4) {
        log.debug("[이첩] 법정동코드 없음. 차량번호: {}", vhclno);
        return false;
    }

    // 2. 사용자 정보 조회
    SystemUserVO userInfo = userMapper.selectUser(userId);
    if (userInfo == null || userInfo.getOrgCd() == null) {
        log.debug("[이첩] 사용자 정보 없음. 사용자ID: {}", userId);
        return false;
    }

    // 3. 법정동코드 앞 4자리 vs 사용자 조직코드 앞 4자리 비교
    String legalDong4 = useStrnghldLegaldongCode.substring(0, 4);
    String userOrgCd = userInfo.getOrgCd();
    String userOrg4 = userOrgCd.length() >= 4
        ? userOrgCd.substring(0, 4)
        : userOrgCd;

    // 4. 일치 여부 판단
    if (legalDong4.equals(userOrg4)) {
        log.debug("[이첩] 법정동코드 일치. 차량번호: {}, 법정동: {}, 조직: {}",
            vhclno, legalDong4, userOrg4);
        return false;  // 일치하면 이첩 대상 아님
    }

    log.info("[이첩] 법정동코드 불일치! 차량번호: {}, 법정동: {}, 조직: {}",
        vhclno, legalDong4, userOrg4);
    return true;  // 불일치하면 이첩 대상
}

결과 처리

구분 조건 비고 컬럼 형식
이첩-1 DAYCNT <= 115 "서울시 용산구/ 이경호, 검사일사용본거지, [검사대상, 사용자 조직코드 앞 4자리 및 법정동명]"
이첩-2 DAYCNT > 115 "전라남도 순천시 / 김정대, 115일 도래지, [2개의 api 법정동코드 및 법정동명]"

데이터 모델

TB_CAR_FFNLG_TRGT (과태료 대상 테이블)

컬럼명 설명 용도
검사일 검사 기준일 API 호출 파라미터
검사종료일자 검사 종료 일자 115일 계산 기준
유효기간만료일 유효기간 만료일 상품용 갑부 비교 시작일
DAYCNT textFile 일수 이첩-1/2 분기 조건
비고 검증 결과 메시지 결과 저장

코드 정의

코드 코드값 설명
CHANGE_JOB_SE_CODE 11 명의이전

처리 흐름도

시작
  │
  ▼
[차량번호 조회]
  │
  ▼
┌────────────────────────────────────────────────────────────┐
│ 1. 상품용 Case1 - 검사일 소유자가 상품용                   │
├────────────────────────────────────────────────────────────┤
│ Step 1: 자동차기본정보(차량번호, 검사일)                    │
│         → 소유자명에 "상품용" 포함?                         │
│              │                                              │
│              ├─ (No) ──────────────────────────────────┐   │
│              │                                          │   │
│              └─ (Yes)                                   │   │
│                  ▼                                       │   │
│ Step 2: 자동차기본정보(차대번호, 오늘일자)               │   │
│         → 현재 소유자 정보                               │   │
│              ▼                                           │   │
│ Step 3: 자동차등록원부(갑) 조회                          │   │
│         → 갑부 상세 List                                 │   │
│              ▼                                           │   │
│         검사종료일자 이전 가장 마지막 명의이전(11)       │   │
│         레코드 찾기 (CHG_YMD <= 검사종료일자)            │   │
│              │                                           │   │
│              ├─ (없음) ─────────────────────────────┐   │   │
│              │                                       │   │   │
│              └─ (발견)                               │   │   │
│                  ▼                                   │   │   │
│ Step 4: 자동차기본정보(차대번호, CHG_YMD)            │   │   │
│         → 명의이전 시점 소유자명                     │   │   │
│              │                                       │   │   │
│              ▼                                       │   │   │
│         소유자명 일치?                               │   │   │
│              │                                       │   │   │
│              ├─ (No) ─────────────────────────┐     │   │   │
│              │                                 │     │   │   │
│              └─ (Yes)                          │     │   │   │
│                  ▼                             │     │   │   │
│         [상품용(02) 처리 완료]                  │     │   │   │
│              │                                 │     │   │   │
│              └──> [다음 차량] <────────────────┴─────┴───┘   │
└────────────────────────────────────────────────────────────┘
  │
  │ (조건 미충족)
  ▼
┌────────────────────────────────────────────────────────────┐
│ 2. 내사종결 Case2 - 명의이전 후 상품용                     │
├────────────────────────────────────────────────────────────┤
│ Step 1: 자동차기본정보(차량번호, 검사일)                    │
│         → 소유자명에 "상품용" 미포함?                       │
│              │                                              │
│              ├─ (No) ──────────────────────────────────┐   │
│              │                                          │   │
│              └─ (Yes)                                   │   │
│                  ▼                                       │   │
│ Step 2: 자동차기본정보(차대번호, 오늘일자)               │   │
│         → 현재 소유자 정보                               │   │
│              ▼                                           │   │
│ Step 3: 자동차등록원부(갑) 조회                          │   │
│         → 갑부 상세 List                                 │   │
│              ▼                                           │   │
│         검사일 이전 가장 마지막 명의이전(11)             │   │
│         레코드 찾기 (CHG_YMD <= 검사일)                  │   │
│              │                                           │   │
│              ├─ (없음) ─────────────────────────────┐   │   │
│              │                                       │   │   │
│              └─ (발견)                               │   │   │
│                  ▼                                   │   │   │
│         명의이전일자가 검사일의 30일 이내?           │   │   │
│              │                                       │   │   │
│              ├─ (No) ─────────────────────────┐     │   │   │
│              │                                 │     │   │   │
│              └─ (Yes)                          │     │   │   │
│                  ▼                             │     │   │   │
│ Step 4: 자동차기본정보(차대번호, CHG_YMD-1일)   │     │   │   │
│         → 명의이전 직전 소유자명                │     │   │   │
│              │                                 │     │   │   │
│              ▼                                 │     │   │   │
│         소유자명에 "상품용" 포함?              │     │   │   │
│              │                                 │     │   │   │
│              ├─ (No) ───────────────────┐     │     │   │   │
│              │                           │     │     │   │   │
│              └─ (Yes)                    │     │     │   │   │
│                  ▼                       │     │     │   │   │
│         [내사종결(04) 처리 완료]          │     │     │   │   │
│              │                           │     │     │   │   │
│              └──> [다음 차량] <──────────┴─────┴─────┴───┘   │
└────────────────────────────────────────────────────────────┘
  │
  │ (조건 미충족)
  ▼
┌────────────────────────────────────────────────────────────┐
│ 3. 이첩 검증 (이첩-1, 이첩-2)                               │
├────────────────────────────────────────────────────────────┤
│ DAYCNT 확인                                                 │
│  │                                                          │
│  ├─ (> 115) ──> [이첩-2: 부과기준일 = 검사종료일+115일]    │
│  │                                                          │
│  └─ (<= 115) ─> [이첩-1: 부과기준일 = 검사일]              │
│        │                                                    │
│        ▼                                                    │
│  [자동차기본정보 API 호출]                                  │
│        │                                                    │
│        ▼                                                    │
│  [법정동코드 앞 4자리 vs 사용자 조직코드 앞 4자리]          │
│        │                                                    │
│        ├─ (불일치) ──> [이첩(03) 처리 완료] ──> [다음 차량] │
│        │                                                    │
│        └─ (일치) ────> [조건 미충족] ──> [다음 차량]        │
└────────────────────────────────────────────────────────────┘

구현 시 주의사항

공통 주의사항

  1. API 호출 순서 준수: 각 검증 단계별로 필요한 API만 호출
  2. 조건 우선순위: 상품용 Case1 > 내사종결 Case2 > 이첩 순서로 검증
  3. 조기 종료: 조건 충족 시 즉시 다음 차량으로 이동
  4. 비고 컬럼: 각 조건별 정해진 형식으로 기록
  5. 날짜 형식 지원: yyyyMMdd, yyyy-MM-dd 두 형식 모두 자동 처리 (DateUtil 사용)

상품용 Case1 주의사항

  1. 4단계 API 호출 필수: Step 1~4 모두 순차적으로 실행
  2. 가장 마지막 명의이전 레코드: 검사종료일자 이전일자 중 CHG_YMD가 가장 큰 값 선택
  3. 소유자명 정확한 일치: Step1 소유자명 == Step4 소유자명 (완전 일치)
  4. 주석처리된 조건:
    • 검사일 이전일자 제한 → 검사종료일자 이전일자만 제한
    • 기간 범위 확인 → 제거됨
  5. 로그 상세 기록: 각 단계별 결과를 상세히 로깅하여 디버깅 지원

내사종결 Case2 주의사항

  1. 검사일 소유자 확인: 검사일 소유자가 상품용이 아니어야 함 (Case1과 구분)
  2. 30일 이내 조건: 명의이전일자가 검사일의 30일 이내여야 함
  3. CHG_YMD-1일 조회: Step 4에서 명의이전 직전 시점(CHG_YMD - 1일) 조회
  4. 검사일 기준: 갑부 레코드는 검사일 이전일자 중 가장 마지막 일자 선택
  5. 비고 형식: 명의이전 날짜, 차량번호, 검사기간 등 상세 정보 포함

이첩 검증 주의사항

  1. 법정동코드 길이 검증: 최소 4자리 이상 필요
  2. DAYCNT 기준 명확: > 115 (초과), <= 115 (이하) 구분
  3. 사용자 조직코드 유효성: null 체크 및 길이 확인 필수

요약 정리

비교 로직 실행 순서 (executeComparison)

// ComparisonServiceImpl.java Line 50-85
public String executeComparison(CarFfnlgTrgtVO existingData) {
    // 1. 상품용 Case1 - 검사일 소유자가 상품용
    String result1 = checkProductUseCase1(existingData);
    if (result1 != null) return result1;  // "02"

    // 2. 내사종결 Case2 - 명의이전 후 상품용
    String result2 = checkCloseProductUse(existingData);
    if (result2 != null) return result2;  // "04"

    // 3. 이첩 검증
    String result3 = checkTransferCase115Day(existingData);
    if (result3 != null) return result3;  // "03"

    // 모든 조건 미충족
    return null;
}

처리상태코드 매핑

코드 상태명 처리 로직 메서드
02 상품용 검사일 소유자가 상품용 checkProductUseCase1()
04 내사종결 명의이전(30일 이내) 후 상품용 checkCloseProductUse()
03 이첩 법정동코드 불일치 checkTransferCase115Day()

주요 차이점 정리

구분 Case1 (상품용) Case2 (내사종결)
검사일 소유자 상품용 포함 상품용 미포함
갑부 레코드 기준 CHG_YMD <= 검사종료일자 CHG_YMD <= 검사일
추가 조건 - 명의이전일자가 검사일의 30일 이내
Step 4 조회일 CHG_YMD CHG_YMD - 1일
Step 4 확인사항 소유자명 일치 소유자명에 "상품용" 포함
처리상태코드 02 04

구현 완료 상태

상품용 Case1 로직

  • 구현 완료: 신규 로직으로 구현 완료
  • 구현 위치: ComparisonServiceImpl.checkProductUseCase1() (Line 113-306)
  • 주요 변경사항:
    • 검사일 이전일자 조건 주석처리 → 검사종료일자 이전일자만 제한
    • 기간 범위 확인 조건 주석처리 → 제거됨
    • 비고 내용 간소화

내사종결 Case2 로직 (신규 추가)

  • 구현 완료: 신규 로직 추가 완료
  • 구현 위치: ComparisonServiceImpl.checkCloseProductUse() (Line 329-515)
  • 주요 특징:
    • 명의이전(30일 이내) 후 상품용인 경우 처리
    • CHG_YMD - 1일 기준 조회
    • 상세한 비고 정보 생성

이첩 로직

  • 구현 완료: 이첩-1, 이첩-2 병합 로직 구현 완료
  • 구현 위치: ComparisonServiceImpl.checkTransferCase115Day() (Line 531-648)