diff --git a/docs/자동차과태료_비교로직_정리-[미필].md b/docs/자동차과태료_비교로직_정리-[미필].md index 57df33f..efa94fb 100644 --- a/docs/자동차과태료_비교로직_정리-[미필].md +++ b/docs/자동차과태료_비교로직_정리-[미필].md @@ -20,6 +20,7 @@ | 일자 | 변경 내용 | 비고 | |------|----------|------| | 2025-12-08 | om_checker 소스 기준 전면 작성 | 실제 코드와 일치하도록 정리 | +| 2025-12-09 | 검사유효기간 종료일 + 31일 로직 반영 | ProductUseOmChecker, OwnerTransferOmChecker | ### 처리 규칙 @@ -56,7 +57,7 @@ 3. 갑부에서 명의이전(11) 레코드 찾기 - CHG_TASK_SE_CD == "11" - - CHG_YMD <= 검사유효기간 종료일 + - CHG_YMD <= 검사유효기간 종료일 + 31일 - 가장 마지막 일자 선택 ← 없으면 return null @@ -99,7 +100,7 @@ existingData.setCarRegFrmbkChgYmd(targetRecord.getChgYmd()); 2. 갑부에서 명의이전(11) 레코드 찾기 - CHG_TASK_SE_CD == "11" - - CHG_YMD > 검사유효기간 종료일 + - CHG_YMD > 검사유효기간 종료일 + 31일 - 가장 마지막 일자 선택 ← 없으면 return null @@ -189,6 +190,7 @@ existingData.setCarBscMttrInqSggNm(sggNm); │ 1. 상품용 (ProductUseOmChecker) │ │ 조건: 부과일자 소유자명.contains("상품용") │ │ + 명의이전(11) 레코드 존재 │ +│ + CHG_YMD <= 검사유효기간종료일+31일 │ │ + 소유자회원번호 일치 │ │ → 조건 충족: 상품용(02) │ └──────────────────────────────────────────────┘ @@ -197,7 +199,8 @@ existingData.setCarBscMttrInqSggNm(sggNm); ┌──────────────────────────────────────────────┐ │ 2. 명의이전 소유자 확인 (OwnerTransferOmChecker)│ │ 조건: 부과일자 소유자명에 상품용 미포함 │ -│ + 검사유효기간종료일 이후 명의이전 존재│ +│ + CHG_YMD > 검사유효기간종료일+31일 │ +│ + 명의이전(11) 레코드 존재 │ │ → 조건 충족: 접수(01) │ └──────────────────────────────────────────────┘ │ (조건 미충족) @@ -223,8 +226,8 @@ existingData.setCarBscMttrInqSggNm(sggNm); | 코드 | 상태명 | 처리 로직 | 클래스 | |------|--------|--------------------------|--------| -| 02 | 상품용 | 부과일자 소유자가 상품용 + 명의이전(11) | `ProductUseOmChecker` | -| 02 | 상품용 | 검사유효기간종료일 이후 명의이전 존재 | `OwnerTransferOmChecker` | +| 02 | 상품용 | 부과일자 소유자가 상품용 + 명의이전(11) CHG_YMD <= 종료일+31일 | `ProductUseOmChecker` | +| 01 | 접수 | 검사유효기간종료일+31일 이후 명의이전 존재 | `OwnerTransferOmChecker` | | 03 | 이첩 | 소유자 일치 + 법정동코드 불일치 | `TransferOmChecker` | ### 미필 vs 지연 비교 @@ -255,6 +258,7 @@ TaskPrcsSttsConstants.TASK_PRCS_STTS_CD_03_TRANSFER = "03" --- -**문서 작성 완료일**: 2025-12-08 +**문서 최종 수정일**: 2025-12-09 **실제 소스 코드 기준**: om_checker 폴더 내 Checker 클래스들 **분석 대상 클래스**: 3개 (ProductUseOmChecker, OwnerTransferOmChecker, TransferOmChecker) +**주요 변경사항**: 검사유효기간 종료일 + 31일 로직 적용 diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/controller/CarFfnlgTrgtIncmpController.java.bak b/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/controller/CarFfnlgTrgtIncmpController.java.bak deleted file mode 100644 index 76b8439..0000000 --- a/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/controller/CarFfnlgTrgtIncmpController.java.bak +++ /dev/null @@ -1,572 +0,0 @@ -package go.kr.project.carInspectionPenalty.registrationOm.controller; - -import egovframework.constant.MessageConstants; -import egovframework.constant.TilesConstants; -import egovframework.util.ApiResponseUtil; -import egovframework.util.SessionUtil; -import egovframework.util.excel.ExcelSheetData; -import egovframework.util.excel.SxssfExcelFile; -import go.kr.project.carInspectionPenalty.registrationOm.model.CarFfnlgTrgtIncmpExcelVO; -import go.kr.project.carInspectionPenalty.registrationOm.model.CarFfnlgTrgtIncmpModifiedDataVO; -import go.kr.project.carInspectionPenalty.registrationOm.model.CarFfnlgTrgtIncmpVO; -import go.kr.project.carInspectionPenalty.registrationOm.service.CarFfnlgTrgtIncmpService; -import go.kr.project.common.model.CmmnCodeSearchVO; -import go.kr.project.common.service.CommonCodeService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 자동차 미필 과태료 대상 등록 Controller - * 미필 과태료 대상 목록 조회, PRN 파일 업로드 기능 제공 - * 미필의 경우 부과일자 = 검사유효기간 종료일 + 145일 - */ -@Controller -@RequestMapping("/carInspectionPenalty/registration-om") -@RequiredArgsConstructor -@Slf4j -@Tag(name = "자동차 미필 과태료 대상 등록", description = "자동차 미필 과태료 대상 등록 및 목록 조회 API") -public class CarFfnlgTrgtIncmpController { - - private final CarFfnlgTrgtIncmpService service; - private final CommonCodeService commonCodeService; - - /** - * 미필 과태료 대상 목록 화면 - * @param model 모델 - * @return 목록 화면 경로 - */ - @GetMapping("/list.do") - @Operation(summary = "미필 과태료 대상 목록 화면", description = "미필 과태료 대상 목록 조회 화면을 제공합니다.") - public String list(Model model) { - log.debug("미필 과태료 대상 목록 화면 요청"); - - // 업무 처리 상태 코드 조회 (공통코드) - CmmnCodeSearchVO taskPrcsSttsCdSearchVO = CmmnCodeSearchVO.builder() - .searchCdGroupId("TASK_PRCS_STTS_CD") - .searchUseYn("Y") - .sortColumn("SORT_ORDR") - .sortAscending(true) - .build(); - model.addAttribute("taskPrcsSttsCdList", commonCodeService.selectCodeDetailList(taskPrcsSttsCdSearchVO)); - - // 미필 과태료 대상 구분 코드 조회 (공통코드) - CmmnCodeSearchVO ffnlgTrgtSeCdSearchVO = CmmnCodeSearchVO.builder() - .searchCdGroupId("FFNLG_TRGT_SE_CD") - .searchUseYn("Y") - .sortColumn("SORT_ORDR") - .sortAscending(true) - .build(); - model.addAttribute("ffnlgTrgtSeCdList", commonCodeService.selectCodeDetailList(ffnlgTrgtSeCdSearchVO)); - - return "carInspectionPenalty/registrationOm/list" + TilesConstants.BASE; - } - - /** - * 미필 과태료 대상 목록 조회 AJAX - * @param paramVO 검색 조건 - * @return 목록 데이터 - */ - @PostMapping("/list.ajax") - @Operation(summary = "미필 과태료 대상 목록 조회", description = "미필 과태료 대상 목록을 조회하고 JSON 형식으로 반환합니다.") - public ResponseEntity listAjax(@ModelAttribute CarFfnlgTrgtIncmpVO paramVO) { - log.debug("미필 과태료 대상 목록 조회 AJAX - 검색조건: {}", paramVO); - - // 1. 총 개수 조회 - int totalCount = service.selectListTotalCount(paramVO); - - // 2. totalCount 설정 - paramVO.setTotalCount(totalCount); - - // 3. 페이징 활성화 - paramVO.setPagingYn("Y"); - - // 목록 조회 - List list = service.selectList(paramVO); - - return ApiResponseUtil.successWithGrid(list, paramVO); - } - - /** - * 미필 과태료 대상 목록 다운로드 (EUC-KR 텍스트) - * 샘플 파일과 동일한 고정폭 포맷으로 생성하여 다운로드 제공합니다. - * - 인코딩: EUC-KR (한글 2바이트) - */ - @GetMapping("/download.do") - @Operation(summary = "미필 과태료 대상 목록 다운로드", description = "EUC-KR 인코딩의 고정폭 텍스트로 목록을 샘플과 동일한 포맷으로 다운로드합니다.") - public void download( - @ModelAttribute CarFfnlgTrgtIncmpVO paramVO, - HttpServletResponse response - ) { - try { - - // 페이징 없이 전체 조회를 위해 페이징 비활성화 - paramVO.setPagingYn("N"); - - // 서비스에서 EUC-KR 텍스트 콘텐츠 생성 - byte[] fileBytes = service.generateEucKrDownloadBytes(paramVO); - - // EUC-KR 바이트를 UTF-8 바이트로 변환 (다운로드 시에만) - String content = new String(fileBytes, "EUC-KR"); - byte[] utfFileBytes = content.getBytes(StandardCharsets.UTF_8); - - String fileName = URLEncoder.encode("미필_유효기간경과_과태료부과대상_리스트.prn", "UTF-8"); - - // 응답 헤더 설정 (텍스트 파일, UTF-8 인코딩) - response.setContentType("text/plain; charset=UTF-8"); - response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); - response.setContentLength(utfFileBytes.length); - - // UTF-8 바이트 스트림으로 전송 - response.getOutputStream().write(utfFileBytes); - response.getOutputStream().flush(); - } catch (Exception e) { - log.error("목록 다운로드 중 오류", e); - try { - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - response.getWriter().write("다운로드 처리 중 오류가 발생했습니다: " + e.getMessage()); - } catch (Exception ignored) { - - } - } - } - - /** - * 파일 업로드 팝업 화면 - * @return 팝업 화면 - */ - @GetMapping("/uploadPopup.do") - @Operation(summary = "파일 업로드 팝업", description = "PRN 파일 업로드 팝업 화면을 제공합니다.") - public ModelAndView uploadPopup() { - log.debug("파일 업로드 팝업 화면 요청"); - - ModelAndView mav = new ModelAndView("carInspectionPenalty/registrationOm/uploadPopup" + TilesConstants.POPUP); - - return mav; - } - - /** - * PRN 파일 업로드 및 처리 - * - * 주의: 파일 단위로 업로드하므로 한 건이라도 실패하면 전체 롤백 처리됨 - * - * @param file 업로드된 PRN 파일 - * @return 처리 결과 - */ - @PostMapping("/upload.ajax") - @Operation(summary = "PRN 파일 업로드", description = "PRN 파일을 업로드하고 파싱하여 DB에 저장합니다. 한 건이라도 실패 시 전체 롤백됩니다.") - public ResponseEntity upload( - @Parameter(description = "PRN 파일") @RequestParam("file") MultipartFile file) { - - log.info("PRN 파일 업로드 요청 - 파일명: {}", file != null ? file.getOriginalFilename() : "null"); - - try { - // 세션에서 사용자 ID 가져오기 - String rgtr = SessionUtil.getUserId(); - if (rgtr == null || rgtr.isEmpty()) { - return ApiResponseUtil.error("로그인 정보가 없습니다."); - } - - // UTF-8 파일을 EUC-KR로 변환 (시스템은 EUC-KR 기준으로 처리) - MultipartFile convertedFile = convertUtf8ToEucKr(file); - - // 파일 업로드 및 파싱 (한 건이라도 실패 시 전체 롤백) - Map result = service.uploadAndParsePrnFile(convertedFile, rgtr); - - int successCount = (int) result.get("successCount"); - int failCount = (int) result.get("failCount"); - @SuppressWarnings("unchecked") - List errorMessages = (List) result.get("errorMessages"); - - if (failCount == 0 && successCount > 0) { - // 모든 데이터가 성공적으로 저장됨 - String message = String.format("파일 업로드가 완료되었습니다.\n\n성공: %d건", successCount); - return ApiResponseUtil.success(result, message); - } else if (successCount > 0) { - // 일부 성공, 일부 실패 - String message = String.format("파일 업로드가 완료되었습니다.\n\n성공: %d건, 실패: %d건", successCount, failCount); - return ApiResponseUtil.success(result, message); - } else { - // 모두 실패 - StringBuilder message = new StringBuilder(); - if (!errorMessages.isEmpty()) { - for (String errorMsg : errorMessages) { - message.append(errorMsg).append("\n"); - } - } else { - message.append("파일 업로드 중 오류가 발생했습니다."); - } - return ApiResponseUtil.error(message.toString()); - } - - } catch (RuntimeException e) { - // 데이터 처리 중 오류 발생 - 전체 롤백됨 - log.error("PRN 파일 업로드 중 오류 발생 - 전체 롤백", e); - return ApiResponseUtil.error(e.getMessage()); - } catch (Exception e) { - // 예상치 못한 오류 - log.error("PRN 파일 업로드 중 예상치 못한 오류 발생", e); - return ApiResponseUtil.error("파일 업로드 중 오류가 발생했습니다: " + e.getMessage()); - } - } - - /** - * 미필 과태료 대상 상세 조회 - * @param carFfnlgTrgtIncmpId 미필 과태료 대상 ID - * @return 상세 정보 - */ - @GetMapping("/selectOne.ajax") - @Operation(summary = "미필 과태료 대상 상세 조회", description = "미필 과태료 대상 상세 정보를 조회합니다.") - public ResponseEntity selectOne( - @Parameter(description = "미필 과태료 대상 ID") @RequestParam String carFfnlgTrgtIncmpId) { - - log.debug("미필 과태료 대상 상세 조회 - ID: {}", carFfnlgTrgtIncmpId); - - try { - CarFfnlgTrgtIncmpVO vo = new CarFfnlgTrgtIncmpVO(); - vo.setCarFfnlgTrgtIncmpId(carFfnlgTrgtIncmpId); - - CarFfnlgTrgtIncmpVO result = service.selectOne(vo); - - if (result != null) { - return ApiResponseUtil.success(result, "조회 성공"); - } else { - return ApiResponseUtil.error("조회된 데이터가 없습니다."); - } - - } catch (Exception e) { - log.error("미필 과태료 대상 상세 조회 중 오류 발생", e); - return ApiResponseUtil.error("조회 중 오류가 발생했습니다: " + e.getMessage()); - } - } - - /** - * 미필 과태료 대상 삭제 (논리삭제) - * @param carFfnlgTrgtIncmpId 미필 과태료 대상 ID - * @return 삭제 결과 - */ - @PostMapping("/delete.ajax") - @Operation(summary = "미필 과태료 대상 삭제", description = "미필 과태료 대상을 삭제(논리삭제)합니다.") - public ResponseEntity delete( - @Parameter(description = "미필 과태료 대상 ID") @RequestParam String carFfnlgTrgtIncmpId) { - - log.info("미필 과태료 대상 삭제 요청 - ID: {}", carFfnlgTrgtIncmpId); - - try { - String dltr = SessionUtil.getUserId(); - if (dltr == null || dltr.isEmpty()) { - return ApiResponseUtil.error("로그인 정보가 없습니다."); - } - - CarFfnlgTrgtIncmpVO vo = new CarFfnlgTrgtIncmpVO(); - vo.setCarFfnlgTrgtIncmpId(carFfnlgTrgtIncmpId); - vo.setDltr(dltr); - - int result = service.delete(vo); - - if (result > 0) { - return ApiResponseUtil.success(MessageConstants.Common.DELETE_SUCCESS); - } else { - return ApiResponseUtil.error(MessageConstants.Common.DELETE_ERROR); - } - - } catch (Exception e) { - log.error("미필 과태료 대상 삭제 중 오류 발생", e); - return ApiResponseUtil.error("삭제 중 오류가 발생했습니다: " + e.getMessage()); - } - } - - /** - * 선택된 목록에 대해 API 호출 및 기본정보/등록원부 비교 - * 미필의 경우 부과일자 = 검사유효기간 종료일 + 145일 - * @param targetList 선택된 미필 과태료 대상 목록 - * @return 비교 결과 - */ - @PostMapping("/compareWithApi.ajax") - @ResponseBody - @Operation(summary = "API 호출 및 데이터 비교", description = "선택된 목록에 대해 차량 API를 호출하고 기본정보 및 등록원부와 비교합니다. 미필의 경우 부과일자 = 검사유효기간 종료일 + 145일") - public ResponseEntity compareWithApi(@RequestBody List> targetList) { - log.info("API 호출 및 비교 요청 (미필) - 선택된 데이터 건수: {}", targetList != null ? targetList.size() : 0); - - try { - 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건", successCount, failCount); - - return ApiResponseUtil.success(resultData, message); - - } catch (IllegalArgumentException e) { - log.error("파라미터 검증 오류", e); - return ApiResponseUtil.error(e.getMessage()); - } catch (Exception e) { - log.error("API 호출 및 비교 중 오류 발생", e); - return ApiResponseUtil.error("처리 중 오류가 발생했습니다: " + e.getMessage()); - } - } - - /** - * 검색조건 전체 목록에 대해 API 호출 및 기본정보/등록원부 비교 (페이징 없이) - * 미필의 경우 부과일자 = 검사유효기간 종료일 + 145일 - * @param searchParams 검색 조건 파라미터 - * @return 비교 결과 - */ - @PostMapping("/compareWithApiAll.ajax") - @ResponseBody - @Operation(summary = "검색조건 전체 API 호출 및 데이터 비교", description = "검색조건에 해당하는 전체 목록에 대해 차량 API를 호출하고 비교합니다. 미필의 경우 부과일자 = 검사유효기간 종료일 + 145일") - public ResponseEntity compareWithApiAll(@RequestBody CarFfnlgTrgtIncmpVO 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("carFfnlgTrgtIncmpId", vo.getCarFfnlgTrgtIncmpId()); - map.put("vhclno", vo.getVhclno()); - map.put("inspVldPrd", vo.getInspVldPrd()); - 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 { - CarFfnlgTrgtIncmpVO vo = new CarFfnlgTrgtIncmpVO(); - vo.setCarFfnlgTrgtIncmpId(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()); - } - } - - /** - * 미필 과태료 대상 정보를 일괄 저장하는 AJAX 메소드 - * 생성, 수정, 삭제된 데이터를 처리합니다. - * - * @param modifyData 생성/수정/삭제할 데이터를 담은 VO 객체 - * @return 저장 결과 메시지와 성공/실패 상태를 담은 ResponseEntity 객체 - */ - @PostMapping("/saveAll.ajax") - @ResponseBody - @Operation(summary = "미필 과태료 대상 정보 일괄 저장", description = "생성, 수정, 삭제된 미필 과태료 대상 데이터를 일괄 처리합니다.") - public ResponseEntity saveAllAjax(@RequestBody CarFfnlgTrgtIncmpModifiedDataVO modifyData) { - log.info("미필 과태료 대상 일괄 저장 요청 - 수정: {}건, 생성: {}건, 삭제: {}건", - modifyData.getUpdatedRows() != null ? modifyData.getUpdatedRows().size() : 0, - modifyData.getCreatedRows() != null ? modifyData.getCreatedRows().size() : 0, - modifyData.getDeletedRows() != null ? modifyData.getDeletedRows().size() : 0); - - try { - int result = service.saveCarFfnlgTrgtIncmps(modifyData); - if (result > 0) { - return ApiResponseUtil.success("미필 과태료 대상 정보가 저장되었습니다."); - } else { - return ApiResponseUtil.error("저장할 데이터가 없습니다."); - } - } catch (Exception e) { - log.error("미필 과태료 대상 일괄 저장 중 오류 발생", e); - return ApiResponseUtil.error("저장 중 오류가 발생했습니다: " + e.getMessage()); - } - } - - /** - * UTF-8 인코딩된 MultipartFile을 EUC-KR 인코딩으로 변환 - * - * @param utf8File UTF-8 인코딩된 원본 파일 - * @return EUC-KR 인코딩으로 변환된 파일 - * @throws IOException 파일 읽기/변환 중 오류 발생 시 - */ - private MultipartFile convertUtf8ToEucKr(MultipartFile utf8File) throws IOException { - // UTF-8로 파일 내용 읽기 - String content = new String(utf8File.getBytes(), StandardCharsets.UTF_8); - - // EUC-KR 바이트로 변환 - byte[] eucKrBytes = content.getBytes("EUC-KR"); - String eucKrContent = new String(eucKrBytes, "EUC-KR"); - - log.info("파일 인코딩 변환 - UTF-8({} bytes) → EUC-KR({} bytes)", - utf8File.getSize(), eucKrBytes.length); - - // EUC-KR 바이트로 변환된 새로운 MultipartFile 반환 - return new EucKrMultipartFile( - utf8File.getName(), - utf8File.getOriginalFilename(), - utf8File.getContentType(), - eucKrBytes - ); - } - - /** - * EUC-KR 인코딩 변환을 위한 커스텀 MultipartFile 구현체 - */ - private static class EucKrMultipartFile implements MultipartFile { - private final String name; - private final String originalFilename; - private final String contentType; - private final byte[] bytes; - - public EucKrMultipartFile(String name, String originalFilename, String contentType, byte[] bytes) { - this.name = name; - this.originalFilename = originalFilename; - this.contentType = contentType; - this.bytes = bytes; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getOriginalFilename() { - return originalFilename; - } - - @Override - public String getContentType() { - return contentType; - } - - @Override - public boolean isEmpty() { - return bytes == null || bytes.length == 0; - } - - @Override - public long getSize() { - return bytes.length; - } - - @Override - public byte[] getBytes() { - return bytes; - } - - @Override - public InputStream getInputStream() { - return new ByteArrayInputStream(bytes); - } - - @Override - public void transferTo(java.io.File dest) throws IOException, IllegalStateException { - try (java.io.FileOutputStream fos = new java.io.FileOutputStream(dest)) { - fos.write(bytes); - } - } - } - - /** - * 미필 과태료 대상 목록 엑셀 다운로드 - * - * @param paramVO 검색 조건을 담은 VO 객체 - * @param request HTTP 요청 객체 - * @param response HTTP 응답 객체 - */ - @PostMapping("/excel.do") - @Operation(summary = "미필 과태료 대상 목록 엑셀 다운로드", description = "미필 과태료 대상 목록을 엑셀 파일로 다운로드합니다.") - public void downloadExcel( - @ModelAttribute CarFfnlgTrgtIncmpVO paramVO, - HttpServletRequest request, - HttpServletResponse response) { - try { - log.debug("미필 과태료 대상 목록 엑셀 다운로드 요청"); - - // 페이징 처리 없이 전체 데이터 조회 - paramVO.setPagingYn("N"); - - // 미필 과태료 대상 목록 조회 - List excelList = service.selectListForExcel(paramVO); - - // 엑셀 파일 생성 및 다운로드 - String filename = "미필_과태료대상목록_" + java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx"; - new SxssfExcelFile(ExcelSheetData.of(excelList, CarFfnlgTrgtIncmpExcelVO.class, "미필 과태료 대상 목록 " + excelList.size() + "건"), request, response, filename); - - log.debug("미필 과태료 대상 목록 엑셀 다운로드 완료 - 파일명: {}, 건수: {}", filename, excelList.size()); - } catch (Exception e) { - log.error("엑셀 다운로드 중 오류 발생", e); - } - } -} diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/service/impl/om_checker/OwnerTransferOmChecker.java b/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/service/impl/om_checker/OwnerTransferOmChecker.java index 0e8b5f1..e9a162a 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/service/impl/om_checker/OwnerTransferOmChecker.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/service/impl/om_checker/OwnerTransferOmChecker.java @@ -33,7 +33,7 @@ import java.util.List; * 비교조건: * - 1단계: 소유자명에 "상품용" 미포함 * - 2단계: 갑부 상세에서 다음 조건을 만족하는 명의이전(코드 11) 레코드 찾기 - * • CHG_YMD > 검사유효기간 종료일 + * • CHG_YMD > 검사유효기간 종료일 + 31일 * • 명의이전 중 마지막 일자 * • CHG_TASK_SE_CD == "11" (명의이전) * - 3단계: 4번 API 소유자명 확인 diff --git a/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/service/impl/om_checker/ProductUseOmChecker.java b/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/service/impl/om_checker/ProductUseOmChecker.java index d6523dd..0c23d9a 100644 --- a/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/service/impl/om_checker/ProductUseOmChecker.java +++ b/src/main/java/go/kr/project/carInspectionPenalty/registrationOm/service/impl/om_checker/ProductUseOmChecker.java @@ -20,10 +20,26 @@ import java.time.LocalDate; import java.util.List; /** - * 1. 상품용 체크 (미필) + * 미필 - 상품용 검증 * - *

