diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonRemarkBuilder.java b/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonRemarkBuilder.java index 3f604f6..1156123 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonRemarkBuilder.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/ComparisonRemarkBuilder.java @@ -118,6 +118,34 @@ public class ComparisonRemarkBuilder { return sb.toString(); } + /** + * 날짜 수정 후 부과 비고 생성 + * + * 비고 형식: + * 명의이전일자: YYYY-MM-DD + * 검사일: YYYY-MM-DD + * 일수차이: N일 + * + * @param targetChgYmd 명의이전일자 + * @param inspYmd 검사일 + * @param daysBetween 일수차이 + * @return 비고 문자열 + */ + public static String buildDateModifiedLevyRemark(String targetChgYmd, String inspYmd, long daysBetween) { + StringBuilder sb = new StringBuilder(); + + // 첫 줄: 명의이전일자 + sb.append("명의이전일자: ").append(DateUtil.formatDateString(targetChgYmd)).append("\n"); + + // 둘째 줄: 검사일 + sb.append("검사일: ").append(DateUtil.formatDateString(inspYmd)).append("\n"); + + // 셋째 줄: 일수차이 + sb.append("일수차이: ").append(daysBetween).append("일"); + + return sb.toString(); + } + /** * 등록원부 갑부 레코드 상세 정보 생성 * 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 7a2572b..31f14db 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 @@ -60,21 +60,28 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co return productUseResult1; } - // ========== 2. 내사종결 상품용 체크명의이전 후 상품용인 경우 ========== + // ========== 2. 내사종결 상품용 체크명의이전 후 상품용인 경우, 31일 이내 ========== String closeUseResult = checkCloseProductUse(existingData); if (closeUseResult != null) { log.info("========== 비교 로직 종료 (내사종결 : 상품용인데 기간 지나서 검사 받았을 경우): {} ==========", vhclno); return closeUseResult; } - // ========== 3. 이첩 체크 ========== + // ========== 3. 날짜 수정 후 부과 체크 - 명의이전 후 상품용인 경우 (31일 초과) ========== + String dateModifiedLevyResult = checkDateModifiedLevy(existingData); + if (dateModifiedLevyResult != null) { + log.info("========== 비교 로직 종료 (날짜 수정 후 부과 : 상품용인데 기간 많이 지나서 검사 받았을 경우): {} ==========", vhclno); + return dateModifiedLevyResult; + } + + // ========== 4. 이첩 체크 ========== String transferResult = checkTransferCase115Day(existingData); if (transferResult != null) { log.info("========== 비교 로직 종료 (이첩): {} ==========", vhclno); return transferResult; } - // ========== 4. 향후 추가될 비교 로직들 ========== + // ========== 5. 향후 추가될 비교 로직들 ========== // String investigationClosedResult = checkInvestigationClosed(existingData); // if (investigationClosedResult != null) { // return investigationClosedResult; @@ -517,6 +524,215 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co } } + /** + * 3. 날짜 수정 후 부과 검증 not( api-1번호출.소유자명.contains("상품용") ) + * + * 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("상품용") + * + * @param existingData 과태료 대상 데이터 + * @return 05 (날짜 수정 후 부과) 또는 null (미적용) + */ + private String checkDateModifiedLevy(CarFfnlgTrgtVO existingData) { + String vhclno = existingData.getVhclno(); + String inspYmd = existingData.getInspYmd(); + String vldPrdExpryYmd = existingData.getVldPrdExpryYmd(); + String inspEndYmd = existingData.getInspEndYmd(); + + try { + // ========== Step 1: 자동차기본정보 조회 (차량번호, 부과일자=검사일) ========== + 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); + return null; + } + + NewBasicResponse.Record step1Record = step1Response.getRecord().get(0); + String vin = step1Record.getVin(); // 차대번호 + String step1OwnerName = step1Record.getRprsOwnrNm(); // 검사일 기준 소유자명 + + log.info("[날짜수정후부과] Step 1 결과 - 차대번호: {}, 소유자명: {}", vin, step1OwnerName); + + // 조건 1: 소유자명에 "상품용" 포함 여부 확인 + if (step1OwnerName == null || step1OwnerName.contains("상품용")) { + log.debug("[날짜수정후부과] 소유자명에 '상품용' 미포함 - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName); + return null; + } + + log.info("[날짜수정후부과] 소유자명에 '상품용' 포함 확인! - 차량번호: {}, 소유자명: {}", vhclno, step1OwnerName); + + // ========== Step 2: 자동차기본정보 조회 (차대번호, 부과일자=오늘일자) ========== + String today = LocalDate.now().format(DATE_FORMATTER); + 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); + return null; + } + + NewBasicResponse.Record step2Record = step2Response.getRecord().get(0); + String currentVhclno = step2Record.getVhrno(); + String currentOwnerName = step2Record.getRprsOwnrNm(); + String currentIdecno = step2Record.getRprsvOwnrIdecno(); + String currentLegalDongCode = step2Record.getUsgsrhldStdgCd(); + + log.info("[날짜수정후부과] Step 2 결과 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}", + currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode); + + // ========== Step 3: 자동차등록원부(갑) 조회 ========== + log.info("[날짜수정후부과] Step 3: 자동차등록원부(갑) 조회 - 차량번호: {}, 성명: {}, 주민번호: {}, 법정동코드: {}", + currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode); + + NewLedgerRequest step3Request = createLedgerRequest(currentVhclno, currentOwnerName, currentIdecno, currentLegalDongCode); + NewLedgerResponse step3Response = apiService.getLedgerInfo(step3Request); + ledgerLogService.updateCarFfnlgTrgtIdByTxIdNewTx(step3Response, existingData.getCarFfnlgTrgtId()); + + if (step3Response == null) { + log.warn("[날짜수정후부과] Step 3 응답 없음 - 차량번호: {}", currentVhclno); + return null; + } + + List ledgerRecords = step3Response.getRecord(); + if (ledgerRecords == null || ledgerRecords.isEmpty()) { + log.debug("[날짜수정후부과] 갑부 상세 내역 없음 - 차량번호: {}", vhclno); + return null; + } + + // ========== 갑부 상세에서 조건에 맞는 레코드 찾기 ========== + log.info("[날짜수정후부과] 갑부 상세 레코드 검색 시작 - 검사일: {}, 검사종료일자: {}", inspYmd, inspEndYmd); + + 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)) { + log.debug("[날짜수정후부과] CHG_YMD > 검사종료일자 - 변경일자: {}, 검사종료일자: {}", chgYmd, inspEndYmd); + continue; + } + + // 가장 마지막 일자 찾기 + if (latestChgDate == null || chgDate.isAfter(latestChgDate)) { + latestChgDate = chgDate; + targetRecord = record; + log.debug("[날짜수정후부과] 조건 충족 레코드 발견 - 변경일자: {}, 변경업무: {}", chgYmd, chgTaskSeCd); + } + } + + if (targetRecord == null) { + log.debug("[날짜수정후부과] 조건에 맞는 명의이전 레코드 없음 - 차량번호: {}", vhclno); + return null; + } + + // 조건: 가장 마지막 명의이전일자가 검사일의 31일 초과인지 확인 + long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(latestChgDate, inspDate); + if (daysBetween <= 31) { + log.debug("[날짜수정후부과] 명의이전일자가 검사일의 31일 이내임 - 변경일자: {}, 검사일: {}, 일수차이: {}일", + targetRecord.getChgYmd(), inspYmd, daysBetween); + return null; + } + log.info("[날짜수정후부과] 명의이전일자가 검사일의 31일 초과 확인 - 변경일자: {}, 검사일: {}, 일수차이: {}일", + targetRecord.getChgYmd(), inspYmd, daysBetween); + + String targetChgYmd = targetRecord.getChgYmd(); + 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); + + 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); + return null; + } + + NewBasicResponse.Record step4Record = step4Response.getRecord().get(0); + String step4OwnerName = step4Record.getRprsOwnrNm(); // CHG_YMD 시점의 소유자명 + + log.info("[날짜수정후부과] Step 4 결과 - 소유자명: {}", step4OwnerName); + + // ========== 소유자명 비교 - 상품용 포함 여부 확인 ========== + if (step4OwnerName == null || !step4OwnerName.contains("상품용")) { + log.debug("[날짜수정후부과] 소유자명에 '상품용' 미포함 - Step4 소유자명: {}", step4OwnerName); + return null; + } + + log.info("[날짜수정후부과] 명의이전 시점 소유자명에 '상품용' 확인! - 소유자명: {}", step4OwnerName); + + log.info("[날짜수정후부과] 모든 조건 충족! 차량번호: {}, 변경일자: {}", vhclno, targetChgYmd); + + // ========== 비고 생성 ========== + String rmrk = ComparisonRemarkBuilder.buildDateModifiedLevyRemark( + targetChgYmd, inspYmd, daysBetween + ); + + // ========== DB 업데이트 ========== + existingData.setCarBassMatterInqireId(step1Response.getGeneratedId()); + existingData.setCarLedgerFrmbkId(step3Response.getGeneratedId()); + existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_05_DATE_MODIFIED_LEVY); + existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); + existingData.setCarBscMttrInqFlnm(step4OwnerName); + existingData.setCarRegFrmbkChgTaskSeCd(targetRecord.getChgTaskSeCd()); + existingData.setCarRegFrmbkChgTaskSeNm(targetRecord.getChgTaskSeNm()); + existingData.setCarRegFrmbkChgYmd(targetRecord.getChgYmd().replace("-", "")); + existingData.setCarRegFrmbkDtl(ComparisonRemarkBuilder.buildLedgerRecordDetail(targetRecord)); + existingData.setRmrk(rmrk); + + int updateCount = carFfnlgTrgtMapper.update(existingData); + if (updateCount == 0) { + throw new MessageException(String.format("[날짜수정후부과] 업데이트 실패: %s", 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); + } + } + /** * 2. 이첩 검증