|
|
|
|
@ -233,6 +233,161 @@ public class CarFfnlgTrgtServiceImpl implements CarFfnlgTrgtService {
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 목록을 EUC-KR 텍스트로 생성하여 다운로드용 바이트 배열을 반환
|
|
|
|
|
* 중요: 샘플 파일과 완전히 동일한 고정폭 포맷을 맞추기 위해 각 필드를 바이트 기준으로 패딩/절단 처리함 (한글 2바이트)
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public byte[] generateEucKrDownloadBytes(CarFfnlgTrgtVO vo) {
|
|
|
|
|
try {
|
|
|
|
|
// 인코딩 및 바이트 규칙 설정 (기본 EUC-KR, 한글 2바이트)
|
|
|
|
|
final String encoding = parseConfig.getEncoding() == null || parseConfig.getEncoding().trim().isEmpty()
|
|
|
|
|
? "EUC-KR" : parseConfig.getEncoding().trim();
|
|
|
|
|
|
|
|
|
|
// 1) 데이터 조회 (페이징 비활성화)
|
|
|
|
|
List<CarFfnlgTrgtVO> list = mapper.selectList(vo);
|
|
|
|
|
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
// 2) 헤더 구성 (샘플 텍스트와 동일)
|
|
|
|
|
sb.append(padLeftBytes("유효기간경과 과태료부과대상 리스트", 46, encoding)).append("\r\n");
|
|
|
|
|
sb.append(padLeftBytes("------------------------------------", 48, encoding)).append("\r\n");
|
|
|
|
|
sb.append("\r\n ");
|
|
|
|
|
sb.append("\r\n ");
|
|
|
|
|
sb.append(" * 최종등록일이 검사일자보다 늦는 경우는 소유자 및 사용본거지 주소를 재확인하여 주시기 바랍니다. (재검여부 = *일수)\r\n");
|
|
|
|
|
sb.append(" * 전출차량( *차번호)인 경우 전출 전의 주소입니다. 소유자 및 사용본거지 주소를 재확인하여 주시기 바랍니다.\r\n");
|
|
|
|
|
sb.append("-------------------------------------------------------------------------------------------------------------------------------------------------\r\n");
|
|
|
|
|
sb.append("검사소 검사일자 자동차번호 소유자명 주민등록번호 차 명 차 종 용 도 종료일 일수 과태료\r\n");
|
|
|
|
|
sb.append(" 최종등록일 주 소 유효기간만료일 매매상품용\r\n");
|
|
|
|
|
sb.append("-------------------------------------------------------------------------------------------------------------------------------------------------\r\n");
|
|
|
|
|
|
|
|
|
|
// 3) 데이터 라인 생성 (각 항목 2줄)
|
|
|
|
|
for (CarFfnlgTrgtVO row : list) {
|
|
|
|
|
// 첫째줄: 고정폭 필드들 연결
|
|
|
|
|
String firstLine =
|
|
|
|
|
padRightBytes(nvl(row.getInspstnCd()), parseConfig.getFirstLineLength("inspstn-cd"), encoding) +
|
|
|
|
|
padRightBytes(formatYmd(row.getInspYmd(), true), parseConfig.getFirstLineLength("insp-ymd"), encoding) +
|
|
|
|
|
padRightBytes(nvl(row.getVhclno()), parseConfig.getFirstLineLength("vhclno"), encoding) +
|
|
|
|
|
padRightBytes(nvl(row.getOwnrNm()), parseConfig.getFirstLineLength("ownr-nm"), encoding) +
|
|
|
|
|
padRightBytes(nvl(row.getRrno()), parseConfig.getFirstLineLength("rrno"), encoding) +
|
|
|
|
|
padRightBytes(nvl(row.getCarNm()), parseConfig.getFirstLineLength("car-nm"), encoding) +
|
|
|
|
|
padRightBytes(nvl(row.getCarKnd()), parseConfig.getFirstLineLength("car-knd"), encoding) +
|
|
|
|
|
padRightBytes(nvl(row.getCarUsg()), parseConfig.getFirstLineLength("car-usg"), encoding) +
|
|
|
|
|
padRightBytes(formatYmd(row.getInspEndYmd(), true), parseConfig.getFirstLineLength("insp-end-ymd"), encoding) +
|
|
|
|
|
padLeftBytes(nvl(row.getDaycnt()), parseConfig.getFirstLineLength("daycnt"), encoding) +
|
|
|
|
|
padLeftBytes(formatAmtToManWon(row.getFfnlgAmt()), parseConfig.getFirstLineLength("ffnlg-amt"), encoding);
|
|
|
|
|
|
|
|
|
|
sb.append(firstLine).append("\r\n");
|
|
|
|
|
|
|
|
|
|
// 둘째줄: skip + 나머지 필드
|
|
|
|
|
String secondLine =
|
|
|
|
|
padRightBytes("", parseConfig.getSecondLineLength("skip"), encoding) +
|
|
|
|
|
padRightBytes(formatYmd(row.getLastRegYmd(), true), parseConfig.getSecondLineLength("last-reg-ymd"), encoding) +
|
|
|
|
|
padRightBytes(nvl(row.getAddr()), parseConfig.getSecondLineLength("addr"), encoding) +
|
|
|
|
|
padRightBytes(formatYmd(row.getVldPrdExpryYmd(), true), parseConfig.getSecondLineLength("vld-prd-expry-ymd"), encoding) +
|
|
|
|
|
padRightBytes(nvl(row.getTrdGds()), parseConfig.getSecondLineLength("trd-gds"), encoding);
|
|
|
|
|
|
|
|
|
|
sb.append(secondLine).append("\r\n");
|
|
|
|
|
sb.append("\r\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sb.toString().getBytes(encoding);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new RuntimeException("다운로드 파일 생성 중 오류: " + e.getMessage(), e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================== 내부 유틸 메서드 ==================
|
|
|
|
|
|
|
|
|
|
/** null 안전 치환 */
|
|
|
|
|
private static String nvl(String s) { return s == null ? "" : s; }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 날짜 포맷 변환: YYYYMMDD → YYYY-MM-DD (입력에 '-'가 이미 있으면 그대로 사용)
|
|
|
|
|
* - 샘플 출력과 동일한 포맷을 위함
|
|
|
|
|
*/
|
|
|
|
|
private static String formatYmd(String ymd, boolean withHyphen) {
|
|
|
|
|
if (ymd == null || ymd.trim().isEmpty()) return "";
|
|
|
|
|
String v = ymd.trim();
|
|
|
|
|
if (!withHyphen) return v;
|
|
|
|
|
if (v.contains("-")) return v; // 이미 하이픈 포함
|
|
|
|
|
if (v.length() == 8) {
|
|
|
|
|
return v.substring(0,4) + "-" + v.substring(4,6) + "-" + v.substring(6,8);
|
|
|
|
|
}
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 과태료 금액을 "만원" 단위로 표현
|
|
|
|
|
* - 입력이 숫자형(원)일 경우: 300000 → 30만원
|
|
|
|
|
* - 이미 "만원" 문자열 포함 시 그대로 사용
|
|
|
|
|
*/
|
|
|
|
|
private static String formatAmtToManWon(String amt) {
|
|
|
|
|
String v = nvl(amt).trim();
|
|
|
|
|
if (v.isEmpty()) return "";
|
|
|
|
|
if (v.endsWith("만원")) return v;
|
|
|
|
|
try {
|
|
|
|
|
long won = Long.parseLong(v.replaceAll("[^0-9]", ""));
|
|
|
|
|
long man = Math.round(won / 10000.0);
|
|
|
|
|
return String.valueOf(man) + "만원";
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
return v; // 숫자 변환 실패 시 원문 유지
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 주어진 문자열을 지정 바이트 길이로 오른쪽 공백 패딩 (EUC-KR 기준 바이트) */
|
|
|
|
|
private static String padRightBytes(String s, int byteLen, String encoding) throws Exception {
|
|
|
|
|
if (byteLen <= 0) return nvl(s);
|
|
|
|
|
String v = nvl(s);
|
|
|
|
|
byte[] b = v.getBytes(encoding);
|
|
|
|
|
if (b.length == byteLen) return v;
|
|
|
|
|
if (b.length > byteLen) {
|
|
|
|
|
return truncateToBytes(v, byteLen, encoding);
|
|
|
|
|
}
|
|
|
|
|
// 패딩
|
|
|
|
|
StringBuilder sb = new StringBuilder(v);
|
|
|
|
|
while (sb.toString().getBytes(encoding).length < byteLen) {
|
|
|
|
|
sb.append(' ');
|
|
|
|
|
}
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 주어진 문자열을 지정 바이트 길이로 왼쪽 공백 패딩 (주로 숫자용 정렬) */
|
|
|
|
|
private static String padLeftBytes(String s, int byteLen, String encoding) throws Exception {
|
|
|
|
|
if (byteLen <= 0) return nvl(s);
|
|
|
|
|
String v = nvl(s);
|
|
|
|
|
byte[] b = v.getBytes(encoding);
|
|
|
|
|
if (b.length == byteLen) return v;
|
|
|
|
|
if (b.length > byteLen) {
|
|
|
|
|
return truncateToBytes(v, byteLen, encoding);
|
|
|
|
|
}
|
|
|
|
|
StringBuilder sb = new StringBuilder(v);
|
|
|
|
|
while (sb.toString().getBytes(encoding).length < byteLen) {
|
|
|
|
|
sb.insert(0, ' ');
|
|
|
|
|
}
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 지정 바이트 길이에 맞게 문자열을 잘라냄 (문자 중간 분리 방지) */
|
|
|
|
|
private static String truncateToBytes(String s, int byteLen, String encoding) throws Exception {
|
|
|
|
|
if (s == null) return "";
|
|
|
|
|
byte[] b = s.getBytes(encoding);
|
|
|
|
|
if (b.length <= byteLen) return s;
|
|
|
|
|
// 바이트 배열을 직접 잘라 안전한 문자열 생성
|
|
|
|
|
byte[] cut = new byte[byteLen];
|
|
|
|
|
System.arraycopy(b, 0, cut, 0, byteLen);
|
|
|
|
|
// 잘린 바이트가 멀티바이트 문자를 깨뜨렸을 수 있으므로
|
|
|
|
|
// 디코딩 실패 시 한 바이트씩 줄이며 복구
|
|
|
|
|
for (int len = byteLen; len > 0; len--) {
|
|
|
|
|
try {
|
|
|
|
|
return new String(cut, 0, len, encoding);
|
|
|
|
|
} catch (Exception ignore) {
|
|
|
|
|
// len 감소
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 오류 메시지 목록을 하나의 문자열로 조합
|
|
|
|
|
|