api-1번호출.소유자명.contains("상품용")

- *

미필의 경우 부과일자 = 검사유효기간 종료일 + 146일

+ *

부과일자 소유자가 상품용인 경우, 명의이전 이력을 확인하여 상품용 처리

+ * + * API 호출 순서: + * 1) 자동차기본정보(차량번호, 부과일자=검사유효기간 종료일+146일) → 차대번호, 소유자명, 대표소유자회원번호 + * 2) 자동차기본정보(차대번호, 부과일자=오늘) → 차량번호, 성명, 주민번호, 법정동코드 + * 3) 자동차등록원부(갑)(차량번호, 성명, 주민번호, 법정동코드) → 갑부 상세 List + * 4) 자동차기본정보(차대번호, 부과일자=CHG_YMD) → 소유자명, 대표소유자회원번호 + * + * 비교조건: + * - 1단계: 소유자명에 "상품용" 포함 + * - 2단계: 2단계 대표소유자회원번호 == 1단계 대표소유자회원번호 + * - 3단계: 갑부 상세에서 다음 조건을 만족하는 명의이전(코드 11) 레코드 찾기 + * • CHG_YMD <= 검사유효기간 종료일 + 31일 + * • 명의이전 중 마지막 일자 + * • CHG_TASK_SE_CD == "11" (명의이전) + * - 4단계: 4단계 대표소유자회원번호 == 1단계 대표소유자회원번호 + * + * 결과: 모든 조건 충족 시 상품용(02) 처리 */ @Slf4j @Component