OBP GwMetric 검증

This commit is contained in:
Rinjae
2025-12-10 15:12:42 +09:00
parent ada110ce91
commit a90b481120
8 changed files with 175 additions and 49 deletions
+5
View File
@@ -0,0 +1,5 @@
# ObpGwMetricHourJob
- Job명 : ObpGwMetricHourJob
- 설명 : GW 시간당 거래 지표 - OBP 과금기초데이터
- com.eactive.eai.rms.onl.common.service.ObpGwMetricHourJob
- aggregation.hour.range : 집계 범위(최대 24로 제한)
+1 -4
View File
@@ -668,7 +668,4 @@ insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Mon
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.query_partnercode_uri', '/api/customer/findCustomerByBusinessManRegistrationNo/%s');
insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', 'kjb.obp.create_partnercode_uri', '/not-yet-implement/%s');
commit;
```
## 라이선스
© 광주은행 (Kwangju Bank)
```
@@ -3,6 +3,7 @@ package com.eactive.eai.rms.data.entity.onl.apim.obp;
import com.eactive.eai.rms.common.base.SqlMapClientTemplateDao;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -44,16 +45,19 @@ public class ObpGwMetricDAO extends SqlMapClientTemplateDao {
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("schemaId", schemaId);
paramMap.put("apiIds", apiIds);
paramMap.put("apiIds", new ArrayList<>(apiIds)); // Set → List 변환 (iBATIS iterate 호환)
List<Map<String, String>> resultList = this.template.queryForList("ObpGwMetric.selectApiNames", paramMap);
return resultList.stream()
.collect(Collectors.toMap(
m -> m.get("API_ID"),
m -> m.get("API_NAME"),
(v1, v2) -> v1
));
Map<String, String> result = new HashMap<>();
for (Map<String, String> m : resultList) {
String apiId = m.get("API_ID");
String apiName = m.get("API_NAME");
if (apiId != null && apiName != null) {
result.put(apiId, apiName);
}
}
return result;
}
/**
@@ -67,16 +71,19 @@ public class ObpGwMetricDAO extends SqlMapClientTemplateDao {
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("schemaId", schemaId);
paramMap.put("apiIds", apiIds);
paramMap.put("apiIds", new ArrayList<>(apiIds)); // Set → List 변환 (iBATIS iterate 호환)
List<Map<String, String>> resultList = this.template.queryForList("ObpGwMetric.selectUris", paramMap);
return resultList.stream()
.collect(Collectors.toMap(
m -> m.get("API_ID"),
m -> m.get("URI"),
(v1, v2) -> v1
));
Map<String, String> result = new HashMap<>();
for (Map<String, String> m : resultList) {
String apiId = m.get("API_ID");
String uri = m.get("URI");
if (apiId != null && uri != null) {
result.put(apiId, uri);
}
}
return result;
}
/**
@@ -89,15 +96,18 @@ public class ObpGwMetricDAO extends SqlMapClientTemplateDao {
}
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("orgIds", orgIds);
paramMap.put("orgIds", new ArrayList<>(orgIds)); // Set → List 변환 (iBATIS iterate 호환)
List<Map<String, String>> resultList = this.template.queryForList("ObpGwMetric.selectAppIdsByOrgIds", paramMap);
return resultList.stream()
.collect(Collectors.toMap(
m -> m.get("ORG_ID"),
m -> m.get("APP_ID"),
(v1, v2) -> v1
));
Map<String, String> result = new HashMap<>();
for (Map<String, String> m : resultList) {
String orgId = m.get("ORG_ID");
String appId = m.get("APP_ID");
if (orgId != null && appId != null) {
result.put(orgId, appId);
}
}
return result;
}
}
@@ -1,7 +1,6 @@
package com.eactive.eai.rms.data.entity.onl.apim.obp;
import com.eactive.apim.portal.obp.entity.ObpGwMetric;
import com.eactive.eai.rms.data.jpa.AbstractEMSDataSerivce;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -14,13 +13,19 @@ import java.util.Optional;
*/
@Service
@Transactional(transactionManager = "transactionManagerForEMS")
public class ObpGwMetricService extends AbstractEMSDataSerivce<ObpGwMetric, String, ObpGwMetricRepository> {
public class ObpGwMetricService {
private final ObpGwMetricRepository repository;
public ObpGwMetricService(ObpGwMetricRepository repository) {
this.repository = repository;
}
/**
* UPSERT 판단을 위한 기존 데이터 조회
*/
public Optional<ObpGwMetric> findByUniqueKey(LocalDateTime timeslice, String apiId, String clientId, String hostname, String orgId) {
return getRepository().findByTimesliceAndApiIdAndClientIdAndHostnameAndOrgId(
return repository.findByTimesliceAndApiIdAndClientIdAndHostnameAndOrgId(
timeslice, apiId, clientId, hostname, orgId);
}
@@ -28,27 +33,27 @@ public class ObpGwMetricService extends AbstractEMSDataSerivce<ObpGwMetric, Stri
* 특정 시간대의 모든 GwMetric 데이터 조회
*/
public List<ObpGwMetric> findByTimeslice(LocalDateTime timeslice) {
return getRepository().findByTimeslice(timeslice);
return repository.findByTimeslice(timeslice);
}
/**
* 특정 시간 범위의 GwMetric 데이터 조회
*/
public List<ObpGwMetric> findByTimesliceBetween(LocalDateTime startTime, LocalDateTime endTime) {
return getRepository().findByTimesliceBetween(startTime, endTime);
return repository.findByTimesliceBetween(startTime, endTime);
}
/**
* 엔티티 저장 (INSERT 또는 UPDATE)
*/
public ObpGwMetric save(ObpGwMetric entity) {
return getRepository().save(entity);
return repository.save(entity);
}
/**
* 엔티티 목록 저장
*/
public List<ObpGwMetric> saveAll(List<ObpGwMetric> entities) {
return getRepository().saveAll(entities);
return repository.saveAll(entities);
}
}
@@ -45,7 +45,7 @@ public class ObpGwMetricVO {
if (timeslice == null || timeslice.isEmpty()) {
return null;
}
// yyyyMMddHH0000000 형식에서 앞 10자리(yyyyMMddHH)만 사용
// "yyyyMMddHH0000000" 형식에서 앞 10자리(yyyyMMddHH)만 사용
String dateTimeStr = timeslice.substring(0, 10) + "0000";
return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
}
@@ -31,11 +31,13 @@ public class ObpGwMetricManService extends BaseService {
private static final Logger log = LoggerFactory.getLogger(ObpGwMetricManService.class);
/**
* 집계 범위 시간 (단위: 시간)
* 기본 집계 범위 시간 (단위: 시간)
* - 1: 직전 1시간만 집계
* - 5: 직전 5시간까지 각각 1시간 단위로 집계
*
* <p>Job 파라미터로 오버라이드 가능 (aggregation.hour.range)</p>
*/
private static final int AGGREGATION_HOUR_RANGE = 1;
public static final int DEFAULT_AGGREGATION_HOUR_RANGE = 1;
private static final DateTimeFormatter TIMESLICE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHH0000000");
private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
@@ -50,22 +52,35 @@ public class ObpGwMetricManService extends BaseService {
}
/**
* 다중 시간대 집계 실행 (Job에서 호출)
* 다중 시간대 집계 실행 (Job에서 호출) - 기본 집계 범위 사용
*/
public void executeHourlyAggregation() {
executeHourlyAggregation(DEFAULT_AGGREGATION_HOUR_RANGE);
}
/**
* 다중 시간대 집계 실행 (Job에서 호출)
*
* @param aggregationHourRange 집계 범위 시간 (단위: 시간)
* 예: 1=직전 1시간, 3=직전 3시간 각각 집계
*/
public void executeHourlyAggregation(int aggregationHourRange) {
long totalStartTime = System.currentTimeMillis();
LocalDateTime now = LocalDateTime.now();
LocalDateTime baseHour = now.truncatedTo(ChronoUnit.HOURS); // 현재 시간의 정각
log.info("========== ObpGwMetric 집계 시작 ==========");
log.info("실행 시간: {}, AGGREGATION_HOUR_RANGE: {}", now, AGGREGATION_HOUR_RANGE);
log.info("실행 시간: {}, aggregationHourRange: {}", now, aggregationHourRange);
for (int i = 1; i <= AGGREGATION_HOUR_RANGE; i++) {
for (int i = 1; i <= aggregationHourRange; i++) {
LocalDateTime targetHour = baseHour.minusHours(i);
log.info("집계 대상 시간대: {}", targetHour);
createHourlyData(targetHour);
}
long totalElapsed = System.currentTimeMillis() - totalStartTime;
log.info("========== ObpGwMetric 집계 완료 ==========");
log.info("전체 집계 소요시간: {}ms ({}초)", totalElapsed, String.format("%.2f", totalElapsed / 1000.0));
}
/**
@@ -184,6 +199,8 @@ public class ObpGwMetricManService extends BaseService {
entity.setUri(uriMap.get(vo.getApiId()));
entity.setAppId(appIdMap.get(vo.getOrgId()));
entity.setUpdateCount(entity.getUpdateCount() + 1); // 보정 횟수 증가
entity.setUpdatedAt(LocalDateTime.now());
entity.setUpdatedBy("GwMetricHourJob");
obpGwMetricService.save(entity);
updateCount++;
} else {
@@ -239,6 +256,8 @@ public class ObpGwMetricManService extends BaseService {
entity.setUri(uriMap.get(vo.getApiId()));
entity.setAppId(appIdMap.get(vo.getOrgId()));
entity.setUpdateCount(0);
entity.setCreatedAt(LocalDateTime.now());
entity.setCreatedBy("GwMetricHourJob");
return entity;
}
@@ -4,12 +4,13 @@ import com.eactive.eai.rms.common.context.MonitoringContext;
import com.eactive.eai.rms.common.util.CommonUtil;
import com.eactive.eai.rms.onl.apim.obp.ObpGwMetricManService;
import com.eactive.eai.rms.onl.common.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
/**
@@ -22,18 +23,45 @@ import org.springframework.context.ApplicationContext;
* <li>보정 (15분): 0 5,20,35,50 * * * ? (15분 간격 4회 실행)</li>
* <li>보정 (10분): 0 5,15,25,35,45,55 * * * ? (10분 간격 6회 실행)</li>
* </ul>
*
* <p>Job 파라미터 (JobDataMap):</p>
* <ul>
* <li><b>aggregation.hour.range</b>: 집계 범위 시간 (단위: 시간, 기본값: 1)
* <ul>
* <li>1: 직전 1시간만 집계</li>
* <li>3: 직전 3시간 각각 집계</li>
* <li>5: 직전 5시간 각각 집계</li>
* </ul>
* </li>
* </ul>
*
* <p>예시 (Quartz 스케줄 등록 시):</p>
* <pre>
* JobDataMap jobDataMap = new JobDataMap();
* jobDataMap.put("aggregation.hour.range", "3");
* </pre>
*/
public class ObpGwMetricHourJob implements Job {
private static final Logger log = LoggerFactory.getLogger(ObpGwMetricHourJob.class);
/** Job 파라미터 키: 집계 범위 시간 */
public static final String PARAM_AGGREGATION_HOUR_RANGE = "aggregation.hour.range";
/** 기본 집계 범위 시간 (시간 단위) */
public static final int DEFAULT_AGGREGATION_HOUR_RANGE = 1;
private transient MonitoringContext monitoringContext;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("*** START ObpGwMetricHourJob run({})", CommonUtil.getToday("yyyy-MM-dd HH:mm"));
ApplicationContext appContext = null;
// Job 파라미터 로깅
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
logJobParameters(jobDataMap);
ApplicationContext appContext;
try {
appContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
} catch (SchedulerException e) {
@@ -44,8 +72,11 @@ public class ObpGwMetricHourJob implements Job {
monitoringContext = (MonitoringContext) appContext.getBean("monitoringContext");
ObpGwMetricManService obpGwMetricManService = appContext.getBean(ObpGwMetricManService.class);
// 집계 범위 시간 파라미터 파싱
int aggregationHourRange = parseAggregationHourRange(jobDataMap);
try {
obpGwMetricManService.executeHourlyAggregation();
obpGwMetricManService.executeHourlyAggregation(aggregationHourRange);
} catch (Exception e) {
log.error("ObpGwMetricHourJob execution failed", e);
throw new JobExecutionException(e);
@@ -53,4 +84,64 @@ public class ObpGwMetricHourJob implements Job {
log.info("*** END ObpGwMetricHourJob run({})", DateUtil.getDateTime("yyyy-MM-dd HH:mm"));
}
/**
* Job 파라미터 로깅
*/
private void logJobParameters(JobDataMap jobDataMap) {
if (jobDataMap == null || jobDataMap.isEmpty()) {
log.debug("Job 파라미터 없음 (기본값 사용)");
return;
}
StringBuilder sb = new StringBuilder("Job 파라미터: ");
for (String key : jobDataMap.getKeys()) {
sb.append(key).append("=").append(jobDataMap.getString(key)).append(", ");
}
log.info(sb.toString());
}
/**
* 집계 범위 시간 파라미터 파싱
*
* @param jobDataMap Job 파라미터 맵
* @return 집계 범위 시간 (파싱 실패 시 기본값 반환)
*/
private int parseAggregationHourRange(JobDataMap jobDataMap) {
if (jobDataMap == null) {
return DEFAULT_AGGREGATION_HOUR_RANGE;
}
try {
String value = jobDataMap.getString(PARAM_AGGREGATION_HOUR_RANGE);
if (value != null && !value.trim().isEmpty()) {
int parsedValue = Integer.parseInt(value.trim());
if (parsedValue < 1) {
log.warn("aggregation.hour.range 값이 1 미만입니다. 기본값({}) 사용: input={}",
DEFAULT_AGGREGATION_HOUR_RANGE, parsedValue);
return DEFAULT_AGGREGATION_HOUR_RANGE;
}
// 개발 모드(D)가 아닌 경우에만 24시간 제한 적용
if (parsedValue > 24 && !isDevMode()) {
log.warn("aggregation.hour.range 값이 24 초과입니다. 24로 제한: input={}", parsedValue);
return 24;
}
return parsedValue;
}
} catch (NumberFormatException e) {
log.warn("aggregation.hour.range 파싱 실패, 기본값({}) 사용: {}",
DEFAULT_AGGREGATION_HOUR_RANGE, e.getMessage());
}
return DEFAULT_AGGREGATION_HOUR_RANGE;
}
/**
* 개발 모드 여부 확인
* @return eai.systemmode=D 이면 true
*/
private boolean isDevMode() {
String systemMode = System.getProperty("eai.systemmode", "");
return "D".equalsIgnoreCase(systemMode);
}
}
@@ -24,9 +24,9 @@
<!--
EAIBZWKDSTCD별 시간대 로그 집계 (Index Range Scan 활용)
에러 판단 기준 (DailyBackup-oracle.xml 참고):
- 에러 조건: RSPNSERRCDNAME LIKE 'RE%' OR RSPNSERRCDNAME LIKE 'RF%'
- 성공 조건: 위 조건의 반대 (NULL 또는 RE%/RF%가 아닌 경우)
에러 판단 기준:
- 성공 조건: RSPNSERRCDNAME IS NULL 또는 RSPNSERRCDNAME LIKE 'ESB%'
- 에러 조건: RE%, RF%, REC%, RIB%, 기타 모든 에러 코드
성능 최적화:
- PK 인덱스 (EAIBZWKDSTCD, MSGDPSTYMS, EAISVCSERNO, LOGPRCSSSERNO) 활용
@@ -41,8 +41,7 @@
'$timeslice$' AS TIMESLICE,
SUM(CASE WHEN LOGPRCSSSERNO = '100' THEN 1 ELSE 0 END) AS ATTEMPTED_COUNT,
SUM(CASE WHEN LOGPRCSSSERNO = '400'
AND (RSPNSERRCDNAME IS NULL
OR (RSPNSERRCDNAME NOT LIKE 'RE%' AND RSPNSERRCDNAME NOT LIKE 'RF%'))
AND (RSPNSERRCDNAME IS NULL OR RSPNSERRCDNAME LIKE 'ESB%')
THEN 1 ELSE 0 END) AS COMPLETED_COUNT
FROM $schemaId$.$tableName$
WHERE EAIBZWKDSTCD = #bzwkCode#
@@ -64,11 +63,11 @@
</iterate>
</statement>
<!-- URI 조회 (TSEAIHS04) -->
<!-- URI 조회 (TSEAIHS04) - APIFULLPATH 사용 -->
<statement id="selectUris" parameterClass="java.util.HashMap" resultClass="java.util.HashMap">
SELECT
EAISVCNAME AS API_ID,
BZWKSVCKEYNAME AS URI
APIFULLPATH AS URI
FROM $schemaId$.TSEAIHS04
WHERE EAISVCNAME IN
<iterate property="apiIds" open="(" close=")" conjunction=",">