+
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/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/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 {
}
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("");
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;
+}