diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsAggregationMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsAggregationMan.jsp index cebd75b..5bc3e2f 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsAggregationMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsAggregationMan.jsp @@ -289,6 +289,7 @@ var year = yesterday.getFullYear(); var month = String(yesterday.getMonth() + 1).padStart(2, '0'); var day = String(yesterday.getDate()).padStart(2, '0'); + $("#targetHour").val(year + month + day); $("#targetDate").val(year + month + day); // 전월 diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp index 7b93d90..f0beefe 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp @@ -638,7 +638,7 @@
API별 사용현황
diff --git a/WebContent/jsp/onl/kjb/statistics/apiUseStatsMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiUseStatsMan.jsp new file mode 100644 index 0000000..e948408 --- /dev/null +++ b/WebContent/jsp/onl/kjb/statistics/apiUseStatsMan.jsp @@ -0,0 +1,313 @@ +<%@ 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 사용현황
+ + + + + + + + + + + + + + + +
조회기간 + + ~ + + (최대 31일) +
조회구분 + + + + 제휴사명 + + API명 + +
+ +
+
+
+
+ + +
+
+ + diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsExcelExportService.java b/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsExcelExportService.java new file mode 100644 index 0000000..b79aae2 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsExcelExportService.java @@ -0,0 +1,256 @@ +package com.eactive.eai.rms.data.ext.djb.statistics; + +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.stereotype.Service; + +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; + +import lombok.extern.slf4j.Slf4j; + +/** + * API 통계 Excel Export 서비스 + * Hour/Minute 통계 데이터를 Excel 파일로 생성 + */ +@Slf4j +@Service +public class ApiUseStatsExcelExportService { + + private static final String[] COLUMN_HEADERS = { + "구분", "총건수", "성공", "성공율(%)", "실패율(%)", "Timeout", "시스템오류", + "평균응답(ms)", "최소응답(ms)", "최대응답(ms)" + }; + + /** + * API 통계 데이터를 Excel 파일로 생성하여 HTTP 응답으로 전송 + * + * @param dataList API 통계 데이터 리스트 + * @param fileName 다운로드 파일명 + * @param response HTTP 응답 객체 + * @throws IOException 파일 생성 실패 시 + */ + public void exportApiStats(List dataList, String fileName, HttpServletResponse response) + throws IOException { + + log.info("Starting Excel export - rows: {}, filename: {}", dataList.size(), fileName); + + try (Workbook workbook = new XSSFWorkbook()) { + Sheet sheet = workbook.createSheet("API통계"); + log.debug("Excel sheet created"); + + // 스타일 생성 + CellStyle headerStyle = createHeaderStyle(workbook); + CellStyle stringStyle = createStringStyle(workbook); + CellStyle numberStyle = createNumberStyle(workbook); + CellStyle decimalStyle = createDecimalStyle(workbook); + log.debug("Excel styles created"); + + // 헤더 행 생성 + createHeaderRow(sheet, headerStyle); + log.debug("Header row created"); + + // 데이터 행 생성 + int rowNum = 1; + for (ApiStatsUI data : dataList) { + createDataRow(sheet, rowNum++, data, stringStyle, numberStyle, decimalStyle); + } + log.info("Data rows created - count: {}", dataList.size()); + + // 컬럼 너비 자동 조정 + autoSizeColumns(sheet); + log.debug("Column widths adjusted"); + + // HTTP 응답 설정 + setHttpResponse(response, fileName, workbook); + log.info("Excel file sent to response"); + + } catch (Exception e) { + log.error("Error creating Excel file", e); + throw new IOException("Excel file creation failed: " + e.getMessage(), e); + } + } + + /** + * 헤더 스타일 생성 (회색 배경, 볼드, 테두리) + */ + private CellStyle createHeaderStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + + // 배경색 + style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + + // 테두리 + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + + // 정렬 + style.setAlignment(HorizontalAlignment.CENTER); + + // 폰트 (볼드) + Font font = workbook.createFont(); + font.setBold(true); + style.setFont(font); + + return style; + } + + /** + * 문자열 데이터 스타일 생성 (기본 테두리) + */ + private CellStyle createStringStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + return style; + } + + /** + * 숫자 데이터 스타일 생성 (우측정렬 + 쉼표 구분) + */ + private CellStyle createNumberStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + style.setAlignment(HorizontalAlignment.RIGHT); + style.setDataFormat(workbook.createDataFormat().getFormat("#,##0")); + return style; + } + + /** + * 소수점 데이터 스타일 생성 (우측정렬 + 소수점 2자리) + */ + private CellStyle createDecimalStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + style.setAlignment(HorizontalAlignment.RIGHT); + style.setDataFormat(workbook.createDataFormat().getFormat("0.00")); + return style; + } + + /** + * 헤더 행 생성 + */ + private void createHeaderRow(Sheet sheet, CellStyle headerStyle) { + Row headerRow = sheet.createRow(0); + + for (int i = 0; i < COLUMN_HEADERS.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(COLUMN_HEADERS[i]); + cell.setCellStyle(headerStyle); + } + } + + /** + * 데이터 행 생성 + */ + private void createDataRow(Sheet sheet, int rowNum, ApiStatsUI data, + CellStyle stringStyle, CellStyle numberStyle, CellStyle decimalStyle) { + + Row row = sheet.createRow(rowNum); + int colNum = 0; + + createStringCell(row, colNum++, data.getOrgName(), stringStyle); + createLongCell(row, colNum++, data.getTotalCnt(), numberStyle); + createLongCell(row, colNum++, data.getSuccessCnt(), numberStyle); + createDecimalCell(row, colNum++, data.getSuccessRate(), decimalStyle); + createDecimalCell(row, colNum++, data.getFailRate(), decimalStyle); + createLongCell(row, colNum++, data.getTimeoutCnt(), numberStyle); + createLongCell(row, colNum++, data.getSystemErrCnt(), numberStyle); + createDecimalCell(row, colNum++, data.getAvgRespTime(), numberStyle); + createDecimalCell(row, colNum++, data.getMinRespTime(), numberStyle); + createDecimalCell(row, colNum++, data.getMaxRespTime(), numberStyle); + } + + /** + * 문자열 셀 생성 + */ + private void createStringCell(Row row, int colNum, String value, CellStyle style) { + Cell cell = row.createCell(colNum); + cell.setCellValue(value != null ? value : ""); + cell.setCellStyle(style); + } + + /** + * Long 타입 숫자 셀 생성 + */ + private void createLongCell(Row row, int colNum, Long value, CellStyle style) { + Cell cell = row.createCell(colNum); + if (value != null) { + cell.setCellValue(value.doubleValue()); + } else { + cell.setCellValue(0); + } + cell.setCellStyle(style); + } + + /** + * BigDecimal 타입 소수점 셀 생성 + */ + private void createDecimalCell(Row row, int colNum, BigDecimal value, CellStyle style) { + Cell cell = row.createCell(colNum); + if (value != null) { + cell.setCellValue(value.doubleValue()); + } else { + cell.setCellValue(0.0); + } + cell.setCellStyle(style); + } + + /** + * 컬럼 너비 자동 조정 + */ + private void autoSizeColumns(Sheet sheet) { + for (int i = 0; i < COLUMN_HEADERS.length; i++) { + sheet.autoSizeColumn(i); + // 한글 문자 고려하여 약간 여유 공간 추가 + sheet.setColumnWidth(i, sheet.getColumnWidth(i) + 512); + } + } + + /** + * HTTP 응답 설정 및 파일 전송 + */ + private void setHttpResponse(HttpServletResponse response, String fileName, Workbook workbook) + throws IOException { + + // Content-Type 설정 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + + // Content-Disposition 설정 (파일명 UTF-8 인코딩) + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()) + .replaceAll("\\+", "%20"); + response.setHeader("Content-Disposition", + "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName); + + // Workbook을 응답 스트림으로 전송 + workbook.write(response.getOutputStream()); + response.getOutputStream().flush(); + } +} diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsRepository.java b/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsRepository.java new file mode 100644 index 0000000..055127d --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsRepository.java @@ -0,0 +1,12 @@ +package com.eactive.eai.rms.data.ext.djb.statistics; + +import com.eactive.eai.data.jpa.BaseRepository; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDay; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDayId; + +/** + * API 사용현황 Repository + */ +interface ApiUseStatsRepository extends BaseRepository { + +} diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsService.java b/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsService.java new file mode 100644 index 0000000..e6923f6 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/ext/djb/statistics/ApiUseStatsService.java @@ -0,0 +1,211 @@ +package com.eactive.eai.rms.data.ext.djb.statistics; + +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Collectors; + +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; + +import com.eactive.eai.data.jpa.AbstractDataService; +import com.eactive.eai.rms.common.util.StringUtils; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDay; +import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDayId; +import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; + +@Service +@Transactional +public class ApiUseStatsService + extends AbstractDataService { + + private static final int MAX_DAYS = 31; + + @PersistenceContext + private EntityManager entityManager; + + @SuppressWarnings("unchecked") + public Page selectList(ApiStatsSearch search, Pageable pageable) { + Query dataQuery = getDataQuery(search); + dataQuery.setFirstResult((int) pageable.getOffset()); + dataQuery.setMaxResults(pageable.getPageSize()); + + List rows = dataQuery.getResultList(); + List 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 selectList(ApiStatsSearch search) { + Query dataQuery = getDataQuery(search); + List rows = dataQuery.getResultList(); + return rows.stream() + .map(this::toVO) + .collect(Collectors.toList()); + } + + + private Query getDataQuery(ApiStatsSearch search) { + + String schemaEms = System.getProperty("eai.tableowner", "EMSADM").toUpperCase(); + + StringBuilder where = buildNativeWhere(search); + + String dataSql = ""; + if ("ORG".equals(search.getSearchType())) { + dataSql = "SELECT COUNT(*) OVER () AS RECORDS " + + ", B.ORGNAME" + + ", 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 " + schemaEms + ".PTL_CREDENTIAL B ON A.CLIENT_ID = B.CLIENTID" + + " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME " + + where + + " GROUP BY B.ORGNAME" + + " ORDER BY ORGNAME"; + } else if ("API".equals(search.getSearchType())) { + dataSql = "SELECT COUNT(*) OVER () AS RECORDS " + + ", C.EAISVCDESC" + + ", 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 " + schemaEms + ".PTL_CREDENTIAL B ON A.CLIENT_ID = B.CLIENTID" + + " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME " + + where + + " GROUP BY C.EAISVCDESC" + + " ORDER BY EAISVCDESC"; + } 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 " + schemaEms + ".PTL_CREDENTIAL B ON A.CLIENT_ID = B.CLIENTID" + + " LEFT OUTER JOIN TSEAIHE01 C ON A.API_NAME = C.EAISVCNAME " + + where + + " GROUP BY TO_CHAR(STAT_TIME,'YYYY-MM-DD')" + + " ORDER BY STAT_TIME"; + } + + Query dataQuery = entityManager.createNativeQuery(dataSql); + if (search.getParsedStartDate() != null) { + dataQuery.setParameter("searchStartDate", search.getParsedStartDate()); + dataQuery.setParameter("searchEndDate", search.getParsedEndDate()); + } + if (StringUtils.isNotBlank(search.getSearchOrgName())) { + dataQuery.setParameter("searchOrgName", "%" + search.getSearchOrgName().toUpperCase() + "%"); + } + if (StringUtils.isNotBlank(search.getSearchApiName())) { + dataQuery.setParameter("searchApiName", "%" + search.getSearchApiName().toUpperCase() + "%"); + } + return dataQuery; + } + + + /** + * Native Query 결과를 ApiStatsUI로 변환 + */ + 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])); + return vo; + } + + + /** + * native SQL용 동적 WHERE 절 생성 + */ + private StringBuilder buildNativeWhere(ApiStatsSearch search) { + StringBuilder where = new StringBuilder(" WHERE 1=1"); + + calculateDateRange(search); + + if (search.getParsedStartDate() != null) { + where.append(" AND A.STAT_TIME >= :searchStartDate "); + where.append(" AND A.STAT_TIME <= :searchEndDate "); + } + + if (StringUtils.isNotBlank(search.getSearchOrgName())) { + where.append(" AND UPPER(B.ORGNAME) LIKE :searchOrgName "); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + where.append(" AND UPPER(C.EAISVCDESC) LIKE :searchApiName "); + } + + return where; + } + + + /** + * 조회 기간 계산 (최대 31일 제한) + */ + private void calculateDateRange(ApiStatsSearch search) { + if (StringUtils.isBlank(search.getSearchStartDateTime())) { + return; + } + + LocalDate startDate = LocalDate.parse(search.getSearchStartDateTime(), + DateTimeFormatter.ofPattern("yyyyMMdd")); + LocalDate endDate; + + if (StringUtils.isNotBlank(search.getSearchEndDateTime())) { + endDate = LocalDate.parse(search.getSearchEndDateTime(), + DateTimeFormatter.ofPattern("yyyyMMdd")); + if (java.time.Period.between(startDate, endDate).getDays() > MAX_DAYS) { + endDate = startDate.plusDays(MAX_DAYS); + } + } else { + endDate = startDate.plusDays(MAX_DAYS); + } + + search.setParsedStartDate(startDate); + search.setParsedEndDate(endDate); + } + +} diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/job/ApiStatsHourlyAggregationJob.java b/src/main/java/com/eactive/eai/rms/ext/djb/job/ApiStatsHourlyAggregationJob.java index d637000..606f50c 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/job/ApiStatsHourlyAggregationJob.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/job/ApiStatsHourlyAggregationJob.java @@ -94,8 +94,8 @@ public class ApiStatsHourlyAggregationJob implements Job { // 2. 집계 및 데이터 입력 /** - * EAISVCSERNO로 그룹핑하여 1행으로 집계를 한 다음 - * 총건수 : EAISVCSERNO로 그룹핑한 총 건수 + * EAISVCSERNO로 그룹핑하여 1행으로 집계를 한 다음 (한거래 안에서 adapter가 여러개 있을 경우 seq=100의 adapter를 기준) + * 총건수 : EAISVCSERNO로 그룹핑한 총 건수 * 성공건수 : seq = 400 and EAIERRCD = null 인 건수 * Timeout : EAIERRCD in (공통코드 CODEGROUP = 'ERRCODE_TIMEOUT') 인 건수 * 시스템오류 : (seq400 = null or EAIERRCD is not null) and 공통코드(ERRCODE_TIMEOUT)에 정의되지 않은 errorcode 인 건수 @@ -120,9 +120,8 @@ public class ApiStatsHourlyAggregationJob implements Job { " SELECT EAISVCSERNO" + " , MAX(EAISVCNAME) AS API_NAME, MAX(EAISEVRINSTNCNAME) AS GW_INSTANCE_ID" + " , MAX(EAIBZWKDSTCD) AS BIZ_DIV_CODE, MAX(CLIENTID) AS CLIENT_ID" + - " , MAX(CLIENTNAME) AS CLIENT_NAME, MAX(ORGID) AS ORG_ID, MAX(ORGNAME) AS ORG_NAME" + - " , MAX(GSTATSYSADPTRBZWKGROUPNAME) AS INBOUND_ADAPTER" + - " , MAX(PSVSYSADPTRBZWKGROUPNAME) AS OUTBOUND_ADAPTER" + + " , MAX(CASE WHEN LOGPRCSSSERNO = '100' THEN GSTATSYSADPTRBZWKGROUPNAME ELSE '' END) AS INBOUND_ADAPTER" + + " , MAX(CASE WHEN LOGPRCSSSERNO = '100' THEN PSVSYSADPTRBZWKGROUPNAME ELSE '' END) AS OUTBOUND_ADAPTER" + " , MIN(MSGDPSTYMS) AS DT" + " , MAX(MSGPRCSSYMS) - MIN(MSGDPSTYMS) AS RESP_TIME" + " , MAX(CASE WHEN LOGPRCSSSERNO = '400' THEN LOGPRCSSSERNO ELSE '' END) AS E400" + diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/statistics/ApiUseStatsController.java b/src/main/java/com/eactive/eai/rms/ext/djb/statistics/ApiUseStatsController.java index 3b79e3c..8676b9f 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/statistics/ApiUseStatsController.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/statistics/ApiUseStatsController.java @@ -1,29 +1,22 @@ package com.eactive.eai.rms.ext.djb.statistics; import java.io.IOException; -import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; -import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.StringUtils; 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.ApiStatsDay; -import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDayService; -import com.eactive.ext.kjb.statistics.mapping.ApiStatsUIMapper; -import com.eactive.ext.kjb.statistics.service.ApiStatsExcelExportService; -import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; +import com.eactive.eai.rms.data.ext.djb.statistics.ApiUseStatsExcelExportService; +import com.eactive.eai.rms.data.ext.djb.statistics.ApiUseStatsService; import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; @@ -35,37 +28,29 @@ import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor public class ApiUseStatsController { - private final ApiStatsDayService service; - private final ApiStatsUIMapper mapper; - private final ApiStatsExcelExportService excelExportService; + private final ApiUseStatsService service; + private final ApiUseStatsExcelExportService excelExportService; - @GetMapping(value = "/onl/djb/statistics/apiUseStatsMan.view") + @GetMapping(value = "/onl/kjb/statistics/apiUseStatsMan.view") public String view() { - return "/onl/djb/statistics/apiUseStatsMan"; + return "/onl/kjb/statistics/apiUseStatsMan"; } - @PostMapping(value = "/onl/djb/statistics/apiUseStatsMan.json", params = "cmd=LIST") + @PostMapping(value = "/onl/kjb/statistics/apiUseStatsMan.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)); + Page page = service.selectList(search, pageable); + return ResponseEntity.ok(new GridResponse<>(page)); } - @PostMapping(value = "/onl/djb/statistics/apiStatsDayMan.json", params = "cmd=EXCEL_EXPORT") + @PostMapping(value = "/onl/kjb/statistics/apiUseStatsMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); try { - List dataList = service.selectListForExcel(search); - log.info("Data retrieved - count: {}", dataList.size()); + List uiList = service.selectList(search); + log.info("Data retrieved - count: {}", uiList.size()); - if (dataList.isEmpty()) { + if (uiList.isEmpty()) { log.warn("No data found for export"); response.setStatus(HttpServletResponse.SC_NO_CONTENT); response.setContentType("application/json; charset=UTF-8"); @@ -74,11 +59,6 @@ public class ApiUseStatsController { return; } - List uiList = dataList.stream() - .map(mapper::toVo) - .collect(Collectors.toList()); - log.info("Data converted to UI list - count: {}", uiList.size()); - String fileName = generateFileName(search); log.info("Generating Excel file: {}", fileName); @@ -101,10 +81,7 @@ public class ApiUseStatsController { } private String generateFileName(ApiStatsSearch search) { - String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) - ? search.getSearchStartDateTime() - : LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); - return "API사용현황_" + timeRange + ".xlsx"; + return "API사용현황_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx"; } } 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 index 96c0e58..2f354fd 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsSearch.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsSearch.java @@ -18,6 +18,8 @@ public class ApiStatsSearch { private String searchClientId; private String searchInboundAdapter; private String searchOutboundAdapter; + private String searchOrgName; + private String searchType; /** 파싱된 시작시간 */ private LocalDateTime parsedStartDateTime; 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 index 3a57ab9..83ee405 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ui/ApiStatsUI.java @@ -14,6 +14,7 @@ public class ApiStatsUI { private String gwInstanceId; private String bizDivCode; private String clientId; + private String orgName; private String inboundAdapter; private String outboundAdapter; @@ -27,4 +28,8 @@ public class ApiStatsUI { private BigDecimal avgRespTime; private BigDecimal minRespTime; private BigDecimal maxRespTime; + + //성공율,실패율 + private BigDecimal successRate; + private BigDecimal failRate; }