diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registration/service/ComparisonService.java b/src/main/java/go/kr/project/carInspectionPenalty/registration/service/ComparisonService.java index 33784b9..ba73459 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/registration/service/ComparisonService.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/registration/service/ComparisonService.java @@ -1,12 +1,11 @@ package go.kr.project.carInspectionPenalty.registration.service; -import go.kr.project.api.model.VehicleApiResponseVO; import go.kr.project.carInspectionPenalty.registration.model.CarFfnlgTrgtVO; /** * 과태료 대상 비교 서비스 * - *

차량 정보를 API 응답과 비교하여 상태를 자동으로 분류합니다.

+ *

차량 정보를 외부 API와 비교하여 상태를 자동으로 분류합니다.

*/ public interface ComparisonService { @@ -17,16 +16,13 @@ public interface ComparisonService { *
    *
  1. 상품용 체크
  2. *
  3. 이첩 체크
  4. - *
  5. 내사종결 체크 (향후 추가)
  6. - *
  7. 기타 비교 로직들...
  8. + *
  9. 향후 추가될 비교 로직들...
  10. *
* *

한 가지 조건이라도 만족하면 즉시 해당 상태로 업데이트하고 종료합니다.

* * @param existingData 기존 과태료 대상 데이터 - * @param apiResponse API 응답 데이터 - * @param userId 사용자 ID - * @return 처리 상태 코드 (02=상품용, 03=이첩, 04=내사종결, null=해당없음) + * @return 처리 상태 코드 (02=상품용, 03=이첩, null=해당없음) */ - String executeComparison(CarFfnlgTrgtVO existingData, VehicleApiResponseVO apiResponse, String userId); + String executeComparison(CarFfnlgTrgtVO existingData); } diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/CarFfnlgTrgtServiceImpl.java b/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/CarFfnlgTrgtServiceImpl.java index 7652c42..51ac199 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/CarFfnlgTrgtServiceImpl.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/registration/service/impl/CarFfnlgTrgtServiceImpl.java @@ -36,7 +36,6 @@ public class CarFfnlgTrgtServiceImpl extends EgovAbstractServiceImpl implements private final CarFfnlgTrgtMapper mapper; private final CarFfnlgTxtParseConfig parseConfig; private final ExternalVehicleApiService service; - private final ComparisonService comparisonService; // 날짜 형식 (YYYYMMDD) @@ -947,7 +946,7 @@ public class CarFfnlgTrgtServiceImpl extends EgovAbstractServiceImpl implements // 3. 비교 로직 실행 - String statusCode = null; + String statusCode = comparisonService.executeComparison(existingData); // 결과 처리 if (statusCode != null) { 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 f689d59..d71992c 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 @@ -2,13 +2,16 @@ package go.kr.project.carInspectionPenalty.registration.service.impl; import egovframework.constant.TaskPrcsSttsConstants; import egovframework.exception.MessageException; -import go.kr.project.api.model.VehicleApiResponseVO; -import go.kr.project.api.model.response.OldBasicResponse; +import egovframework.util.SessionUtil; +import go.kr.project.api.model.request.NewBasicRequest; +import go.kr.project.api.model.request.NewLedgerRequest; +import go.kr.project.api.model.response.NewBasicResponse; +import go.kr.project.api.model.response.NewLedgerResponse; +import go.kr.project.api.service.ExternalVehicleApiService; import go.kr.project.carInspectionPenalty.registration.mapper.CarFfnlgTrgtMapper; import go.kr.project.carInspectionPenalty.registration.model.CarFfnlgTrgtVO; import go.kr.project.carInspectionPenalty.registration.service.ComparisonService; -import go.kr.project.system.user.mapper.UserMapper; -import go.kr.project.system.user.model.SystemUserVO; +import go.kr.project.login.model.LoginUserVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl; @@ -16,6 +19,7 @@ import org.springframework.stereotype.Service; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.List; /** * 과태료 대상 비교 서비스 구현체 @@ -29,7 +33,7 @@ import java.time.format.DateTimeFormatter; public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements ComparisonService { private final CarFfnlgTrgtMapper carFfnlgTrgtMapper; - private final UserMapper userMapper; + private final ExternalVehicleApiService apiService; private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); @@ -39,28 +43,26 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co *

순차적으로 각 비교 메서드를 실행하고, 하나라도 적용되면 즉시 종료합니다.

*/ @Override - public String executeComparison(CarFfnlgTrgtVO existingData, VehicleApiResponseVO apiResponse, String userId) { + public String executeComparison(CarFfnlgTrgtVO existingData) { String vhclno = existingData.getVhclno(); log.info("========== 비교 로직 시작: {} ==========", vhclno); - OldBasicResponse.Record basicInfo = apiResponse.getBasicInfo().getRecord().get(0); - // ========== 1. 상품용 체크 ========== - String productUseResult = checkProductUse(existingData, basicInfo); + String productUseResult = checkProductUse(existingData); if (productUseResult != null) { log.info("========== 비교 로직 종료 (상품용): {} ==========", vhclno); return productUseResult; } // ========== 2. 이첩 체크 ========== - String transferResult = checkTransfer(existingData, basicInfo, userId); + String transferResult = checkTransfer(existingData); if (transferResult != null) { log.info("========== 비교 로직 종료 (이첩): {} ==========", vhclno); return transferResult; } // ========== 3. 향후 추가될 비교 로직들 ========== - // String investigationClosedResult = checkInvestigationClosed(existingData, basicInfo); + // String investigationClosedResult = checkInvestigationClosed(existingData); // if (investigationClosedResult != null) { // return investigationClosedResult; // } @@ -75,172 +77,433 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co // ======================================== /** - * 1. 상품용 체크 + * 1. 상품용 검증 + * + * API 호출 3단계: + * 1) 자동차기본정보(차량번호, 부과일자=검사일) -> 차대번호, 소유자명 + * 2) 자동차기본정보(차대번호, 부과일자=오늘일자) -> 차량번호, 성명, 민원인주민번호, 민원인법정동코드 + * 3) 자동차등록원부(갑)(차량번호, 성명, 민원인주민번호, 민원인법정동코드) -> 갑부 상세 List * - *

조건: 대표소유자성명(MBER_NM)에 "상품용" 문자열 포함

- *

처리: TASK_PRCS_STTS_CD = 02, CAR_BSC_MTTR_INQ_FLNM = 소유자명

+ * 비교조건: + * - 소유자명에 "상품용" 포함 + * - 갑부 상세에서 명의이전(코드 11) 이력이 유효기간만료일~검사종료일자 사이에 있는지 * - * @return 02 (적용됨) 또는 null (미적용) + * @param existingData 과태료 대상 데이터 + * @return 02 (상품용) 또는 null (미적용) */ - private String checkProductUse(CarFfnlgTrgtVO existingData, OldBasicResponse.Record basicInfo) { + private String checkProductUse(CarFfnlgTrgtVO existingData) { String vhclno = existingData.getVhclno(); - String mberNm = basicInfo.getMberNm(); // 대표소유자성명 + String inspYmd = existingData.getInspYmd(); - // 조건 체크 - if (mberNm == null || !mberNm.contains("상품용")) { - log.debug("[상품용] 조건 미충족. 차량번호: {}, 소유자명: {}", vhclno, mberNm); + try { + // Step 1: 자동차기본정보 조회 (차량번호, 부과일자=검사일) + log.info("[상품용] Step 1: 자동차기본정보 조회 - 차량번호: {}, 검사일: {}", vhclno, inspYmd); + + NewBasicRequest step1Request = createBasicRequest(vhclno, null, inspYmd); + NewBasicResponse step1Response = apiService.getBasicInfo(step1Request); + + 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 ownerName = step1Record.getRprsOwnrNm(); // 대표소유자성명 + + log.info("[상품용] Step 1 결과 - 차대번호: {}, 소유자명: {}", vin, ownerName); + + // 조건 1: 소유자명에 "상품용" 포함 여부 확인 + if (ownerName == null || !ownerName.contains("상품용")) { + log.debug("[상품용] 소유자명에 '상품용' 미포함 - 차량번호: {}, 소유자명: {}", vhclno, ownerName); + return null; + } + + log.info("[상품용] 소유자명에 '상품용' 포함 - 차량번호: {}, 소유자명: {}", vhclno, ownerName); + + // 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); + + 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); + + if (step3Response == null) { + log.warn("[상품용] Step 3 응답 없음 - 차량번호: {}", currentVhclno); + return null; + } + + // 조건 2: 갑부 상세에서 명의이전 이력 확인 + List ledgerRecords = step3Response.getRecord(); + if (ledgerRecords == null || ledgerRecords.isEmpty()) { + log.debug("[상품용] 갑부 상세 내역 없음 - 차량번호: {}", vhclno); + return null; + } + + // 유효기간만료일 ~ 검사종료일자 사이에 명의이전(코드 11) 이력이 있는지 확인 + String vldPrdExpryYmd = existingData.getVldPrdExpryYmd(); + String inspEndYmd = existingData.getInspEndYmd(); + + log.info("[상품용] 갑부 상세 비교 기간 - 유효기간만료일: {}, 검사종료일자: {}", vldPrdExpryYmd, inspEndYmd); + + for (NewLedgerResponse.Record record : ledgerRecords) { + String chgYmd = record.getChgYmd(); // 변경일자 + String chgTaskSeCd = record.getChgTaskSeCd(); // 변경업무구분코드 + + log.debug("[상품용] 갑부 레코드 - 변경일자: {}, 변경업무구분코드: {}", chgYmd, chgTaskSeCd); + + // 명의이전 코드 11 확인 및 기간 확인 + if ("11".equals(chgTaskSeCd) && chgYmd != null) { + if (isDateBetween(chgYmd, vldPrdExpryYmd, inspEndYmd)) { + log.info("[상품용] 조건 충족! 차량번호: {}, 변경일자: {}, 변경업무: {}", vhclno, chgYmd, chgTaskSeCd); + + // 비고 생성 - 갑부 상세 정보 포함 + String rmrk = buildProductUseRemark( + step1Record, step2Record, record, + vldPrdExpryYmd, inspEndYmd + ); + + // DB 업데이트 + existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE); + existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); + 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_02_PRODUCT_USE; + } + } + } + + log.debug("[상품용] 명의이전 이력이 기간 내 없음 - 차량번호: {}", vhclno); return null; - } - log.info("[상품용] 조건 충족! 차량번호: {}, 소유자명: {}", vhclno, mberNm); - - // DB 업데이트 - existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE); // 상품용 - existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); - existingData.setCarBscMttrInqFlnm(mberNm); // 소유자명 저장 - existingData.setCarBscMttrInqSggCd(null); - existingData.setCarBscMttrInqSggNm(null); - - int updateCount = carFfnlgTrgtMapper.update(existingData); - if (updateCount == 0) { - throw new MessageException(String.format("[상품용] 업데이트 실패: %s", vhclno)); + } catch (Exception e) { + log.error("[상품용] 검증 중 오류 발생 - 차량번호: {}", vhclno, e); + throw new MessageException(String.format("[상품용] 검증 중 오류 발생 - 차량번호: %s", vhclno), e); } - - log.info("[상품용] 처리 완료! 차량번호: {}", vhclno); - return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE; } /** - * 2. 이첩 체크 (OR 로직: 여러 조건 중 하나라도 만족하면 이첩) + * 2. 이첩 검증 * - *

처리: TASK_PRCS_STTS_CD = 03, CAR_BSC_MTTR_INQ_SGG_CD = 시군구코드, CAR_BSC_MTTR_INQ_SGG_NM = 시군구명

+ * DAYCNT 기반 부과기준일 계산: + * - DAYCNT > 115: 이첩-2 (부과기준일 = 검사종료일자 + 115일) + * - DAYCNT <= 115: 이첩-1 (부과기준일 = 검사일자) * - * @return 03 (적용됨) 또는 null (미적용) + * 법정동코드 비교: + * - 사용본거지법정동코드 앞 4자리 != 사용자 조직코드 앞 4자리 + * + * @param existingData 과태료 대상 데이터 + * @return 03 (이첩) 또는 null (미적용) */ - private String checkTransfer(CarFfnlgTrgtVO existingData, OldBasicResponse.Record basicInfo, String userId) { + private String checkTransfer(CarFfnlgTrgtVO existingData) { String vhclno = existingData.getVhclno(); - // ========== 이첩 조건들 (OR 로직: 하나라도 만족하면 이첩) ========== - - // 조건1: 법정동코드 불일치 - if (checkTransferCondition1_LegalDongMismatch(basicInfo, userId, vhclno)) { - return processTransfer(existingData, basicInfo, vhclno, "법정동코드 불일치"); + try { + // DAYCNT 가져오기 + String daycntStr = existingData.getDaycnt(); + if (daycntStr == null || daycntStr.isEmpty()) { + log.debug("[이첩] DAYCNT 없음 - 차량번호: {}", vhclno); + return null; + } + + int daycnt = Integer.parseInt(daycntStr); + log.info("[이첩] DAYCNT: {} - 차량번호: {}", daycnt, vhclno); + + // 부과기준일 계산 + String levyCrtrYmd; + String transferType; + + if (daycnt > 115) { + // 이첩-2: 부과기준일 = 검사종료일자 + 115일 + String inspEndYmd = existingData.getInspEndYmd(); + LocalDate inspEndDate = LocalDate.parse(inspEndYmd, DATE_FORMATTER); + LocalDate levyCrtrDate = inspEndDate.plusDays(115); + levyCrtrYmd = levyCrtrDate.format(DATE_FORMATTER); + transferType = "이첩-2"; + log.info("[이첩-2] 부과기준일 = 검사종료일자({}) + 115일 = {}", inspEndYmd, levyCrtrYmd); + } else { + // 이첩-1: 부과기준일 = 검사일자 + levyCrtrYmd = existingData.getInspYmd(); + transferType = "이첩-1"; + log.info("[이첩-1] 부과기준일 = 검사일자 = {}", levyCrtrYmd); + } + + // 자동차기본정보 API 호출 (부과기준일 기준) + log.info("[{}] 자동차기본정보 조회 - 차량번호: {}, 부과기준일: {}", transferType, vhclno, levyCrtrYmd); + + NewBasicRequest request = createBasicRequest(vhclno, null, levyCrtrYmd); + NewBasicResponse response = apiService.getBasicInfo(request); + + if (response == null || response.getRecord() == null || response.getRecord().isEmpty()) { + log.warn("[{}] 응답 없음 - 차량번호: {}", transferType, vhclno); + return null; + } + + NewBasicResponse.Record record = response.getRecord().get(0); + String usgsrhldStdgCd = record.getUsgsrhldStdgCd(); // 사용본거지법정동코드 + + log.info("[{}] API 응답 - 사용본거지법정동코드: {}", transferType, usgsrhldStdgCd); + + // 법정동코드 유효성 검사 + if (usgsrhldStdgCd == null || usgsrhldStdgCd.length() < 4) { + log.debug("[{}] 법정동코드 없음 - 차량번호: {}", transferType, vhclno); + return null; + } + + // 세션에서 사용자 정보 조회 + LoginUserVO userInfo = SessionUtil.getLoginUser(); + if (userInfo == null || userInfo.getOrgCd() == null) { + log.debug("[{}] 사용자 정보 없음", transferType); + 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("[{}] 법정동코드 일치 - 차량번호: {}, 법정동: {}, 조직: {}", + transferType, vhclno, legalDong4, userOrg4); + return null; + } + + log.info("[{}] 법정동코드 불일치! 차량번호: {}, 법정동: {}, 조직: {}", + transferType, vhclno, legalDong4, userOrg4); + + // 시군구 코드 및 시군구명 조회 + String sggCd = usgsrhldStdgCd.length() >= 5 ? usgsrhldStdgCd.substring(0, 5) : usgsrhldStdgCd; + String sggNm = carFfnlgTrgtMapper.selectSggNmBySggCd(sggCd); + if (sggNm == null || sggNm.isEmpty()) { + log.warn("[{}] 시군구명 조회 실패 - 시군구코드: {}", transferType, sggCd); + sggNm = ""; + } + + // 비고 생성 + String rmrk; + if ("이첩-1".equals(transferType)) { + rmrk = String.format("%s, 검사일사용본거지, [검사대상, 사용자 조직코드 앞 4자리: %s, 법정동명: %s]", + sggNm, userOrg4, sggNm); + } else { + rmrk = String.format("%s, 115일 도래지, [법정동코드: %s, 법정동명: %s]", + sggNm, legalDong4, sggNm); + } + + // DB 업데이트 + existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER); + existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); + existingData.setCarBscMttrInqSggCd(sggCd); + existingData.setCarBscMttrInqSggNm(sggNm); + existingData.setRmrk(rmrk); + + int updateCount = carFfnlgTrgtMapper.update(existingData); + if (updateCount == 0) { + throw new MessageException(String.format("[%s] 업데이트 실패: %s", transferType, vhclno)); + } + + log.info("[{}] 처리 완료! 차량번호: {}, 시군구: {}({})", transferType, vhclno, sggNm, sggCd); + return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER; + + } catch (Exception e) { + log.error("[이첩] 검증 중 오류 발생 - 차량번호: {}", vhclno, e); + throw new MessageException(String.format("[이첩] 검증 중 오류 발생 - 차량번호: %s", vhclno), e); } + } - // 조건2: 향후 추가될 이첩 조건들 (예시) - // if (checkTransferCondition2_OtherReason(basicInfo, userId, vhclno)) { - // return processTransfer(existingData, basicInfo, vhclno, "다른 사유"); - // } + // ======================================== + // 헬퍼 메서드들 + // ======================================== - // 모든 이첩 조건에 해당하지 않음 - log.debug("[이첩] 모든 조건 미충족. 차량번호: {}", vhclno); - return null; + /** + * 자동차기본정보 요청 객체 생성 + * + * @param vhrno 차량번호 (null 가능) + * @param vin 차대번호 (null 가능) + * @param levyCrtrYmd 부과기준일 + * @return NewBasicRequest + */ + private NewBasicRequest createBasicRequest(String vhrno, String vin, String levyCrtrYmd) { + NewBasicRequest request = new NewBasicRequest(); + + NewBasicRequest.Record record = new NewBasicRequest.Record(); + record.setLevyCrtrYmd(levyCrtrYmd); + + if (vhrno != null) { + record.setVhrno(vhrno); + record.setInqSeCd("3"); // 3: 자동차번호 + } else if (vin != null) { + record.setVin(vin); + record.setInqSeCd("2"); // 2: 차대번호 + } + + request.setRecord(java.util.Arrays.asList(record)); + return request; } /** - * 이첩 조건1: 법정동코드 불일치 - * 사용본거지법정동코드 앞 4자리 != 사용자 조직코드 앞 4자리 + * 자동차등록원부(갑) 요청 객체 생성 + * + * @param vhrno 차량번호 + * @param ownerNm 성명 + * @param idecno 주민번호 + * @param legalDongCd 법정동코드 + * @return NewLedgerRequest */ - private boolean checkTransferCondition1_LegalDongMismatch(OldBasicResponse.Record basicInfo, String userId, String vhclno) { - String useStrnghldLegaldongCode = basicInfo.getUseStrnghldLegaldongCode(); + private NewLedgerRequest createLedgerRequest(String vhrno, String ownerNm, String idecno, String legalDongCd) { + NewLedgerRequest request = new NewLedgerRequest(); - // 법정동코드 유효성 검사 - if (useStrnghldLegaldongCode == null || useStrnghldLegaldongCode.length() < 4) { - log.debug("[이첩][조건1] 법정동코드 없음. 차량번호: {}", vhclno); - return false; - } + // 차량번호 + request.setVhrno(vhrno); - // 사용자 정보 조회 - SystemUserVO userInfo = userMapper.selectUser(userId); - if (userInfo == null || userInfo.getOrgCd() == null) { - log.debug("[이첩][조건1] 사용자 정보 없음. 사용자ID: {}", userId); - return false; - } + // 민원인 정보 + request.setCvlprNm(ownerNm); + request.setCvlprIdecno(idecno); + request.setCvlprStdgCd(legalDongCd); - // 법정동코드 앞 4자리 vs 사용자 조직코드 앞 4자리 비교 - String legalDong4 = useStrnghldLegaldongCode.substring(0, 4); - String userOrgCd = userInfo.getOrgCd(); - String userOrg4 = userOrgCd.length() >= 4 ? userOrgCd.substring(0, 4) : userOrgCd; + // 개인정보공개 (1:소유자공개) + request.setPrvcRls("1"); - if (legalDong4.equals(userOrg4)) { - log.debug("[이첩][조건1] 법정동코드 일치. 차량번호: {}, 법정동: {}, 조직: {}", - vhclno, legalDong4, userOrg4); - return false; - } + // 경로구분코드 (고정값 3) + request.setPathSeCd("3"); + + // 내역표시 (1:전체내역) + request.setDsctnIndct("1"); + + // 조회구분코드 (1:열람) + request.setInqSeCd("1"); - log.info("[이첩][조건1] 법정동코드 불일치! 차량번호: {}, 법정동: {}, 조직: {}", - vhclno, legalDong4, userOrg4); - return true; + return request; } /** - * 이첩 처리 (공통 로직) + * 날짜가 범위 내에 있는지 확인 + * + * @param targetDate 확인할 날짜 + * @param startDate 시작일 + * @param endDate 종료일 + * @return 범위 내 여부 */ - private String processTransfer(CarFfnlgTrgtVO existingData, OldBasicResponse.Record basicInfo, - String vhclno, String reason) { - log.info("[이첩] 조건 충족! 차량번호: {}, 사유: {}", vhclno, reason); - - // 시군구 코드 및 시군구명 조회 - String useStrnghldLegaldongCode = basicInfo.getUseStrnghldLegaldongCode(); - String sggCd = (useStrnghldLegaldongCode != null && useStrnghldLegaldongCode.length() >= 5) - ? useStrnghldLegaldongCode.substring(0, 5) - : (useStrnghldLegaldongCode != null ? useStrnghldLegaldongCode : ""); - - String sggNm = carFfnlgTrgtMapper.selectSggNmBySggCd(sggCd); - if (sggNm == null || sggNm.isEmpty()) { - log.warn("[이첩] 시군구명 조회 실패. 시군구코드: {}", sggCd); - sggNm = ""; + private boolean isDateBetween(String targetDate, String startDate, String endDate) { + if (targetDate == null || startDate == null || endDate == null) { + return false; } - // DB 업데이트 - existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER); // 이첩 - existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); - existingData.setCarBscMttrInqFlnm(null); - existingData.setCarBscMttrInqSggCd(sggCd); // 시군구 코드 - existingData.setCarBscMttrInqSggNm(sggNm); // 시군구명 + try { + LocalDate target = LocalDate.parse(targetDate, DATE_FORMATTER); + LocalDate start = LocalDate.parse(startDate, DATE_FORMATTER); + LocalDate end = LocalDate.parse(endDate, DATE_FORMATTER); - int updateCount = carFfnlgTrgtMapper.update(existingData); - if (updateCount == 0) { - throw new MessageException(String.format("[이첩] 업데이트 실패: %s", vhclno)); + return !target.isBefore(start) && !target.isAfter(end); + } catch (Exception e) { + log.error("날짜 비교 중 오류 발생 - target: {}, start: {}, end: {}", targetDate, startDate, endDate, e); + return false; } - - log.info("[이첩] 처리 완료! 차량번호: {}, 시군구: {}({}), 사유: {}", vhclno, sggNm, sggCd, reason); - return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER; } /** - * 3. 내사종결 체크 (예시 - 향후 추가) - * - *

조건: 차량이 말소된 경우 등

- *

처리: TASK_PRCS_STTS_CD = 04

+ * 상품용 비고 생성 * - * @return 04 (적용됨) 또는 null (미적용) + * @param step1Record Step 1 API 응답 (검사일 기준) + * @param step2Record Step 2 API 응답 (오늘 기준) + * @param ledgerRecord 조건에 걸린 갑부 레코드 + * @param vldPrdExpryYmd 유효기간만료일 + * @param inspEndYmd 검사종료일자 + * @return 비고 문자열 */ - @SuppressWarnings("unused") - private String checkInvestigationClosed(CarFfnlgTrgtVO existingData, OldBasicResponse.Record basicInfo) { - String vhclno = existingData.getVhclno(); - String ersrRegistSeCode = basicInfo.getErsrRegistSeCode(); // 말소등록구분코드 - - // 조건 체크: 말소된 차량인지 - if (ersrRegistSeCode == null || ersrRegistSeCode.isEmpty()) { - log.debug("[내사종결] 조건 미충족. 차량번호: {}", vhclno); - return null; - } - - log.info("[내사종결] 조건 충족! 차량번호: {}, 말소구분: {}", vhclno, ersrRegistSeCode); + private String buildProductUseRemark( + NewBasicResponse.Record step1Record, + NewBasicResponse.Record step2Record, + NewLedgerResponse.Record ledgerRecord, + String vldPrdExpryYmd, + String inspEndYmd) { + + StringBuilder sb = new StringBuilder(); + sb.append("[상품용]\n"); + + // 1. 검사일 기준 정보 + sb.append("■ 검사일 기준 차량정보\n"); + sb.append(" - 차대번호: ").append(nvl(step1Record.getVin())).append("\n"); + sb.append(" - 소유자명: ").append(nvl(step1Record.getRprsOwnrNm())).append("\n"); + + // 2. 현재(오늘) 기준 정보 + sb.append("\n■ 현재 차량정보\n"); + sb.append(" - 차량번호: ").append(nvl(step2Record.getVhrno())).append("\n"); + sb.append(" - 소유자명: ").append(nvl(step2Record.getRprsOwnrNm())).append("\n"); + sb.append(" - 주민번호: ").append(maskIdecno(step2Record.getRprsvOwnrIdecno())).append("\n"); + sb.append(" - 법정동코드: ").append(nvl(step2Record.getUsgsrhldStdgCd())).append("\n"); + + // 3. 갑부 상세 정보 (명의이전 이력) + sb.append("\n■ 갑부 상세 (명의이전 이력)\n"); + sb.append(" - 변경일자: ").append(formatDateWithHyphen(ledgerRecord.getChgYmd())).append("\n"); + sb.append(" - 변경업무코드: ").append(nvl(ledgerRecord.getChgTaskSeCd())).append("\n"); + sb.append(" - 변경업무명: ").append(nvl(ledgerRecord.getChgTaskSeNm())).append("\n"); + sb.append(" - 접수번호: ").append(nvl(ledgerRecord.getAplyRcptNo())).append("\n"); + sb.append(" - 주번호: ").append(nvl(ledgerRecord.getMainNo())).append("\n"); + sb.append(" - 부번호: ").append(nvl(ledgerRecord.getSno())).append("\n"); + sb.append(" - 사항란: ").append(nvl(ledgerRecord.getSpcablMttr())).append("\n"); + sb.append(" - 세대주명: ").append(nvl(ledgerRecord.getHshldrNm())).append("\n"); + + // 4. 비교 기간 + sb.append("\n■ 비교 기간\n"); + sb.append(" - 유효기간만료일: ").append(formatDateWithHyphen(vldPrdExpryYmd)).append("\n"); + sb.append(" - 검사종료일자: ").append(formatDateWithHyphen(inspEndYmd)).append("\n"); + sb.append(" - 판정: 명의이전일자가 기간 내 존재"); + + return sb.toString(); + } - // DB 업데이트 - existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED); // 내사종결 - existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); - // 필요한 추가 필드 설정 + /** + * null 안전 처리 + */ + private String nvl(String value) { + return value != null ? value : ""; + } - int updateCount = carFfnlgTrgtMapper.update(existingData); - if (updateCount == 0) { - throw new MessageException(String.format("[내사종결] 업데이트 실패: %s", vhclno)); + /** + * 주민번호 마스킹 (뒤 7자리 마스킹) + */ + private String maskIdecno(String idecno) { + if (idecno == null || idecno.length() < 7) { + return idecno != null ? idecno : ""; } - - log.info("[내사종결] 처리 완료! 차량번호: {}", vhclno); - return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED; + String front = idecno.substring(0, Math.min(6, idecno.length())); + return front + "*******"; } + /** + * 날짜 형식 변환 (YYYYMMDD → YYYY-MM-DD) + */ + private String formatDateWithHyphen(String date) { + if (date == null || date.isEmpty() || date.length() != 8) { + return date != null ? date : ""; + } + return date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6, 8); + } }