diff --git a/src/main/java/egovframework/constant/BatchConstants.java b/src/main/java/egovframework/constant/BatchConstants.java deleted file mode 100644 index 41015d5..0000000 --- a/src/main/java/egovframework/constant/BatchConstants.java +++ /dev/null @@ -1,113 +0,0 @@ -package egovframework.constant; - -/** - * packageName : go.kr.project.common.constant - * fileName : BatchConstants - * author : 개발자 - * date : 2025-06-10 - * description : 배치 작업 관련 상수를 정의하는 클래스 - * =========================================================== - * DATE AUTHOR NOTE - * ----------------------------------------------------------- - * 2025-06-10 개발자 최초 생성 - */ -public class BatchConstants { - - /** - * 배치 작업 정보 상태 코드 (TB_BATCH_JOB_INFO.STATUS) - */ - public static final String JOB_INFO_STATUS_ACTIVE = "ACTIVE"; - public static final String JOB_INFO_STATUS_PAUSED = "PAUSED"; - public static final String JOB_INFO_STATUS_DELETED = "DELETED"; - - /** - * 배치 작업 실행 상태 코드 (TB_BATCH_JOB_EXECUTION.STATUS) - * 실행상태에서 부분완료는 제거됨 - 시작,완료,실패,거부만 사용 - */ - public static final String JOB_EXECUTION_STATUS_STARTED = "STARTED"; - public static final String JOB_EXECUTION_STATUS_COMPLETED = "COMPLETED"; - public static final String JOB_EXECUTION_STATUS_FAILED = "FAILED"; - public static final String JOB_EXECUTION_STATUS_VETOED = "VETOED"; - - /** - * 배치 작업 실행 종료 코드 (TB_BATCH_JOB_EXECUTION.EXIT_CODE) - */ - public static final String JOB_EXECUTION_EXIT_COMPLETED = "COMPLETED"; - public static final String JOB_EXECUTION_EXIT_FAILED = "FAILED"; - public static final String JOB_EXECUTION_EXIT_UNKNOWN = "UNKNOWN"; - public static final String JOB_EXECUTION_EXIT_PARTIALLY_COMPLETED = "PARTIALLY_COMPLETED"; - - - /** - * 로그 레벨 - */ - public static final String LOG_LEVEL_INFO = "INFO"; - public static final String LOG_LEVEL_WARN = "WARN"; - public static final String LOG_LEVEL_ERROR = "ERROR"; - - /** - * 날짜 및 시간 형식 - */ - public static final String DATE_FORMAT_DEFAULT = "yyyy-MM-dd HH:mm:ss"; - - /** - * 시간 값 (밀리초) - */ - public static final long TIME_ITEM_PROCESS_DELAY = 100; // 0.1초 - public static final long TIME_JOB_DELAY_TEST = 3000; // 3초 (테스트용) - public static final long TIME_JOB_DELAY_1_MIN = 60000; // 1분 - public static final long TIME_JOB_DELAY_3_MIN = 180000; // 3분 - public static final long TIME_JOB_DELAY_5_MIN = 300000; // 5분 - - /** - * JobDataMap 키 - */ - public static final String JOB_DATA_EXECUTION_ID = "executionId"; - - /** - * 작업 이름 및 그룹 - */ - public static final String JOB_NAME_SAMPLE = "sampleJob"; - public static final String JOB_GROUP_SAMPLE = "sampleGroup"; - public static final String JOB_TRIGGER_SUFFIX = "Trigger"; - - /** - * Cron 표현식 - */ - public static final String CRON_EVERY_MINUTE = "0 * * * * ?"; - public static final String CRON_EVERY_ONE_SECOND = "1 0 * * * ?"; - - /** - * 작업 설명 - */ - public static final String JOB_DESC_SAMPLE = "샘플 배치 작업 (매 분마다 실행)"; - - /** - * 배치 로그 메시지 템플릿 - */ - // 기본 배치 작업 로그 메시지 - public static final String LOG_MSG_BATCH_JOB_START = "===== 배치 작업 시작: %s.%s - %s ====="; - public static final String LOG_MSG_BATCH_JOB_COMPLETE = "===== 배치 작업 완료: %s.%s - %s ====="; - public static final String LOG_MSG_BATCH_JOB_PARTIAL_COMPLETE = "===== 배치 작업 부분 완료: %s.%s - 총 %d개 파일 중 처리: %d개, 성공: %d개, 실패: %d개 (%s) ====="; - public static final String LOG_MSG_BATCH_JOB_FAILED = "===== 배치 작업 실패: %s.%s - 총 %d개 파일 중 처리: %d개, 성공: %d개, 실패: %d개 (%s) ====="; - public static final String LOG_MSG_BATCH_JOB_ERROR = "===== 배치 작업 실행 중 오류 발생: %s.%s - %s ====="; - - /** - * 배치 작업 초기화 관련 메시지 - */ - public static final String LOG_MSG_BATCH_INIT_SERVER_BOOT_DETECTED = "서버 부팅 완료 감지 - 배치 작업 초기화를 5초 후에 시작합니다."; - public static final String LOG_MSG_BATCH_INIT_START = "배치 작업 초기화 시작"; - public static final String LOG_MSG_BATCH_INIT_COMPLETE = "배치 작업 초기화 완료"; - public static final String LOG_MSG_BATCH_INIT_INTERRUPT_ERROR = "배치 작업 초기화 대기 중 인터럽트 발생: {}"; - public static final String LOG_MSG_BATCH_INIT_ERROR = "배치 작업 초기화 중 오류 발생: %s"; - public static final String LOG_MSG_BATCH_INIT_NO_JOBS = "등록된 배치 작업이 없습니다."; - public static final String LOG_MSG_BATCH_INIT_JOBS_COUNT = "총 {}개의 배치 작업 정보를 조회했습니다."; - public static final String LOG_MSG_BATCH_INIT_SKIP_DELETED = "삭제된 작업은 스케줄러에 등록하지 않습니다: {}.{}"; - public static final String LOG_MSG_BATCH_INIT_SCHEDULE_SUCCESS = "배치 작업을 스케줄러에 등록했습니다: {}.{}"; - public static final String LOG_MSG_BATCH_INIT_SCHEDULE_FAILED = "배치 작업 스케줄링에 실패했습니다: {}.{}"; - public static final String LOG_MSG_BATCH_INIT_CLASS_NOT_FOUND = "배치 작업 클래스를 찾을 수 없습니다: {}"; - public static final String LOG_MSG_BATCH_INIT_SCHEDULE_ERROR = "배치 작업 스케줄링 중 오류 발생: {}.{} - {}"; - public static final String LOG_MSG_BATCH_INIT_DB_ERROR = "배치 작업 초기화 중 오류 발생: {}"; - public static final String LOG_MSG_BATCH_INIT_REGISTER_COMPLETE = "배치 작업 정보 등록 완료: %s"; - public static final String LOG_MSG_BATCH_INIT_REGISTER_ERROR = "샘플 배치 작업 등록 중 오류 발생: %s"; -} diff --git a/src/main/java/egovframework/constant/CrdnPrcsSttsConstants.java b/src/main/java/egovframework/constant/CrdnPrcsSttsConstants.java deleted file mode 100644 index 574c576..0000000 --- a/src/main/java/egovframework/constant/CrdnPrcsSttsConstants.java +++ /dev/null @@ -1,59 +0,0 @@ -package egovframework.constant; - -/** - * packageName : egovframework.constant - * fileName : CrdnPrcsSttsConstants - * author : 시스템 관리자 - * date : 2025-08-26 - * description : 단속 처리 상태 코드 상수를 정의하는 클래스 - * 중요로직: 처리 단계별 상태 코드, 순서는 처리 순서대로 정의 - * =========================================================== - * DATE AUTHOR NOTE - * ----------------------------------------------------------- - * 2025-08-26 시스템 관리자 최초 생성 - */ -public class CrdnPrcsSttsConstants { - - /** - * 단속 처리 상태 코드 - 10: 단속 - * 최초 단속 등록시 기본값으로 설정되는 상태 - */ - public static final String CRDN_PRCS_STTS_CD_10_CRDN = "10"; - - /** - * 단속 처리 상태 코드 - 20: 처분사전 - * 처분 사전 통지 단계 - */ - public static final String CRDN_PRCS_STTS_CD_20_DSPS_BFHD = "20"; - - /** - * 단속 처리 상태 코드 - 30: 시정명령 - * 시정 명령 단계 - */ - public static final String CRDN_PRCS_STTS_CD_30_CRC_CMD = "30"; - - /** - * 단속 처리 상태 코드 - 40: 시정촉구 - * 시정 촉구 단계 - */ - public static final String CRDN_PRCS_STTS_CD_40_CRC_URG = "40"; - - /** - * 단속 처리 상태 코드 - 50: 부과예고 - * 부과 예고 단계 - */ - public static final String CRDN_PRCS_STTS_CD_50_LEVY_PRVNTC = "50"; - - /** - * 단속 처리 상태 코드 - 60: 부과 - * 부과 단계 - */ - public static final String CRDN_PRCS_STTS_CD_60_LEVY = "60"; - - /** - * 단속 처리 상태 코드 - 70: 납부촉구 - * 납부 촉구 단계 - */ - public static final String CRDN_PRCS_STTS_CD_70_PAY_URG = "70"; - -} \ No newline at end of file diff --git a/src/main/java/egovframework/constant/ImpltTaskSeConstants.java b/src/main/java/egovframework/constant/ImpltTaskSeConstants.java deleted file mode 100644 index be098ef..0000000 --- a/src/main/java/egovframework/constant/ImpltTaskSeConstants.java +++ /dev/null @@ -1,106 +0,0 @@ -package egovframework.constant; - -/** - * packageName : egovframework.constant - * fileName : ImpltTaskSeConstants - * author : 시스템 관리자 - * date : 2025-09-08 - * description : 이행업무구분코드 상수를 정의하는 클래스 - * 중요로직: 이행업무 처리 단계별 구분 코드, 순서는 처리 순서대로 정의 - * =========================================================== - * DATE AUTHOR NOTE - * ----------------------------------------------------------- - * 2025-09-08 시스템 관리자 최초 생성 - */ -public class ImpltTaskSeConstants { - - /** - * 이행업무구분코드 - 1: 처분사전 - * 처분 사전 통지 단계 - */ - public static final String IMPLT_TASK_SE_CD_1_DSPS_BFHD = "1"; - - /** - * 이행업무구분코드 - 2: 시정명령 - * 시정 명령 단계 - */ - public static final String IMPLT_TASK_SE_CD_2_CRC_CMD = "2"; - - /** - * 이행업무구분코드 - 3: 시정촉구 - * 시정 촉구 단계 - */ - public static final String IMPLT_TASK_SE_CD_3_CRC_URG = "3"; - - /** - * 이행업무구분코드 - 4: 부과예고 - * 부과 예고 단계 - */ - public static final String IMPLT_TASK_SE_CD_4_LEVY_PRVNTC = "4"; - - /** - * 이행업무구분코드 - 5: 부과 - * 부과 단계 - */ - public static final String IMPLT_TASK_SE_CD_5_LEVY = "5"; - - /** - * 이행업무구분코드 - 6: 납부촉구 - * 납부 촉구 단계 - */ - public static final String IMPLT_TASK_SE_CD_6_PAY_URG = "6"; - - /** - * 이행업무구분코드명 - 처분사전 - */ - public static final String IMPLT_TASK_SE_CD_NM_DSPS_BFHD = "처분사전"; - - /** - * 이행업무구분코드명 - 시정명령 - */ - public static final String IMPLT_TASK_SE_CD_NM_CRC_CMD = "시정명령"; - - /** - * 이행업무구분코드명 - 시정촉구 - */ - public static final String IMPLT_TASK_SE_CD_NM_CRC_URG = "시정촉구"; - - /** - * 이행업무구분코드명 - 부과예고 - */ - public static final String IMPLT_TASK_SE_CD_NM_LEVY_PRVNTC = "부과예고"; - - /** - * 이행업무구분코드명 - 부과 - */ - public static final String IMPLT_TASK_SE_CD_NM_LEVY = "부과"; - - /** - * 이행업무구분코드명 - 납부촉구 - */ - public static final String IMPLT_TASK_SE_CD_NM_PAY_URG = "납부촉구"; - - /** - * 알수없음 - */ - public static final String IMPLT_TASK_SE_CD_NM_UNKNOWN = "알수없음"; - - /** - * 이행업무구분코드에 해당하는 코드명을 반환합니다. - * - * @param impltTaskSeCd 이행업무구분코드 - * @return 코드명 - */ - public static String getImpltTaskSeCdNm(String impltTaskSeCd) { - switch (impltTaskSeCd) { - case IMPLT_TASK_SE_CD_1_DSPS_BFHD: return IMPLT_TASK_SE_CD_NM_DSPS_BFHD; - case IMPLT_TASK_SE_CD_2_CRC_CMD: return IMPLT_TASK_SE_CD_NM_CRC_CMD; - case IMPLT_TASK_SE_CD_3_CRC_URG: return IMPLT_TASK_SE_CD_NM_CRC_URG; - case IMPLT_TASK_SE_CD_4_LEVY_PRVNTC: return IMPLT_TASK_SE_CD_NM_LEVY_PRVNTC; - case IMPLT_TASK_SE_CD_5_LEVY: return IMPLT_TASK_SE_CD_NM_LEVY; - case IMPLT_TASK_SE_CD_6_PAY_URG: return IMPLT_TASK_SE_CD_NM_PAY_URG; - default: return IMPLT_TASK_SE_CD_NM_UNKNOWN; - } - } - -} \ No newline at end of file diff --git a/src/main/java/egovframework/constant/SEQConstants.java b/src/main/java/egovframework/constant/SEQConstants.java deleted file mode 100644 index 4595f9f..0000000 --- a/src/main/java/egovframework/constant/SEQConstants.java +++ /dev/null @@ -1,18 +0,0 @@ -package egovframework.constant; - -/** - * packageName : egovframework.constant - * fileName : SEQConstants - * author : 박성영 - * date : 25. 8. 26. - * description : - * =========================================================== - * DATE AUTHOR NOTE - * ----------------------------------------------------------- - * 25. 8. 26. 박성영 최초 생성 - */ -public class SEQConstants { - - public static final String SEQ_CRDN = "seq_crdn_no_"; - -} diff --git a/src/main/java/egovframework/constant/TaskPrcsSttsConstants.java b/src/main/java/egovframework/constant/TaskPrcsSttsConstants.java new file mode 100644 index 0000000..8914cfd --- /dev/null +++ b/src/main/java/egovframework/constant/TaskPrcsSttsConstants.java @@ -0,0 +1,41 @@ +package egovframework.constant; + +/** + * packageName : egovframework.constant + * fileName : TaskPrcsSttsConstants + * author : 시스템 관리자 + * date : 2025-11-17 + * description : 과태료 업무 처리 상태 코드 상수를 정의하는 클래스 + * 중요로직: 과태료 대상 처리 단계별 상태 코드 + * =========================================================== + * DATE AUTHOR NOTE + * ----------------------------------------------------------- + * 2025-11-17 시스템 관리자 최초 생성 + */ +public class TaskPrcsSttsConstants { + + /** + * 업무 처리 상태 코드 - 01: 접수 + * 최초 과태료 대상 등록 시 기본값으로 설정되는 상태 + */ + public static final String TASK_PRCS_STTS_CD_01_RCPT = "01"; + + /** + * 업무 처리 상태 코드 - 02: 상품용 + * API 조회 결과 소유자명에 "상품용" 문자열이 포함된 경우 + */ + public static final String TASK_PRCS_STTS_CD_02_PRODUCT_USE = "02"; + + /** + * 업무 처리 상태 코드 - 03: 이첩 + * API 조회 결과 법정동코드가 사용자 조직코드와 불일치하는 경우 + */ + public static final String TASK_PRCS_STTS_CD_03_TRANSFER = "03"; + + /** + * 업무 처리 상태 코드 - 04: 내사종결 + * API 조회 결과 특정 조건에 해당하는 경우 + */ + public static final String TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED = "04"; + +} diff --git a/src/main/java/go/kr/project/api/internal/service/impl/InternalVehicleInfoServiceImpl.java b/src/main/java/go/kr/project/api/internal/service/impl/InternalVehicleInfoServiceImpl.java index db8309e..692bd9a 100644 --- a/src/main/java/go/kr/project/api/internal/service/impl/InternalVehicleInfoServiceImpl.java +++ b/src/main/java/go/kr/project/api/internal/service/impl/InternalVehicleInfoServiceImpl.java @@ -56,6 +56,7 @@ public class InternalVehicleInfoServiceImpl extends EgovAbstractServiceImpl impl private final VmisCarBassMatterInqireService carBassMatterInqireService; private final VmisCarLedgerFrmbkService carLedgerFrmbkService; + private final go.kr.project.carInspectionPenalty.history.service.VehicleApiHistoryService vehicleApiHistoryService; @Override @@ -97,6 +98,19 @@ public class InternalVehicleInfoServiceImpl extends EgovAbstractServiceImpl impl response.setSuccess(true); response.setMessage("조회 성공"); log.info("[Internal Mode] 차량번호 {} 조회 성공", vehicleNumber); + + // 4. API 호출 성공 시 히스토리 ID 조회 및 설정 + try { + String carBassMatterInqireId = vehicleApiHistoryService.selectLatestCarBassMatterInqireIdByVhclno(vehicleNumber); + String carLedgerFrmbkId = vehicleApiHistoryService.selectLatestCarLedgerFrmbkIdByVhclno(vehicleNumber); + response.setCarBassMatterInqireId(carBassMatterInqireId); + response.setCarLedgerFrmbkId(carLedgerFrmbkId); + log.debug("[Internal Mode] 히스토리 ID 설정 완료 - 차량번호: {}, 기본정보ID: {}, 원부ID: {}", + vehicleNumber, carBassMatterInqireId, carLedgerFrmbkId); + } catch (Exception e) { + log.warn("[Internal Mode] 히스토리 ID 조회 실패 - 차량번호: {}", vehicleNumber, e); + // ID 조회 실패는 치명적이지 않으므로 계속 진행 + } } else { response.setSuccess(false); response.setMessage(basicInfo != null ? basicInfo.getCntcResultDtls() : "조회 실패"); diff --git a/src/main/java/go/kr/project/api/model/VehicleApiResponseVO.java b/src/main/java/go/kr/project/api/model/VehicleApiResponseVO.java index 51309c5..cb61c8c 100644 --- a/src/main/java/go/kr/project/api/model/VehicleApiResponseVO.java +++ b/src/main/java/go/kr/project/api/model/VehicleApiResponseVO.java @@ -24,6 +24,9 @@ public class VehicleApiResponseVO { private BasicResponse basicInfo; // 차량 기본정보 private LedgerResponse ledgerInfo; // 등록원부 정보 + private String carBassMatterInqireId; // 자동차 기본 사항 조회 ID + private String carLedgerFrmbkId; // 자동차 등록 원부 갑 ID + public VehicleApiResponseVO() { this.success = true; } diff --git a/src/main/java/go/kr/project/carInspectionPenalty/history/mapper/VehicleApiHistoryMapper.java b/src/main/java/go/kr/project/carInspectionPenalty/history/mapper/VehicleApiHistoryMapper.java index c459fdf..dbf4004 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/history/mapper/VehicleApiHistoryMapper.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/history/mapper/VehicleApiHistoryMapper.java @@ -91,4 +91,20 @@ public interface VehicleApiHistoryMapper { * @return 자동차 등록원부(갑) 상세 조회 이력 상세 */ CarLedgerFrmbkDtlVO selectCarLedgerFrmbkDtlOne(String carLedgerFrmbkDtlId); + + /** + * 차량번호로 최신 자동차 기본정보 조회 이력 ID를 조회합니다. + * + * @param vhclno 차량번호 + * @return 자동차 기본 사항 조회 ID + */ + String selectLatestCarBassMatterInqireIdByVhclno(String vhclno); + + /** + * 차량번호로 최신 자동차 등록원부(갑) 조회 이력 ID를 조회합니다. + * + * @param vhclno 차량번호 + * @return 자동차 등록 원부 갑 ID + */ + String selectLatestCarLedgerFrmbkIdByVhclno(String vhclno); } diff --git a/src/main/java/go/kr/project/carInspectionPenalty/history/service/VehicleApiHistoryService.java b/src/main/java/go/kr/project/carInspectionPenalty/history/service/VehicleApiHistoryService.java index b6f5ecc..439f6b2 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/history/service/VehicleApiHistoryService.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/history/service/VehicleApiHistoryService.java @@ -89,4 +89,20 @@ public interface VehicleApiHistoryService { * @return 자동차 등록원부(갑) 상세 조회 이력 상세 */ CarLedgerFrmbkDtlVO selectCarLedgerFrmbkDtlOne(String carLedgerFrmbkDtlId); + + /** + * 차량번호로 최신 자동차 기본정보 조회 이력 ID를 조회합니다. + * + * @param vhclno 차량번호 + * @return 자동차 기본 사항 조회 ID + */ + String selectLatestCarBassMatterInqireIdByVhclno(String vhclno); + + /** + * 차량번호로 최신 자동차 등록원부(갑) 조회 이력 ID를 조회합니다. + * + * @param vhclno 차량번호 + * @return 자동차 등록 원부 갑 ID + */ + String selectLatestCarLedgerFrmbkIdByVhclno(String vhclno); } diff --git a/src/main/java/go/kr/project/carInspectionPenalty/history/service/impl/VehicleApiHistoryServiceImpl.java b/src/main/java/go/kr/project/carInspectionPenalty/history/service/impl/VehicleApiHistoryServiceImpl.java index a4f88d8..c9ca978 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/history/service/impl/VehicleApiHistoryServiceImpl.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/history/service/impl/VehicleApiHistoryServiceImpl.java @@ -84,4 +84,16 @@ public class VehicleApiHistoryServiceImpl extends EgovAbstractServiceImpl implem log.debug("자동차 등록원부(갑) 상세 조회 이력 상세 조회 - ID: {}", carLedgerFrmbkDtlId); return mapper.selectCarLedgerFrmbkDtlOne(carLedgerFrmbkDtlId); } + + @Override + public String selectLatestCarBassMatterInqireIdByVhclno(String vhclno) { + log.debug("차량번호로 최신 자동차 기본정보 조회 이력 ID 조회 - 차량번호: {}", vhclno); + return mapper.selectLatestCarBassMatterInqireIdByVhclno(vhclno); + } + + @Override + public String selectLatestCarLedgerFrmbkIdByVhclno(String vhclno) { + log.debug("차량번호로 최신 자동차 등록원부(갑) 조회 이력 ID 조회 - 차량번호: {}", vhclno); + return mapper.selectLatestCarLedgerFrmbkIdByVhclno(vhclno); + } } diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registration/controller/CarFfnlgTrgtController.java b/src/main/java/go/kr/project/carInspectionPenalty/registration/controller/CarFfnlgTrgtController.java index eb6e07c..ecf76f5 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/registration/controller/CarFfnlgTrgtController.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/registration/controller/CarFfnlgTrgtController.java @@ -22,6 +22,7 @@ import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletResponse; import java.net.URLEncoder; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -293,4 +294,101 @@ public class CarFfnlgTrgtController { return ApiResponseUtil.error("처리 중 오류가 발생했습니다: " + e.getMessage()); } } + + /** + * 검색조건 전체 목록에 대해 API 호출 및 기본정보/등록원부 비교 (페이징 없이) + * @param searchParams 검색 조건 파라미터 + * @return 비교 결과 + */ + @PostMapping("/compareWithApiAll.ajax") + @ResponseBody + @Operation(summary = "검색조건 전체 API 호출 및 데이터 비교", description = "검색조건에 해당하는 전체 목록에 대해 차량 API를 호출하고 비교합니다.") + public ResponseEntity compareWithApiAll(@RequestBody CarFfnlgTrgtVO searchParams) { + log.info("전체 API 호출 및 비교 요청 - 검색 조건: {}", searchParams); + + try { + // 페이징 비활성화 + searchParams.setPagingYn("N"); + + // 전체 목록 조회 + List allData = service.selectList(searchParams); + + // 목록을 Map 형태로 변환 + List> targetList = allData.stream() + .map(vo -> { + Map map = new HashMap<>(); + map.put("carFfnlgTrgtId", vo.getCarFfnlgTrgtId()); + map.put("vhclno", vo.getVhclno()); + map.put("inspYmd", vo.getInspYmd()); + map.put("ownrNm", vo.getOwnrNm()); + map.put("carNm", vo.getCarNm()); + return map; + }) + .collect(java.util.stream.Collectors.toList()); + + // API 호출 및 비교 + Map resultData = service.compareWithApi(targetList); + + int successCount = (int) resultData.get("successCount"); + int failCount = (int) resultData.get("failCount"); + String message = String.format("전체 API 호출 및 비교 완료\n대상: %d건, 성공: %d건, 실패: %d건", + allData.size(), successCount, failCount); + + return ApiResponseUtil.success(resultData, message); + + } catch (Exception e) { + log.error("전체 API 호출 및 비교 중 오류 발생", e); + return ApiResponseUtil.error("처리 중 오류가 발생했습니다: " + e.getMessage()); + } + } + + /** + * 과태료 대상 일괄 삭제 + * @param deleteIds 삭제할 과태료 대상 ID 목록 + * @return 삭제 결과 + */ + @PostMapping("/deleteBatch.ajax") + @ResponseBody + @Operation(summary = "과태료 대상 일괄 삭제", description = "선택된 과태료 대상 목록을 일괄 삭제합니다.") + public ResponseEntity deleteBatch(@RequestBody List deleteIds) { + log.info("일괄 삭제 요청 - 선택된 데이터 건수: {}", deleteIds != null ? deleteIds.size() : 0); + + try { + if (deleteIds == null || deleteIds.isEmpty()) { + return ApiResponseUtil.error("삭제할 데이터가 없습니다."); + } + + int successCount = 0; + int failCount = 0; + + for (String id : deleteIds) { + try { + CarFfnlgTrgtVO vo = new CarFfnlgTrgtVO(); + vo.setCarFfnlgTrgtId(id); + int result = service.delete(vo); + + if (result > 0) { + successCount++; + } else { + failCount++; + } + } catch (Exception e) { + log.error("삭제 실패 - ID: {}", id, e); + failCount++; + } + } + + String message = String.format("삭제 완료\n성공: %d건, 실패: %d건", successCount, failCount); + + Map resultData = new HashMap<>(); + resultData.put("successCount", successCount); + resultData.put("failCount", failCount); + + return ApiResponseUtil.success(resultData, message); + + } catch (Exception e) { + log.error("일괄 삭제 중 오류 발생", e); + return ApiResponseUtil.error("삭제 중 오류가 발생했습니다: " + e.getMessage()); + } + } } 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 3ccf936..f99fc26 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 @@ -1,5 +1,6 @@ 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.request.BasicRequest; @@ -191,7 +192,7 @@ public class CarFfnlgTrgtServiceImpl extends EgovAbstractServiceImpl implements } // 업무 처리 상태 및 등록자 설정 - vo.setTaskPrcsSttsCd("01"); // 01=접수 + vo.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_01_RCPT); // 01=접수 vo.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); vo.setRcptYmd(LocalDate.now().format(DATE_FORMATTER)); // 접수일자는 현재 날짜 vo.setRgtr(rgtr); @@ -961,7 +962,13 @@ public class CarFfnlgTrgtServiceImpl extends EgovAbstractServiceImpl implements continue; } - + // 2.5. API 호출 성공 시 히스토리 ID 업데이트 + if (apiResponse.getCarBassMatterInqireId() != null || apiResponse.getCarLedgerFrmbkId() != null) { + existingData.setCarBassMatterInqireId(apiResponse.getCarBassMatterInqireId()); + existingData.setCarLedgerFrmbkId(apiResponse.getCarLedgerFrmbkId()); + log.info("API 히스토리 ID 업데이트 - 차량번호: {}, 기본정보ID: {}, 원부ID: {}", + vhclno, apiResponse.getCarBassMatterInqireId(), apiResponse.getCarLedgerFrmbkId()); + } // 3. 비교 로직 실행 String statusCode = comparisonService.executeComparison(existingData, apiResponse, rgtr); @@ -969,15 +976,15 @@ public class CarFfnlgTrgtServiceImpl extends EgovAbstractServiceImpl implements // 결과 처리 if (statusCode != null) { // 비교 규칙이 적용됨 - if ("02".equals(statusCode)) { + if (TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE.equals(statusCode)) { productUseCount++; compareResult.put("processStatus", "상품용"); compareResult.put("message", "상품용으로 처리되었습니다."); - } else if ("03".equals(statusCode)) { + } else if (TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER.equals(statusCode)) { transferCount++; compareResult.put("processStatus", "이첩"); compareResult.put("message", "이첩으로 처리되었습니다."); - } else if ("04".equals(statusCode)) { + } else if (TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED.equals(statusCode)) { normalCount++; compareResult.put("processStatus", "내사종결"); compareResult.put("message", "내사종결로 처리되었습니다."); @@ -999,9 +1006,12 @@ public class CarFfnlgTrgtServiceImpl extends EgovAbstractServiceImpl implements } catch (Exception e) { log.error("데이터 비교 중 오류 발생 - 차량번호: {}", vhclno, e); + throw new MessageException(String.format("데이터 비교 중 오류 발생 - 차량번호: {}", vhclno), e); + /* compareResult.put("success", false); compareResult.put("message", "비교 중 오류: " + e.getMessage()); failCount++; + */ } compareResults.add(compareResult); 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 1863440..e5e136b 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 @@ -1,5 +1,6 @@ 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.BasicResponse; @@ -94,7 +95,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co log.info("[상품용] 조건 충족! 차량번호: {}, 소유자명: {}", vhclno, mberNm); // DB 업데이트 - existingData.setTaskPrcsSttsCd("02"); // 상품용 + existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE); // 상품용 existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); existingData.setCarBscMttrInqFlnm(mberNm); // 소유자명 저장 existingData.setCarBscMttrInqSggCd(null); @@ -106,7 +107,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co } log.info("[상품용] 처리 완료! 차량번호: {}", vhclno); - return "02"; + return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_02_PRODUCT_USE; } /** @@ -192,7 +193,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co } // DB 업데이트 - existingData.setTaskPrcsSttsCd("03"); // 이첩 + existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER); // 이첩 existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); existingData.setCarBscMttrInqFlnm(null); existingData.setCarBscMttrInqSggCd(sggCd); // 시군구 코드 @@ -204,7 +205,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co } log.info("[이첩] 처리 완료! 차량번호: {}, 시군구: {}({}), 사유: {}", vhclno, sggNm, sggCd, reason); - return "03"; + return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER; } /** @@ -229,7 +230,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co log.info("[내사종결] 조건 충족! 차량번호: {}, 말소구분: {}", vhclno, ersrRegistSeCode); // DB 업데이트 - existingData.setTaskPrcsSttsCd("04"); // 내사종결 + existingData.setTaskPrcsSttsCd(TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED); // 내사종결 existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); // 필요한 추가 필드 설정 @@ -239,7 +240,7 @@ public class ComparisonServiceImpl extends EgovAbstractServiceImpl implements Co } log.info("[내사종결] 처리 완료! 차량번호: {}", vhclno); - return "04"; + return TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_04_INVESTIGATION_CLOSED; } } diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registration/비교로직_구현가이드.md b/src/main/java/go/kr/project/carInspectionPenalty/registration/비교로직_구현가이드.md deleted file mode 100644 index b5c6ec0..0000000 --- a/src/main/java/go/kr/project/carInspectionPenalty/registration/비교로직_구현가이드.md +++ /dev/null @@ -1,788 +0,0 @@ -# 과태료 대상 비교 로직 구현 가이드 - -## 📌 목차 - -1. [개요](#개요) -2. [두 가지 구현 방법 비교](#두-가지-구현-방법-비교) -3. [방법1: Service/Impl 패턴 (추천)](#방법1-serviceimpl-패턴-추천) -4. [방법2: Chain of Responsibility 패턴](#방법2-chain-of-responsibility-패턴) -5. [어떤 방법을 선택해야 할까?](#어떤-방법을-선택해야-할까) -6. [참고사항](#참고사항) - ---- - -## 개요 - -이 모듈은 과태료 대상 차량을 API 응답 데이터와 비교하여 자동으로 분류하는 기능을 제공합니다. -현재 프로젝트는 **두 가지 비교 로직 구현 방법**을 제공하며, 프로젝트 상황에 맞게 선택하여 사용할 수 있습니다. - -### 비교 로직 처리 흐름 - -``` -API 응답 데이터 수신 - ↓ -비교 로직 실행 - ↓ -조건1 (상품용) 체크 → 충족 → TASK_PRCS_STTS_CD = 02 → 종료 - ↓ 미충족 -조건2 (이첩) 체크 → 충족 → TASK_PRCS_STTS_CD = 03 → 종료 - ↓ 미충족 -조건3 (내사종결) 체크 → 충족 → TASK_PRCS_STTS_CD = 04 → 종료 - ↓ 미충족 -모든 조건 미충족 → 정상 처리 (null 반환) -``` - ---- - -## 두 가지 구현 방법 비교 - -### 비교표 - -| 항목 | 방법1 (Service/Impl) ⭐ | 방법2 (Chain of Responsibility) | -|------|---------------------|--------------------------------| -| **구현 난이도** | ⭐ 쉬움 | ⭐⭐⭐ 어려움 | -| **학습 시간** | 10분 | 1시간+ | -| **조건 추가 시간** | 5분 | 15분 | -| **코드 가독성** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | -| **확장성** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | -| **테스트 용이성** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | -| **파일 개수** | 2개 | N+4개 | -| **디버깅** | ⭐⭐⭐⭐⭐ 쉬움 | ⭐⭐⭐ 보통 | -| **추천 조건 개수** | ~10개 | 10개 이상 | - ---- - -## 방법1: Service/Impl 패턴 (추천) - -### 🌟 특징 - -- ✅ **간단하고 직관적** - 학습 곡선 낮음 -- ✅ **명확한 코드 흐름** - 순차적 실행으로 이해하기 쉬움 -- ✅ **빠른 개발** - 메서드 하나만 추가하면 됨 -- ✅ **디버깅 쉬움** - 코드 추적이 직관적 -- ✅ **한 파일에서 관리** - 모든 비교 로직을 한눈에 확인 - -### 📁 디렉토리 구조 - -``` -service/ -├── ComparisonService.java # 인터페이스 -└── impl/ - └── ComparisonServiceImpl.java # 구현체 (모든 비교 로직 포함) -``` - -### 🚀 새로운 비교 조건 추가 방법 - -#### 예제 1: 일반 비교 조건 추가 - -**1단계: ComparisonServiceImpl에 private 메서드 추가** - -```java -/** - * 4. 장기미검차량 체크 - * - *

조건: 검사 유효기간 종료일로부터 1년 이상 경과

- *

처리: TASK_PRCS_STTS_CD = 05

- * - * @return 05 (적용됨) 또는 null (미적용) - */ -private String checkLongTermUninspected(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo) { - String vhclno = existingData.getVhclno(); - String insptValidPdEndde = basicInfo.getInsptValidPdEndde(); // 검사유효기간종료일자 - - // 조건 체크 로직 - if (insptValidPdEndde == null || insptValidPdEndde.isEmpty()) { - log.debug("[장기미검] 조건 미충족. 차량번호: {}", vhclno); - return null; - } - - // 1년 경과 여부 확인 로직 - LocalDate endDate = LocalDate.parse(insptValidPdEndde, DATE_FORMATTER); - LocalDate oneYearAgo = LocalDate.now().minusYears(1); - - if (endDate.isAfter(oneYearAgo)) { - log.debug("[장기미검] 조건 미충족. 차량번호: {}, 종료일: {}", vhclno, insptValidPdEndde); - return null; - } - - log.info("[장기미검] 조건 충족! 차량번호: {}, 종료일: {}", vhclno, insptValidPdEndde); - - // DB 업데이트 - existingData.setTaskPrcsSttsCd("05"); // 장기미검 - existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); - - int updateCount = carFfnlgTrgtMapper.update(existingData); - if (updateCount == 0) { - throw new RuntimeException(String.format("[장기미검] 업데이트 실패: %s", vhclno)); - } - - log.info("[장기미검] 처리 완료! 차량번호: {}", vhclno); - return "05"; -} -``` - -**2단계: executeComparison() 메서드에 호출 추가** - -```java -@Override -public String executeComparison(CarFfnlgTrgtVO existingData, VehicleApiResponseVO apiResponse, String userId) { - String vhclno = existingData.getVhclno(); - log.info("========== 비교 로직 시작: {} ==========", vhclno); - - // API 응답 데이터 유효성 검사 - if (!isValidApiResponse(apiResponse)) { - log.warn("API 응답 데이터가 유효하지 않습니다. 차량번호: {}", vhclno); - return null; - } - - BasicResponse.Record basicInfo = apiResponse.getBasicInfo().getRecord().get(0); - - // ========== 1. 상품용 체크 ========== - String productUseResult = checkProductUse(existingData, basicInfo); - if (productUseResult != null) { - log.info("========== 비교 로직 종료 (상품용): {} ==========", vhclno); - return productUseResult; - } - - // ========== 2. 이첩 체크 ========== - String transferResult = checkTransfer(existingData, basicInfo, userId); - if (transferResult != null) { - log.info("========== 비교 로직 종료 (이첩): {} ==========", vhclno); - return transferResult; - } - - // ========== 3. 장기미검 체크 (새로 추가!) ========== - String longTermResult = checkLongTermUninspected(existingData, basicInfo); - if (longTermResult != null) { - log.info("========== 비교 로직 종료 (장기미검): {} ==========", vhclno); - return longTermResult; - } - - // 모든 비교 로직에 해당하지 않음 - log.info("========== 비교 로직 종료 (정상): {} ==========", vhclno); - return null; -} -``` - -**끝! 매우 간단합니다.** - ---- - -#### 예제 2: 이첩 조건 추가 (OR 구조) - -이첩 조건은 **OR 로직**으로 구성되어 있습니다. **여러 조건 중 하나라도 만족하면** 이첩으로 처리됩니다. - -**현재 이첩 구조:** - -```java -private String checkTransfer(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo, String userId) { - String vhclno = existingData.getVhclno(); - - // ========== 이첩 조건들 (OR 로직: 하나라도 만족하면 이첩) ========== - - // 조건1: 법정동코드 불일치 - if (checkTransferCondition1_LegalDongMismatch(basicInfo, userId, vhclno)) { - return processTransfer(existingData, basicInfo, vhclno, "법정동코드 불일치"); - } - - // 조건2: 향후 추가될 이첩 조건들... - // if (checkTransferCondition2_XXX(basicInfo, userId, vhclno)) { - // return processTransfer(existingData, basicInfo, vhclno, "XXX"); - // } - - // 모든 이첩 조건에 해당하지 않음 - log.debug("[이첩] 모든 조건 미충족. 차량번호: {}", vhclno); - return null; -} -``` - -**새로운 이첩 조건 추가:** - -**1단계: 조건 체크 메서드 추가** - -```java -/** - * 이첩 조건2: 차량 소유자 주소 불일치 (예시) - * 조건: 차량 등록지와 소유자 실거주지가 다른 경우 - */ -private boolean checkTransferCondition2_AddressMismatch(BasicResponse.Record basicInfo, String userId, String vhclno) { - String useStrnghldAdresNm = basicInfo.getUseStrnghldAdresNm(); // 사용본거지주소명 - String ownerAdresNm = basicInfo.getOwnerAdresNm(); // 소유자주소명 - - // 주소 유효성 검사 - if (useStrnghldAdresNm == null || ownerAdresNm == null) { - log.debug("[이첩][조건2] 주소 정보 없음. 차량번호: {}", vhclno); - return false; - } - - // 주소 일치 여부 확인 (시/도 단위 비교 등) - if (useStrnghldAdresNm.equals(ownerAdresNm)) { - log.debug("[이첩][조건2] 주소 일치. 차량번호: {}", vhclno); - return false; - } - - log.info("[이첩][조건2] 주소 불일치! 차량번호: {}, 사용본거지: {}, 소유자주소: {}", - vhclno, useStrnghldAdresNm, ownerAdresNm); - return true; -} -``` - -**2단계: checkTransfer() 메서드에 조건 추가** - -```java -private String checkTransfer(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo, String userId) { - String vhclno = existingData.getVhclno(); - - // ========== 이첩 조건들 (OR 로직: 하나라도 만족하면 이첩) ========== - - // 조건1: 법정동코드 불일치 - if (checkTransferCondition1_LegalDongMismatch(basicInfo, userId, vhclno)) { - return processTransfer(existingData, basicInfo, vhclno, "법정동코드 불일치"); - } - - // 조건2: 주소 불일치 (새로 추가!) - if (checkTransferCondition2_AddressMismatch(basicInfo, userId, vhclno)) { - return processTransfer(existingData, basicInfo, vhclno, "주소 불일치"); - } - - // 조건3: 향후 추가될 조건들... - // if (checkTransferCondition3_XXX(basicInfo, userId, vhclno)) { - // return processTransfer(existingData, basicInfo, vhclno, "XXX"); - // } - - return null; -} -``` - -**끝! 매우 간단합니다.** - ---- - -### 📊 이첩 조건 OR 로직 동작 방식 - -```java -// 이첩 체크 시작 -checkTransfer(...) - ↓ -조건1 체크 → TRUE → 즉시 이첩 처리 후 return "03" - ↓ FALSE -조건2 체크 → TRUE → 즉시 이첩 처리 후 return "03" - ↓ FALSE -조건3 체크 → TRUE → 즉시 이첩 처리 후 return "03" - ↓ FALSE -모든 조건 FALSE → return null (이첩 아님) -``` - -### 🔍 로그 예시 - -#### 조건1에 걸린 경우 -``` -[이첩][조건1] 법정동코드 불일치! 차량번호: 12가3456, 법정동: 1100, 조직: 4100 -[이첩] 조건 충족! 차량번호: 12가3456, 사유: 법정동코드 불일치 -[이첩] 처리 완료! 차량번호: 12가3456, 시군구: 서울특별시(11000), 사유: 법정동코드 불일치 -``` - -#### 조건2에 걸린 경우 -``` -[이첩][조건1] 법정동코드 일치. 차량번호: 12가3456, 법정동: 1100, 조직: 1100 -[이첩][조건2] 주소 불일치! 차량번호: 12가3456 -[이첩] 조건 충족! 차량번호: 12가3456, 사유: 주소 불일치 -[이첩] 처리 완료! 차량번호: 12가3456, 시군구: 서울특별시(11000), 사유: 주소 불일치 -``` - -#### 모든 조건에 안 걸린 경우 -``` -[이첩][조건1] 법정동코드 일치. 차량번호: 12가3456 -[이첩][조건2] 주소 일치. 차량번호: 12가3456 -[이첩] 모든 조건 미충족. 차량번호: 12가3456 -``` - ---- - -### 🚀 빠른 템플릿 - -#### 일반 비교 조건 템플릿 - -```java -/** - * N. 비교 조건명 - * - *

조건: 조건 설명

- *

처리: TASK_PRCS_STTS_CD = XX

- * - * @return XX (적용됨) 또는 null (미적용) - */ -private String checkXXX(CarFfnlgTrgtVO existingData, BasicResponse.Record basicInfo) { - String vhclno = existingData.getVhclno(); - - // 1. 데이터 추출 - String data1 = basicInfo.getXXX(); - String data2 = basicInfo.getYYY(); - - // 2. 유효성 검사 - if (data1 == null || data2 == null) { - log.debug("[비교조건명] 조건 미충족. 차량번호: {}", vhclno); - return null; - } - - // 3. 조건 체크 - if (조건_만족) { - log.info("[비교조건명] 조건 충족! 차량번호: {}, 상세정보...", vhclno); - - // 4. DB 업데이트 - existingData.setTaskPrcsSttsCd("XX"); - existingData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); - // 필요한 필드 추가 설정 - - int updateCount = carFfnlgTrgtMapper.update(existingData); - if (updateCount == 0) { - throw new RuntimeException(String.format("[비교조건명] 업데이트 실패: %s", vhclno)); - } - - log.info("[비교조건명] 처리 완료! 차량번호: {}", vhclno); - return "XX"; - } - - log.debug("[비교조건명] 조건 미충족. 차량번호: {}", vhclno); - return null; -} -``` - -#### 이첩 조건 템플릿 - -```java -/** - * 이첩 조건N: XXX - * 조건 설명 - */ -private boolean checkTransferConditionN_XXX(BasicResponse.Record basicInfo, String userId, String vhclno) { - // 1. 데이터 추출 - String data1 = basicInfo.getXXX(); - String data2 = basicInfo.getYYY(); - - // 2. 유효성 검사 - if (data1 == null || data2 == null) { - log.debug("[이첩][조건N] 데이터 없음. 차량번호: {}", vhclno); - return false; - } - - // 3. 조건 체크 - if (조건_만족) { - log.info("[이첩][조건N] 조건 충족! 차량번호: {}, 상세정보...", vhclno); - return true; // 이첩! - } - - log.debug("[이첩][조건N] 조건 미충족. 차량번호: {}", vhclno); - return false; -} - -// checkTransfer()에 추가 -if (checkTransferConditionN_XXX(basicInfo, userId, vhclno)) { - return processTransfer(existingData, basicInfo, vhclno, "XXX"); -} -``` - ---- - -### ⚠️ 주의사항 - -1. **조건 순서**: 위에서부터 순차적으로 체크됩니다 - - 자주 걸리는 조건을 위쪽에 배치하면 성능 향상 - -2. **조건 메서드 네이밍**: - - 일반: `checkXXX` - - 이첩: `checkTransferConditionN_XXX` 형식 권장 - -3. **return 값**: - - 일반 조건: `"상태코드"` (적용됨) 또는 `null` (미적용) - - 이첩 조건: `true` (해당함) 또는 `false` (해당 안함) - -4. **processTransfer() 공통 사용**: - - DB 업데이트는 `processTransfer()` 메서드에서 공통으로 처리 - - 각 조건에서는 `true/false`만 반환 - ---- - -### 📝 체크리스트 - -새로운 비교 조건 추가 시 확인사항: - -- [ ] 조건 메서드 작성 완료 -- [ ] executeComparison()에 조건 추가 완료 (일반 조건) -- [ ] checkTransfer()에 조건 추가 완료 (이첩 조건) -- [ ] 로그 메시지에 조건 정보 포함 -- [ ] null 체크 처리 완료 -- [ ] 컴파일 테스트 완료 -- [ ] 실제 데이터로 테스트 완료 - ---- - -## 방법2: Chain of Responsibility 패턴 - -### 🌟 특징 - -- ✅ **확장성 우수** - 규칙 추가/삭제가 독립적 -- ✅ **테스트 용이** - 각 규칙을 독립적으로 테스트 가능 -- ✅ **순서 제어** - getOrder()로 실행 순서 명확히 제어 -- ✅ **관심사 분리** - 각 규칙이 독립된 파일로 관리 -- ❌ **복잡한 구조** - 학습 곡선 높음 -- ❌ **파일 분산** - 여러 파일을 확인해야 함 - -### 📁 디렉토리 구조 - -``` -comparison/ -├── ComparisonRule.java # 비교 규칙 인터페이스 -├── ComparisonContext.java # 비교에 필요한 데이터 컨테이너 -├── ComparisonResult.java # 비교 결과 객체 -├── ComparisonRuleProcessor.java # 규칙 실행 체인 관리자 -└── rules/ # 개별 규칙 구현체 - ├── ProductUseComparisonRule.java # 상품용 규칙 - └── TransferComparisonRule.java # 이첩 규칙 -``` - -### 🔄 동작 방식 - -1. **ComparisonRuleProcessor**가 모든 `@Component` 규칙을 자동으로 찾아서 등록 -2. 규칙들을 `getOrder()` 순서대로 정렬 (낮을수록 먼저 실행) -3. 각 규칙을 순차적으로 실행 -4. 규칙이 `applied=true`를 반환하면 즉시 중단 -5. 모든 규칙이 `applied=false`를 반환하면 "정상" 처리 - -``` -ComparisonRuleProcessor - ↓ -규칙 자동 스캔 (@Component) - ↓ -순서대로 정렬 (getOrder()) - ↓ -ProductUseComparisonRule (order=10) → 적용됨? → 종료 - ↓ 미적용 -TransferComparisonRule (order=20) → 적용됨? → 종료 - ↓ 미적용 -YourNewRule (order=30) → 적용됨? → 종료 - ↓ 미적용 -정상 처리 -``` - ---- - -### 🚀 새로운 비교 규칙 추가 방법 - -#### 1단계: rules/ 폴더에 새 클래스 생성 - -```java -package go.kr.project.carInspectionPenalty.registration.comparison.rules; - -import go.kr.project.carInspectionPenalty.registration.comparison.*; -import go.kr.project.carInspectionPenalty.registration.mapper.CarFfnlgTrgtMapper; -import go.kr.project.carInspectionPenalty.registration.model.CarFfnlgTrgtVO; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; - -/** - * 내사종결 비교 규칙 - * - * 적용 조건: - * - 예시: 차량이 말소된 경우 - * - * 처리 내용: - * - TASK_PRCS_STTS_CD = 04 (내사종결) - * - TASK_PRCS_YMD = 현재 날짜 - */ -@Slf4j -@Component -@RequiredArgsConstructor -public class InvestigationClosedComparisonRule implements ComparisonRule { - - private static final String STATUS_CODE = "04"; // 내사종결 - private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); - - private final CarFfnlgTrgtMapper mapper; - - @Override - public ComparisonResult execute(ComparisonContext context) { - String vhclno = context.getVhclno(); - - // 1. API 응답 데이터 유효성 검사 - if (context.getApiResponse().getBasicInfo() == null || - context.getApiResponse().getBasicInfo().getRecord() == null || - context.getApiResponse().getBasicInfo().getRecord().isEmpty()) { - log.debug("[{}] API 응답 데이터가 없어 규칙을 적용할 수 없습니다. 차량번호: {}", - getRuleName(), vhclno); - return ComparisonResult.notApplied(); - } - - // 2. 필요한 데이터 추출 - go.kr.project.api.model.response.BasicResponse.Record basicInfo = - context.getApiResponse().getBasicInfo().getRecord().get(0); - String ersrRegistSeCode = basicInfo.getErsrRegistSeCode(); // 말소등록구분코드 - - // 3. 비교 로직 (말소된 차량인지 확인) - if (ersrRegistSeCode == null || ersrRegistSeCode.isEmpty()) { - log.debug("[{}] 말소되지 않은 차량입니다. 차량번호: {}", getRuleName(), vhclno); - return ComparisonResult.notApplied(); - } - - log.info("[{}] 내사종결 감지! 차량번호: {}, 말소구분: {}", - getRuleName(), vhclno, ersrRegistSeCode); - - // 4. DB 업데이트 - CarFfnlgTrgtVO updateData = context.getExistingData(); - updateData.setTaskPrcsSttsCd(STATUS_CODE); - updateData.setTaskPrcsYmd(LocalDate.now().format(DATE_FORMATTER)); - // 필요시 추가 필드 설정 - - int updateResult = mapper.update(updateData); - - // 5. 결과 반환 - if (updateResult > 0) { - log.info("[{}] 처리 완료! 차량번호: {}", getRuleName(), vhclno); - return ComparisonResult.applied(STATUS_CODE, "내사종결로 처리되었습니다."); - } else { - log.error("[{}] 업데이트 실패! 차량번호: {}", getRuleName(), vhclno); - throw new RuntimeException(String.format("내사종결 업데이트 실패: %s", vhclno)); - } - } - - @Override - public String getRuleName() { - return "내사종결"; - } - - @Override - public int getOrder() { - return 30; // 상품용(10), 이첩(20) 다음으로 실행 - } -} -``` - -#### 2단계: 자동 등록 - -Spring이 자동으로 `@Component` 어노테이션이 붙은 클래스를 찾아서 등록합니다. -**별도의 설정 파일 수정이 필요 없습니다!** - -#### 끝! 자동으로 등록됩니다. - ---- - -### 📊 실행 순서 제어 - -`getOrder()` 메서드로 실행 순서를 제어할 수 있습니다. - -```java -@Override -public int getOrder() { - return 30; // 숫자가 낮을수록 먼저 실행 -} -``` - -**현재 순서:** -- 상품용: 10 -- 이첩: 20 -- (향후 추가): 30, 40, 50... - ---- - -### 📦 API 응답 데이터 활용 - -`ComparisonContext`를 통해 다음 데이터에 접근할 수 있습니다: - -```java -// 기존 과태료 대상 데이터 -CarFfnlgTrgtVO existingData = context.getExistingData(); - -// API 응답 - 기본 정보 -var basicInfo = context.getApiResponse().getBasicInfo().getRecord().get(0); -String mberNm = basicInfo.getMberNm(); // 대표소유자성명 -String vhrno = basicInfo.getVhrno(); // 차량번호 -String useStrnghldLegaldongCode = basicInfo.getUseStrnghldLegaldongCode(); // 사용본거지법정동코드 -String ersrRegistSeCode = basicInfo.getErsrRegistSeCode(); // 말소등록구분코드 -// ... 기타 필드들 - -// API 응답 - 등록원부 -var ledgerInfo = context.getApiResponse().getLedgerInfo(); - -// 사용자 ID -String userId = context.getUserId(); -``` - ---- - -### 💉 의존성 주입 - -규칙 클래스에서 필요한 빈을 자유롭게 주입받을 수 있습니다. - -```java -@Component -@RequiredArgsConstructor -public class MyComparisonRule implements ComparisonRule { - - private final CarFfnlgTrgtMapper mapper; // Mapper - private final UserMapper userMapper; // User 정보 필요 시 - private final SomeOtherService someService; // 다른 서비스 - - // ... -} -``` - ---- - -### 📝 로깅 - -규칙 실행 중 상세한 로그가 자동으로 출력됩니다. - -``` -[상품용] 규칙 실행 중... 차량번호: 12가3456 -[상품용] 상품용 감지! 차량번호: 12가3456, 소유자명: 상품용차량 -[상품용] 처리 완료! 차량번호: 12가3456 -``` - ---- - -### 🧪 테스트 방법 - -규칙을 독립적으로 테스트할 수 있습니다. - -```java -@SpringBootTest -class InvestigationClosedComparisonRuleTest { - - @Autowired - private InvestigationClosedComparisonRule rule; - - @Test - void 말소된_차량은_내사종결로_처리된다() { - // Given - ComparisonContext context = ComparisonContext.builder() - .existingData(existingData) - .apiResponse(apiResponse) - .userId("USER001") - .build(); - - // When - ComparisonResult result = rule.execute(context); - - // Then - assertTrue(result.isApplied()); - assertEquals("04", result.getStatusCode()); - } -} -``` - ---- - -### 📚 기존 규칙 예제 - -#### 상품용 규칙 (ProductUseComparisonRule) - -```java -// 대표소유자성명에 "상품용" 문자열이 포함되어 있는지 확인 -String mberNm = basicInfo.getMberNm(); -if (mberNm != null && mberNm.contains("상품용")) { - // 상품용으로 처리 - return ComparisonResult.applied("02", "상품용으로 처리되었습니다."); -} -``` - -#### 이첩 규칙 (TransferComparisonRule) - -```java -// 법정동코드 앞 4자리와 사용자 조직코드 앞 4자리 비교 -String legalDong4 = useStrnghldLegaldongCode.substring(0, 4); -String userOrg4 = userOrgCd.substring(0, 4); - -if (!legalDong4.equals(userOrg4)) { - // 이첩으로 처리 - return ComparisonResult.applied("03", "이첩으로 처리되었습니다."); -} -``` - ---- - -## 어떤 방법을 선택해야 할까? - -### 방법1 선택 (Service/Impl) - 다음과 같은 경우 추천 ⭐ - -- ✅ **간단한 프로젝트** - 비교 조건이 10개 이하 -- ✅ **빠른 개발 필요** - 당장 구현해야 할 때 -- ✅ **팀원 경험 부족** - 디자인 패턴에 익숙하지 않은 경우 -- ✅ **유지보수 단순** - 한 파일에서 모든 로직 확인 가능 -- ✅ **작은 팀** - 1~3명 정도의 소규모 팀 - -### 방법2 선택 (Chain of Responsibility) - 다음과 같은 경우 추천 - -- ✅ **복잡한 프로젝트** - 비교 조건이 10개 이상 -- ✅ **장기 유지보수** - 규칙이 자주 추가/변경될 것으로 예상 -- ✅ **팀 규모 큽** - 여러 개발자가 동시에 작업 -- ✅ **테스트 중요** - 각 규칙을 독립적으로 테스트해야 함 -- ✅ **확장성 중시** - 규칙을 동적으로 추가/제거 필요 - ---- - -## 참고사항 - -### 💡 현재 설정 변경 방법 - -`CarFfnlgTrgtServiceImpl.java`의 `executeComparisonLogic()` 메서드에서 주석을 변경하면 됩니다. - -```java -private String executeComparisonLogic(...) { - // 방법1 사용하려면: (현재 설정) - return executeWithServicePattern(existingData, apiResponse, userId); - - // 방법2 사용하려면: - // return executeWithChainPattern(existingData, apiResponse, userId); -} -``` - ---- - -### ⚠️ 주의사항 - -1. **두 방법을 동시에 사용하지 마세요** - 하나만 선택 -2. **트랜잭션 관리** - 각 비교 메서드는 DB 업데이트를 직접 수행 -3. **예외 처리** - 업데이트 실패 시 RuntimeException 발생 → 전체 롤백 -4. **로깅** - 각 조건의 충족/미충족 여부를 명확히 로깅 -5. **순서 관리** (방법2) - `getOrder()` 값이 중복되지 않도록 주의 -6. **null 체크** - API 응답 데이터는 항상 null 체크 필수 - ---- - -### 🚀 빠른 시작 - -#### 방법1로 시작하기 (추천) -1. `ComparisonServiceImpl.java` 열기 -2. 기존 메서드 참고하여 새 메서드 추가 -3. `executeComparison()`에서 호출 -4. 끝! - -#### 방법2로 시작하기 -1. `comparison/rules/` 폴더에 새 규칙 클래스 생성 -2. `@Component` 붙이기 -3. `ComparisonRule` 인터페이스 구현 -4. 끝! - ---- - -### 📞 문의 - -추가 질문이나 제안사항은 개발팀에 문의하세요. - ---- - -### 📖 관련 파일 - -- **방법1 파일**: - - `service/ComparisonService.java` - - `service/impl/ComparisonServiceImpl.java` - -- **방법2 파일**: - - `comparison/` 패키지 전체 - -- **통합 지점**: - - `CarFfnlgTrgtServiceImpl.java` - `executeComparisonLogic()` 메서드 diff --git a/src/main/resources/mybatis/mapper/carInspectionPenalty/VehicleApiHistoryMapper_maria.xml b/src/main/resources/mybatis/mapper/carInspectionPenalty/VehicleApiHistoryMapper_maria.xml index 5b9b374..4648dd5 100644 --- a/src/main/resources/mybatis/mapper/carInspectionPenalty/VehicleApiHistoryMapper_maria.xml +++ b/src/main/resources/mybatis/mapper/carInspectionPenalty/VehicleApiHistoryMapper_maria.xml @@ -184,4 +184,24 @@ WHERE CAR_LEDGER_FRMBK_DTL_ID = #{carLedgerFrmbkDtlId} + + + + + + + + diff --git a/src/main/webapp/WEB-INF/views/carInspectionPenalty/registration/list.jsp b/src/main/webapp/WEB-INF/views/carInspectionPenalty/registration/list.jsp index 9fc3423..1f04b6e 100644 --- a/src/main/webapp/WEB-INF/views/carInspectionPenalty/registration/list.jsp +++ b/src/main/webapp/WEB-INF/views/carInspectionPenalty/registration/list.jsp @@ -58,6 +58,7 @@
  • 과태료 대상 목록
  • + 총 0건