parent
46b5c2df25
commit
8778338eef
@ -0,0 +1,6 @@
|
||||
CREATE SEQUENCE seq_zip_file_detail_log_id
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
MINVALUE 1
|
||||
MAXVALUE 99999999
|
||||
CYCLE;
|
||||
@ -0,0 +1,6 @@
|
||||
CREATE SEQUENCE seq_zip_file_process_log_id
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
MINVALUE 1
|
||||
MAXVALUE 99999999
|
||||
CYCLE;
|
||||
@ -0,0 +1,33 @@
|
||||
create table tb_zip_file_detail_log
|
||||
(
|
||||
detail_log_id varchar(12) not null comment '상세로그ID'
|
||||
primary key,
|
||||
log_id varchar(12) not null comment '로그ID',
|
||||
file_name varchar(255) not null comment '파일명',
|
||||
file_path varchar(500) not null comment '파일경로',
|
||||
file_size bigint default 0 null comment '파일크기(bytes)',
|
||||
file_type varchar(50) null comment '파일타입',
|
||||
is_image char(1) default 'N' null comment '이미지여부(Y/N)',
|
||||
is_corrupted char(1) default 'N' null comment '손상여부(Y/N)',
|
||||
process_status varchar(20) default 'SUCCESS' null comment '처리상태(SUCCESS/ERROR)',
|
||||
error_message text null comment '에러메시지',
|
||||
process_time datetime default current_timestamp() null comment '처리시간',
|
||||
reg_date datetime default current_timestamp() null comment '등록일시',
|
||||
reg_user_id varchar(20) default 'BATCH_SYSTEM' null comment '등록자ID',
|
||||
constraint fk_zip_detail_log_id
|
||||
foreign key (log_id) references tb_zip_file_process_log (log_id)
|
||||
on delete cascade
|
||||
)
|
||||
comment 'ZIP파일처리상세로그' collate = utf8mb4_unicode_ci;
|
||||
|
||||
create index idx_log_id
|
||||
on tb_zip_file_detail_log (log_id);
|
||||
|
||||
create index idx_file_name
|
||||
on tb_zip_file_detail_log (file_name);
|
||||
|
||||
create index idx_process_status
|
||||
on tb_zip_file_detail_log (process_status);
|
||||
|
||||
create index idx_is_image
|
||||
on tb_zip_file_detail_log (is_image);
|
||||
@ -0,0 +1,33 @@
|
||||
create table tb_zip_file_process_log
|
||||
(
|
||||
log_id varchar(12) not null comment '로그ID'
|
||||
primary key,
|
||||
zip_file_name varchar(255) not null comment 'ZIP파일명',
|
||||
zip_file_path varchar(500) not null comment 'ZIP파일경로',
|
||||
extract_path varchar(500) null comment '압축해제경로',
|
||||
archive_path varchar(500) null comment '원본보관경로',
|
||||
total_files int default 0 null comment '총파일수',
|
||||
success_files int default 0 null comment '성공파일수',
|
||||
error_files int default 0 null comment '실패파일수',
|
||||
image_files int default 0 null comment '이미지파일수',
|
||||
non_image_files int default 0 null comment '비이미지파일수',
|
||||
corrupted_files int default 0 null comment '손상파일수',
|
||||
process_status varchar(20) default 'PROCESSING' null comment '처리상태(PROCESSING/SUCCESS/ERROR)',
|
||||
error_message text null comment '에러메시지',
|
||||
start_time datetime default current_timestamp() null comment '시작시간',
|
||||
end_time datetime null comment '종료시간',
|
||||
reg_date datetime default current_timestamp() null comment '등록일시',
|
||||
reg_user_id varchar(20) default 'BATCH_SYSTEM' null comment '등록자ID',
|
||||
mod_date datetime default current_timestamp() null on update current_timestamp() comment '수정일시',
|
||||
mod_user_id varchar(20) default 'BATCH_SYSTEM' null comment '수정자ID'
|
||||
)
|
||||
comment 'ZIP파일처리로그' collate = utf8mb4_unicode_ci;
|
||||
|
||||
create index idx_zip_file_name
|
||||
on tb_zip_file_process_log (zip_file_name);
|
||||
|
||||
create index idx_process_status
|
||||
on tb_zip_file_process_log (process_status);
|
||||
|
||||
create index idx_start_time
|
||||
on tb_zip_file_process_log (start_time);
|
||||
@ -0,0 +1,135 @@
|
||||
# ZIP 파일 처리 배치 작업 구현
|
||||
|
||||
## 개요
|
||||
특정 디렉토리에 *.zip 파일이 들어오면 압축을 풀어서 특정 디렉토리로 이동시키고, 원본 *.zip 파일은 원본 디렉토리로 이동시키는 배치 작업을 구현했습니다. 이미지 파일 검증 기능과 정상/실패에 대한 로그를 테이블에 정확히 남기는 기능이 포함되어 있습니다.
|
||||
|
||||
## 구현된 기능
|
||||
|
||||
### 1. 데이터베이스 테이블
|
||||
- **tb_zip_file_process_log**: ZIP 파일 처리 메인 로그 테이블
|
||||
- **tb_zip_file_detail_log**: ZIP 파일 내 개별 파일 처리 상세 로그 테이블
|
||||
- **seq_zip_file_process_log_id**: 메인 로그 ID 시퀀스
|
||||
- **seq_zip_file_detail_log_id**: 상세 로그 ID 시퀀스
|
||||
|
||||
### 2. 주요 클래스
|
||||
|
||||
#### 모델 클래스
|
||||
- `ZipFileProcessLogVO`: ZIP 파일 처리 로그 VO
|
||||
- `ZipFileDetailLogVO`: ZIP 파일 처리 상세 로그 VO
|
||||
|
||||
#### 매퍼 클래스
|
||||
- `ZipFileProcessLogMapper`: ZIP 파일 처리 로그 매퍼
|
||||
- `ZipFileDetailLogMapper`: ZIP 파일 처리 상세 로그 매퍼
|
||||
|
||||
#### 서비스 클래스
|
||||
- `ZipFileProcessLogService`: ZIP 파일 처리 로그 서비스
|
||||
- `ZipFileDetailLogService`: ZIP 파일 처리 상세 로그 서비스
|
||||
|
||||
#### 유틸리티 클래스
|
||||
- `ImageValidationUtil`: 이미지 파일 검증 유틸리티
|
||||
|
||||
#### 배치 작업 클래스
|
||||
- `ZipFileProcessBatchJob`: ZIP 파일 처리 배치 작업 메인 클래스
|
||||
|
||||
## 주요 기능
|
||||
|
||||
### 1. ZIP 파일 처리 흐름
|
||||
1. 소스 디렉토리에서 *.zip 파일 검색
|
||||
2. ZIP 파일 압축 해제
|
||||
3. 개별 파일 처리 및 검증
|
||||
4. 이미지 파일 손상 여부 확인
|
||||
5. 처리 결과 로그 저장
|
||||
6. ZIP 파일을 아카이브 디렉토리로 이동
|
||||
|
||||
### 2. 이미지 파일 검증
|
||||
- 확장자 기반 이미지 파일 판별
|
||||
- MIME 타입 기반 이미지 파일 검증
|
||||
- ImageIO를 이용한 이미지 손상 여부 확인
|
||||
- 지원 이미지 형식: jpg, jpeg, png, gif, bmp, tiff, tif, webp
|
||||
|
||||
### 3. 로그 기능
|
||||
- ZIP 파일별 처리 로그 (처리 상태, 파일 수, 성공/실패 통계)
|
||||
- 개별 파일별 상세 로그 (파일 정보, 이미지 여부, 손상 여부)
|
||||
- 배치 작업 실행 로그 (BatchJobLogUtil 활용)
|
||||
|
||||
## 설정
|
||||
|
||||
### application.yml 설정
|
||||
```yaml
|
||||
batch:
|
||||
zip:
|
||||
source:
|
||||
directory: ${user.home}/batch/zip/source # ZIP 파일 소스 디렉토리
|
||||
extract:
|
||||
directory: ${user.home}/batch/zip/extract # ZIP 파일 압축 해제 디렉토리
|
||||
archive:
|
||||
directory: ${user.home}/batch/zip/archive # ZIP 파일 아카이브 디렉토리
|
||||
```
|
||||
|
||||
## 디렉토리 구조
|
||||
```
|
||||
${user.home}/batch/zip/
|
||||
├── source/ # ZIP 파일이 들어오는 디렉토리
|
||||
├── extract/ # ZIP 파일 압축 해제 디렉토리
|
||||
└── archive/ # 처리 완료된 ZIP 파일 보관 디렉토리
|
||||
```
|
||||
|
||||
## 사용 방법
|
||||
|
||||
### 1. 데이터베이스 테이블 생성
|
||||
```sql
|
||||
-- 시퀀스 생성
|
||||
source DB-DDL/maria/ddl/xitframework/seq_zip_file_process_log_id.sql
|
||||
source DB-DDL/maria/ddl/xitframework/seq_zip_file_detail_log_id.sql
|
||||
|
||||
-- 테이블 생성
|
||||
source DB-DDL/maria/ddl/xitframework/tb_zip_file_process_log.sql
|
||||
source DB-DDL/maria/ddl/xitframework/tb_zip_file_detail_log.sql
|
||||
```
|
||||
|
||||
### 2. 배치 작업 등록
|
||||
Quartz 스케줄러를 통해 `ZipFileProcessBatchJob` 클래스를 등록하여 주기적으로 실행
|
||||
|
||||
### 3. ZIP 파일 처리
|
||||
1. `${user.home}/batch/zip/source/` 디렉토리에 ZIP 파일 업로드
|
||||
2. 배치 작업이 실행되면 자동으로 처리
|
||||
3. 처리 결과는 데이터베이스 로그 테이블에서 확인
|
||||
|
||||
## 로그 확인
|
||||
|
||||
### 메인 처리 로그 조회
|
||||
```sql
|
||||
SELECT * FROM tb_zip_file_process_log
|
||||
ORDER BY start_time DESC;
|
||||
```
|
||||
|
||||
### 상세 처리 로그 조회
|
||||
```sql
|
||||
SELECT * FROM tb_zip_file_detail_log
|
||||
WHERE log_id = 'ZPFL00000001'
|
||||
ORDER BY process_time ASC;
|
||||
```
|
||||
|
||||
### 이미지 파일 처리 결과 조회
|
||||
```sql
|
||||
SELECT
|
||||
file_name,
|
||||
is_image,
|
||||
is_corrupted,
|
||||
process_status,
|
||||
error_message
|
||||
FROM tb_zip_file_detail_log
|
||||
WHERE log_id = 'ZPFL00000001'
|
||||
AND is_image = 'Y';
|
||||
```
|
||||
|
||||
## 에러 처리
|
||||
- ZIP 파일 처리 중 오류 발생 시 로그 테이블에 오류 정보 저장
|
||||
- 개별 파일 처리 실패 시에도 다른 파일 처리 계속 진행
|
||||
- 손상된 이미지 파일 발견 시 ERROR 상태로 로그 저장
|
||||
|
||||
## 주의사항
|
||||
1. 디렉토리 권한 확인 필요
|
||||
2. 대용량 ZIP 파일 처리 시 메모리 사용량 모니터링 필요
|
||||
3. 동일한 파일명의 ZIP 파일 처리 시 타임스탬프 추가하여 중복 방지
|
||||
4. 배치 작업은 `@DisallowConcurrentExecution` 어노테이션으로 동시 실행 방지
|
||||
@ -0,0 +1,513 @@
|
||||
package go.kr.project.batch.job;
|
||||
|
||||
import egovframework.constant.BatchConstants;
|
||||
import egovframework.util.SessionUtil;
|
||||
import go.kr.project.batch.model.BatchJobResult;
|
||||
import go.kr.project.batch.model.ZipFileDetailLogVO;
|
||||
import go.kr.project.batch.model.ZipFileProcessLogVO;
|
||||
import go.kr.project.batch.service.ZipFileDetailLogService;
|
||||
import go.kr.project.batch.service.ZipFileProcessLogService;
|
||||
import go.kr.project.batch.util.BatchJobLogUtil;
|
||||
import go.kr.project.batch.util.ImageValidationUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.job
|
||||
* fileName : ZipFileProcessBatchJob
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 배치 작업 클래스
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@DisallowConcurrentExecution // 동시 실행 방지
|
||||
public class ZipFileProcessBatchJob implements Job {
|
||||
|
||||
// ===========================================================
|
||||
// 상수 정의
|
||||
// ===========================================================
|
||||
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(BatchConstants.DATE_FORMAT_DEFAULT);
|
||||
private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
private static final String BATCH_USER_ID = "BATCH_SYSTEM";
|
||||
|
||||
// ===========================================================
|
||||
// 설정값 주입
|
||||
// ===========================================================
|
||||
@Value("${batch.zip.source-dir:/batch/zip/source}")
|
||||
private String sourceDirectory;
|
||||
|
||||
@Value("${batch.zip.extract-dir:/batch/zip/extract}")
|
||||
private String extractDirectory;
|
||||
|
||||
@Value("${batch.zip.archive-dir:/batch/zip/archive}")
|
||||
private String archiveDirectory;
|
||||
|
||||
@Value("${batch.zip.create-date-subdir:false}")
|
||||
private boolean createDateSubdir;
|
||||
|
||||
// ===========================================================
|
||||
// 의존성 주입
|
||||
// ===========================================================
|
||||
@Autowired
|
||||
private ZipFileProcessLogService zipFileProcessLogService;
|
||||
|
||||
@Autowired
|
||||
private ZipFileDetailLogService zipFileDetailLogService;
|
||||
|
||||
// ===========================================================
|
||||
// 메인 실행 메소드
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* 배치 작업을 실행합니다.
|
||||
*
|
||||
* @param context 작업 실행 컨텍스트
|
||||
* @throws JobExecutionException 작업 실행 중 오류 발생 시
|
||||
*/
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
String jobName = context.getJobDetail().getKey().getName();
|
||||
String jobGroup = context.getJobDetail().getKey().getGroup();
|
||||
String currentTime = LocalDateTime.now().format(formatter);
|
||||
|
||||
logJobStart(context, jobGroup, jobName, currentTime);
|
||||
|
||||
try {
|
||||
logBatchSessionInfo();
|
||||
BatchJobResult result = processZipFiles(context);
|
||||
handleJobResult(context, result, jobGroup, jobName, currentTime);
|
||||
} catch (Exception e) {
|
||||
handleJobError(context, jobGroup, jobName, e);
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// 배치 작업 로깅 메소드들
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* 배치 작업 시작 로그를 출력합니다.
|
||||
*/
|
||||
private void logJobStart(JobExecutionContext context, String jobGroup, String jobName, String currentTime) {
|
||||
String startMessage = String.format(BatchConstants.LOG_MSG_ZIP_JOB_START, jobGroup, jobName, currentTime);
|
||||
BatchJobLogUtil.info(context, startMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 배치 작업 실행 시 세션 정보를 안전하게 로깅합니다.
|
||||
*/
|
||||
private void logBatchSessionInfo() {
|
||||
try {
|
||||
String batchUserId = SessionUtil.getBatchUserId();
|
||||
String batchUserAccount = SessionUtil.getBatchUserAccount();
|
||||
boolean isBatchSystem = SessionUtil.isBatchSystem();
|
||||
|
||||
log.info("배치 세션 정보 - 사용자ID: {}, 계정: {}, 배치시스템여부: {}",
|
||||
batchUserId, batchUserAccount, isBatchSystem);
|
||||
} catch (Exception e) {
|
||||
log.warn("배치 세션 정보 조회 중 오류 발생", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업 결과를 처리합니다.
|
||||
*/
|
||||
private void handleJobResult(JobExecutionContext context, BatchJobResult result, String jobGroup, String jobName, String currentTime) {
|
||||
if (result.getStatus().equals(BatchConstants.JOB_EXECUTION_STATUS_PARTIALLY_COMPLETED)) {
|
||||
handlePartialComplete(context, result, jobGroup, jobName, currentTime);
|
||||
} else if (result.getStatus().equals(BatchConstants.JOB_EXECUTION_STATUS_FAILED)) {
|
||||
handleJobFailed(context, result, jobGroup, jobName, currentTime);
|
||||
} else {
|
||||
handleJobComplete(context, jobGroup, jobName, currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 부분 완료 처리
|
||||
*/
|
||||
private void handlePartialComplete(JobExecutionContext context, BatchJobResult result, String jobGroup, String jobName, String currentTime) {
|
||||
String partialCompleteMessage = String.format(BatchConstants.LOG_MSG_ZIP_JOB_PARTIAL_COMPLETE,
|
||||
jobGroup, jobName, currentTime, result.getProcessedCount(), result.getSuccessCount(), result.getErrorCount());
|
||||
BatchJobLogUtil.info(context, partialCompleteMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업 실패 처리
|
||||
*/
|
||||
private void handleJobFailed(JobExecutionContext context, BatchJobResult result, String jobGroup, String jobName, String currentTime) {
|
||||
String failedMessage = String.format(BatchConstants.LOG_MSG_ZIP_JOB_FAILED,
|
||||
jobGroup, jobName, currentTime, result.getTotalItems(), result.getProcessedCount(), result.getSuccessCount(), result.getErrorCount(), result.getErrorMessage());
|
||||
BatchJobLogUtil.error(context, failedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업 완료 처리
|
||||
*/
|
||||
private void handleJobComplete(JobExecutionContext context, String jobGroup, String jobName, String currentTime) {
|
||||
String completeMessage = String.format(BatchConstants.LOG_MSG_ZIP_JOB_COMPLETE, jobGroup, jobName, currentTime);
|
||||
BatchJobLogUtil.info(context, completeMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업 오류 처리
|
||||
*/
|
||||
private void handleJobError(JobExecutionContext context, String jobGroup, String jobName, Exception e) throws JobExecutionException {
|
||||
String errorMessage = String.format(BatchConstants.LOG_MSG_ZIP_JOB_ERROR, jobGroup, jobName);
|
||||
BatchJobLogUtil.error(context, errorMessage, e);
|
||||
throw new JobExecutionException(e);
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// ZIP 파일 처리 메소드들
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* ZIP 파일들을 처리합니다.
|
||||
*/
|
||||
private BatchJobResult processZipFiles(JobExecutionContext context) {
|
||||
BatchJobResult result = new BatchJobResult();
|
||||
result.setStatus(BatchConstants.JOB_EXECUTION_STATUS_COMPLETED);
|
||||
|
||||
try {
|
||||
// 디렉토리 생성
|
||||
createDirectories();
|
||||
|
||||
// 처리할 ZIP 파일 목록 조회
|
||||
List<Path> zipFiles = getZipFilesToProcess();
|
||||
|
||||
if (zipFiles.isEmpty()) {
|
||||
BatchJobLogUtil.info(context, "처리할 파일이 없습니다.");
|
||||
return result;
|
||||
}
|
||||
|
||||
BatchJobLogUtil.info(context, String.format("처리할 ZIP 파일 수: %d개", zipFiles.size()));
|
||||
result.setTotalItems(zipFiles.size());
|
||||
|
||||
// 각 ZIP 파일 처리
|
||||
for (Path zipFile : zipFiles) {
|
||||
try {
|
||||
boolean success = processZipFile(context, zipFile);
|
||||
result.incrementProcessed();
|
||||
|
||||
if (success) {
|
||||
result.incrementSuccess();
|
||||
} else {
|
||||
result.incrementError();
|
||||
result.setStatus(BatchConstants.JOB_EXECUTION_STATUS_PARTIALLY_COMPLETED);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 중 오류 발생: {}", zipFile, e);
|
||||
result.incrementError();
|
||||
result.setStatus(BatchConstants.JOB_EXECUTION_STATUS_PARTIALLY_COMPLETED);
|
||||
}
|
||||
}
|
||||
|
||||
// 처리 결과 로깅
|
||||
logProcessingSummary(context, result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 배치 작업 중 오류 발생", e);
|
||||
result.setStatus(BatchConstants.JOB_EXECUTION_STATUS_FAILED);
|
||||
result.setErrorMessage(e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 필요한 디렉토리들을 생성합니다.
|
||||
*/
|
||||
private void createDirectories() throws IOException {
|
||||
Files.createDirectories(Paths.get(sourceDirectory));
|
||||
Files.createDirectories(Paths.get(extractDirectory));
|
||||
Files.createDirectories(Paths.get(archiveDirectory));
|
||||
}
|
||||
|
||||
/**
|
||||
* 처리할 ZIP 파일 목록을 조회합니다.
|
||||
*/
|
||||
private List<Path> getZipFilesToProcess() throws IOException {
|
||||
List<Path> zipFiles = new ArrayList<>();
|
||||
Path sourcePath = Paths.get(sourceDirectory);
|
||||
|
||||
if (!Files.exists(sourcePath)) {
|
||||
return zipFiles;
|
||||
}
|
||||
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(sourcePath, "*.zip")) {
|
||||
for (Path file : stream) {
|
||||
if (Files.isRegularFile(file)) {
|
||||
zipFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return zipFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 개별 ZIP 파일을 처리합니다.
|
||||
*/
|
||||
private boolean processZipFile(JobExecutionContext context, Path zipFile) {
|
||||
String zipFileName = zipFile.getFileName().toString();
|
||||
BatchJobLogUtil.info(context, String.format("ZIP 파일 처리 시작: %s", zipFileName));
|
||||
|
||||
// 처리 로그 생성
|
||||
ZipFileProcessLogVO processLog = createProcessLog(zipFile);
|
||||
String logId = zipFileProcessLogService.generateZipFileProcessLogId();
|
||||
processLog.setLogId(logId);
|
||||
zipFileProcessLogService.insertZipFileProcessLog(processLog);
|
||||
|
||||
try {
|
||||
// ZIP 파일 압축 해제 및 파일 처리
|
||||
List<ZipFileDetailLogVO> detailLogs = extractAndProcessZipFile(zipFile, logId);
|
||||
|
||||
// 상세 로그 저장
|
||||
if (!detailLogs.isEmpty()) {
|
||||
zipFileDetailLogService.insertZipFileDetailLogList(detailLogs);
|
||||
}
|
||||
|
||||
// 처리 결과 집계
|
||||
updateProcessLogSummary(processLog, detailLogs);
|
||||
|
||||
// ZIP 파일을 아카이브 디렉토리로 이동
|
||||
moveZipFileToArchive(zipFile);
|
||||
|
||||
// 처리 로그 완료 처리
|
||||
processLog.setProcessStatus("SUCCESS");
|
||||
processLog.setEndTime(LocalDateTime.now());
|
||||
zipFileProcessLogService.updateZipFileProcessLog(processLog);
|
||||
|
||||
BatchJobLogUtil.info(context, String.format("ZIP 파일 처리 완료: %s (총: %d, 성공: %d, 실패: %d)",
|
||||
zipFileName, processLog.getTotalFiles(), processLog.getSuccessFiles(), processLog.getErrorFiles()));
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 중 오류 발생: {}", zipFileName, e);
|
||||
|
||||
// 처리 로그 오류 처리
|
||||
processLog.setProcessStatus("ERROR");
|
||||
processLog.setErrorMessage(e.getMessage());
|
||||
processLog.setEndTime(LocalDateTime.now());
|
||||
zipFileProcessLogService.updateZipFileProcessLog(processLog);
|
||||
|
||||
BatchJobLogUtil.error(context, String.format("ZIP 파일 처리 실패: %s", zipFileName), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 처리 로그를 생성합니다.
|
||||
*/
|
||||
private ZipFileProcessLogVO createProcessLog(Path zipFile) {
|
||||
ZipFileProcessLogVO processLog = new ZipFileProcessLogVO();
|
||||
processLog.setZipFileName(zipFile.getFileName().toString());
|
||||
processLog.setZipFilePath(zipFile.toString());
|
||||
processLog.setExtractPath(extractDirectory);
|
||||
processLog.setArchivePath(archiveDirectory);
|
||||
processLog.setProcessStatus("PROCESSING");
|
||||
processLog.setStartTime(LocalDateTime.now());
|
||||
processLog.setRegUserId(BATCH_USER_ID);
|
||||
processLog.setModUserId(BATCH_USER_ID);
|
||||
|
||||
// 초기값 설정
|
||||
processLog.setTotalFiles(0);
|
||||
processLog.setSuccessFiles(0);
|
||||
processLog.setErrorFiles(0);
|
||||
processLog.setImageFiles(0);
|
||||
processLog.setNonImageFiles(0);
|
||||
processLog.setCorruptedFiles(0);
|
||||
|
||||
return processLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일을 압축 해제하고 각 파일을 처리합니다.
|
||||
*/
|
||||
private List<ZipFileDetailLogVO> extractAndProcessZipFile(Path zipFile, String logId) throws IOException {
|
||||
List<ZipFileDetailLogVO> detailLogs = new ArrayList<>();
|
||||
Path extractPath = Paths.get(extractDirectory);
|
||||
|
||||
// 날짜별 하위 디렉토리 생성
|
||||
if (createDateSubdir) {
|
||||
String dateStr = LocalDateTime.now().format(dateFormatter);
|
||||
extractPath = extractPath.resolve(dateStr);
|
||||
}
|
||||
|
||||
// 추출 디렉토리 생성
|
||||
if (!Files.exists(extractPath)) {
|
||||
Files.createDirectories(extractPath);
|
||||
log.info("압축 해제 디렉토리 생성: {}", extractPath);
|
||||
}
|
||||
|
||||
try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(zipFile))) {
|
||||
ZipEntry entry;
|
||||
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
if (!entry.isDirectory()) {
|
||||
ZipFileDetailLogVO detailLog = processZipEntry(zis, entry, extractPath, logId);
|
||||
detailLogs.add(detailLog);
|
||||
}
|
||||
zis.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
return detailLogs;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 엔트리(개별 파일)를 처리합니다.
|
||||
*/
|
||||
private ZipFileDetailLogVO processZipEntry(ZipInputStream zis, ZipEntry entry, Path extractPath, String logId) {
|
||||
ZipFileDetailLogVO detailLog = new ZipFileDetailLogVO();
|
||||
detailLog.setLogId(logId);
|
||||
detailLog.setFileName(entry.getName());
|
||||
detailLog.setFileSize(entry.getSize());
|
||||
detailLog.setProcessTime(LocalDateTime.now());
|
||||
detailLog.setRegUserId(BATCH_USER_ID);
|
||||
|
||||
try {
|
||||
// 파일을 추출 디렉토리에 저장
|
||||
Path targetFile = extractPath.resolve(entry.getName());
|
||||
Files.createDirectories(targetFile.getParent());
|
||||
Files.copy(zis, targetFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
detailLog.setFilePath(targetFile.toString());
|
||||
detailLog.setFileSize(Files.size(targetFile));
|
||||
|
||||
// 파일 타입 확인
|
||||
String mimeType = Files.probeContentType(targetFile);
|
||||
detailLog.setFileType(mimeType != null ? mimeType : "unknown");
|
||||
|
||||
// 이미지 파일 검증
|
||||
boolean isImage = ImageValidationUtil.isImageFile(targetFile);
|
||||
detailLog.setIsImage(isImage ? "Y" : "N");
|
||||
|
||||
if (isImage) {
|
||||
// 이미지 파일인 경우 손상 여부 확인
|
||||
boolean isCorrupted = ImageValidationUtil.isImageCorrupted(targetFile);
|
||||
detailLog.setIsCorrupted(isCorrupted ? "Y" : "N");
|
||||
|
||||
if (isCorrupted) {
|
||||
detailLog.setProcessStatus("ERROR");
|
||||
detailLog.setErrorMessage("손상된 이미지 파일");
|
||||
log.warn("손상된 이미지 파일 발견: {}", entry.getName());
|
||||
} else {
|
||||
detailLog.setProcessStatus("SUCCESS");
|
||||
log.debug("정상 이미지 파일 처리: {}", entry.getName());
|
||||
}
|
||||
} else {
|
||||
detailLog.setProcessStatus("SUCCESS");
|
||||
log.debug("비이미지 파일 처리: {}", entry.getName());
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("ZIP 엔트리 처리 중 오류 발생: {}", entry.getName(), e);
|
||||
detailLog.setProcessStatus("ERROR");
|
||||
detailLog.setErrorMessage("파일 추출 실패: " + e.getMessage());
|
||||
detailLog.setIsImage("N");
|
||||
detailLog.setIsCorrupted("N");
|
||||
}
|
||||
|
||||
return detailLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* 처리 로그의 집계 정보를 업데이트합니다.
|
||||
*/
|
||||
private void updateProcessLogSummary(ZipFileProcessLogVO processLog, List<ZipFileDetailLogVO> detailLogs) {
|
||||
int totalFiles = detailLogs.size();
|
||||
int successFiles = 0;
|
||||
int errorFiles = 0;
|
||||
int imageFiles = 0;
|
||||
int nonImageFiles = 0;
|
||||
int corruptedFiles = 0;
|
||||
|
||||
for (ZipFileDetailLogVO detailLog : detailLogs) {
|
||||
if ("SUCCESS".equals(detailLog.getProcessStatus())) {
|
||||
successFiles++;
|
||||
} else {
|
||||
errorFiles++;
|
||||
}
|
||||
|
||||
if ("Y".equals(detailLog.getIsImage())) {
|
||||
imageFiles++;
|
||||
if ("Y".equals(detailLog.getIsCorrupted())) {
|
||||
corruptedFiles++;
|
||||
}
|
||||
} else {
|
||||
nonImageFiles++;
|
||||
}
|
||||
}
|
||||
|
||||
processLog.setTotalFiles(totalFiles);
|
||||
processLog.setSuccessFiles(successFiles);
|
||||
processLog.setErrorFiles(errorFiles);
|
||||
processLog.setImageFiles(imageFiles);
|
||||
processLog.setNonImageFiles(nonImageFiles);
|
||||
processLog.setCorruptedFiles(corruptedFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일을 아카이브 디렉토리로 이동합니다.
|
||||
*/
|
||||
private void moveZipFileToArchive(Path zipFile) throws IOException {
|
||||
Path archivePath = Paths.get(archiveDirectory);
|
||||
|
||||
// 날짜별 하위 디렉토리 생성
|
||||
if (createDateSubdir) {
|
||||
String dateStr = LocalDateTime.now().format(dateFormatter);
|
||||
archivePath = archivePath.resolve(dateStr);
|
||||
}
|
||||
|
||||
// 아카이브 디렉토리 생성
|
||||
if (!Files.exists(archivePath)) {
|
||||
Files.createDirectories(archivePath);
|
||||
log.info("아카이브 디렉토리 생성: {}", archivePath);
|
||||
}
|
||||
|
||||
Path targetPath = archivePath.resolve(zipFile.getFileName());
|
||||
|
||||
// 동일한 파일명이 있는 경우 타임스탬프 추가
|
||||
if (Files.exists(targetPath)) {
|
||||
String fileName = zipFile.getFileName().toString();
|
||||
String baseName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||
String extension = fileName.substring(fileName.lastIndexOf('.'));
|
||||
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
targetPath = archivePath.resolve(baseName + "_" + timestamp + extension);
|
||||
}
|
||||
|
||||
Files.move(zipFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
log.info("ZIP 파일 아카이브 완료: {} -> {}", zipFile, targetPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 처리 결과 요약을 로깅합니다.
|
||||
*/
|
||||
private void logProcessingSummary(JobExecutionContext context, BatchJobResult result) {
|
||||
String summary = String.format("ZIP 파일 처리 결과 - 처리: %d개, 성공: %d개, 실패: %d개",
|
||||
result.getProcessedCount(), result.getSuccessCount(), result.getErrorCount());
|
||||
BatchJobLogUtil.info(context, summary);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
package go.kr.project.batch.mapper;
|
||||
|
||||
import go.kr.project.batch.model.ZipFileDetailLogVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.mapper
|
||||
* fileName : ZipFileDetailLogMapper
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 상세 로그 매퍼
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
@Mapper
|
||||
public interface ZipFileDetailLogMapper {
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 ID를 생성합니다.
|
||||
*
|
||||
* @return 생성된 상세 로그 ID
|
||||
*/
|
||||
String generateZipFileDetailLogId();
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 등록합니다.
|
||||
*
|
||||
* @param zipFileDetailLog 등록할 ZIP 파일 처리 상세 로그 정보
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
int insertZipFileDetailLog(ZipFileDetailLogVO zipFileDetailLog);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록을 일괄 등록합니다.
|
||||
*
|
||||
* @param zipFileDetailLogList 등록할 ZIP 파일 처리 상세 로그 목록
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
int insertZipFileDetailLogList(List<ZipFileDetailLogVO> zipFileDetailLogList);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 삭제합니다.
|
||||
*
|
||||
* @param detailLogId 삭제할 상세 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
int deleteZipFileDetailLog(@Param("detailLogId") String detailLogId);
|
||||
|
||||
/**
|
||||
* 로그 ID로 ZIP 파일 처리 상세 로그를 삭제합니다.
|
||||
*
|
||||
* @param logId 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
int deleteZipFileDetailLogByLogId(@Param("logId") String logId);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 조회합니다.
|
||||
*
|
||||
* @param detailLogId 조회할 상세 로그 ID
|
||||
* @return ZIP 파일 처리 상세 로그 정보
|
||||
*/
|
||||
ZipFileDetailLogVO selectZipFileDetailLog(@Param("detailLogId") String detailLogId);
|
||||
|
||||
/**
|
||||
* 로그 ID로 ZIP 파일 처리 상세 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param logId 로그 ID
|
||||
* @return ZIP 파일 처리 상세 로그 목록
|
||||
*/
|
||||
List<ZipFileDetailLogVO> selectZipFileDetailLogListByLogId(@Param("logId") String logId);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록의 총 개수를 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건
|
||||
* @return 총 개수
|
||||
*/
|
||||
int selectZipFileDetailLogListTotalCount(ZipFileDetailLogVO paramVO);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건 및 페이징 정보
|
||||
* @return ZIP 파일 처리 상세 로그 목록
|
||||
*/
|
||||
List<ZipFileDetailLogVO> selectZipFileDetailLogList(ZipFileDetailLogVO paramVO);
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
package go.kr.project.batch.mapper;
|
||||
|
||||
import go.kr.project.batch.model.ZipFileProcessLogVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.mapper
|
||||
* fileName : ZipFileProcessLogMapper
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 로그 매퍼
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
@Mapper
|
||||
public interface ZipFileProcessLogMapper {
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 ID를 생성합니다.
|
||||
*
|
||||
* @return 생성된 로그 ID
|
||||
*/
|
||||
String generateZipFileProcessLogId();
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 등록합니다.
|
||||
*
|
||||
* @param zipFileProcessLog 등록할 ZIP 파일 처리 로그 정보
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
int insertZipFileProcessLog(ZipFileProcessLogVO zipFileProcessLog);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 수정합니다.
|
||||
*
|
||||
* @param zipFileProcessLog 수정할 ZIP 파일 처리 로그 정보
|
||||
* @return 수정된 행의 수
|
||||
*/
|
||||
int updateZipFileProcessLog(ZipFileProcessLogVO zipFileProcessLog);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 삭제합니다.
|
||||
*
|
||||
* @param logId 삭제할 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
int deleteZipFileProcessLog(@Param("logId") String logId);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 조회합니다.
|
||||
*
|
||||
* @param logId 조회할 로그 ID
|
||||
* @return ZIP 파일 처리 로그 정보
|
||||
*/
|
||||
ZipFileProcessLogVO selectZipFileProcessLog(@Param("logId") String logId);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 목록의 총 개수를 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건
|
||||
* @return 총 개수
|
||||
*/
|
||||
int selectZipFileProcessLogListTotalCount(ZipFileProcessLogVO paramVO);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건 및 페이징 정보
|
||||
* @return ZIP 파일 처리 로그 목록
|
||||
*/
|
||||
List<ZipFileProcessLogVO> selectZipFileProcessLogList(ZipFileProcessLogVO paramVO);
|
||||
|
||||
/**
|
||||
* 처리 중인 ZIP 파일 로그를 조회합니다.
|
||||
*
|
||||
* @param zipFileName ZIP 파일명
|
||||
* @return 처리 중인 ZIP 파일 로그 정보
|
||||
*/
|
||||
ZipFileProcessLogVO selectProcessingZipFileLog(@Param("zipFileName") String zipFileName);
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package go.kr.project.batch.model;
|
||||
|
||||
import go.kr.project.common.model.PagingVO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.model
|
||||
* fileName : ZipFileDetailLogVO
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 상세 로그 VO
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ZipFileDetailLogVO extends PagingVO {
|
||||
|
||||
/**
|
||||
* 상세로그ID
|
||||
*/
|
||||
private String detailLogId;
|
||||
|
||||
/**
|
||||
* 로그ID
|
||||
*/
|
||||
private String logId;
|
||||
|
||||
/**
|
||||
* 파일명
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 파일경로
|
||||
*/
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 파일크기(bytes)
|
||||
*/
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 파일타입
|
||||
*/
|
||||
private String fileType;
|
||||
|
||||
/**
|
||||
* 이미지여부(Y/N)
|
||||
*/
|
||||
private String isImage;
|
||||
|
||||
/**
|
||||
* 손상여부(Y/N)
|
||||
*/
|
||||
private String isCorrupted;
|
||||
|
||||
/**
|
||||
* 처리상태(SUCCESS/ERROR)
|
||||
*/
|
||||
private String processStatus;
|
||||
|
||||
/**
|
||||
* 에러메시지
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 처리시간
|
||||
*/
|
||||
private LocalDateTime processTime;
|
||||
|
||||
/**
|
||||
* 등록일시
|
||||
*/
|
||||
private LocalDateTime regDate;
|
||||
|
||||
/**
|
||||
* 등록자ID
|
||||
*/
|
||||
private String regUserId;
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
package go.kr.project.batch.model;
|
||||
|
||||
import go.kr.project.common.model.PagingVO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.model
|
||||
* fileName : ZipFileProcessLogVO
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 로그 VO
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ZipFileProcessLogVO extends PagingVO {
|
||||
|
||||
/**
|
||||
* 로그ID
|
||||
*/
|
||||
private String logId;
|
||||
|
||||
/**
|
||||
* ZIP파일명
|
||||
*/
|
||||
private String zipFileName;
|
||||
|
||||
/**
|
||||
* ZIP파일경로
|
||||
*/
|
||||
private String zipFilePath;
|
||||
|
||||
/**
|
||||
* 압축해제경로
|
||||
*/
|
||||
private String extractPath;
|
||||
|
||||
/**
|
||||
* 원본보관경로
|
||||
*/
|
||||
private String archivePath;
|
||||
|
||||
/**
|
||||
* 총파일수
|
||||
*/
|
||||
private Integer totalFiles;
|
||||
|
||||
/**
|
||||
* 성공파일수
|
||||
*/
|
||||
private Integer successFiles;
|
||||
|
||||
/**
|
||||
* 실패파일수
|
||||
*/
|
||||
private Integer errorFiles;
|
||||
|
||||
/**
|
||||
* 이미지파일수
|
||||
*/
|
||||
private Integer imageFiles;
|
||||
|
||||
/**
|
||||
* 비이미지파일수
|
||||
*/
|
||||
private Integer nonImageFiles;
|
||||
|
||||
/**
|
||||
* 손상파일수
|
||||
*/
|
||||
private Integer corruptedFiles;
|
||||
|
||||
/**
|
||||
* 처리상태(PROCESSING/SUCCESS/ERROR)
|
||||
*/
|
||||
private String processStatus;
|
||||
|
||||
/**
|
||||
* 에러메시지
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 시작시간
|
||||
*/
|
||||
private LocalDateTime startTime;
|
||||
|
||||
/**
|
||||
* 종료시간
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/**
|
||||
* 등록일시
|
||||
*/
|
||||
private LocalDateTime regDate;
|
||||
|
||||
/**
|
||||
* 등록자ID
|
||||
*/
|
||||
private String regUserId;
|
||||
|
||||
/**
|
||||
* 수정일시
|
||||
*/
|
||||
private LocalDateTime modDate;
|
||||
|
||||
/**
|
||||
* 수정자ID
|
||||
*/
|
||||
private String modUserId;
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package go.kr.project.batch.service;
|
||||
|
||||
import go.kr.project.batch.model.ZipFileDetailLogVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.service
|
||||
* fileName : ZipFileDetailLogService
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 상세 로그 서비스
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
public interface ZipFileDetailLogService {
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 ID를 생성합니다.
|
||||
*
|
||||
* @return 생성된 상세 로그 ID
|
||||
*/
|
||||
String generateZipFileDetailLogId();
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 등록합니다.
|
||||
*
|
||||
* @param zipFileDetailLog 등록할 ZIP 파일 처리 상세 로그 정보
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
int insertZipFileDetailLog(ZipFileDetailLogVO zipFileDetailLog);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록을 일괄 등록합니다.
|
||||
*
|
||||
* @param zipFileDetailLogList 등록할 ZIP 파일 처리 상세 로그 목록
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
int insertZipFileDetailLogList(List<ZipFileDetailLogVO> zipFileDetailLogList);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 삭제합니다.
|
||||
*
|
||||
* @param detailLogId 삭제할 상세 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
int deleteZipFileDetailLog(String detailLogId);
|
||||
|
||||
/**
|
||||
* 로그 ID로 ZIP 파일 처리 상세 로그를 삭제합니다.
|
||||
*
|
||||
* @param logId 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
int deleteZipFileDetailLogByLogId(String logId);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 조회합니다.
|
||||
*
|
||||
* @param detailLogId 조회할 상세 로그 ID
|
||||
* @return ZIP 파일 처리 상세 로그 정보
|
||||
*/
|
||||
ZipFileDetailLogVO selectZipFileDetailLog(String detailLogId);
|
||||
|
||||
/**
|
||||
* 로그 ID로 ZIP 파일 처리 상세 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param logId 로그 ID
|
||||
* @return ZIP 파일 처리 상세 로그 목록
|
||||
*/
|
||||
List<ZipFileDetailLogVO> selectZipFileDetailLogListByLogId(String logId);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록의 총 개수를 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건
|
||||
* @return 총 개수
|
||||
*/
|
||||
int selectZipFileDetailLogListTotalCount(ZipFileDetailLogVO paramVO);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건 및 페이징 정보
|
||||
* @return ZIP 파일 처리 상세 로그 목록
|
||||
*/
|
||||
List<ZipFileDetailLogVO> selectZipFileDetailLogList(ZipFileDetailLogVO paramVO);
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package go.kr.project.batch.service;
|
||||
|
||||
import go.kr.project.batch.model.ZipFileProcessLogVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.service
|
||||
* fileName : ZipFileProcessLogService
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 로그 서비스
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
public interface ZipFileProcessLogService {
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 ID를 생성합니다.
|
||||
*
|
||||
* @return 생성된 로그 ID
|
||||
*/
|
||||
String generateZipFileProcessLogId();
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 등록합니다.
|
||||
*
|
||||
* @param zipFileProcessLog 등록할 ZIP 파일 처리 로그 정보
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
int insertZipFileProcessLog(ZipFileProcessLogVO zipFileProcessLog);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 수정합니다.
|
||||
*
|
||||
* @param zipFileProcessLog 수정할 ZIP 파일 처리 로그 정보
|
||||
* @return 수정된 행의 수
|
||||
*/
|
||||
int updateZipFileProcessLog(ZipFileProcessLogVO zipFileProcessLog);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 삭제합니다.
|
||||
*
|
||||
* @param logId 삭제할 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
int deleteZipFileProcessLog(String logId);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 조회합니다.
|
||||
*
|
||||
* @param logId 조회할 로그 ID
|
||||
* @return ZIP 파일 처리 로그 정보
|
||||
*/
|
||||
ZipFileProcessLogVO selectZipFileProcessLog(String logId);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 목록의 총 개수를 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건
|
||||
* @return 총 개수
|
||||
*/
|
||||
int selectZipFileProcessLogListTotalCount(ZipFileProcessLogVO paramVO);
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건 및 페이징 정보
|
||||
* @return ZIP 파일 처리 로그 목록
|
||||
*/
|
||||
List<ZipFileProcessLogVO> selectZipFileProcessLogList(ZipFileProcessLogVO paramVO);
|
||||
|
||||
/**
|
||||
* 처리 중인 ZIP 파일 로그를 조회합니다.
|
||||
*
|
||||
* @param zipFileName ZIP 파일명
|
||||
* @return 처리 중인 ZIP 파일 로그 정보
|
||||
*/
|
||||
ZipFileProcessLogVO selectProcessingZipFileLog(String zipFileName);
|
||||
}
|
||||
@ -0,0 +1,184 @@
|
||||
package go.kr.project.batch.service.impl;
|
||||
|
||||
import go.kr.project.batch.mapper.ZipFileDetailLogMapper;
|
||||
import go.kr.project.batch.model.ZipFileDetailLogVO;
|
||||
import go.kr.project.batch.service.ZipFileDetailLogService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.service.impl
|
||||
* fileName : ZipFileDetailLogServiceImpl
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 상세 로그 서비스 구현
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class ZipFileDetailLogServiceImpl extends EgovAbstractServiceImpl implements ZipFileDetailLogService {
|
||||
|
||||
private final ZipFileDetailLogMapper zipFileDetailLogMapper;
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 ID를 생성합니다.
|
||||
*
|
||||
* @return 생성된 상세 로그 ID
|
||||
*/
|
||||
@Override
|
||||
public String generateZipFileDetailLogId() {
|
||||
try {
|
||||
return zipFileDetailLogMapper.generateZipFileDetailLogId();
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 상세 로그 ID 생성 중 오류 발생", e);
|
||||
throw new RuntimeException("ZIP 파일 처리 상세 로그 ID 생성 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 등록합니다.
|
||||
*
|
||||
* @param zipFileDetailLog 등록할 ZIP 파일 처리 상세 로그 정보
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int insertZipFileDetailLog(ZipFileDetailLogVO zipFileDetailLog) {
|
||||
try {
|
||||
return zipFileDetailLogMapper.insertZipFileDetailLog(zipFileDetailLog);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 상세 로그 등록 중 오류 발생: {}", zipFileDetailLog, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 상세 로그 등록 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록을 일괄 등록합니다.
|
||||
*
|
||||
* @param zipFileDetailLogList 등록할 ZIP 파일 처리 상세 로그 목록
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int insertZipFileDetailLogList(List<ZipFileDetailLogVO> zipFileDetailLogList) {
|
||||
try {
|
||||
if (zipFileDetailLogList == null || zipFileDetailLogList.isEmpty()) {
|
||||
log.warn("등록할 ZIP 파일 처리 상세 로그 목록이 비어있습니다.");
|
||||
return 0;
|
||||
}
|
||||
return zipFileDetailLogMapper.insertZipFileDetailLogList(zipFileDetailLogList);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 상세 로그 목록 일괄 등록 중 오류 발생: size={}",
|
||||
zipFileDetailLogList != null ? zipFileDetailLogList.size() : 0, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 상세 로그 목록 일괄 등록 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 삭제합니다.
|
||||
*
|
||||
* @param detailLogId 삭제할 상세 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int deleteZipFileDetailLog(String detailLogId) {
|
||||
try {
|
||||
return zipFileDetailLogMapper.deleteZipFileDetailLog(detailLogId);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 상세 로그 삭제 중 오류 발생: detailLogId={}", detailLogId, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 상세 로그 삭제 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그 ID로 ZIP 파일 처리 상세 로그를 삭제합니다.
|
||||
*
|
||||
* @param logId 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int deleteZipFileDetailLogByLogId(String logId) {
|
||||
try {
|
||||
return zipFileDetailLogMapper.deleteZipFileDetailLogByLogId(logId);
|
||||
} catch (Exception e) {
|
||||
log.error("로그 ID로 ZIP 파일 처리 상세 로그 삭제 중 오류 발생: logId={}", logId, e);
|
||||
throw new RuntimeException("로그 ID로 ZIP 파일 처리 상세 로그 삭제 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그를 조회합니다.
|
||||
*
|
||||
* @param detailLogId 조회할 상세 로그 ID
|
||||
* @return ZIP 파일 처리 상세 로그 정보
|
||||
*/
|
||||
@Override
|
||||
public ZipFileDetailLogVO selectZipFileDetailLog(String detailLogId) {
|
||||
try {
|
||||
return zipFileDetailLogMapper.selectZipFileDetailLog(detailLogId);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 상세 로그 조회 중 오류 발생: detailLogId={}", detailLogId, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 상세 로그 조회 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그 ID로 ZIP 파일 처리 상세 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param logId 로그 ID
|
||||
* @return ZIP 파일 처리 상세 로그 목록
|
||||
*/
|
||||
@Override
|
||||
public List<ZipFileDetailLogVO> selectZipFileDetailLogListByLogId(String logId) {
|
||||
try {
|
||||
return zipFileDetailLogMapper.selectZipFileDetailLogListByLogId(logId);
|
||||
} catch (Exception e) {
|
||||
log.error("로그 ID로 ZIP 파일 처리 상세 로그 목록 조회 중 오류 발생: logId={}", logId, e);
|
||||
throw new RuntimeException("로그 ID로 ZIP 파일 처리 상세 로그 목록 조회 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록의 총 개수를 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건
|
||||
* @return 총 개수
|
||||
*/
|
||||
@Override
|
||||
public int selectZipFileDetailLogListTotalCount(ZipFileDetailLogVO paramVO) {
|
||||
try {
|
||||
return zipFileDetailLogMapper.selectZipFileDetailLogListTotalCount(paramVO);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 상세 로그 목록 총 개수 조회 중 오류 발생: {}", paramVO, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 상세 로그 목록 총 개수 조회 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 상세 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건 및 페이징 정보
|
||||
* @return ZIP 파일 처리 상세 로그 목록
|
||||
*/
|
||||
@Override
|
||||
public List<ZipFileDetailLogVO> selectZipFileDetailLogList(ZipFileDetailLogVO paramVO) {
|
||||
try {
|
||||
return zipFileDetailLogMapper.selectZipFileDetailLogList(paramVO);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 상세 로그 목록 조회 중 오류 발생: {}", paramVO, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 상세 로그 목록 조회 실패", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
package go.kr.project.batch.service.impl;
|
||||
|
||||
import go.kr.project.batch.mapper.ZipFileProcessLogMapper;
|
||||
import go.kr.project.batch.model.ZipFileProcessLogVO;
|
||||
import go.kr.project.batch.service.ZipFileProcessLogService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.service.impl
|
||||
* fileName : ZipFileProcessLogServiceImpl
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : ZIP 파일 처리 로그 서비스 구현
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class ZipFileProcessLogServiceImpl extends EgovAbstractServiceImpl implements ZipFileProcessLogService {
|
||||
|
||||
private final ZipFileProcessLogMapper zipFileProcessLogMapper;
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 ID를 생성합니다.
|
||||
*
|
||||
* @return 생성된 로그 ID
|
||||
*/
|
||||
@Override
|
||||
public String generateZipFileProcessLogId() {
|
||||
try {
|
||||
return zipFileProcessLogMapper.generateZipFileProcessLogId();
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 로그 ID 생성 중 오류 발생", e);
|
||||
throw new RuntimeException("ZIP 파일 처리 로그 ID 생성 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 등록합니다.
|
||||
*
|
||||
* @param zipFileProcessLog 등록할 ZIP 파일 처리 로그 정보
|
||||
* @return 등록된 행의 수
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int insertZipFileProcessLog(ZipFileProcessLogVO zipFileProcessLog) {
|
||||
try {
|
||||
return zipFileProcessLogMapper.insertZipFileProcessLog(zipFileProcessLog);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 로그 등록 중 오류 발생: {}", zipFileProcessLog, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 로그 등록 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 수정합니다.
|
||||
*
|
||||
* @param zipFileProcessLog 수정할 ZIP 파일 처리 로그 정보
|
||||
* @return 수정된 행의 수
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int updateZipFileProcessLog(ZipFileProcessLogVO zipFileProcessLog) {
|
||||
try {
|
||||
return zipFileProcessLogMapper.updateZipFileProcessLog(zipFileProcessLog);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 로그 수정 중 오류 발생: {}", zipFileProcessLog, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 로그 수정 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 삭제합니다.
|
||||
*
|
||||
* @param logId 삭제할 로그 ID
|
||||
* @return 삭제된 행의 수
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int deleteZipFileProcessLog(String logId) {
|
||||
try {
|
||||
return zipFileProcessLogMapper.deleteZipFileProcessLog(logId);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 로그 삭제 중 오류 발생: logId={}", logId, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 로그 삭제 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그를 조회합니다.
|
||||
*
|
||||
* @param logId 조회할 로그 ID
|
||||
* @return ZIP 파일 처리 로그 정보
|
||||
*/
|
||||
@Override
|
||||
public ZipFileProcessLogVO selectZipFileProcessLog(String logId) {
|
||||
try {
|
||||
return zipFileProcessLogMapper.selectZipFileProcessLog(logId);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 로그 조회 중 오류 발생: logId={}", logId, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 로그 조회 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 목록의 총 개수를 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건
|
||||
* @return 총 개수
|
||||
*/
|
||||
@Override
|
||||
public int selectZipFileProcessLogListTotalCount(ZipFileProcessLogVO paramVO) {
|
||||
try {
|
||||
return zipFileProcessLogMapper.selectZipFileProcessLogListTotalCount(paramVO);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 로그 목록 총 개수 조회 중 오류 발생: {}", paramVO, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 로그 목록 총 개수 조회 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ZIP 파일 처리 로그 목록을 조회합니다.
|
||||
*
|
||||
* @param paramVO 검색 조건 및 페이징 정보
|
||||
* @return ZIP 파일 처리 로그 목록
|
||||
*/
|
||||
@Override
|
||||
public List<ZipFileProcessLogVO> selectZipFileProcessLogList(ZipFileProcessLogVO paramVO) {
|
||||
try {
|
||||
return zipFileProcessLogMapper.selectZipFileProcessLogList(paramVO);
|
||||
} catch (Exception e) {
|
||||
log.error("ZIP 파일 처리 로그 목록 조회 중 오류 발생: {}", paramVO, e);
|
||||
throw new RuntimeException("ZIP 파일 처리 로그 목록 조회 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 처리 중인 ZIP 파일 로그를 조회합니다.
|
||||
*
|
||||
* @param zipFileName ZIP 파일명
|
||||
* @return 처리 중인 ZIP 파일 로그 정보
|
||||
*/
|
||||
@Override
|
||||
public ZipFileProcessLogVO selectProcessingZipFileLog(String zipFileName) {
|
||||
try {
|
||||
return zipFileProcessLogMapper.selectProcessingZipFileLog(zipFileName);
|
||||
} catch (Exception e) {
|
||||
log.error("처리 중인 ZIP 파일 로그 조회 중 오류 발생: zipFileName={}", zipFileName, e);
|
||||
throw new RuntimeException("처리 중인 ZIP 파일 로그 조회 실패", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
package go.kr.project.batch.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* packageName : go.kr.project.batch.util
|
||||
* fileName : ImageValidationUtil
|
||||
* author : 개발자
|
||||
* date : 2025-01-27
|
||||
* description : 이미지 파일 검증 유틸리티
|
||||
* ===========================================================
|
||||
* DATE AUTHOR NOTE
|
||||
* -----------------------------------------------------------
|
||||
* 2025-01-27 개발자 최초 생성
|
||||
*/
|
||||
@Slf4j
|
||||
public class ImageValidationUtil {
|
||||
|
||||
/**
|
||||
* 지원되는 이미지 파일 확장자 목록
|
||||
*/
|
||||
private static final List<String> SUPPORTED_IMAGE_EXTENSIONS = Arrays.asList(
|
||||
"jpg", "jpeg", "png", "gif", "bmp", "tiff", "tif", "webp"
|
||||
);
|
||||
|
||||
/**
|
||||
* 이미지 파일 MIME 타입 목록
|
||||
*/
|
||||
private static final List<String> IMAGE_MIME_TYPES = Arrays.asList(
|
||||
"image/jpeg", "image/jpg", "image/png", "image/gif",
|
||||
"image/bmp", "image/tiff", "image/webp"
|
||||
);
|
||||
|
||||
/**
|
||||
* 파일이 이미지 파일인지 확장자로 확인합니다.
|
||||
*
|
||||
* @param fileName 파일명
|
||||
* @return 이미지 파일 여부
|
||||
*/
|
||||
public static boolean isImageByExtension(String fileName) {
|
||||
if (fileName == null || fileName.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int lastDotIndex = fileName.lastIndexOf('.');
|
||||
if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String extension = fileName.substring(lastDotIndex + 1).toLowerCase();
|
||||
return SUPPORTED_IMAGE_EXTENSIONS.contains(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일이 이미지 파일인지 MIME 타입으로 확인합니다.
|
||||
*
|
||||
* @param filePath 파일 경로
|
||||
* @return 이미지 파일 여부
|
||||
*/
|
||||
public static boolean isImageByMimeType(Path filePath) {
|
||||
try {
|
||||
String mimeType = Files.probeContentType(filePath);
|
||||
if (mimeType == null) {
|
||||
return false;
|
||||
}
|
||||
return IMAGE_MIME_TYPES.contains(mimeType.toLowerCase());
|
||||
} catch (IOException e) {
|
||||
log.warn("MIME 타입 확인 중 오류 발생: {}", filePath, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 파일이 손상되었는지 확인합니다.
|
||||
*
|
||||
* @param filePath 이미지 파일 경로
|
||||
* @return 손상 여부 (true: 손상됨, false: 정상)
|
||||
*/
|
||||
public static boolean isImageCorrupted(Path filePath) {
|
||||
try {
|
||||
File imageFile = filePath.toFile();
|
||||
|
||||
// 파일이 존재하지 않거나 크기가 0인 경우
|
||||
if (!imageFile.exists() || imageFile.length() == 0) {
|
||||
log.warn("이미지 파일이 존재하지 않거나 크기가 0입니다: {}", filePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ImageIO를 사용하여 이미지 읽기 시도
|
||||
BufferedImage image = ImageIO.read(imageFile);
|
||||
|
||||
// 이미지를 읽을 수 없는 경우 손상된 것으로 판단
|
||||
if (image == null) {
|
||||
log.warn("이미지 파일을 읽을 수 없습니다: {}", filePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 이미지 크기가 유효하지 않은 경우
|
||||
if (image.getWidth() <= 0 || image.getHeight() <= 0) {
|
||||
log.warn("이미지 크기가 유효하지 않습니다: {} ({}x{})",
|
||||
filePath, image.getWidth(), image.getHeight());
|
||||
return true;
|
||||
}
|
||||
|
||||
log.debug("이미지 파일 검증 성공: {} ({}x{})",
|
||||
filePath, image.getWidth(), image.getHeight());
|
||||
return false;
|
||||
|
||||
} catch (IOException e) {
|
||||
log.warn("이미지 파일 검증 중 오류 발생: {}", filePath, e);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("이미지 파일 검증 중 예상치 못한 오류 발생: {}", filePath, e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일이 이미지 파일인지 종합적으로 확인합니다.
|
||||
* (확장자 + MIME 타입 검사)
|
||||
*
|
||||
* @param filePath 파일 경로
|
||||
* @return 이미지 파일 여부
|
||||
*/
|
||||
public static boolean isImageFile(Path filePath) {
|
||||
String fileName = filePath.getFileName().toString();
|
||||
|
||||
// 1차: 확장자 검사
|
||||
boolean isImageByExt = isImageByExtension(fileName);
|
||||
|
||||
// 2차: MIME 타입 검사 (확장자가 이미지인 경우에만)
|
||||
boolean isImageByMime = isImageByExt && isImageByMimeType(filePath);
|
||||
|
||||
log.debug("이미지 파일 검사 결과: {} - 확장자: {}, MIME: {}",
|
||||
fileName, isImageByExt, isImageByMime);
|
||||
|
||||
return isImageByMime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 이미지 파일의 상세 정보를 반환합니다.
|
||||
*
|
||||
* @param filePath 이미지 파일 경로
|
||||
* @return 이미지 정보 문자열
|
||||
*/
|
||||
public static String getImageInfo(Path filePath) {
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(filePath.toFile());
|
||||
if (image == null) {
|
||||
return "이미지 정보를 읽을 수 없음";
|
||||
}
|
||||
|
||||
return String.format("크기: %dx%d, 타입: %d",
|
||||
image.getWidth(), image.getHeight(), image.getType());
|
||||
|
||||
} catch (IOException e) {
|
||||
log.warn("이미지 정보 조회 중 오류 발생: {}", filePath, e);
|
||||
return "이미지 정보 조회 실패: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="go.kr.project.batch.mapper.ZipFileDetailLogMapper">
|
||||
|
||||
<!-- ZIP 파일 처리 상세 로그 ID 생성 -->
|
||||
<select id="generateZipFileDetailLogId" resultType="String">
|
||||
SELECT CONCAT('ZFDL', LPAD(NEXTVAL(seq_zip_file_detail_log_id), 8, '0'))
|
||||
</select>
|
||||
|
||||
<!-- ZIP 파일 처리 상세 로그 등록 -->
|
||||
<insert id="insertZipFileDetailLog" parameterType="go.kr.project.batch.model.ZipFileDetailLogVO">
|
||||
<selectKey keyProperty="detailLogId" resultType="String" order="BEFORE">
|
||||
SELECT CONCAT('ZFDL', LPAD(NEXTVAL(seq_zip_file_detail_log_id), 8, '0'))
|
||||
</selectKey>
|
||||
INSERT INTO tb_zip_file_detail_log (
|
||||
detail_log_id,
|
||||
log_id,
|
||||
file_name,
|
||||
file_path,
|
||||
file_size,
|
||||
file_type,
|
||||
is_image,
|
||||
is_corrupted,
|
||||
process_status,
|
||||
error_message,
|
||||
process_time,
|
||||
reg_date,
|
||||
reg_user_id
|
||||
) VALUES (
|
||||
#{detailLogId},
|
||||
#{logId},
|
||||
#{fileName},
|
||||
#{filePath},
|
||||
#{fileSize},
|
||||
#{fileType},
|
||||
#{isImage},
|
||||
#{isCorrupted},
|
||||
#{processStatus},
|
||||
#{errorMessage},
|
||||
#{processTime},
|
||||
NOW(),
|
||||
#{regUserId}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- ZIP 파일 처리 상세 로그 목록 일괄 등록 -->
|
||||
<insert id="insertZipFileDetailLogList" parameterType="java.util.List">
|
||||
INSERT INTO tb_zip_file_detail_log (
|
||||
detail_log_id,
|
||||
log_id,
|
||||
file_name,
|
||||
file_path,
|
||||
file_size,
|
||||
file_type,
|
||||
is_image,
|
||||
is_corrupted,
|
||||
process_status,
|
||||
error_message,
|
||||
process_time,
|
||||
reg_date,
|
||||
reg_user_id
|
||||
) VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(
|
||||
CONCAT('ZFDL', LPAD(NEXTVAL(seq_zip_file_detail_log_id), 8, '0')),
|
||||
#{item.logId},
|
||||
#{item.fileName},
|
||||
#{item.filePath},
|
||||
#{item.fileSize},
|
||||
#{item.fileType},
|
||||
#{item.isImage},
|
||||
#{item.isCorrupted},
|
||||
#{item.processStatus},
|
||||
#{item.errorMessage},
|
||||
#{item.processTime},
|
||||
NOW(),
|
||||
#{item.regUserId}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- ZIP 파일 처리 상세 로그 삭제 -->
|
||||
<delete id="deleteZipFileDetailLog" parameterType="String">
|
||||
DELETE FROM tb_zip_file_detail_log
|
||||
WHERE detail_log_id = #{detailLogId}
|
||||
</delete>
|
||||
|
||||
<!-- 로그 ID로 ZIP 파일 처리 상세 로그 삭제 -->
|
||||
<delete id="deleteZipFileDetailLogByLogId" parameterType="String">
|
||||
DELETE FROM tb_zip_file_detail_log
|
||||
WHERE log_id = #{logId}
|
||||
</delete>
|
||||
|
||||
<!-- ZIP 파일 처리 상세 로그 조회 -->
|
||||
<select id="selectZipFileDetailLog" parameterType="String" resultType="go.kr.project.batch.model.ZipFileDetailLogVO">
|
||||
SELECT
|
||||
detail_log_id AS detailLogId,
|
||||
log_id AS logId,
|
||||
file_name AS fileName,
|
||||
file_path AS filePath,
|
||||
file_size AS fileSize,
|
||||
file_type AS fileType,
|
||||
is_image AS isImage,
|
||||
is_corrupted AS isCorrupted,
|
||||
process_status AS processStatus,
|
||||
error_message AS errorMessage,
|
||||
process_time AS processTime,
|
||||
reg_date AS regDate,
|
||||
reg_user_id AS regUserId
|
||||
FROM tb_zip_file_detail_log
|
||||
WHERE detail_log_id = #{detailLogId}
|
||||
</select>
|
||||
|
||||
<!-- 로그 ID로 ZIP 파일 처리 상세 로그 목록 조회 -->
|
||||
<select id="selectZipFileDetailLogListByLogId" parameterType="String" resultType="go.kr.project.batch.model.ZipFileDetailLogVO">
|
||||
SELECT
|
||||
detail_log_id AS detailLogId,
|
||||
log_id AS logId,
|
||||
file_name AS fileName,
|
||||
file_path AS filePath,
|
||||
file_size AS fileSize,
|
||||
file_type AS fileType,
|
||||
is_image AS isImage,
|
||||
is_corrupted AS isCorrupted,
|
||||
process_status AS processStatus,
|
||||
error_message AS errorMessage,
|
||||
process_time AS processTime,
|
||||
reg_date AS regDate,
|
||||
reg_user_id AS regUserId
|
||||
FROM tb_zip_file_detail_log
|
||||
WHERE log_id = #{logId}
|
||||
ORDER BY process_time ASC
|
||||
</select>
|
||||
|
||||
<!-- ZIP 파일 처리 상세 로그 목록 총 개수 조회 -->
|
||||
<select id="selectZipFileDetailLogListTotalCount" parameterType="go.kr.project.batch.model.ZipFileDetailLogVO" resultType="int">
|
||||
SELECT COUNT(*)
|
||||
FROM tb_zip_file_detail_log
|
||||
WHERE 1=1
|
||||
<if test="logId != null and logId != ''">
|
||||
AND log_id = #{logId}
|
||||
</if>
|
||||
<if test="fileName != null and fileName != ''">
|
||||
AND file_name LIKE CONCAT('%', #{fileName}, '%')
|
||||
</if>
|
||||
<if test="processStatus != null and processStatus != ''">
|
||||
AND process_status = #{processStatus}
|
||||
</if>
|
||||
<if test="isImage != null and isImage != ''">
|
||||
AND is_image = #{isImage}
|
||||
</if>
|
||||
<if test="isCorrupted != null and isCorrupted != ''">
|
||||
AND is_corrupted = #{isCorrupted}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- ZIP 파일 처리 상세 로그 목록 조회 -->
|
||||
<select id="selectZipFileDetailLogList" parameterType="go.kr.project.batch.model.ZipFileDetailLogVO" resultType="go.kr.project.batch.model.ZipFileDetailLogVO">
|
||||
SELECT
|
||||
detail_log_id AS detailLogId,
|
||||
log_id AS logId,
|
||||
file_name AS fileName,
|
||||
file_path AS filePath,
|
||||
file_size AS fileSize,
|
||||
file_type AS fileType,
|
||||
is_image AS isImage,
|
||||
is_corrupted AS isCorrupted,
|
||||
process_status AS processStatus,
|
||||
error_message AS errorMessage,
|
||||
process_time AS processTime,
|
||||
reg_date AS regDate,
|
||||
reg_user_id AS regUserId
|
||||
FROM tb_zip_file_detail_log
|
||||
WHERE 1=1
|
||||
<if test="logId != null and logId != ''">
|
||||
AND log_id = #{logId}
|
||||
</if>
|
||||
<if test="fileName != null and fileName != ''">
|
||||
AND file_name LIKE CONCAT('%', #{fileName}, '%')
|
||||
</if>
|
||||
<if test="processStatus != null and processStatus != ''">
|
||||
AND process_status = #{processStatus}
|
||||
</if>
|
||||
<if test="isImage != null and isImage != ''">
|
||||
AND is_image = #{isImage}
|
||||
</if>
|
||||
<if test="isCorrupted != null and isCorrupted != ''">
|
||||
AND is_corrupted = #{isCorrupted}
|
||||
</if>
|
||||
ORDER BY process_time ASC
|
||||
<if test="pagingYn == 'Y'">
|
||||
LIMIT #{startIndex}, #{recordCountPerPage}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,186 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="go.kr.project.batch.mapper.ZipFileProcessLogMapper">
|
||||
|
||||
<!-- ZIP 파일 처리 로그 ID 생성 -->
|
||||
<select id="generateZipFileProcessLogId" resultType="String">
|
||||
SELECT CONCAT('ZPFL', LPAD(NEXTVAL(seq_zip_file_process_log_id), 8, '0'))
|
||||
</select>
|
||||
|
||||
<!-- ZIP 파일 처리 로그 등록 -->
|
||||
<insert id="insertZipFileProcessLog" parameterType="go.kr.project.batch.model.ZipFileProcessLogVO">
|
||||
INSERT INTO tb_zip_file_process_log (
|
||||
log_id,
|
||||
zip_file_name,
|
||||
zip_file_path,
|
||||
extract_path,
|
||||
archive_path,
|
||||
total_files,
|
||||
success_files,
|
||||
error_files,
|
||||
image_files,
|
||||
non_image_files,
|
||||
corrupted_files,
|
||||
process_status,
|
||||
error_message,
|
||||
start_time,
|
||||
end_time,
|
||||
reg_date,
|
||||
reg_user_id,
|
||||
mod_date,
|
||||
mod_user_id
|
||||
) VALUES (
|
||||
#{logId},
|
||||
#{zipFileName},
|
||||
#{zipFilePath},
|
||||
#{extractPath},
|
||||
#{archivePath},
|
||||
#{totalFiles},
|
||||
#{successFiles},
|
||||
#{errorFiles},
|
||||
#{imageFiles},
|
||||
#{nonImageFiles},
|
||||
#{corruptedFiles},
|
||||
#{processStatus},
|
||||
#{errorMessage},
|
||||
#{startTime},
|
||||
#{endTime},
|
||||
NOW(),
|
||||
#{regUserId},
|
||||
NOW(),
|
||||
#{modUserId}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- ZIP 파일 처리 로그 수정 -->
|
||||
<update id="updateZipFileProcessLog" parameterType="go.kr.project.batch.model.ZipFileProcessLogVO">
|
||||
UPDATE tb_zip_file_process_log
|
||||
SET
|
||||
extract_path = #{extractPath},
|
||||
archive_path = #{archivePath},
|
||||
total_files = #{totalFiles},
|
||||
success_files = #{successFiles},
|
||||
error_files = #{errorFiles},
|
||||
image_files = #{imageFiles},
|
||||
non_image_files = #{nonImageFiles},
|
||||
corrupted_files = #{corruptedFiles},
|
||||
process_status = #{processStatus},
|
||||
error_message = #{errorMessage},
|
||||
end_time = #{endTime},
|
||||
mod_date = NOW(),
|
||||
mod_user_id = #{modUserId}
|
||||
WHERE log_id = #{logId}
|
||||
</update>
|
||||
|
||||
<!-- ZIP 파일 처리 로그 삭제 -->
|
||||
<delete id="deleteZipFileProcessLog" parameterType="String">
|
||||
DELETE FROM tb_zip_file_process_log
|
||||
WHERE log_id = #{logId}
|
||||
</delete>
|
||||
|
||||
<!-- ZIP 파일 처리 로그 조회 -->
|
||||
<select id="selectZipFileProcessLog" parameterType="String" resultType="go.kr.project.batch.model.ZipFileProcessLogVO">
|
||||
SELECT
|
||||
log_id AS logId,
|
||||
zip_file_name AS zipFileName,
|
||||
zip_file_path AS zipFilePath,
|
||||
extract_path AS extractPath,
|
||||
archive_path AS archivePath,
|
||||
total_files AS totalFiles,
|
||||
success_files AS successFiles,
|
||||
error_files AS errorFiles,
|
||||
image_files AS imageFiles,
|
||||
non_image_files AS nonImageFiles,
|
||||
corrupted_files AS corruptedFiles,
|
||||
process_status AS processStatus,
|
||||
error_message AS errorMessage,
|
||||
start_time AS startTime,
|
||||
end_time AS endTime,
|
||||
reg_date AS regDate,
|
||||
reg_user_id AS regUserId,
|
||||
mod_date AS modDate,
|
||||
mod_user_id AS modUserId
|
||||
FROM tb_zip_file_process_log
|
||||
WHERE log_id = #{logId}
|
||||
</select>
|
||||
|
||||
<!-- ZIP 파일 처리 로그 목록 총 개수 조회 -->
|
||||
<select id="selectZipFileProcessLogListTotalCount" parameterType="go.kr.project.batch.model.ZipFileProcessLogVO" resultType="int">
|
||||
SELECT COUNT(*)
|
||||
FROM tb_zip_file_process_log
|
||||
WHERE 1=1
|
||||
<if test="zipFileName != null and zipFileName != ''">
|
||||
AND zip_file_name LIKE CONCAT('%', #{zipFileName}, '%')
|
||||
</if>
|
||||
<if test="processStatus != null and processStatus != ''">
|
||||
AND process_status = #{processStatus}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- ZIP 파일 처리 로그 목록 조회 -->
|
||||
<select id="selectZipFileProcessLogList" parameterType="go.kr.project.batch.model.ZipFileProcessLogVO" resultType="go.kr.project.batch.model.ZipFileProcessLogVO">
|
||||
SELECT
|
||||
log_id AS logId,
|
||||
zip_file_name AS zipFileName,
|
||||
zip_file_path AS zipFilePath,
|
||||
extract_path AS extractPath,
|
||||
archive_path AS archivePath,
|
||||
total_files AS totalFiles,
|
||||
success_files AS successFiles,
|
||||
error_files AS errorFiles,
|
||||
image_files AS imageFiles,
|
||||
non_image_files AS nonImageFiles,
|
||||
corrupted_files AS corruptedFiles,
|
||||
process_status AS processStatus,
|
||||
error_message AS errorMessage,
|
||||
start_time AS startTime,
|
||||
end_time AS endTime,
|
||||
reg_date AS regDate,
|
||||
reg_user_id AS regUserId,
|
||||
mod_date AS modDate,
|
||||
mod_user_id AS modUserId
|
||||
FROM tb_zip_file_process_log
|
||||
WHERE 1=1
|
||||
<if test="zipFileName != null and zipFileName != ''">
|
||||
AND zip_file_name LIKE CONCAT('%', #{zipFileName}, '%')
|
||||
</if>
|
||||
<if test="processStatus != null and processStatus != ''">
|
||||
AND process_status = #{processStatus}
|
||||
</if>
|
||||
ORDER BY start_time DESC
|
||||
<if test="pagingYn == 'Y'">
|
||||
LIMIT #{startIndex}, #{recordCountPerPage}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 처리 중인 ZIP 파일 로그 조회 -->
|
||||
<select id="selectProcessingZipFileLog" parameterType="String" resultType="go.kr.project.batch.model.ZipFileProcessLogVO">
|
||||
SELECT
|
||||
log_id AS logId,
|
||||
zip_file_name AS zipFileName,
|
||||
zip_file_path AS zipFilePath,
|
||||
extract_path AS extractPath,
|
||||
archive_path AS archivePath,
|
||||
total_files AS totalFiles,
|
||||
success_files AS successFiles,
|
||||
error_files AS errorFiles,
|
||||
image_files AS imageFiles,
|
||||
non_image_files AS nonImageFiles,
|
||||
corrupted_files AS corruptedFiles,
|
||||
process_status AS processStatus,
|
||||
error_message AS errorMessage,
|
||||
start_time AS startTime,
|
||||
end_time AS endTime,
|
||||
reg_date AS regDate,
|
||||
reg_user_id AS regUserId,
|
||||
mod_date AS modDate,
|
||||
mod_user_id AS modUserId
|
||||
FROM tb_zip_file_process_log
|
||||
WHERE zip_file_name = #{zipFileName}
|
||||
AND process_status = 'PROCESSING'
|
||||
ORDER BY start_time DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue