entities) {
+ return getRepository().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
new file mode 100644
index 0000000..20adc9a
--- /dev/null
+++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricVO.java
@@ -0,0 +1,68 @@
+package com.eactive.eai.rms.data.entity.onl.apim.obp;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * ObpGwMetric VO - 로그 집계 결과 매핑용
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ObpGwMetricVO {
+
+ private static final DateTimeFormatter TIMESLICE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHH0000000");
+
+ /** API ID (EAISVCNAME) */
+ private String apiId;
+
+ /** 클라이언트 ID (CLIENTID) */
+ private String clientId;
+
+ /** 호스트 이름 (EAISEVRINSTNCNAME) */
+ private String hostname;
+
+ /** 기관 ID (ORGID) */
+ private String orgId;
+
+ /** 타임슬라이스 문자열 (yyyyMMddHH0000000) */
+ private String timeslice;
+
+ /** 시도 횟수 (LOGPRCSSSERNO='100' 건수) */
+ private Long attemptedCount;
+
+ /** 완료 횟수 (LOGPRCSSSERNO='400' AND 성공 조건 건수) */
+ private Long completedCount;
+
+ /**
+ * timeslice 문자열을 LocalDateTime으로 변환
+ */
+ public LocalDateTime getTimesliceAsLocalDateTime() {
+ if (timeslice == null || timeslice.isEmpty()) {
+ return null;
+ }
+ // yyyyMMddHH0000000 형식에서 앞 10자리(yyyyMMddHH)만 사용
+ String dateTimeStr = timeslice.substring(0, 10) + "0000";
+ return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+ }
+
+ /**
+ * 집계 키 생성 (apiId + clientId + hostname + orgId)
+ */
+ public String getAggregationKey() {
+ return String.join("|",
+ nullSafe(apiId),
+ nullSafe(clientId),
+ nullSafe(hostname),
+ nullSafe(orgId)
+ );
+ }
+
+ private String nullSafe(String value) {
+ return value != null ? value : "";
+ }
+}
diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java
new file mode 100644
index 0000000..fb3168b
--- /dev/null
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java
@@ -0,0 +1,97 @@
+package com.eactive.eai.rms.onl.apim.obp;
+
+import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+/**
+ * ObpGwMetric REST Controller
+ * API Gateway 시간별 거래량 지표 조회 API
+ *
+ * 세션 체크를 우회하기 위해 InterceptorSkipController 인터페이스 구현
+ */
+@Controller
+public class ObpGwMetricController implements InterceptorSkipController {
+
+ private static final Logger log = LoggerFactory.getLogger(ObpGwMetricController.class);
+
+ private static final DateTimeFormatter PATH_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd_HH:mm:ss");
+
+ private final ObpGwMetricManService obpGwMetricManService;
+
+ public ObpGwMetricController(ObpGwMetricManService obpGwMetricManService) {
+ this.obpGwMetricManService = obpGwMetricManService;
+ }
+
+ /**
+ * 특정 시간대의 GwMetric 데이터 조회
+ *
+ * @param datetime 조회 시간대 (형식: yyyy-MM-dd_HH:mm:ss)
+ * @return JSON Array
+ */
+ @RequestMapping(
+ value = "/kjb/gw-metrics/{datetime}",
+ produces = "application/json;charset=utf-8"
+ )
+ @ResponseBody
+ public String getGwMetrics(@PathVariable("datetime") String datetime) {
+ log.debug("GwMetric 조회 요청: datetime={}", datetime);
+ long startTime = System.currentTimeMillis();
+
+ try {
+ // Path 파라미터 파싱
+ LocalDateTime targetTime = LocalDateTime.parse(datetime, PATH_FORMATTER);
+ LocalDateTime timeslice = targetTime.withMinute(0).withSecond(0).withNano(0);
+
+ // 조회
+ List metrics = obpGwMetricManService.findByTimeslice(timeslice);
+
+ // JSON 변환 (GSON)
+ Gson gson = new GsonBuilder()
+ .serializeNulls()
+ .create();
+
+ String json = gson.toJson(metrics);
+
+ // 응답 크기 계산 (UTF-8 기준)
+ int jsonBytes = json.getBytes(StandardCharsets.UTF_8).length;
+ String jsonSize = formatByteSize(jsonBytes);
+
+ long elapsed = System.currentTimeMillis() - startTime;
+ log.info("GwMetric 조회 완료: datetime={}, count={}, size={}, elapsed={}ms",
+ datetime, metrics.size(), jsonSize, elapsed);
+
+ return json;
+
+ } catch (Exception e) {
+ log.error("GwMetric 조회 실패: datetime={}", datetime, e);
+ throw e;
+ }
+ }
+
+ /**
+ * 바이트 크기를 읽기 쉬운 형식으로 변환
+ * 예: 1024 -> "1.0 KB", 1048576 -> "1.0 MB"
+ */
+ private String formatByteSize(long bytes) {
+ if (bytes < 1024) {
+ return bytes + " B";
+ } else if (bytes < 1024 * 1024) {
+ return String.format("%.1f KB", bytes / 1024.0);
+ } else {
+ return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
+ }
+ }
+}
diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricDTO.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricDTO.java
new file mode 100644
index 0000000..02e8785
--- /dev/null
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricDTO.java
@@ -0,0 +1,98 @@
+package com.eactive.eai.rms.onl.apim.obp;
+
+import com.eactive.apim.portal.obp.entity.ObpGwMetric;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * ObpGwMetric JSON 응답 DTO
+ * Entity 주석에 명시된 JSON Key 사용
+ */
+@Data
+public class ObpGwMetricDTO {
+
+ private static final DateTimeFormatter ISO8601_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'+0000'");
+
+ @SerializedName("appKey")
+ private String clientId;
+
+ @SerializedName("clusterHostname")
+ private String hostname;
+
+ @SerializedName("timeslice")
+ private String timeslice;
+
+ @SerializedName("apiName")
+ private String apiName;
+
+ @SerializedName("apiRoutingUri")
+ private String uri;
+
+ @SerializedName("attemptedCount")
+ private Long attemptedCount;
+
+ @SerializedName("completedCount")
+ private Long completedCount;
+
+ // EAPIM ID
+ @SerializedName("apiIdByEapim")
+ private String apiId;
+
+ @SerializedName("appIdByEapim")
+ private String appId;
+
+ @SerializedName("orgIdByEapim")
+ private String orgId;
+
+ // AS-IS 호환용 필드
+ @SerializedName("appId")
+ private String appIdByCaPortal;
+
+ @SerializedName("portalOrgId")
+ private String orgIdByCaPortal;
+
+ @SerializedName("portalApiId")
+ private String apiIdByCaPortal;
+
+ @SerializedName("partnerCode")
+ private String partnerCode;
+
+ @SerializedName("apiId")
+ private String apiIdByCa;
+
+ /**
+ * Entity → DTO 변환
+ */
+ public static ObpGwMetricDTO from(ObpGwMetric entity) {
+ ObpGwMetricDTO dto = new ObpGwMetricDTO();
+ dto.setClientId(entity.getClientId());
+ dto.setHostname(entity.getHostname());
+ dto.setTimeslice(formatTimeslice(entity.getTimeslice()));
+ dto.setApiName(entity.getApiName());
+ dto.setUri(entity.getUri());
+ dto.setAttemptedCount(entity.getAttemptedCount());
+ dto.setCompletedCount(entity.getCompletedCount());
+ dto.setApiId(entity.getApiId());
+ dto.setAppId(entity.getAppId());
+ dto.setOrgId(entity.getOrgId());
+ dto.setAppIdByCaPortal(entity.getAppIdByCaPortal());
+ dto.setOrgIdByCaPortal(entity.getOrgIdByCaPortal());
+ dto.setApiIdByCaPortal(entity.getApiIdByCaPortal());
+ dto.setPartnerCode(entity.getPartnerCode());
+ dto.setApiIdByCa(entity.getApiIdByCa());
+ return dto;
+ }
+
+ /**
+ * timeslice를 ISO8601 형식 + UTC 시간대로 변환
+ * 예: 2025-12-02T01:00:00+0000
+ */
+ private static String formatTimeslice(LocalDateTime timeslice) {
+ if (timeslice == null) return null;
+ return timeslice.format(ISO8601_FORMATTER);
+ }
+}
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
new file mode 100644
index 0000000..8543dec
--- /dev/null
+++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java
@@ -0,0 +1,257 @@
+package com.eactive.eai.rms.onl.apim.obp;
+
+import com.eactive.apim.portal.obp.entity.ObpGwMetric;
+import com.eactive.eai.rms.common.base.BaseService;
+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.apim.obp.ObpGwMetricDAO;
+import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricService;
+import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricVO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * ObpGwMetric Man Service - 비즈니스 로직
+ * API Gateway 로그 데이터를 집계하여 ObpGwMetric 엔티티로 저장
+ */
+@Service
+@Transactional(transactionManager = "transactionManagerForEMS")
+public class ObpGwMetricManService extends BaseService {
+
+ private static final Logger log = LoggerFactory.getLogger(ObpGwMetricManService.class);
+
+ /**
+ * 집계 범위 시간 (단위: 시간)
+ * - 1: 직전 1시간만 집계
+ * - 5: 직전 5시간까지 각각 1시간 단위로 집계
+ */
+ private static final int AGGREGATION_HOUR_RANGE = 1;
+
+ private static final DateTimeFormatter TIMESLICE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHH0000000");
+ private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
+ private static final DateTimeFormatter YYYYMMDD_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
+
+ private final ObpGwMetricDAO obpGwMetricDAO;
+ private final ObpGwMetricService obpGwMetricService;
+
+ public ObpGwMetricManService(ObpGwMetricDAO obpGwMetricDAO, ObpGwMetricService obpGwMetricService) {
+ this.obpGwMetricDAO = obpGwMetricDAO;
+ this.obpGwMetricService = obpGwMetricService;
+ }
+
+ /**
+ * 다중 시간대 집계 실행 (Job에서 호출)
+ */
+ public void executeHourlyAggregation() {
+ LocalDateTime now = LocalDateTime.now();
+ LocalDateTime baseHour = now.truncatedTo(ChronoUnit.HOURS); // 현재 시간의 정각
+
+ log.info("========== ObpGwMetric 집계 시작 ==========");
+ log.info("실행 시간: {}, AGGREGATION_HOUR_RANGE: {}", now, AGGREGATION_HOUR_RANGE);
+
+ for (int i = 1; i <= AGGREGATION_HOUR_RANGE; i++) {
+ LocalDateTime targetHour = baseHour.minusHours(i);
+ log.info("집계 대상 시간대: {}", targetHour);
+ createHourlyData(targetHour);
+ }
+
+ log.info("========== ObpGwMetric 집계 완료 ==========");
+ }
+
+ /**
+ * 1시간 단위 ObpGwMetric 데이터 생성 (UPSERT)
+ */
+ public void createHourlyData(LocalDateTime targetHour) {
+ long jobStartTime = System.currentTimeMillis();
+ log.info("ObpGwMetric 집계 시작: targetHour={}", targetHour);
+
+ try {
+ String timeslice = targetHour.format(TIMESLICE_FORMATTER);
+ String yyyymmdd = targetHour.format(YYYYMMDD_FORMATTER);
+ String tableName = CommonUtil.getLogTable(yyyymmdd, true);
+ String startTime = targetHour.format(TIMESTAMP_FORMATTER);
+ String endTime = targetHour.plusHours(1).minusNanos(1).format(TIMESTAMP_FORMATTER);
+
+ // ═══════════════════════════════════════════════════════════════════
+ // 1단계: GW DB에서 EAIBZWKDSTCD 코드 목록 조회
+ // ═══════════════════════════════════════════════════════════════════
+ long step1Start = System.currentTimeMillis();
+ DataSourceType apigwDataSourceType = DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW);
+ DataSourceContextHolder.setDataSourceType(apigwDataSourceType);
+
+ List bzwkCodes = obpGwMetricDAO.selectAllBzwkCodes(apigwDataSourceType.getSchema());
+ log.info("[1단계] EAIBZWKDSTCD 코드 조회 완료: count={}, elapsed={}ms",
+ bzwkCodes.size(), System.currentTimeMillis() - step1Start);
+
+ // ═══════════════════════════════════════════════════════════════════
+ // 2단계: 코드별 개별 쿼리 실행 후 결과 병합
+ // ═══════════════════════════════════════════════════════════════════
+ long step2Start = System.currentTimeMillis();
+ Map aggregatedMap = new HashMap<>();
+ int totalLogCount = 0;
+
+ for (String bzwkCode : bzwkCodes) {
+ long codeStart = System.currentTimeMillis();
+
+ HashMap paramMap = new HashMap<>();
+ paramMap.put("schemaId", apigwDataSourceType.getSchema());
+ paramMap.put("tableName", tableName);
+ paramMap.put("bzwkCode", bzwkCode);
+ paramMap.put("startTime", startTime);
+ paramMap.put("endTime", endTime);
+ paramMap.put("timeslice", timeslice);
+
+ List partialResult = obpGwMetricDAO.selectHourlyMetricsByBzwkCode(paramMap);
+
+ for (ObpGwMetricVO vo : partialResult) {
+ String key = vo.getAggregationKey();
+ aggregatedMap.merge(key, vo, this::mergeMetricVO);
+ }
+
+ if (!partialResult.isEmpty()) {
+ log.debug("[2단계] bzwkCode={}, 집계건수={}, elapsed={}ms",
+ bzwkCode, partialResult.size(), System.currentTimeMillis() - codeStart);
+ }
+ totalLogCount += partialResult.size();
+ }
+
+ List aggregatedList = new ArrayList<>(aggregatedMap.values());
+ log.info("[2단계] 로그 집계 완료: 원본건수={}, 병합후건수={}, elapsed={}ms",
+ totalLogCount, aggregatedList.size(), System.currentTimeMillis() - step2Start);
+
+ if (aggregatedList.isEmpty()) {
+ log.warn("집계 대상 데이터 없음: targetHour={}", targetHour);
+ return;
+ }
+
+ // ═══════════════════════════════════════════════════════════════════
+ // 3단계: 부가 정보 조회 (apiName, uri) - GW DB
+ // ═══════════════════════════════════════════════════════════════════
+ long step3Start = System.currentTimeMillis();
+ Set apiIds = aggregatedList.stream()
+ .map(ObpGwMetricVO::getApiId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+
+ Map apiNameMap = obpGwMetricDAO.selectApiNames(apigwDataSourceType.getSchema(), apiIds);
+ Map uriMap = obpGwMetricDAO.selectUris(apigwDataSourceType.getSchema(), apiIds);
+ log.info("[3단계] 부가정보 조회 완료: apiCount={}, elapsed={}ms",
+ apiIds.size(), System.currentTimeMillis() - step3Start);
+
+ // ═══════════════════════════════════════════════════════════════════
+ // 4단계: 지표 DB로 전환 후 appId 조회 및 UPSERT
+ // ═══════════════════════════════════════════════════════════════════
+ long step4Start = System.currentTimeMillis();
+ DataSourceContextHolder.setDataSourceType(
+ DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.MONITORING));
+
+ Set orgIds = aggregatedList.stream()
+ .map(ObpGwMetricVO::getOrgId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ Map appIdMap = obpGwMetricDAO.selectAppIdsByOrgIds(orgIds);
+
+ int insertCount = 0;
+ int updateCount = 0;
+
+ for (ObpGwMetricVO vo : aggregatedList) {
+ LocalDateTime timesliceDateTime = vo.getTimesliceAsLocalDateTime();
+
+ Optional existing = obpGwMetricService.findByUniqueKey(
+ timesliceDateTime,
+ vo.getApiId(),
+ vo.getClientId(),
+ vo.getHostname(),
+ vo.getOrgId()
+ );
+
+ if (existing.isPresent()) {
+ // UPDATE: 기존 데이터 갱신
+ ObpGwMetric entity = existing.get();
+ entity.setAttemptedCount(vo.getAttemptedCount());
+ entity.setCompletedCount(vo.getCompletedCount());
+ entity.setApiName(apiNameMap.get(vo.getApiId()));
+ entity.setUri(uriMap.get(vo.getApiId()));
+ entity.setAppId(appIdMap.get(vo.getOrgId()));
+ entity.setUpdateCount(entity.getUpdateCount() + 1); // 보정 횟수 증가
+ obpGwMetricService.save(entity);
+ updateCount++;
+ } else {
+ // INSERT: 신규 데이터 생성
+ ObpGwMetric entity = convertToEntity(vo, apiNameMap, uriMap, appIdMap);
+ obpGwMetricService.save(entity);
+ insertCount++;
+ }
+ }
+
+ log.info("[4단계] UPSERT 완료: INSERT={}, UPDATE={}, elapsed={}ms",
+ insertCount, updateCount, System.currentTimeMillis() - step4Start);
+
+ long totalElapsed = System.currentTimeMillis() - jobStartTime;
+ log.info("ObpGwMetric 집계 완료: targetHour={}, 총 소요시간={}ms ({}초)",
+ targetHour, totalElapsed, totalElapsed / 1000.0);
+
+ } catch (Exception e) {
+ log.error("ObpGwMetric 집계 실패: targetHour={}", targetHour, e);
+ throw e;
+ }
+ }
+
+ /**
+ * ObpGwMetricVO 병합 (count 누적)
+ */
+ private ObpGwMetricVO mergeMetricVO(ObpGwMetricVO existing, ObpGwMetricVO incoming) {
+ existing.setAttemptedCount(
+ (existing.getAttemptedCount() != null ? existing.getAttemptedCount() : 0L) +
+ (incoming.getAttemptedCount() != null ? incoming.getAttemptedCount() : 0L)
+ );
+ existing.setCompletedCount(
+ (existing.getCompletedCount() != null ? existing.getCompletedCount() : 0L) +
+ (incoming.getCompletedCount() != null ? incoming.getCompletedCount() : 0L)
+ );
+ return existing;
+ }
+
+ /**
+ * VO → Entity 변환
+ */
+ private ObpGwMetric convertToEntity(ObpGwMetricVO vo, Map apiNameMap,
+ Map uriMap, Map appIdMap) {
+ ObpGwMetric entity = new ObpGwMetric();
+ entity.setClientId(vo.getClientId());
+ entity.setHostname(vo.getHostname());
+ entity.setTimeslice(vo.getTimesliceAsLocalDateTime());
+ entity.setApiId(vo.getApiId());
+ entity.setOrgId(vo.getOrgId());
+ entity.setAttemptedCount(vo.getAttemptedCount());
+ entity.setCompletedCount(vo.getCompletedCount());
+ entity.setApiName(apiNameMap.get(vo.getApiId()));
+ entity.setUri(uriMap.get(vo.getApiId()));
+ entity.setAppId(appIdMap.get(vo.getOrgId()));
+ entity.setUpdateCount(0);
+ return entity;
+ }
+
+ /**
+ * 특정 시간대의 GwMetric 데이터 조회 (REST API용)
+ */
+ public List findByTimeslice(LocalDateTime timeslice) {
+ DataSourceContextHolder.setDataSourceType(
+ DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.MONITORING));
+
+ List entities = obpGwMetricService.findByTimeslice(timeslice);
+ return entities.stream()
+ .map(ObpGwMetricDTO::from)
+ .collect(Collectors.toList());
+ }
+}
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
new file mode 100644
index 0000000..35befd3
--- /dev/null
+++ b/src/main/java/com/eactive/eai/rms/onl/common/service/ObpGwMetricHourJob.java
@@ -0,0 +1,56 @@
+package com.eactive.eai.rms.onl.common.service;
+
+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.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.SchedulerException;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * ObpGwMetric Hour Job - Quartz Job
+ * API Gateway 로그 데이터를 1시간 단위로 집계하여 ObpGwMetric 테이블에 저장
+ *
+ * Cron Schedule 권장:
+ *
+ * - 기본: 0 5 * * * ? (매시 05분 1회 실행)
+ * - 보정 (15분): 0 5,20,35,50 * * * ? (15분 간격 4회 실행)
+ * - 보정 (10분): 0 5,15,25,35,45,55 * * * ? (10분 간격 6회 실행)
+ *
+ */
+public class ObpGwMetricHourJob implements Job {
+
+ private static final Logger log = LoggerFactory.getLogger(ObpGwMetricHourJob.class);
+
+ 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;
+ try {
+ appContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
+ } catch (SchedulerException e) {
+ log.error("applicationContext get module error", e);
+ return;
+ }
+
+ monitoringContext = (MonitoringContext) appContext.getBean("monitoringContext");
+ ObpGwMetricManService obpGwMetricManService = appContext.getBean(ObpGwMetricManService.class);
+
+ try {
+ obpGwMetricManService.executeHourlyAggregation();
+ } catch (Exception e) {
+ log.error("ObpGwMetricHourJob execution failed", e);
+ throw new JobExecutionException(e);
+ }
+
+ log.info("*** END ObpGwMetricHourJob run({})", DateUtil.getDateTime("yyyy-MM-dd HH:mm"));
+ }
+}
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
new file mode 100644
index 0000000..caeb659
--- /dev/null
+++ b/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+ SELECT EAIBZWKDSTCD
+ FROM $schemaId$.TSEAICM01
+ ORDER BY EAIBZWKDSTCD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SELECT
+ EAISVCNAME AS API_ID,
+ CLIENTID AS CLIENT_ID,
+ EAISEVRINSTNCNAME AS HOSTNAME,
+ ORGID AS ORG_ID,
+ '$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%'))
+ THEN 1 ELSE 0 END) AS COMPLETED_COUNT
+ FROM $schemaId$.$tableName$
+ WHERE EAIBZWKDSTCD = #bzwkCode#
+ AND MSGDPSTYMS BETWEEN #startTime# AND #endTime#
+ GROUP BY EAISVCNAME, CLIENTID, EAISEVRINSTNCNAME, ORGID
+ HAVING SUM(CASE WHEN LOGPRCSSSERNO = '100' THEN 1 ELSE 0 END) > 0
+ OR SUM(CASE WHEN LOGPRCSSSERNO = '400' THEN 1 ELSE 0 END) > 0
+
+
+
+
+ SELECT
+ EAISVCNAME AS API_ID,
+ EAISVCDESC AS API_NAME
+ FROM $schemaId$.TSEAIHE01
+ WHERE EAISVCNAME IN
+
+ #apiIds[]#
+
+
+
+
+
+ SELECT
+ EAISVCNAME AS API_ID,
+ BZWKSVCKEYNAME AS URI
+ FROM $schemaId$.TSEAIHS04
+ WHERE EAISVCNAME IN
+
+ #apiIds[]#
+
+
+
+
+
+ SELECT
+ ORG_ID,
+ ID AS APP_ID
+ FROM PTL_APP_REQUEST
+ WHERE ORG_ID IN
+
+ #orgIds[]#
+
+
+
+