API 통계 footer에 합계 표시
eapim-admin CI / build (push) Has been cancelled

This commit is contained in:
eastargh
2026-08-24 13:01:43 +09:00
parent a6e88f2281
commit 314eb87ea4
4 changed files with 133 additions and 102 deletions
@@ -15,6 +15,12 @@
<jsp:include page="/jsp/common/include/css.jsp"/>
<jsp:include page="/jsp/common/include/script.jsp"/>
<script src="<c:url value="/addon/echarts/echarts.min.js"/>"></script>
<style>
/* 합계(footer) 행 높이를 본문 행 높이(23px)와 동일하게 맞춤 */
#gbox_grid .ui-jqgrid-ftable tr.footrow td {
height: 23px;
}
</style>
<script language="javascript">
var url = '<c:url value="/onl/kjb/statistics/apiUseStatsMan.json"/>';
var url_view = '<c:url value="/onl/kjb/statistics/apiUseStatsMan.view"/>';
@@ -63,7 +69,7 @@
function search() {
var postData = getPostData("cmd", "LIST");
if (postData) {
$("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid");
$("#grid").setGridParam({ url: url, postData: postData }).trigger("reloadGrid");
}
}
@@ -198,19 +204,17 @@
{ name: 'maxRespTime', align: 'right', width: '90', formatter: numberFormatter, sortable: false }
],
jsonReader: { repeatitems: false },
pager: $('#pager'),
page: '${param.page}',
rowNum: '${rmsDefaultRowNum}',
autoheight: true,
height: 'auto',
height: 500,
autowidth: true,
viewrecords: true,
rowList: eval('[${rmsDefaultRowList}]'),
footerrow: true,
userDataOnFooter: true,
loadComplete: function(d) {
var colModel = $(this).getGridParam("colModel");
for (var i = 0; i < colModel.length; i++) {
$(this).setColProp(colModel[i].name, { sortable: false });
}
$(".ui-jqgrid-ftable td").css({ "font-weight": "bold", "background-color": "#f5f5f5" });
}
});
}
@@ -308,7 +312,6 @@
<div>
<table id="grid"></table>
<div id="pager"></div>
</div>
@@ -1,6 +1,7 @@
package com.eactive.eai.rms.common.vo;
import java.util.List;
import java.util.Map;
import org.springframework.data.domain.Page;
@@ -10,6 +11,8 @@ public class GridResponse<T> {
private Integer page;
private Long records;
private List<T> rows;
/** jqGrid footerrow + userDataOnFooter 용 하단 합계행 데이터 (colModel name -> value) */
private Map<String, Object> userdata;
public GridResponse() {
}
@@ -54,6 +57,14 @@ public class GridResponse<T> {
this.rows = rows;
}
public Map<String, Object> getUserdata() {
return userdata;
}
public void setUserdata(Map<String, Object> userdata) {
this.userdata = userdata;
}
public GridResponse(List<T> rows) {
this.rows = rows;
this.total = 1;
@@ -3,12 +3,12 @@ package com.eactive.eai.rms.ext.djb.statistics;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@@ -35,9 +35,30 @@ public class ApiUseStatsController {
}
@PostMapping(value = "/onl/kjb/statistics/apiUseStatsMan.json", params = "cmd=LIST")
public ResponseEntity<GridResponse<ApiStatsUI>> selectList(ApiStatsSearch search, Pageable pageable) {
Page<ApiStatsUI> page = service.selectList(search, pageable);
return ResponseEntity.ok(new GridResponse<>(page));
public ResponseEntity<GridResponse<ApiStatsUI>> selectList(ApiStatsSearch search) {
List<ApiStatsUI> list = service.selectList(search);
GridResponse<ApiStatsUI> response = new GridResponse<>(list);
ApiStatsUI total = service.selectTotal(search);
if (total != null) {
response.setUserdata(toFooterData(total));
}
return ResponseEntity.ok(response);
}
private Map<String, Object> toFooterData(ApiStatsUI total) {
Map<String, Object> footer = new LinkedHashMap<>();
footer.put("orgName", "합계");
footer.put("totalCnt", total.getTotalCnt());
footer.put("successCnt", total.getSuccessCnt());
footer.put("successRate", total.getSuccessRate());
footer.put("failRate", total.getFailRate());
footer.put("timeoutCnt", total.getTimeoutCnt());
footer.put("systemErrCnt", total.getSystemErrCnt());
footer.put("avgRespTime", total.getAvgRespTime());
footer.put("minRespTime", total.getMinRespTime());
footer.put("maxRespTime", total.getMaxRespTime());
return footer;
}
@PostMapping(value = "/onl/kjb/statistics/apiUseStatsMan.json", params = "cmd=EXCEL_EXPORT")
@@ -57,6 +78,12 @@ public class ApiUseStatsController {
return;
}
ApiStatsUI total = service.selectTotal(search);
if (total != null) {
total.setOrgName("합계");
uiList.add(total);
}
String fileName = generateFileName(search);
log.info("Generating Excel file: {}", fileName);
@@ -10,9 +10,6 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -33,23 +30,6 @@ public class ApiUseStatsService
@PersistenceContext
private EntityManager entityManager;
@SuppressWarnings("unchecked")
public Page<ApiStatsUI> selectList(ApiStatsSearch search, Pageable pageable) {
Query dataQuery = getDataQuery(search);
dataQuery.setFirstResult((int) pageable.getOffset());
dataQuery.setMaxResults(pageable.getPageSize());
List<Object[]> rows = dataQuery.getResultList();
List<ApiStatsUI> results = rows.stream()
.map(this::toVO)
.collect(Collectors.toList());
long records = rows.size() > 0 ? StringUtils.toLong(rows.get(0)[0]) : 0;
return new PageImpl<>(results, pageable, records);
}
@SuppressWarnings("unchecked")
public List<ApiStatsUI> selectList(ApiStatsSearch search) {
Query dataQuery = getDataQuery(search);
@@ -60,14 +40,64 @@ public class ApiUseStatsService
}
/**
* 하단 합계(Total)행 조회 (grid footer 용, 그룹핑 없이 전체 집계)
*/
@SuppressWarnings("unchecked")
public ApiStatsUI selectTotal(ApiStatsSearch search) {
Query totalQuery = getTotalQuery(search);
List<Object[]> rows = totalQuery.getResultList();
return rows.isEmpty() ? null : toVO(rows.get(0));
}
private Query getDataQuery(ApiStatsSearch search) {
StringBuilder where = buildNativeWhere(search);
String dataSql = "";
if ("ORG".equals(search.getSearchType())) {
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
+ ", NVL(B.ORGNAME, D.ORG_NAME) AS ORGNAME"
+ ", SUM(A.TOTAL_CNT)"
dataSql = "SELECT NVL(B.ORGNAME, D.ORG_NAME) AS ORGNAME";
dataSql += getCommonQuery(search);
dataSql += " GROUP BY NVL(B.ORGNAME, D.ORG_NAME)";
dataSql += " ORDER BY ORGNAME";
}
else if ("API".equals(search.getSearchType())) {
dataSql = "SELECT A.API_NAME ";
dataSql += getCommonQuery(search);
dataSql += " GROUP BY A.API_NAME";
dataSql += " ORDER BY API_NAME";
}
else if ("DATE".equals(search.getSearchType())) {
dataSql = "SELECT TO_CHAR(STAT_TIME,'YYYY-MM-DD') AS STAT_TIME ";
dataSql += getCommonQuery(search);
dataSql += " GROUP BY TO_CHAR(STAT_TIME,'YYYY-MM-DD')";
dataSql += " ORDER BY STAT_TIME";
}
Query dataQuery = entityManager.createNativeQuery(dataSql);
bindParams(dataQuery, search);
return dataQuery;
}
private Query getTotalQuery(ApiStatsSearch search) {
StringBuilder where = buildNativeWhere(search);
String totalSql;
totalSql = "SELECT '합계' AS ORGNAME ";
totalSql += getCommonQuery(search);
Query totalQuery = entityManager.createNativeQuery(totalSql);
bindParams(totalQuery, search);
return totalQuery;
}
private String getCommonQuery(ApiStatsSearch search) {
String dataSql = ", SUM(A.TOTAL_CNT)"
+ ", SUM(A.SUCCESS_CNT)"
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN SUM(A.SUCCESS_CNT) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN (SUM(A.TOTAL_CNT) - SUM(A.SUCCESS_CNT)) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
@@ -87,7 +117,7 @@ public class ApiUseStatsService
+ " , A.SYSTEM_ERR_CNT"
+ " , A.MIN_RESP_TIME"
+ " , A.MAX_RESP_TIME"
+ " , CASE WHEN API_NAME LIKE '%1' THEN SUBSTR(INBOUND_ADAPTER,2,3) ELSE SUBSTR(OUTBOUND_ADAPTER,2,3) END AS ORG_CODE"
+ " , CASE WHEN API_NAME LIKE '%1' THEN SUBSTR(OUTBOUND_ADAPTER,2,3) ELSE SUBSTR(INBOUND_ADAPTER,2,3) END AS ORG_CODE"
+ " FROM API_STATS_DAY A "
+ " WHERE A.STAT_TIME >= :searchStartDate "
+ " AND A.STAT_TIME <= :searchEndDate ";
@@ -106,61 +136,21 @@ public class ApiUseStatsService
dataSql += " AND UPPER(NVL(B.ORGNAME, D.ORG_NAME)) LIKE :searchOrgName ";
}
dataSql += " GROUP BY NVL(B.ORGNAME, D.ORG_NAME)"
+ " ORDER BY ORGNAME";
}
else if ("API".equals(search.getSearchType())) {
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
+ ", A.API_NAME"
+ ", SUM(A.TOTAL_CNT)"
+ ", SUM(A.SUCCESS_CNT)"
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN SUM(A.SUCCESS_CNT) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN (SUM(A.TOTAL_CNT) - SUM(A.SUCCESS_CNT)) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
+ ", SUM(A.TIMEOUT_CNT)"
+ ", SUM(A.SYSTEM_ERR_CNT)"
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN TRUNC(SUM(A.TOTAL_CNT * A.AVG_RESP_TIME) / SUM(A.TOTAL_CNT)) ELSE NULL END"
+ ", MIN(A.MIN_RESP_TIME)"
+ ", MAX(A.MAX_RESP_TIME)"
+ " FROM API_STATS_DAY A "
+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
+ where
+ " GROUP BY A.API_NAME"
+ " ORDER BY API_NAME";
}
else if ("DATE".equals(search.getSearchType())) {
dataSql = "SELECT COUNT(*) OVER () AS RECORDS "
+ ", TO_CHAR(STAT_TIME,'YYYY-MM-DD') AS STAT_TIME "
+ ", SUM(A.TOTAL_CNT)"
+ ", SUM(A.SUCCESS_CNT)"
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN SUM(A.SUCCESS_CNT) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN (SUM(A.TOTAL_CNT) - SUM(A.SUCCESS_CNT)) * 100 / SUM(A.TOTAL_CNT) ELSE NULL END"
+ ", SUM(A.TIMEOUT_CNT)"
+ ", SUM(A.SYSTEM_ERR_CNT)"
+ ", CASE WHEN SUM(A.TOTAL_CNT) > 0 THEN TRUNC(SUM(A.TOTAL_CNT * A.AVG_RESP_TIME) / SUM(A.TOTAL_CNT)) ELSE NULL END"
+ ", MIN(A.MIN_RESP_TIME)"
+ ", MAX(A.MAX_RESP_TIME)"
+ " FROM API_STATS_DAY A "
+ " LEFT OUTER JOIN TSEAIAU01 B ON A.CLIENT_ID = B.CLIENTID"
+ where
+ " GROUP BY TO_CHAR(STAT_TIME,'YYYY-MM-DD')"
+ " ORDER BY STAT_TIME";
return dataSql;
}
Query dataQuery = entityManager.createNativeQuery(dataSql);
dataQuery.setParameter("searchStartDate", search.getParsedStartDate());
dataQuery.setParameter("searchEndDate", search.getParsedEndDate());
private void bindParams(Query query, ApiStatsSearch search) {
query.setParameter("searchStartDate", search.getParsedStartDate());
query.setParameter("searchEndDate", search.getParsedEndDate());
if (StringUtils.isNotBlank(search.getSearchApiName())) {
dataQuery.setParameter("searchApiName", "%" + search.getSearchApiName().toUpperCase() + "%");
query.setParameter("searchApiName", "%" + search.getSearchApiName().toUpperCase() + "%");
}
if (StringUtils.isNotBlank(search.getSearchOrgName())) {
dataQuery.setParameter("searchOrgName", "%" + search.getSearchOrgName().toUpperCase() + "%");
query.setParameter("searchOrgName", "%" + search.getSearchOrgName().toUpperCase() + "%");
}
return dataQuery;
}
@@ -169,16 +159,16 @@ public class ApiUseStatsService
*/
private ApiStatsUI toVO(Object[] row) {
ApiStatsUI vo = new ApiStatsUI();
vo.setOrgName(StringUtils.toString(row[1]));
vo.setTotalCnt(StringUtils.toLong(row[2]));
vo.setSuccessCnt(StringUtils.toLong(row[3]));
vo.setSuccessRate(StringUtils.toDecimal(row[4]).setScale(2, RoundingMode.HALF_UP));
vo.setFailRate(StringUtils.toDecimal(row[5]).setScale(2, RoundingMode.HALF_UP));
vo.setTimeoutCnt(StringUtils.toLong(row[6]));
vo.setSystemErrCnt(StringUtils.toLong(row[7]));
vo.setAvgRespTime(StringUtils.toDecimal(row[8]));
vo.setMinRespTime(StringUtils.toDecimal(row[9]));
vo.setMaxRespTime(StringUtils.toDecimal(row[10]));
vo.setOrgName(StringUtils.toString(row[0]));
vo.setTotalCnt(StringUtils.toLong(row[1]));
vo.setSuccessCnt(StringUtils.toLong(row[2]));
vo.setSuccessRate(StringUtils.toDecimal(row[3]).setScale(2, RoundingMode.HALF_UP));
vo.setFailRate(StringUtils.toDecimal(row[4]).setScale(2, RoundingMode.HALF_UP));
vo.setTimeoutCnt(StringUtils.toLong(row[5]));
vo.setSystemErrCnt(StringUtils.toLong(row[6]));
vo.setAvgRespTime(StringUtils.toDecimal(row[7]));
vo.setMinRespTime(StringUtils.toDecimal(row[8]));
vo.setMaxRespTime(StringUtils.toDecimal(row[9]));
return vo;
}