API 사용현황 신규개발
This commit is contained in:
@@ -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);
|
||||
|
||||
// 전월
|
||||
|
||||
@@ -638,7 +638,7 @@
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<div class="title" style="margin: 0;">API별 사용현황</div>
|
||||
<button type="button" class="cssbtn" id="btn_excel_export_summary" level="R">
|
||||
<i class="material-icons">file_download</i> Excel 다운로드 (요약)
|
||||
<i class="material-icons">file_download</i> Excel 다운로드
|
||||
</button>
|
||||
</div>
|
||||
<table id="gridSummary"></table>
|
||||
|
||||
@@ -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");
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<title>대시보드</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<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>
|
||||
<script language="javascript">
|
||||
var url = '<c:url value="/onl/kjb/statistics/apiUseStatsMan.json"/>';
|
||||
var url_view = '<c:url value="/onl/kjb/statistics/apiUseStatsMan.view"/>';
|
||||
|
||||
|
||||
function numberFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
return Number(cellvalue).toLocaleString();
|
||||
}
|
||||
|
||||
function decimalFormatter(cellvalue, options, rowObject) {
|
||||
if (cellvalue == null || cellvalue == '') return '0';
|
||||
return (cellvalue).toFixed(2);
|
||||
}
|
||||
|
||||
function getPostData() {
|
||||
var postData = {}
|
||||
if (arguments.length == 2) {
|
||||
postData[arguments[0]] = arguments[1];
|
||||
}
|
||||
|
||||
var searchStartDate = $("input[name=searchStartDate]").val().replace(/-/g, "");
|
||||
var searchEndDate = $("input[name=searchEndDate]").val().replace(/-/g, "");
|
||||
|
||||
if (searchStartDate && searchEndDate) {
|
||||
var start = new Date(searchStartDate.substring(0, 4), parseInt(searchStartDate.substring(4, 6)) - 1, searchStartDate.substring(6, 8));
|
||||
var end = new Date(searchEndDate.substring(0, 4), parseInt(searchEndDate.substring(4, 6)) - 1, searchEndDate.substring(6, 8));
|
||||
var diffDays = Math.ceil((end - start) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffDays > 31) {
|
||||
alert('조회 기간은 최대 31일까지 가능합니다.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
postData.searchType = $('input[name="searchType"]:checked').val();
|
||||
postData.searchOrgName = $('#searchOrgName').val();
|
||||
postData.searchApiName = $('#searchApiName').val();
|
||||
postData.searchStartDateTime = searchStartDate;
|
||||
postData.searchEndDateTime = searchEndDate;
|
||||
|
||||
return postData;
|
||||
}
|
||||
|
||||
|
||||
function search() {
|
||||
var postData = getPostData("cmd", "LIST");
|
||||
if (postData) {
|
||||
$("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid");
|
||||
}
|
||||
}
|
||||
|
||||
function exportToExcel() {
|
||||
|
||||
var postData = getPostData("cmd", "EXCEL_EXPORT");
|
||||
if (!postData) {
|
||||
return;
|
||||
}
|
||||
postData.serviceType = '${param.serviceType}';
|
||||
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
|
||||
xhr.onload = function() {
|
||||
console.log('[Excel Export] Response received - Status:', xhr.status);
|
||||
|
||||
if (xhr.status === 200) {
|
||||
var blob = xhr.response;
|
||||
var contentType = xhr.getResponseHeader('Content-Type');
|
||||
console.log('[Excel Export] Blob size:', blob.size, 'Content-Type:', contentType);
|
||||
|
||||
// JSON 에러 응답인지 확인
|
||||
if (contentType && contentType.indexOf('application/json') !== -1) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
try {
|
||||
var errorObj = JSON.parse(reader.result);
|
||||
alert(errorObj.message || 'Excel 파일 생성에 실패했습니다.');
|
||||
} catch (e) {
|
||||
alert('Excel 파일 생성에 실패했습니다.');
|
||||
}
|
||||
};
|
||||
reader.readAsText(blob);
|
||||
return;
|
||||
}
|
||||
|
||||
// Blob 크기가 0이면 에러
|
||||
if (blob.size === 0) {
|
||||
alert('Excel 파일 생성에 실패했습니다. (빈 응답)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 파일명 추출
|
||||
var filename = 'api_stats.xlsx';
|
||||
var disposition = xhr.getResponseHeader('Content-Disposition');
|
||||
console.log('[Excel Export] Content-Disposition:', disposition);
|
||||
|
||||
if (disposition && disposition.indexOf('filename=') !== -1) {
|
||||
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
|
||||
var matches = filenameRegex.exec(disposition);
|
||||
if (matches != null && matches[1]) {
|
||||
filename = matches[1].replace(/['"]/g, '');
|
||||
filename = decodeURIComponent(filename);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[Excel Export] Downloading as:', filename);
|
||||
|
||||
// 파일 다운로드
|
||||
var link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(link.href);
|
||||
|
||||
console.log('[Excel Export] Download triggered');
|
||||
|
||||
} else if (xhr.status === 204) {
|
||||
alert('조회된 데이터가 없습니다.');
|
||||
} else {
|
||||
// 에러 응답 처리
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
var message = 'Excel 다운로드 중 오류가 발생했습니다.';
|
||||
console.log('reader.result', reader.result)
|
||||
try {
|
||||
var errorObj = JSON.parse(reader.result);
|
||||
if (errorObj.message) message = errorObj.message;
|
||||
} catch (e) {
|
||||
console.error('[Excel Export] Error parsing error response:', e);
|
||||
}
|
||||
alert(message);
|
||||
};
|
||||
reader.readAsText(xhr.response);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = function() {
|
||||
console.error('[Excel Export] Network error');
|
||||
alert('네트워크 오류가 발생했습니다.');
|
||||
};
|
||||
|
||||
// Form data 생성
|
||||
var formData = [];
|
||||
for (var key in postData) {
|
||||
if (postData.hasOwnProperty(key) && postData[key] != null && postData[key] !== '') {
|
||||
formData.push(encodeURIComponent(key) + '=' + encodeURIComponent(postData[key]));
|
||||
}
|
||||
}
|
||||
|
||||
xhr.send(formData.join('&'));
|
||||
}
|
||||
|
||||
function list() {
|
||||
var gridPostData = getPostData("cmd", "LIST");
|
||||
|
||||
$('#grid').jqGrid({
|
||||
datatype: "json",
|
||||
mtype: 'POST',
|
||||
postData: gridPostData,
|
||||
colNames: [
|
||||
'구분',
|
||||
'총건수', '성공', '성공율(%)', '실패율(%)', 'Timeout', '시스템오류',
|
||||
'평균응답(ms)', '최소응답(ms)', '최대응답(ms)'
|
||||
],
|
||||
colModel: [
|
||||
{ name: 'orgName', align: 'left', width: '250', sortable: false },
|
||||
{ name: 'totalCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'successCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'successRate', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'failRate', align: 'right', width: '80', formatter: decimalFormatter, sortable: false },
|
||||
{ name: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'avgRespTime', align: 'right', width: '90', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'minRespTime', align: 'right', width: '90', formatter: numberFormatter, sortable: false },
|
||||
{ name: 'maxRespTime', align: 'right', width: '90', formatter: numberFormatter, sortable: false }
|
||||
],
|
||||
jsonReader: { repeatitems: false },
|
||||
pager: $('#pager'),
|
||||
page: '${param.page}',
|
||||
rowNum: '${rmsDefaultRowNum}',
|
||||
autoheight: true,
|
||||
height: 'auto',
|
||||
autowidth: true,
|
||||
viewrecords: true,
|
||||
rowList: eval('[${rmsDefaultRowList}]'),
|
||||
loadComplete: function(d) {
|
||||
var colModel = $(this).getGridParam("colModel");
|
||||
for (var i = 0; i < colModel.length; i++) {
|
||||
$(this).setColProp(colModel[i].name, { sortable: false });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
// 날짜/시간 입력 마스크
|
||||
$("input[name=searchStartDate], input[name=searchEndDate]").inputmask("9999-99-99", { 'autoUnmask': true });
|
||||
$("input[name=searchStartDate], input[name=searchEndDate]").datepicker();
|
||||
|
||||
var today = getToday();
|
||||
var startDate = today;
|
||||
var endDate = today;
|
||||
|
||||
|
||||
if (!$("input[name=searchStartDate]").val()) {
|
||||
$("input[name=searchStartDate]").val(startDate);
|
||||
}
|
||||
if (!$("input[name=searchEndDate]").val()) {
|
||||
$("input[name=searchEndDate]").val(endDate);
|
||||
}
|
||||
|
||||
list();
|
||||
|
||||
resizeJqGridWidth('grid', 'content_middle', '1200');
|
||||
|
||||
$("#btn_search").click(function() {
|
||||
search();
|
||||
});
|
||||
|
||||
$("#btn_excel").click(function() {
|
||||
exportToExcel();
|
||||
});
|
||||
|
||||
$("input[name^=search]").keydown(function(key) {
|
||||
if (key.keyCode == 13) {
|
||||
$("#btn_search").click();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
buttonControl();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="right_box">
|
||||
<div class="content_top">
|
||||
<ul class="path">
|
||||
<li><a href="#">${rmsMenuPath}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content_middle" id="content_middle">
|
||||
<div class="search_wrap">
|
||||
<button type="button" class="cssbtn" id="btn_excel" level="R" style="display: inline-block;"><i class="material-icons">table_view</i> 엑셀</button>
|
||||
<button type="button" class="cssbtn" id="btn_search" level="R" style="display: inline-block;">
|
||||
<i class="material-icons">search</i> <%= localeMessage.getString("button.search") %>
|
||||
</button>
|
||||
</div>
|
||||
<div class="title" id="title">API 사용현황</div>
|
||||
<table class="search_condition" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width:120px;">조회기간</th>
|
||||
<td colspan="5">
|
||||
<input type="text" name="searchStartDate" id="searchStartDate" value="${param.searchStartDate}" style="width:100px;">
|
||||
~
|
||||
<input type="text" name="searchEndDate" id="searchEndDate" value="${param.searchEndDate}" style="width:100px;">
|
||||
<span style="color:#888; font-size:12px; margin-left:10px;">(최대 31일)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:120px;">조회구분</th>
|
||||
<td>
|
||||
<input type="radio" name="searchType" id="searchTypeORG" value="ORG" checked="checked"><label for="searchTypeORG">제휴사</label></input>
|
||||
<input type="radio" name="searchType" id="searchTypeAPI" value="API"><label for="searchTypeAPI">API</label></input>
|
||||
<input type="radio" name="searchType" id="searchTypeDATE" value="DATE"><label for="searchTypeDATE">사용일</label></input>
|
||||
</td>
|
||||
<th style="width:120px;">제휴사명</th>
|
||||
<td>
|
||||
<input type="text" name="searchOrgName" id="searchOrgName" value="${param.searchOrgName}">
|
||||
</td>
|
||||
<th style="width:120px;">API명</th>
|
||||
<td>
|
||||
<input type="text" name="searchApiName" id="searchApiName" value="${param.searchApiName}">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<table id="grid"></table>
|
||||
<div id="pager"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+256
@@ -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<ApiStatsUI> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<ApiStatsDay, ApiStatsDayId> {
|
||||
|
||||
}
|
||||
@@ -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<ApiStatsDay, ApiStatsDayId, ApiUseStatsRepository> {
|
||||
|
||||
private static final int MAX_DAYS = 31;
|
||||
|
||||
@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);
|
||||
List<Object[]> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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" +
|
||||
|
||||
@@ -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<GridResponse<ApiStatsUI>> selectList(ApiStatsSearch search, Pageable pageable) {
|
||||
Pageable sortedPageable = PageRequest.of(
|
||||
pageable.getPageNumber(),
|
||||
pageable.getPageSize(),
|
||||
Sort.by(Sort.Order.desc("statTime")));
|
||||
|
||||
Page<ApiStatsDay> page = service.selectList(search, sortedPageable);
|
||||
Page<ApiStatsUI> uiPage = page.map(mapper::toVo);
|
||||
|
||||
return ResponseEntity.ok(new GridResponse<>(uiPage));
|
||||
Page<ApiStatsUI> 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<ApiStatsDay> dataList = service.selectListForExcel(search);
|
||||
log.info("Data retrieved - count: {}", dataList.size());
|
||||
List<ApiStatsUI> 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<ApiStatsUI> 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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user