From 678bfffeeff703c67e6ba2371538d9dcaa6fb3fa Mon Sep 17 00:00:00 2001 From: cho Date: Mon, 19 Jan 2026 18:14:40 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=ED=97=A5=EC=82=AC=EA=B0=92=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=93=A4=EC=96=B4=EC=98=A4=EB=8A=94=20=EC=A0=84?= =?UTF-8?q?=EB=AC=B8=20=EA=B9=A8=EC=A7=80=EB=8A=94=20bugfix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tracking/DbTrackingService.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/onl/transaction/tracking/DbTrackingService.java b/src/main/java/com/eactive/eai/rms/onl/transaction/tracking/DbTrackingService.java index ad9aaa0..b960565 100644 --- a/src/main/java/com/eactive/eai/rms/onl/transaction/tracking/DbTrackingService.java +++ b/src/main/java/com/eactive/eai/rms/onl/transaction/tracking/DbTrackingService.java @@ -219,25 +219,9 @@ public class DbTrackingService extends BaseService { if(data == null) { data = extractDataBetweenPipes2(bizData); } + - // if (split.length > 2) { - // mdclData = split[2]; - // } - - //if (split.length > 3) { - // header = split[3]; - //} - // 종료 - - // 일단 암호화된 것으로 간주하고 처리한다. if (!StringUtils.isBlank(data)) { - - try { - data = new String(HexaConverter.hexaToBytes(data), "EUC-KR"); - } catch (Exception e) { - logger.info("ScpDbAgent decrypt error ", e); - } - eaiLogUI.setBzwkdatactnt(data); } else { eaiLogUI.setBzwkdatactnt(""); From 287d0ac59bdf78dbe27a0b3ed58ec30c658c1b3a Mon Sep 17 00:00:00 2001 From: cho Date: Tue, 20 Jan 2026 09:50:41 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EC=9D=8C..=EC=9E=A0=EC=8B=9C=20=ED=86=B5?= =?UTF-8?q?=ED=85=8C=EC=9A=A9=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onl/transaction/tracking/trackingMan.jsp | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/WebContent/jsp/onl/transaction/tracking/trackingMan.jsp b/WebContent/jsp/onl/transaction/tracking/trackingMan.jsp index 3af9439..c9bccd1 100644 --- a/WebContent/jsp/onl/transaction/tracking/trackingMan.jsp +++ b/WebContent/jsp/onl/transaction/tracking/trackingMan.jsp @@ -567,8 +567,40 @@ $(window).on('resize.jqGrid', function () { jQuery("#grid").jqGrid('setGridWidth', $("#content_middle").width()); }); + + + const checkbox = document.getElementById('schedulerToggle'); + const targetButton = document.getElementById('btn_search'); + let timerId = null; // 인터벌 ID를 저장할 변수 + + // 체크박스 상태 변화 감지 + checkbox.addEventListener('change', function() { + if (this.checked) { + console.log("스케줄러 시작: 1분 간격으로 클릭합니다."); + + // 1. 즉시 한 번 실행하고 싶다면 아래 주석 해제 + // targetButton.click(); + + // 2. 1분(60,000ms)마다 반복 실행 + timerId = setInterval(() => { + console.log("자동 클릭 실행 중..."); + targetButton.click(); + }, 60000); + + } else { + console.log("스케줄러 중지"); + + // 체크 해제 시 반복 멈춤 + if (timerId) { + clearInterval(timerId); + timerId = null; + } + } + }); }); + + @@ -581,6 +613,7 @@
+ From e63b6aa8289d3c2b78db04686b7f75fd96860e2b Mon Sep 17 00:00:00 2001 From: daekuk Date: Thu, 15 Jan 2026 10:24:13 +0900 Subject: [PATCH 3/4] =?UTF-8?q?jpa=20id=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rms/data/entity/man/user/UserAthrRoleChgHistory.java | 6 +++--- .../entity/man/user/UserAthrRoleChgHistoryRepository.java | 2 +- .../data/entity/man/user/UserAthrRoleChgHistoryService.java | 2 +- .../data/entity/man/user/UserLoginHistoryRepository.java | 2 +- .../rms/data/entity/man/user/UserLoginHistoryService.java | 2 +- .../eai/rms/data/entity/onl/statistics/DailyStatistics.java | 4 ++-- .../entity/onl/statistics/HourlyStatisticsRepository.java | 2 +- .../data/entity/onl/statistics/HourlyStatisticsService.java | 2 +- .../rms/data/entity/onl/statistics/YearlyStatistics.java | 4 ++-- .../entity/onl/statistics/YearlyStatisticsRepository.java | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistory.java b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistory.java index 910801e..413da5b 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistory.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistory.java @@ -27,7 +27,7 @@ import lombok.NonNull; @Entity @Table(name = "TB_APIMS_USER_ATHR_ROLE_CHG_H") @org.hibernate.annotations.Table(appliesTo = "TB_APIMS_USER_ATHR_ROLE_CHG_H", comment = "사용자 권한 ROLE 변경 이력") -public class UserAthrRoleChgHistory extends AbstractEntity implements Serializable { +public class UserAthrRoleChgHistory extends AbstractEntity implements Serializable { private static final long serialVersionUID = 1L; @Id @@ -76,8 +76,8 @@ public class UserAthrRoleChgHistory extends AbstractEntity implements Se private LocalDateTime crtnDttm; @Override - public @NonNull String getId() { - return Integer.toString(chgLogId); + public @NonNull Integer getId() { + return chgLogId; } } diff --git a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistoryRepository.java b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistoryRepository.java index 621c7b8..6a16f77 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistoryRepository.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistoryRepository.java @@ -4,6 +4,6 @@ import com.eactive.eai.rms.data.EMSDataSource; import com.eactive.eai.data.jpa.BaseRepository; @EMSDataSource -public interface UserAthrRoleChgHistoryRepository extends BaseRepository { +public interface UserAthrRoleChgHistoryRepository extends BaseRepository { } diff --git a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistoryService.java b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistoryService.java index b04c711..d929db6 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistoryService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserAthrRoleChgHistoryService.java @@ -11,7 +11,7 @@ import com.querydsl.core.types.dsl.BooleanExpression; @Service @Transactional(transactionManager = "transactionManagerForEMS") public class UserAthrRoleChgHistoryService - extends AbstractEMSDataSerivce { + extends AbstractEMSDataSerivce { public Page findAll(Pageable pageable, String userId) { diff --git a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserLoginHistoryRepository.java b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserLoginHistoryRepository.java index 508047c..2120bd7 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserLoginHistoryRepository.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserLoginHistoryRepository.java @@ -4,6 +4,6 @@ import com.eactive.eai.data.jpa.BaseRepository; import com.eactive.eai.rms.data.EMSDataSource; @EMSDataSource -public interface UserLoginHistoryRepository extends BaseRepository { +public interface UserLoginHistoryRepository extends BaseRepository { } diff --git a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserLoginHistoryService.java b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserLoginHistoryService.java index fc555e0..b9c9c93 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserLoginHistoryService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/man/user/UserLoginHistoryService.java @@ -18,7 +18,7 @@ import com.eactive.apim.portal.user.entity.QUserInfo; @Service @Transactional(transactionManager = "transactionManagerForEMS") -public class UserLoginHistoryService extends AbstractEMSDataSerivce { +public class UserLoginHistoryService extends AbstractEMSDataSerivce { final String START_HHmmss = " 00:00:00"; final String END_HHmmss = " 23:59:59"; diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/DailyStatistics.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/DailyStatistics.java index f28ef1f..a714000 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/DailyStatistics.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/DailyStatistics.java @@ -23,13 +23,13 @@ import lombok.EqualsAndHashCode; @Entity @Table(name = "tseaist02") @org.hibernate.annotations.Table(appliesTo = "tseaist02", comment = "시간대별통계") -public class DailyStatistics extends AbstractEntity +public class DailyStatistics extends AbstractEntity implements Serializable { private static final long serialVersionUID = 1L; @EmbeddedId - private HourlyStatisticsId id; + private DailyStatisticsId id; @Column(precision = 10) @Comment("0시 오류건수") diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/HourlyStatisticsRepository.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/HourlyStatisticsRepository.java index 344fcb2..ba5ad72 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/HourlyStatisticsRepository.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/HourlyStatisticsRepository.java @@ -2,6 +2,6 @@ package com.eactive.eai.rms.data.entity.onl.statistics; import com.eactive.eai.data.jpa.BaseRepository; -interface HourlyStatisticsRepository extends BaseRepository { +interface HourlyStatisticsRepository extends BaseRepository { } diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/HourlyStatisticsService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/HourlyStatisticsService.java index 1d716b1..bfeb844 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/HourlyStatisticsService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/HourlyStatisticsService.java @@ -8,6 +8,6 @@ import com.eactive.eai.data.jpa.AbstractDataService; @Service @Transactional public class HourlyStatisticsService - extends AbstractDataService { + extends AbstractDataService { } diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/YearlyStatistics.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/YearlyStatistics.java index b1888c5..a9564a8 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/YearlyStatistics.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/YearlyStatistics.java @@ -23,13 +23,13 @@ import lombok.EqualsAndHashCode; @Entity @Table(name = "tseaist04") @org.hibernate.annotations.Table(appliesTo = "tseaist04", comment = "년별 통계") -public class YearlyStatistics extends AbstractEntity +public class YearlyStatistics extends AbstractEntity implements Serializable { private static final long serialVersionUID = 1L; @EmbeddedId - private MonthlyStatisticsId id; + private YearlyStatisticsId id; @Column(precision = 10) @Comment("10월 오류건수") diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/YearlyStatisticsRepository.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/YearlyStatisticsRepository.java index 5219eb4..1797fb9 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/YearlyStatisticsRepository.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/statistics/YearlyStatisticsRepository.java @@ -2,6 +2,6 @@ package com.eactive.eai.rms.data.entity.onl.statistics; import com.eactive.eai.data.jpa.BaseRepository; -interface YearlyStatisticsRepository extends BaseRepository{ +interface YearlyStatisticsRepository extends BaseRepository { } From 94f7bf851010c112caed3cb29b3d74a53e0a5eb3 Mon Sep 17 00:00:00 2001 From: daekuk1 Date: Tue, 20 Jan 2026 11:04:28 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=ED=86=B5=EA=B3=84=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebContent/WEB-INF/applicationContext-jpa.xml | 2 + WebContent/WEB-INF/springapp-servlet.xml | 2 + .../onl/kjb/statistics/apiStatsHourMan.jsp | 356 +++++++++++++++ .../onl/kjb/statistics/apiStatsMinuteMan.jsp | 421 ++++++++++++++++++ .../onl/kjb/statistics/ApiStatsHour.java | 93 ++++ .../onl/kjb/statistics/ApiStatsHourId.java | 26 ++ .../statistics/ApiStatsHourRepository.java | 7 + .../kjb/statistics/ApiStatsHourService.java | 133 ++++++ .../onl/kjb/statistics/ApiStatsMinute.java | 93 ++++ .../onl/kjb/statistics/ApiStatsMinuteId.java | 26 ++ .../statistics/ApiStatsMinuteRepository.java | 7 + .../kjb/statistics/ApiStatsMinuteService.java | 133 ++++++ .../onl/kjb/statistics/package-info.java | 1 + .../statistics/ApiStatsHourController.java | 56 +++ .../statistics/ApiStatsMinuteController.java | 56 +++ .../com/eactive/ext/kjb/statistics/README.md | 102 +++++ .../statistics/mapping/ApiStatsUIMapper.java | 42 ++ .../ext/kjb/statistics/package-info.java | 1 + ...통계테이블_설계.md => statistics_table.md} | 0 .../kjb/statistics/ui/ApiStatsChartUI.java | 44 ++ .../ext/kjb/statistics/ui/ApiStatsSearch.java | 25 ++ .../ext/kjb/statistics/ui/ApiStatsUI.java | 36 ++ 22 files changed, 1662 insertions(+) create mode 100644 WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp create mode 100644 WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHour.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourId.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourRepository.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourService.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinute.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteId.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteRepository.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteService.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/package-info.java create mode 100644 src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java create mode 100644 src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java create mode 100644 src/main/java/com/eactive/ext/kjb/statistics/README.md create mode 100644 src/main/java/com/eactive/ext/kjb/statistics/mapping/ApiStatsUIMapper.java create mode 100644 src/main/java/com/eactive/ext/kjb/statistics/package-info.java rename src/main/java/com/eactive/ext/kjb/statistics/{통계테이블_설계.md => statistics_table.md} (100%) create mode 100644 src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsChartUI.java create mode 100644 src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsSearch.java create mode 100644 src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java diff --git a/WebContent/WEB-INF/applicationContext-jpa.xml b/WebContent/WEB-INF/applicationContext-jpa.xml index 53b9b53..b1da502 100644 --- a/WebContent/WEB-INF/applicationContext-jpa.xml +++ b/WebContent/WEB-INF/applicationContext-jpa.xml @@ -68,6 +68,8 @@ + + diff --git a/WebContent/WEB-INF/springapp-servlet.xml b/WebContent/WEB-INF/springapp-servlet.xml index 3db1b50..3bef9a1 100644 --- a/WebContent/WEB-INF/springapp-servlet.xml +++ b/WebContent/WEB-INF/springapp-servlet.xml @@ -150,4 +150,6 @@ + + diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp new file mode 100644 index 0000000..0169457 --- /dev/null +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp @@ -0,0 +1,356 @@ +<%@ page language="java" contentType="text/html; charset=utf-8"%> +<%@ page import="java.io.*"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ include file="/jsp/common/include/localemessage.jsp" %> +<% + response.setHeader("Pragma", "No-cache"); + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Expires", "0"); + request.setCharacterEncoding("UTF-8"); +%> + + + API 시간별 통계 + + + + + + + + +
+
+ +
+
+
+ +
+
API 시간별 통계
+ + + + + + + + + + + + + + + + + + + + + + + + + +
조회일자 + + (선택일 00시 ~ 23시 조회) +
API명 + + 인스턴스 + +
업무구분 + + 클라이언트ID + +
Inbound Adapter + + Outbound Adapter + +
+
+
+
+
+
+
+
+
+ + diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp new file mode 100644 index 0000000..7013212 --- /dev/null +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp @@ -0,0 +1,421 @@ +<%@ page language="java" contentType="text/html; charset=utf-8"%> +<%@ page import="java.io.*"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ include file="/jsp/common/include/localemessage.jsp" %> +<% + response.setHeader("Pragma", "No-cache"); + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Expires", "0"); + request.setCharacterEncoding("UTF-8"); +%> + + + API 분단위 통계 + + + + + + + + +
+
+ +
+
+
+ +
+
API 분단위 통계
+ + + + + + + + + + + + + + + + + + + + + + + + + +
조회기간 + + + ~ + + + (최대 1시간, 초과시 자동 조정) +
API명 + + 인스턴스 + +
업무구분 + + 클라이언트ID + +
Inbound Adapter + + Outbound Adapter + +
+
+
+
+
+
+
+
+
+ + diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHour.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHour.java new file mode 100644 index 0000000..e12d5af --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHour.java @@ -0,0 +1,93 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; + +import lombok.Getter; +import lombok.Setter; + +/** + * API Gateway 시간별 처리 통계 + */ +@Getter +@Setter +@Entity +@Table(name = "API_STATS_HOUR") +@IdClass(ApiStatsHourId.class) +public class ApiStatsHour implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @Column(name = "STAT_TIME", nullable = false) + private LocalDateTime statTime; + + @Id + @Column(name = "API_NAME", nullable = false, length = 100) + private String apiName; + + @Id + @Column(name = "GW_INSTANCE_ID", nullable = false, length = 50) + private String gwInstanceId; + + @Id + @Column(name = "BIZ_DIV_CODE", nullable = false, length = 50) + private String bizDivCode = "NONE"; + + @Id + @Column(name = "CLIENT_ID", nullable = false, length = 100) + private String clientId = "NONE"; + + @Id + @Column(name = "INBOUND_ADAPTER", nullable = false, length = 100) + private String inboundAdapter = "NONE"; + + @Id + @Column(name = "OUTBOUND_ADAPTER", nullable = false, length = 100) + private String outboundAdapter = "NONE"; + + @Column(name = "TOTAL_CNT", nullable = false) + private Long totalCnt = 0L; + + @Column(name = "SUCCESS_CNT", nullable = false) + private Long successCnt = 0L; + + @Column(name = "TIMEOUT_CNT", nullable = false) + private Long timeoutCnt = 0L; + + @Column(name = "SYSTEM_ERR_CNT", nullable = false) + private Long systemErrCnt = 0L; + + @Column(name = "BIZ_ERR_CNT", nullable = false) + private Long bizErrCnt = 0L; + + @Column(name = "SEQ900_TIMEOUT_CNT", nullable = false) + private Long seq900TimeoutCnt = 0L; + + @Column(name = "SEQ900_SYSTEM_ERR_CNT", nullable = false) + private Long seq900SystemErrCnt = 0L; + + @Column(name = "SEQ900_BIZ_ERR_CNT", nullable = false) + private Long seq900BizErrCnt = 0L; + + @Column(name = "AVG_RESP_TIME", precision = 10, scale = 3) + private BigDecimal avgRespTime = BigDecimal.ZERO; + + @Column(name = "MIN_RESP_TIME", precision = 10, scale = 3) + private BigDecimal minRespTime = BigDecimal.ZERO; + + @Column(name = "MAX_RESP_TIME", precision = 10, scale = 3) + private BigDecimal maxRespTime = BigDecimal.ZERO; + + @Column(name = "P50_RESP_TIME", precision = 10, scale = 3) + private BigDecimal p50RespTime = BigDecimal.ZERO; + + @Column(name = "P95_RESP_TIME", precision = 10, scale = 3) + private BigDecimal p95RespTime = BigDecimal.ZERO; +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourId.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourId.java new file mode 100644 index 0000000..d6c5ded --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourId.java @@ -0,0 +1,26 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * API 통계 시간별 복합키 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ApiStatsHourId implements Serializable { + private static final long serialVersionUID = 1L; + + private LocalDateTime statTime; + private String apiName; + private String gwInstanceId; + private String bizDivCode; + private String clientId; + private String inboundAdapter; + private String outboundAdapter; +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourRepository.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourRepository.java new file mode 100644 index 0000000..f745db2 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourRepository.java @@ -0,0 +1,7 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; + +import com.eactive.eai.data.jpa.BaseRepository; + +interface ApiStatsHourRepository extends BaseRepository { + +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourService.java new file mode 100644 index 0000000..714e783 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourService.java @@ -0,0 +1,133 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.eactive.eai.data.jpa.AbstractDataService; +import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; +import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.types.Projections; + +@Service +@Transactional +public class ApiStatsHourService + extends AbstractDataService { + + private static final int MAX_HOURS = 24; + + public Page selectList(ApiStatsSearch search, Pageable pageable) { + QApiStatsHour q = QApiStatsHour.apiStatsHour; + BooleanBuilder builder = new BooleanBuilder(); + + calculateDateRange(search); + if (search.getParsedStartDateTime() != null) { + builder.and(q.statTime.goe(search.getParsedStartDateTime())); + builder.and(q.statTime.loe(search.getParsedEndDateTime())); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + builder.and(q.apiName.containsIgnoreCase(search.getSearchApiName())); + } + + if (StringUtils.isNotBlank(search.getSearchGwInstanceId())) { + builder.and(q.gwInstanceId.containsIgnoreCase(search.getSearchGwInstanceId())); + } + + if (StringUtils.isNotBlank(search.getSearchBizDivCode())) { + builder.and(q.bizDivCode.containsIgnoreCase(search.getSearchBizDivCode())); + } + + if (StringUtils.isNotBlank(search.getSearchClientId())) { + builder.and(q.clientId.containsIgnoreCase(search.getSearchClientId())); + } + + if (StringUtils.isNotBlank(search.getSearchInboundAdapter())) { + builder.and(q.inboundAdapter.containsIgnoreCase(search.getSearchInboundAdapter())); + } + + if (StringUtils.isNotBlank(search.getSearchOutboundAdapter())) { + builder.and(q.outboundAdapter.containsIgnoreCase(search.getSearchOutboundAdapter())); + } + + return repository.findAll(builder, pageable); + } + + public List selectChartData(ApiStatsSearch search) { + QApiStatsHour q = QApiStatsHour.apiStatsHour; + BooleanBuilder builder = new BooleanBuilder(); + + calculateDateRange(search); + if (search.getParsedStartDateTime() != null) { + builder.and(q.statTime.goe(search.getParsedStartDateTime())); + builder.and(q.statTime.loe(search.getParsedEndDateTime())); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + builder.and(q.apiName.containsIgnoreCase(search.getSearchApiName())); + } + + if (StringUtils.isNotBlank(search.getSearchGwInstanceId())) { + builder.and(q.gwInstanceId.containsIgnoreCase(search.getSearchGwInstanceId())); + } + + if (StringUtils.isNotBlank(search.getSearchBizDivCode())) { + builder.and(q.bizDivCode.containsIgnoreCase(search.getSearchBizDivCode())); + } + + if (StringUtils.isNotBlank(search.getSearchClientId())) { + builder.and(q.clientId.containsIgnoreCase(search.getSearchClientId())); + } + + return getJPAQueryFactory() + .select(Projections.constructor(ApiStatsChartUI.class, + q.statTime, + q.totalCnt.sum(), + q.successCnt.sum(), + q.timeoutCnt.sum(), + q.systemErrCnt.sum(), + q.bizErrCnt.sum(), + q.avgRespTime.avg(), + q.p50RespTime.avg(), + q.p95RespTime.avg())) + .from(q) + .where(builder) + .groupBy(q.statTime) + .orderBy(q.statTime.asc()) + .fetch(); + } + + /** + * 조회 기간 계산 (최대 24시간 제한) + */ + private void calculateDateRange(ApiStatsSearch search) { + if (StringUtils.isBlank(search.getSearchStartDateTime())) { + return; + } + + LocalDateTime startDateTime = LocalDateTime.parse(search.getSearchStartDateTime() + "00", + DateTimeFormatter.ofPattern("yyyyMMddHHmm")); + LocalDateTime endDateTime; + + if (StringUtils.isNotBlank(search.getSearchEndDateTime())) { + endDateTime = LocalDateTime.parse(search.getSearchEndDateTime() + "00", + DateTimeFormatter.ofPattern("yyyyMMddHHmm")); + // 24시간 초과 시 시작시간 기준 24시간 후로 강제 설정 + if (java.time.Duration.between(startDateTime, endDateTime).toHours() > MAX_HOURS) { + endDateTime = startDateTime.plusHours(MAX_HOURS); + } + } else { + endDateTime = startDateTime.plusHours(MAX_HOURS); + } + + search.setParsedStartDateTime(startDateTime); + search.setParsedEndDateTime(endDateTime); + } +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinute.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinute.java new file mode 100644 index 0000000..78ce5e8 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinute.java @@ -0,0 +1,93 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; + +import lombok.Getter; +import lombok.Setter; + +/** + * API Gateway 분단위 처리 통계 + */ +@Getter +@Setter +@Entity +@Table(name = "API_STATS_MINUTE") +@IdClass(ApiStatsMinuteId.class) +public class ApiStatsMinute implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @Column(name = "STAT_TIME", nullable = false) + private LocalDateTime statTime; + + @Id + @Column(name = "API_NAME", nullable = false, length = 100) + private String apiName; + + @Id + @Column(name = "GW_INSTANCE_ID", nullable = false, length = 50) + private String gwInstanceId; + + @Id + @Column(name = "BIZ_DIV_CODE", nullable = false, length = 50) + private String bizDivCode = "NONE"; + + @Id + @Column(name = "CLIENT_ID", nullable = false, length = 100) + private String clientId = "NONE"; + + @Id + @Column(name = "INBOUND_ADAPTER", nullable = false, length = 100) + private String inboundAdapter = "NONE"; + + @Id + @Column(name = "OUTBOUND_ADAPTER", nullable = false, length = 100) + private String outboundAdapter = "NONE"; + + @Column(name = "TOTAL_CNT", nullable = false) + private Long totalCnt = 0L; + + @Column(name = "SUCCESS_CNT", nullable = false) + private Long successCnt = 0L; + + @Column(name = "TIMEOUT_CNT", nullable = false) + private Long timeoutCnt = 0L; + + @Column(name = "SYSTEM_ERR_CNT", nullable = false) + private Long systemErrCnt = 0L; + + @Column(name = "BIZ_ERR_CNT", nullable = false) + private Long bizErrCnt = 0L; + + @Column(name = "SEQ900_TIMEOUT_CNT", nullable = false) + private Long seq900TimeoutCnt = 0L; + + @Column(name = "SEQ900_SYSTEM_ERR_CNT", nullable = false) + private Long seq900SystemErrCnt = 0L; + + @Column(name = "SEQ900_BIZ_ERR_CNT", nullable = false) + private Long seq900BizErrCnt = 0L; + + @Column(name = "AVG_RESP_TIME", precision = 10, scale = 3) + private BigDecimal avgRespTime = BigDecimal.ZERO; + + @Column(name = "MIN_RESP_TIME", precision = 10, scale = 3) + private BigDecimal minRespTime = BigDecimal.ZERO; + + @Column(name = "MAX_RESP_TIME", precision = 10, scale = 3) + private BigDecimal maxRespTime = BigDecimal.ZERO; + + @Column(name = "P50_RESP_TIME", precision = 10, scale = 3) + private BigDecimal p50RespTime = BigDecimal.ZERO; + + @Column(name = "P95_RESP_TIME", precision = 10, scale = 3) + private BigDecimal p95RespTime = BigDecimal.ZERO; +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteId.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteId.java new file mode 100644 index 0000000..45671fe --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteId.java @@ -0,0 +1,26 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * API 통계 분단위 복합키 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ApiStatsMinuteId implements Serializable { + private static final long serialVersionUID = 1L; + + private LocalDateTime statTime; + private String apiName; + private String gwInstanceId; + private String bizDivCode; + private String clientId; + private String inboundAdapter; + private String outboundAdapter; +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteRepository.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteRepository.java new file mode 100644 index 0000000..85d9091 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteRepository.java @@ -0,0 +1,7 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; + +import com.eactive.eai.data.jpa.BaseRepository; + +interface ApiStatsMinuteRepository extends BaseRepository { + +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteService.java new file mode 100644 index 0000000..4570b34 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteService.java @@ -0,0 +1,133 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.eactive.eai.data.jpa.AbstractDataService; +import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; +import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.types.Projections; + +@Service +@Transactional +public class ApiStatsMinuteService + extends AbstractDataService { + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmm"); + + private static final int MAX_MINUTES = 60; + + public Page selectList(ApiStatsSearch search, Pageable pageable) { + QApiStatsMinute q = QApiStatsMinute.apiStatsMinute; + BooleanBuilder builder = new BooleanBuilder(); + + calculateDateRange(search); + if (search.getParsedStartDateTime() != null) { + builder.and(q.statTime.goe(search.getParsedStartDateTime())); + builder.and(q.statTime.loe(search.getParsedEndDateTime())); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + builder.and(q.apiName.containsIgnoreCase(search.getSearchApiName())); + } + + if (StringUtils.isNotBlank(search.getSearchGwInstanceId())) { + builder.and(q.gwInstanceId.containsIgnoreCase(search.getSearchGwInstanceId())); + } + + if (StringUtils.isNotBlank(search.getSearchBizDivCode())) { + builder.and(q.bizDivCode.containsIgnoreCase(search.getSearchBizDivCode())); + } + + if (StringUtils.isNotBlank(search.getSearchClientId())) { + builder.and(q.clientId.containsIgnoreCase(search.getSearchClientId())); + } + + if (StringUtils.isNotBlank(search.getSearchInboundAdapter())) { + builder.and(q.inboundAdapter.containsIgnoreCase(search.getSearchInboundAdapter())); + } + + if (StringUtils.isNotBlank(search.getSearchOutboundAdapter())) { + builder.and(q.outboundAdapter.containsIgnoreCase(search.getSearchOutboundAdapter())); + } + + return repository.findAll(builder, pageable); + } + + public List selectChartData(ApiStatsSearch search) { + QApiStatsMinute q = QApiStatsMinute.apiStatsMinute; + BooleanBuilder builder = new BooleanBuilder(); + + calculateDateRange(search); + if (search.getParsedStartDateTime() != null) { + builder.and(q.statTime.goe(search.getParsedStartDateTime())); + builder.and(q.statTime.loe(search.getParsedEndDateTime())); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + builder.and(q.apiName.containsIgnoreCase(search.getSearchApiName())); + } + + if (StringUtils.isNotBlank(search.getSearchGwInstanceId())) { + builder.and(q.gwInstanceId.containsIgnoreCase(search.getSearchGwInstanceId())); + } + + if (StringUtils.isNotBlank(search.getSearchBizDivCode())) { + builder.and(q.bizDivCode.containsIgnoreCase(search.getSearchBizDivCode())); + } + + if (StringUtils.isNotBlank(search.getSearchClientId())) { + builder.and(q.clientId.containsIgnoreCase(search.getSearchClientId())); + } + + return getJPAQueryFactory() + .select(Projections.constructor(ApiStatsChartUI.class, + q.statTime, + q.totalCnt.sum(), + q.successCnt.sum(), + q.timeoutCnt.sum(), + q.systemErrCnt.sum(), + q.bizErrCnt.sum(), + q.avgRespTime.avg(), + q.p50RespTime.avg(), + q.p95RespTime.avg())) + .from(q) + .where(builder) + .groupBy(q.statTime) + .orderBy(q.statTime.asc()) + .fetch(); + } + + /** + * 조회 기간 계산 (최대 1시간 제한) + */ + private void calculateDateRange(ApiStatsSearch search) { + if (StringUtils.isBlank(search.getSearchStartDateTime())) { + return; + } + + LocalDateTime startDateTime = LocalDateTime.parse(search.getSearchStartDateTime(), DATE_TIME_FORMATTER); + LocalDateTime endDateTime; + + if (StringUtils.isNotBlank(search.getSearchEndDateTime())) { + endDateTime = LocalDateTime.parse(search.getSearchEndDateTime(), DATE_TIME_FORMATTER); + // 1시간(60분) 초과 시 시작시간 기준 1시간 후로 강제 설정 + if (java.time.Duration.between(startDateTime, endDateTime).toMinutes() > MAX_MINUTES) { + endDateTime = startDateTime.plusMinutes(MAX_MINUTES); + } + } else { + endDateTime = startDateTime.plusMinutes(MAX_MINUTES); + } + + search.setParsedStartDateTime(startDateTime); + search.setParsedEndDateTime(endDateTime); + } +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/package-info.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/package-info.java new file mode 100644 index 0000000..6dda3ec --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/package-info.java @@ -0,0 +1 @@ +package com.eactive.eai.rms.data.entity.onl.kjb.statistics; \ No newline at end of file diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java new file mode 100644 index 0000000..1b11f19 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java @@ -0,0 +1,56 @@ +package com.eactive.ext.kjb.statistics; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; + +import com.eactive.eai.rms.common.vo.GridResponse; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsHour; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsHourService; +import com.eactive.ext.kjb.statistics.mapping.ApiStatsUIMapper; +import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; +import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Controller +@RequiredArgsConstructor +public class ApiStatsHourController { + + private final ApiStatsHourService service; + private final ApiStatsUIMapper mapper; + + @GetMapping(value = "/onl/kjb/statistics/apiStatsHourMan.view") + public String view() { + return "/onl/kjb/statistics/apiStatsHourMan"; + } + + @PostMapping(value = "/onl/kjb/statistics/apiStatsHourMan.json", params = "cmd=LIST") + public ResponseEntity> selectList(ApiStatsSearch search, Pageable pageable) { + Pageable sortedPageable = PageRequest.of( + pageable.getPageNumber(), + pageable.getPageSize(), + Sort.by(Sort.Order.desc("statTime"))); + + Page page = service.selectList(search, sortedPageable); + Page uiPage = page.map(mapper::toVo); + + return ResponseEntity.ok(new GridResponse<>(uiPage)); + } + + @PostMapping(value = "/onl/kjb/statistics/apiStatsHourMan.json", params = "cmd=CHART") + public ResponseEntity> selectChartData(ApiStatsSearch search) { + List chartData = service.selectChartData(search); + return ResponseEntity.ok(chartData); + } +} diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java new file mode 100644 index 0000000..a1b9021 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java @@ -0,0 +1,56 @@ +package com.eactive.ext.kjb.statistics; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; + +import com.eactive.eai.rms.common.vo.GridResponse; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMinute; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMinuteService; +import com.eactive.ext.kjb.statistics.mapping.ApiStatsUIMapper; +import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; +import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Controller +@RequiredArgsConstructor +public class ApiStatsMinuteController { + + private final ApiStatsMinuteService service; + private final ApiStatsUIMapper mapper; + + @GetMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.view") + public String view() { + return "/onl/kjb/statistics/apiStatsMinuteMan"; + } + + @PostMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.json", params = "cmd=LIST") + public ResponseEntity> selectList(ApiStatsSearch search, Pageable pageable) { + Pageable sortedPageable = PageRequest.of( + pageable.getPageNumber(), + pageable.getPageSize(), + Sort.by(Sort.Order.desc("statTime"))); + + Page page = service.selectList(search, sortedPageable); + Page uiPage = page.map(mapper::toVo); + + return ResponseEntity.ok(new GridResponse<>(uiPage)); + } + + @PostMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.json", params = "cmd=CHART") + public ResponseEntity> selectChartData(ApiStatsSearch search) { + List chartData = service.selectChartData(search); + return ResponseEntity.ok(chartData); + } +} diff --git a/src/main/java/com/eactive/ext/kjb/statistics/README.md b/src/main/java/com/eactive/ext/kjb/statistics/README.md new file mode 100644 index 0000000..3a93ba4 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/statistics/README.md @@ -0,0 +1,102 @@ +# API Gateway 통계 화면 + +## 개요 + +API Gateway의 분단위/시간별 처리 통계를 조회하는 관리 화면입니다. + +## 파일 구조 + +``` +com.eactive.ext.kjb.statistics/ +├── ApiStatsMinuteController.java # 분단위 통계 Controller +├── ApiStatsMinuteService.java # 분단위 통계 Service +├── ApiStatsHourController.java # 시간별 통계 Controller +├── ApiStatsHourService.java # 시간별 통계 Service +├── entity/ +│ ├── ApiStatsMinute.java # 분단위 통계 Entity +│ ├── ApiStatsMinuteId.java # 분단위 통계 복합키 +│ ├── ApiStatsHour.java # 시간별 통계 Entity +│ └── ApiStatsHourId.java # 시간별 통계 복합키 +├── repository/ +│ ├── ApiStatsMinuteRepository.java +│ └── ApiStatsHourRepository.java +├── ui/ +│ ├── ApiStatsUI.java # 통계 UI DTO +│ └── ApiStatsSearch.java # 검색조건 DTO +├── mapping/ +│ └── ApiStatsUIMapper.java # Entity ↔ UI 매핑 +└── README.md + +JSP 화면: +WebContent/jsp/kjb/statistics/ +├── apiStatsMinuteMan.jsp # 분단위 통계 화면 +└── apiStatsHourMan.jsp # 시간별 통계 화면 +``` + +## URL + +| 화면 | URL | +|------|-----| +| 분단위 통계 | `/kjb/statistics/apiStatsMinuteMan.view` | +| 시간별 통계 | `/kjb/statistics/apiStatsHourMan.view` | + +## 테이블 + +| 테이블명 | 설명 | +|----------|------| +| API_STATS_MINUTE | 분단위 통계 | +| API_STATS_HOUR | 시간별 통계 | + +## 복합키 구성 + +- statTime (통계 시간) +- apiName (API명) +- gwInstanceId (Gateway 인스턴스 ID) +- bizDivCode (업무구분코드) +- clientId (클라이언트 ID) +- inboundAdapter (Inbound Adapter) +- outboundAdapter (Outbound Adapter) + +## 메트릭 + +### 건수 메트릭 +- totalCnt: 총 처리 건수 +- successCnt: 성공 건수 +- timeoutCnt: Timeout 오류 건수 +- systemErrCnt: 시스템 오류 건수 +- bizErrCnt: 업무 오류 건수 +- seq900TimeoutCnt: 로그시퀀스900 Timeout 건수 +- seq900SystemErrCnt: 로그시퀀스900 시스템 오류 건수 +- seq900BizErrCnt: 로그시퀀스900 업무 오류 건수 + +### 응답시간 메트릭 (ms) +- avgRespTime: 평균 응답시간 +- minRespTime: 최소 응답시간 +- maxRespTime: 최대 응답시간 +- p50RespTime: 50 백분위 응답시간 +- p95RespTime: 95 백분위 응답시간 + +## Multi-tenancy + +API Gateway 통계는 `APIGW` 스키마에 저장됩니다. +Controller에서 `DataSourceContextHolder`를 사용하여 스키마를 전환합니다. + +```java +DataSourceContextHolder.setDataSourceType( + DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW)); +``` + +## 검색 조건 + +- 조회기간 (시작~종료) +- API명 (like 검색) +- Gateway 인스턴스 ID +- 업무구분코드 (like 검색) +- 클라이언트 ID (like 검색) + +## 기술 스택 + +- JPA + QueryDSL +- MapStruct (Entity ↔ UI 매핑) +- Spring MVC (ResponseEntity) +- jqGrid (목록 화면) diff --git a/src/main/java/com/eactive/ext/kjb/statistics/mapping/ApiStatsUIMapper.java b/src/main/java/com/eactive/ext/kjb/statistics/mapping/ApiStatsUIMapper.java new file mode 100644 index 0000000..59b241b --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/statistics/mapping/ApiStatsUIMapper.java @@ -0,0 +1,42 @@ +package com.eactive.ext.kjb.statistics.mapping; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +import com.eactive.eai.data.mapper.BaseMapperConfig; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsHour; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMinute; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; + +@Mapper(config = BaseMapperConfig.class) +public interface ApiStatsUIMapper { + + DateTimeFormatter MINUTE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + DateTimeFormatter HOUR_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:00"); + + @Mapping(source = "statTime", target = "statTime", qualifiedByName = "formatMinuteTime") + ApiStatsUI toVo(ApiStatsMinute entity); + + @Mapping(source = "statTime", target = "statTime", qualifiedByName = "formatHourTime") + ApiStatsUI toVo(ApiStatsHour entity); + + @Named("formatMinuteTime") + default String formatMinuteTime(LocalDateTime dateTime) { + if (dateTime == null) { + return null; + } + return dateTime.format(MINUTE_FORMATTER); + } + + @Named("formatHourTime") + default String formatHourTime(LocalDateTime dateTime) { + if (dateTime == null) { + return null; + } + return dateTime.format(HOUR_FORMATTER); + } +} diff --git a/src/main/java/com/eactive/ext/kjb/statistics/package-info.java b/src/main/java/com/eactive/ext/kjb/statistics/package-info.java new file mode 100644 index 0000000..b70882e --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/statistics/package-info.java @@ -0,0 +1 @@ +package com.eactive.ext.kjb.statistics; \ No newline at end of file diff --git a/src/main/java/com/eactive/ext/kjb/statistics/통계테이블_설계.md b/src/main/java/com/eactive/ext/kjb/statistics/statistics_table.md similarity index 100% rename from src/main/java/com/eactive/ext/kjb/statistics/통계테이블_설계.md rename to src/main/java/com/eactive/ext/kjb/statistics/statistics_table.md diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsChartUI.java b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsChartUI.java new file mode 100644 index 0000000..d2e7c7c --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsChartUI.java @@ -0,0 +1,44 @@ +package com.eactive.ext.kjb.statistics.ui; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * API 통계 차트 데이터 DTO (시간대별 집계) + */ +@Data +@NoArgsConstructor +public class ApiStatsChartUI { + private String statTime; + + // 건수 메트릭 (SUM) + private Long totalCnt; + private Long successCnt; + private Long timeoutCnt; + private Long systemErrCnt; + private Long bizErrCnt; + + // 응답시간 메트릭 (AVG) + private BigDecimal avgRespTime; + private BigDecimal p50RespTime; + private BigDecimal p95RespTime; + + // QueryDSL Projections용 생성자 + public ApiStatsChartUI(LocalDateTime statTime, Long totalCnt, Long successCnt, + Long timeoutCnt, Long systemErrCnt, Long bizErrCnt, + Double avgRespTime, Double p50RespTime, Double p95RespTime) { + this.statTime = statTime.format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")); + this.totalCnt = totalCnt != null ? totalCnt : 0L; + this.successCnt = successCnt != null ? successCnt : 0L; + this.timeoutCnt = timeoutCnt != null ? timeoutCnt : 0L; + this.systemErrCnt = systemErrCnt != null ? systemErrCnt : 0L; + this.bizErrCnt = bizErrCnt != null ? bizErrCnt : 0L; + this.avgRespTime = avgRespTime != null ? BigDecimal.valueOf(avgRespTime) : BigDecimal.ZERO; + this.p50RespTime = p50RespTime != null ? BigDecimal.valueOf(p50RespTime) : BigDecimal.ZERO; + this.p95RespTime = p95RespTime != null ? BigDecimal.valueOf(p95RespTime) : BigDecimal.ZERO; + } +} diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsSearch.java b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsSearch.java new file mode 100644 index 0000000..68c75cd --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsSearch.java @@ -0,0 +1,25 @@ +package com.eactive.ext.kjb.statistics.ui; + +import java.time.LocalDateTime; + +import lombok.Data; + +/** + * API 통계 검색조건 DTO + */ +@Data +public class ApiStatsSearch { + private String searchStartDateTime; + private String searchEndDateTime; + private String searchApiName; + private String searchGwInstanceId; + private String searchBizDivCode; + private String searchClientId; + private String searchInboundAdapter; + private String searchOutboundAdapter; + + /** 파싱된 시작시간 */ + private LocalDateTime parsedStartDateTime; + /** 파싱된 종료시간 */ + private LocalDateTime parsedEndDateTime; +} diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java new file mode 100644 index 0000000..19e0368 --- /dev/null +++ b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java @@ -0,0 +1,36 @@ +package com.eactive.ext.kjb.statistics.ui; + +import java.math.BigDecimal; + +import lombok.Data; + +/** + * API 통계 UI DTO (분단위/시간별 공통) + */ +@Data +public class ApiStatsUI { + private String statTime; + private String apiName; + private String gwInstanceId; + private String bizDivCode; + private String clientId; + private String inboundAdapter; + private String outboundAdapter; + + // 건수 메트릭 + private Long totalCnt; + private Long successCnt; + private Long timeoutCnt; + private Long systemErrCnt; + private Long bizErrCnt; + private Long seq900TimeoutCnt; + private Long seq900SystemErrCnt; + private Long seq900BizErrCnt; + + // 응답시간 메트릭 + private BigDecimal avgRespTime; + private BigDecimal minRespTime; + private BigDecimal maxRespTime; + private BigDecimal p50RespTime; + private BigDecimal p95RespTime; +}