거래로그 > 시간별 집계하는 배치잡

This commit is contained in:
eastargh
2026-05-14 11:19:55 +09:00
parent 0b341b85f1
commit 335c7ec53a
@@ -0,0 +1,147 @@
package com.eactive.eai.rms.ext.djb.job;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.common.datasource.DataSourceType;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.common.util.CommonUtil;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsHourService;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsHour;
/**
* API 로그 테이블을 시간별 통계로 집계하는 배치 작업
* TSEAILGXX → API_STATS_HOUR
* 실행 주기: 매시 00:10 (당일 데이터 집계)
*/
@Component
public class ApiStatsHourlyAggregationJob implements Job {
private static final Logger log = LoggerFactory.getLogger(ApiStatsHourlyAggregationJob.class);
@PersistenceContext
private EntityManager entityManager;
private ApiStatsHourService apiStatsHourService;
@Autowired
public void setApiStatsHourService(ApiStatsHourService apiStatsHourService) {
this.apiStatsHourService = apiStatsHourService;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
ApplicationContext appContext = null;
final ApiStatsHourlyAggregationJob selfJob;
try {
appContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
selfJob = appContext.getBean(ApiStatsHourlyAggregationJob.class);
} catch (SchedulerException e) {
log.error("applicationContext get module error", e);
return;
}
DataSourceType dataType = DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW);
DataSourceContextHolder.setDataSourceType(dataType);
try {
LocalDate targetDate = LocalDate.now();
if (LocalTime.now().isBefore(LocalTime.of(1, 0))) {
targetDate = targetDate.minusDays(1);
}
selfJob.executeManual(targetDate);
} catch (Exception e) {
throw new JobExecutionException(e);
} finally {
DataSourceContextHolder.clearDataSourceType();
}
}
/**
* 수동 실행 메서드 (날짜 지정)
*/
@Transactional
public int executeManual(LocalDate targetDate) {
long startTime = System.currentTimeMillis();
log.info("=== 시간별 통계 집계 작업 시작 === 대상: {}", targetDate);
String searchDate = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String schemaEMS = System.getProperty("eai.tableowner", "EMSADM").toUpperCase();
String logTableName = CommonUtil.getLogTable(searchDate, true);
// 1. 기존 데이터 삭제
QApiStatsHour q = QApiStatsHour.apiStatsHour;
long deletedCount = apiStatsHourService.getJPAQueryFactory()
.delete(q)
.where(q.statTime.goe(targetDate.atStartOfDay())
.and(q.statTime.lt(targetDate.plusDays(1).atStartOfDay())))
.execute();
log.info("기존 데이터 삭제: {} 건", deletedCount);
// 2. 집계 및 데이터 입력
/**
* EAISVCSERNO로 그룹핑하여 1행으로 집계를 한 다음
* 총건수 : EAISVCSERNO로 그룹핑한 총 건수
* 성공건수 : seq = 400 and EAIERRCD = null 인 건수
* Timeout : EAIERRCD in (공통코드 CODEGROUP = 'API_ERROR_CODE' and PARENTCODE = 'T') 인 건수
* 업무오류 : EAIERRCD in (공통코드 CODEGROUP = 'API_ERROR_CODE' and PARENTCODE = 'B') 인 건수
* 시스템오류 : (seq400 = null or EAIERRCD is not null) and 공통코드에 정의되지 않은 errorcode 인 건수
*/
String sql =
"INSERT INTO API_STATS_HOUR" +
" (STAT_TIME, API_NAME, GW_INSTANCE_ID, BIZ_DIV_CODE, CLIENT_ID" +
" , INBOUND_ADAPTER, OUTBOUND_ADAPTER" +
", TOTAL_CNT, SUCCESS_CNT, TIMEOUT_CNT, SYSTEM_ERR_CNT, BIZ_ERR_CNT" +
", SEQ900_TIMEOUT_CNT, SEQ900_SYSTEM_ERR_CNT, SEQ900_BIZ_ERR_CNT" +
", AVG_RESP_TIME, MIN_RESP_TIME, MAX_RESP_TIME, P50_RESP_TIME, P95_RESP_TIME)" +
" SELECT TO_TIMESTAMP(SUBSTR(DT,1,10) || '0000', 'YYYYMMDDHH24MISS')" +
" , API_NAME, GW_INSTANCE_ID, BIZ_DIV_CODE, NVL(A.CLIENT_ID, 'NONE')" +
" , INBOUND_ADAPTER, OUTBOUND_ADAPTER" +
" , COUNT(EAISVCSERNO)" +
" , SUM(CASE WHEN A.ERROR_CODE IS NULL AND E400 IS NOT NULL THEN 1 ELSE 0 END)" +
" , SUM(CASE WHEN B.PARENTCODE = 'T' THEN 1 ELSE 0 END)" +
" , SUM(CASE WHEN (A.E400 IS NULL OR A.ERROR_CODE IS NOT NULL) AND B.PARENTCODE IS NULL THEN 1 ELSE 0 END)" +
" , SUM(CASE WHEN B.PARENTCODE = 'B' THEN 1 ELSE 0 END)" +
" , 0, 0, 0" +
" , TRUNC(AVG(RESP_TIME)), MIN(RESP_TIME), MAX(RESP_TIME), 0, 0" +
" FROM (" +
" SELECT EAISVCSERNO" +
" , MAX(EAISVCNAME) AS API_NAME, MAX(EAISEVRINSTNCNAME) AS GW_INSTANCE_ID" +
" , MAX(EAIBZWKDSTCD) AS BIZ_DIV_CODE, MAX(CLIENTID) AS CLIENT_ID" +
" , MAX(CLIENTNAME) AS CLIENT_NAME, MAX(ORGID) AS ORG_ID, MAX(ORGNAME) AS ORG_NAME" +
" , MAX(GSTATSYSADPTRBZWKGROUPNAME) AS INBOUND_ADAPTER" +
" , MAX(PSVSYSADPTRBZWKGROUPNAME) AS OUTBOUND_ADAPTER" +
" , MIN(MSGDPSTYMS) AS DT" +
" , MAX(MSGPRCSSYMS) - MIN(MSGDPSTYMS) AS RESP_TIME" +
" , MAX(CASE WHEN LOGPRCSSSERNO = '400' THEN LOGPRCSSSERNO ELSE '' END) AS E400" +
" , MAX(EAIERRCD) AS ERROR_CODE" +
" FROM " + logTableName +
" WHERE MSGDPSTYMS LIKE :searchDate || '%'" +
" GROUP BY EAISVCSERNO" +
" ) A LEFT OUTER JOIN " + schemaEMS + ".TSEAIRM28 B ON A.ERROR_CODE = B.CODE AND B.CODEGROUP = 'API_ERROR_CODE'" +
" GROUP BY SUBSTR(DT,1,10), API_NAME, GW_INSTANCE_ID, BIZ_DIV_CODE, NVL(A.CLIENT_ID, 'NONE')" +
" , INBOUND_ADAPTER, OUTBOUND_ADAPTER";
int savedCount = entityManager.createNativeQuery(sql)
.setParameter("searchDate", searchDate)
.executeUpdate();
long elapsedTime = System.currentTimeMillis() - startTime;
log.info("=== 집계 완료 === (처리 건수: {}, 소요 시간: {}ms)", savedCount, elapsedTime);
return savedCount;
}
}