API 사용현황 신규개발
This commit is contained in:
+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