From a90b481120b206a315f970fe0865523de86a716d Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 10 Dec 2025 15:12:42 +0900 Subject: [PATCH] =?UTF-8?q?OBP=20GwMetric=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kjb-docs/GwMetricHourJob.md | 5 + readme.md | 5 +- .../entity/onl/apim/obp/ObpGwMetricDAO.java | 52 ++++++---- .../onl/apim/obp/ObpGwMetricService.java | 19 ++-- .../entity/onl/apim/obp/ObpGwMetricVO.java | 2 +- .../onl/apim/obp/ObpGwMetricManService.java | 29 +++++- .../common/service/ObpGwMetricHourJob.java | 99 ++++++++++++++++++- .../eai/apim/obp/ObpGwMetric-oracle.xml | 13 ++- 8 files changed, 175 insertions(+), 49 deletions(-) create mode 100644 kjb-docs/GwMetricHourJob.md diff --git a/kjb-docs/GwMetricHourJob.md b/kjb-docs/GwMetricHourJob.md new file mode 100644 index 0000000..efce18f --- /dev/null +++ b/kjb-docs/GwMetricHourJob.md @@ -0,0 +1,5 @@ +# ObpGwMetricHourJob +- Job명 : ObpGwMetricHourJob +- 설명 : GW 시간당 거래 지표 - OBP 과금기초데이터 +- com.eactive.eai.rms.onl.common.service.ObpGwMetricHourJob +- aggregation.hour.range : 집계 범위(최대 24로 제한) \ No newline at end of file diff --git a/readme.md b/readme.md index 684f7b1..7ea3719 100644 --- a/readme.md +++ b/readme.md @@ -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) +``` \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java index b61fbac..ca3d661 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java @@ -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 paramMap = new HashMap<>(); paramMap.put("schemaId", schemaId); - paramMap.put("apiIds", apiIds); + paramMap.put("apiIds", new ArrayList<>(apiIds)); // Set → List 변환 (iBATIS iterate 호환) List> 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 result = new HashMap<>(); + for (Map 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 paramMap = new HashMap<>(); paramMap.put("schemaId", schemaId); - paramMap.put("apiIds", apiIds); + paramMap.put("apiIds", new ArrayList<>(apiIds)); // Set → List 변환 (iBATIS iterate 호환) List> 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 result = new HashMap<>(); + for (Map 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 paramMap = new HashMap<>(); - paramMap.put("orgIds", orgIds); + paramMap.put("orgIds", new ArrayList<>(orgIds)); // Set → List 변환 (iBATIS iterate 호환) List> 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 result = new HashMap<>(); + for (Map 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; } } diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java index 79d3022..be973ab 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java @@ -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 { +public class ObpGwMetricService { + + private final ObpGwMetricRepository repository; + + public ObpGwMetricService(ObpGwMetricRepository repository) { + this.repository = repository; + } /** * UPSERT 판단을 위한 기존 데이터 조회 */ public Optional 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 findByTimeslice(LocalDateTime timeslice) { - return getRepository().findByTimeslice(timeslice); + return repository.findByTimeslice(timeslice); } /** * 특정 시간 범위의 GwMetric 데이터 조회 */ public List 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 saveAll(List entities) { - return getRepository().saveAll(entities); + return repository.saveAll(entities); } } diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricVO.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricVO.java index 20adc9a..db55110 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricVO.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricVO.java @@ -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")); } diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java index 8543dec..f4a458f 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java @@ -31,11 +31,13 @@ public class ObpGwMetricManService extends BaseService { private static final Logger log = LoggerFactory.getLogger(ObpGwMetricManService.class); /** - * 집계 범위 시간 (단위: 시간) + * 기본 집계 범위 시간 (단위: 시간) * - 1: 직전 1시간만 집계 * - 5: 직전 5시간까지 각각 1시간 단위로 집계 + * + *

Job 파라미터로 오버라이드 가능 (aggregation.hour.range)

*/ - 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; } diff --git a/src/main/java/com/eactive/eai/rms/onl/common/service/ObpGwMetricHourJob.java b/src/main/java/com/eactive/eai/rms/onl/common/service/ObpGwMetricHourJob.java index 35befd3..7b93297 100644 --- a/src/main/java/com/eactive/eai/rms/onl/common/service/ObpGwMetricHourJob.java +++ b/src/main/java/com/eactive/eai/rms/onl/common/service/ObpGwMetricHourJob.java @@ -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; *
  • 보정 (15분): 0 5,20,35,50 * * * ? (15분 간격 4회 실행)
  • *
  • 보정 (10분): 0 5,15,25,35,45,55 * * * ? (10분 간격 6회 실행)
  • * + * + *

    Job 파라미터 (JobDataMap):

    + *
      + *
    • aggregation.hour.range: 집계 범위 시간 (단위: 시간, 기본값: 1) + *
        + *
      • 1: 직전 1시간만 집계
      • + *
      • 3: 직전 3시간 각각 집계
      • + *
      • 5: 직전 5시간 각각 집계
      • + *
      + *
    • + *
    + * + *

    예시 (Quartz 스케줄 등록 시):

    + *
    + * JobDataMap jobDataMap = new JobDataMap();
    + * jobDataMap.put("aggregation.hour.range", "3");
    + * 
    */ 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); + } } diff --git a/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml b/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml index caeb659..ab42299 100644 --- a/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml +++ b/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml @@ -24,9 +24,9 @@ + SELECT EAISVCNAME AS API_ID, - BZWKSVCKEYNAME AS URI + APIFULLPATH AS URI FROM $schemaId$.TSEAIHS04 WHERE EAISVCNAME IN