feat: 상품용 및 명의이전 로직 보완

- **상품용(ProductUseOmChecker)**
  - 부과일자 소유자가 상품용인 경우의 검증 로직 보완
  - 명의이전(11) 레코드 조건에 `CHG_YMD <= 검사유효기간 종료일 + 31일` 추가

- **명의이전(OwnerTransferOmChecker)**
  - 명의이전 검증 시 `CHG_YMD > 검사유효기간 종료일 + 31일` 로 변경

- **문서 업데이트**
  - 비교로직 문서 (`자동차과태료_비교로직_정리-[미필].md`) 수정
  - 주요 변경사항 및 로직 설명 수정

- **기타**
  - 불필요 파일(CarFfnlgTrgtIncmpController.java.bak) 삭제
main
박성영 6 days ago
parent 6b70444b07
commit d5286a509e

@ -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일 로직 적용

@ -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<CarFfnlgTrgtIncmpVO> 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<String, Object> result = service.uploadAndParsePrnFile(convertedFile, rgtr);
int successCount = (int) result.get("successCount");
int failCount = (int) result.get("failCount");
@SuppressWarnings("unchecked")
List<String> errorMessages = (List<String>) 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<Map<String, String>> targetList) {
log.info("API 호출 및 비교 요청 (미필) - 선택된 데이터 건수: {}", targetList != null ? targetList.size() : 0);
try {
Map<String, Object> 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<CarFfnlgTrgtIncmpVO> allData = service.selectList(searchParams);
// 목록을 Map 형태로 변환
List<Map<String, String>> targetList = allData.stream()
.map(vo -> {
Map<String, String> 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<String, Object> 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<String> 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<String, Object> 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<CarFfnlgTrgtIncmpExcelVO> 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);
}
}
}

@ -33,7 +33,7 @@ import java.util.List;
* :
* - 1: "상품용"
* - 2: ( 11)
* CHG_YMD >
* CHG_YMD > + 31
*
* CHG_TASK_SE_CD == "11" ()
* - 3: 4 API

@ -20,10 +20,26 @@ import java.time.LocalDate;
import java.util.List;
/**
* 1. ()
* -
*
* <p>api-1..contains("상품용")</p>
* <p> = + 146</p>
* <p> , </p>
*
* 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

Loading…
Cancel
Save