From d4739e2bc14b5f2323393e2dd2428582c9b86346 Mon Sep 17 00:00:00 2001 From: daekuk1 Date: Wed, 28 Jan 2026 16:59:42 +0900 Subject: [PATCH] =?UTF-8?q?API=EB=AA=85=EB=B3=84=20=EC=9A=94=EC=95=BD=20?= =?UTF-8?q?=ED=86=B5=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jsp/onl/kjb/statistics/apiStatsDayMan.jsp | 97 +++++++++- .../onl/kjb/statistics/apiStatsHourMan.jsp | 95 +++++++++- .../onl/kjb/statistics/apiStatsMinuteMan.jsp | 99 ++++++++-- .../onl/kjb/statistics/apiStatsMonthMan.jsp | 97 +++++++++- .../onl/kjb/statistics/apiStatsYearMan.jsp | 97 +++++++++- .../kjb/statistics/ApiStatsDayService.java | 112 ++++++++++++ .../kjb/statistics/ApiStatsHourService.java | 172 +++++++++++++++++ .../kjb/statistics/ApiStatsMinuteService.java | 173 ++++++++++++++++++ .../kjb/statistics/ApiStatsMonthService.java | 112 ++++++++++++ .../kjb/statistics/ApiStatsYearService.java | 112 ++++++++++++ .../kjb/statistics/ApiStatsDayController.java | 51 ++++++ .../statistics/ApiStatsHourController.java | 51 ++++++ .../statistics/ApiStatsMinuteController.java | 51 ++++++ .../statistics/ApiStatsMonthController.java | 51 ++++++ .../statistics/ApiStatsYearController.java | 51 ++++++ .../job/DailyToMonthlyAggregationJob.java | 22 ++- .../job/HourlyToDailyAggregationJob.java | 22 ++- .../job/MonthlyToYearlyAggregationJob.java | 22 ++- 18 files changed, 1431 insertions(+), 56 deletions(-) diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsDayMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsDayMan.jsp index fafa922..0e5c270 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsDayMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsDayMan.jsp @@ -391,18 +391,28 @@ postData.searchEndDateTime = searchEndDate; } + var summaryPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + if (searchStartDate) { + summaryPostData.searchStartDateTime = searchStartDate; + } + if (searchEndDate) { + summaryPostData.searchEndDateTime = searchEndDate; + } + + $("#gridSummary").setGridParam({ url: url, postData: summaryPostData, page: 1 }).trigger("reloadGrid"); $("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid"); fetchChartData(); } - function exportToExcel() { - console.log('[Excel Export] Starting...'); + function exportToExcel(isSummary) { + var cmdType = isSummary ? 'EXCEL_EXPORT_SUMMARY' : 'EXCEL_EXPORT'; + console.log('[Excel Export] Starting... (type: ' + cmdType + ')'); var searchStartDate = $("input[name=searchStartDateTime]").val().replace(/-/g, ""); var searchEndDate = $("input[name=searchEndDateTime]").val().replace(/-/g, ""); var postData = { - cmd: 'EXCEL_EXPORT', + cmd: cmdType, searchApiName: $("input[name=searchApiName]").val(), searchGwInstanceId: $("input[name=searchGwInstanceId]").val(), searchBizDivCode: $("input[name=searchBizDivCode]").val(), @@ -517,6 +527,50 @@ xhr.send(formData.join('&')); } + function listSummary() { + var gridPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + $('#gridSummary').jqGrid({ + datatype: "json", + mtype: 'POST', + postData: gridPostData, + colNames: [ + 'API명', + '총건수', '성공', 'Timeout', '시스템오류', '업무오류', + 'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류', + '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' + ], + colModel: [ + { name: 'apiName', 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: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false }, + { name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false } + ], + jsonReader: { repeatitems: false }, + pager: $('#pagerSummary'), + 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 }); + } + } + }); + } + function list() { var gridPostData = getSearchForJqgrid("cmd", "LIST"); $('#grid').jqGrid({ @@ -603,17 +657,23 @@ } initCharts(); + listSummary(); list(); search(); fetchChartData(); + resizeJqGridWidth('gridSummary', 'content_middle', '1200'); resizeJqGridWidth('grid', 'content_middle', '1200'); $("#btn_search").click(function() { search(); }); + $("#btn_excel_export_summary").click(function() { + exportToExcel(true); + }); + $("#btn_excel_export").click(function() { - exportToExcel(); + exportToExcel(false); }); $("input[name^=search]").keydown(function(key) { @@ -646,9 +706,6 @@ -
API 일별 통계
@@ -704,8 +761,30 @@
-
-
+ + +
+
+
API명별 요약 통계
+ +
+
+
+
+ + +
+
+
상세 통계
+ +
+
+
+
diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp index 0fabaf4..ac85e11 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsHourMan.jsp @@ -403,15 +403,23 @@ postData.searchEndDateTime = searchDate + "23"; } + var summaryPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + if (searchDate) { + summaryPostData.searchStartDateTime = searchDate + "00"; + summaryPostData.searchEndDateTime = searchDate + "23"; + } + + $("#gridSummary").setGridParam({ url: url, postData: summaryPostData, page: 1 }).trigger("reloadGrid"); $("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid"); fetchChartData(); } - function exportToExcel() { - console.log('[Excel Export] Starting...'); + function exportToExcel(isSummary) { + var cmdType = isSummary ? 'EXCEL_EXPORT_SUMMARY' : 'EXCEL_EXPORT'; + console.log('[Excel Export] Starting... (type: ' + cmdType + ')'); var postData = { - cmd: 'EXCEL_EXPORT', + cmd: cmdType, searchApiName: $("input[name=searchApiName]").val(), searchGwInstanceId: $("input[name=searchGwInstanceId]").val(), searchBizDivCode: $("input[name=searchBizDivCode]").val(), @@ -525,6 +533,50 @@ xhr.send(formData.join('&')); } + function listSummary() { + var gridPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + $('#gridSummary').jqGrid({ + datatype: "json", + mtype: 'POST', + postData: gridPostData, + colNames: [ + 'API명', + '총건수', '성공', 'Timeout', '시스템오류', '업무오류', + 'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류', + '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' + ], + colModel: [ + { name: 'apiName', 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: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false }, + { name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false } + ], + jsonReader: { repeatitems: false }, + pager: $('#pagerSummary'), + 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 }); + } + } + }); + } + function list() { var gridPostData = getSearchForJqgrid("cmd", "LIST"); $('#grid').jqGrid({ @@ -589,17 +641,23 @@ } initCharts(); + listSummary(); list(); search(); fetchChartData(); + resizeJqGridWidth('gridSummary', 'content_middle', '1200'); resizeJqGridWidth('grid', 'content_middle', '1200'); $("#btn_search").click(function() { search(); }); + $("#btn_excel_export_summary").click(function() { + exportToExcel(true); + }); + $("#btn_excel_export").click(function() { - exportToExcel(); + exportToExcel(false); }); $("input[name^=search]").keydown(function(key) { @@ -632,9 +690,6 @@ -
API 시간별 통계
@@ -688,8 +743,30 @@
-
-
+ + +
+
+
API명별 요약 통계
+ +
+
+
+
+ + +
+
+
상세 통계
+ +
+
+
+
diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp index b94b52b..3220afb 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsMinuteMan.jsp @@ -411,15 +411,23 @@ postData.searchEndDateTime = range.end; } + var summaryPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + if (range) { + summaryPostData.searchStartDateTime = range.start; + summaryPostData.searchEndDateTime = range.end; + } + + $("#gridSummary").setGridParam({ url: url, postData: summaryPostData, page: 1 }).trigger("reloadGrid"); $("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid"); fetchChartData(range); } - function exportToExcel() { - console.log('[Excel Export] Starting...'); + function exportToExcel(isSummary) { + var cmdType = isSummary ? 'EXCEL_EXPORT_SUMMARY' : 'EXCEL_EXPORT'; + console.log('[Excel Export] Starting... (type: ' + cmdType + ')'); var postData = { - cmd: 'EXCEL_EXPORT', + cmd: cmdType, searchApiName: $("input[name=searchApiName]").val(), searchGwInstanceId: $("input[name=searchGwInstanceId]").val(), searchBizDivCode: $("input[name=searchBizDivCode]").val(), @@ -536,7 +544,51 @@ xhr.send(formData.join('&')); } - function list() { + function listSummary() { + var gridPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + $('#gridSummary').jqGrid({ + datatype: "json", + mtype: 'POST', + postData: gridPostData, + colNames: [ + 'API명', + '총건수', '성공', 'Timeout', '시스템오류', '업무오류', + 'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류', + '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' + ], + colModel: [ + { name: 'apiName', 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: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false }, + { name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false } + ], + jsonReader: { repeatitems: false }, + pager: $('#pagerSummary'), + 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 }); + } + } + }); + } + + function listDetail() { var gridPostData = getSearchForJqgrid("cmd", "LIST"); $('#grid').jqGrid({ datatype: "json", @@ -626,17 +678,23 @@ } initCharts(); - list(); + listSummary(); + listDetail(); search(); fetchChartData(validateAndAdjustDateRange()); + resizeJqGridWidth('gridSummary', 'content_middle', '1200'); resizeJqGridWidth('grid', 'content_middle', '1200'); $("#btn_search").click(function() { search(); }); + $("#btn_excel_export_summary").click(function() { + exportToExcel(true); + }); + $("#btn_excel_export").click(function() { - exportToExcel(); + exportToExcel(false); }); $("input[name^=search]").keydown(function(key) { @@ -674,9 +732,6 @@ -
API 분단위 통계
@@ -734,8 +789,30 @@
-
-
+ + +
+
+
API명별 요약 통계
+ +
+
+
+
+ + +
+
+
상세 통계
+ +
+
+
+
diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp index 7c837fc..8282166 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsMonthMan.jsp @@ -395,18 +395,28 @@ postData.searchEndDateTime = searchEndDate; } + var summaryPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + if (searchStartDate) { + summaryPostData.searchStartDateTime = searchStartDate; + } + if (searchEndDate) { + summaryPostData.searchEndDateTime = searchEndDate; + } + + $("#gridSummary").setGridParam({ url: url, postData: summaryPostData, page: 1 }).trigger("reloadGrid"); $("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid"); fetchChartData(); } - function exportToExcel() { - console.log('[Excel Export] Starting...'); + function exportToExcel(isSummary) { + var cmdType = isSummary ? 'EXCEL_EXPORT_SUMMARY' : 'EXCEL_EXPORT'; + console.log('[Excel Export] Starting... (type: ' + cmdType + ')'); var searchStartDate = $("input[name=searchStartDateTime]").val().replace(/-/g, ""); var searchEndDate = $("input[name=searchEndDateTime]").val().replace(/-/g, ""); var postData = { - cmd: 'EXCEL_EXPORT', + cmd: cmdType, searchApiName: $("input[name=searchApiName]").val(), searchGwInstanceId: $("input[name=searchGwInstanceId]").val(), searchBizDivCode: $("input[name=searchBizDivCode]").val(), @@ -521,6 +531,50 @@ xhr.send(formData.join('&')); } + function listSummary() { + var gridPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + $('#gridSummary').jqGrid({ + datatype: "json", + mtype: 'POST', + postData: gridPostData, + colNames: [ + 'API명', + '총건수', '성공', 'Timeout', '시스템오류', '업무오류', + 'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류', + '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' + ], + colModel: [ + { name: 'apiName', 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: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false }, + { name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false } + ], + jsonReader: { repeatitems: false }, + pager: $('#pagerSummary'), + 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 }); + } + } + }); + } + function list() { var gridPostData = getSearchForJqgrid("cmd", "LIST"); $('#grid').jqGrid({ @@ -593,17 +647,23 @@ } initCharts(); + listSummary(); list(); search(); fetchChartData(); + resizeJqGridWidth('gridSummary', 'content_middle', '1200'); resizeJqGridWidth('grid', 'content_middle', '1200'); $("#btn_search").click(function() { search(); }); + $("#btn_excel_export_summary").click(function() { + exportToExcel(true); + }); + $("#btn_excel_export").click(function() { - exportToExcel(); + exportToExcel(false); }); $("input[name^=search]").keydown(function(key) { @@ -636,9 +696,6 @@ -
API 월별 통계
@@ -694,8 +751,30 @@
-
-
+ + +
+
+
API명별 요약 통계
+ +
+
+
+
+ + +
+
+
상세 통계
+ +
+
+
+
diff --git a/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp b/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp index 7e01a46..64e8f2f 100644 --- a/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp +++ b/WebContent/jsp/onl/kjb/statistics/apiStatsYearMan.jsp @@ -369,18 +369,28 @@ postData.searchEndDateTime = searchEndDate; } + var summaryPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + if (searchStartDate) { + summaryPostData.searchStartDateTime = searchStartDate; + } + if (searchEndDate) { + summaryPostData.searchEndDateTime = searchEndDate; + } + + $("#gridSummary").setGridParam({ url: url, postData: summaryPostData, page: 1 }).trigger("reloadGrid"); $("#grid").setGridParam({ url: url, postData: postData, page: 1 }).trigger("reloadGrid"); fetchChartData(); } - function exportToExcel() { - console.log('[Excel Export] Starting...'); + function exportToExcel(isSummary) { + var cmdType = isSummary ? 'EXCEL_EXPORT_SUMMARY' : 'EXCEL_EXPORT'; + console.log('[Excel Export] Starting... (type: ' + cmdType + ')'); var searchStartDate = $("input[name=searchStartDateTime]").val().replace(/-/g, ""); var searchEndDate = $("input[name=searchEndDateTime]").val().replace(/-/g, ""); var postData = { - cmd: 'EXCEL_EXPORT', + cmd: cmdType, searchApiName: $("input[name=searchApiName]").val(), searchGwInstanceId: $("input[name=searchGwInstanceId]").val(), searchBizDivCode: $("input[name=searchBizDivCode]").val(), @@ -495,6 +505,50 @@ xhr.send(formData.join('&')); } + function listSummary() { + var gridPostData = getSearchForJqgrid("cmd", "LIST_SUMMARY"); + $('#gridSummary').jqGrid({ + datatype: "json", + mtype: 'POST', + postData: gridPostData, + colNames: [ + 'API명', + '총건수', '성공', 'Timeout', '시스템오류', '업무오류', + 'Seq900 Timeout', 'Seq900 시스템오류', 'Seq900 업무오류', + '평균응답(ms)', '최소응답(ms)', '최대응답(ms)' + ], + colModel: [ + { name: 'apiName', 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: 'timeoutCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'systemErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'bizErrCnt', align: 'right', width: '80', formatter: numberFormatter, sortable: false }, + { name: 'seq900TimeoutCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'seq900SystemErrCnt', align: 'right', width: '120', formatter: numberFormatter, sortable: false }, + { name: 'seq900BizErrCnt', align: 'right', width: '100', formatter: numberFormatter, sortable: false }, + { name: 'avgRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'minRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false }, + { name: 'maxRespTime', align: 'right', width: '90', formatter: decimalFormatter, sortable: false } + ], + jsonReader: { repeatitems: false }, + pager: $('#pagerSummary'), + 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 }); + } + } + }); + } + function list() { var gridPostData = getSearchForJqgrid("cmd", "LIST"); $('#grid').jqGrid({ @@ -562,17 +616,23 @@ } initCharts(); + listSummary(); list(); search(); fetchChartData(); + resizeJqGridWidth('gridSummary', 'content_middle', '1200'); resizeJqGridWidth('grid', 'content_middle', '1200'); $("#btn_search").click(function() { search(); }); + $("#btn_excel_export_summary").click(function() { + exportToExcel(true); + }); + $("#btn_excel_export").click(function() { - exportToExcel(); + exportToExcel(false); }); $("input[name^=search]").keydown(function(key) { @@ -605,9 +665,6 @@ -
API 연간 통계
@@ -662,8 +719,30 @@
-
-
+ + +
+
+
API명별 요약 통계
+ +
+
+
+
+ + +
+
+
상세 통계
+ +
+
+
+
diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsDayService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsDayService.java index 251b1b4..ad7fc2e 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsDayService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsDayService.java @@ -1,11 +1,14 @@ package com.eactive.eai.rms.data.entity.onl.kjb.statistics; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -13,8 +16,10 @@ import org.springframework.transaction.annotation.Transactional; import com.eactive.eai.data.jpa.AbstractDataService; import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; @Service @Transactional @@ -87,6 +92,113 @@ public class ApiStatsDayService .fetch(); } + /** + * API명별 요약 통계 조회 (GROUP BY apiName) + */ + public Page selectSummaryList(ApiStatsSearch search, Pageable pageable) { + QApiStatsDay q = QApiStatsDay.apiStatsDay; + BooleanBuilder builder = buildSearchConditions(q, search); + + // 전체 건수 조회 + Long total = getJPAQueryFactory() + .select(q.apiName.countDistinct()) + .from(q) + .where(builder) + .fetchOne(); + + // 페이징 적용하여 조회 + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + // 가중 평균: SUM(avg * successCnt) / SUM(successCnt) + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return new PageImpl<>(results, pageable, total != null ? total.longValue() : 0); + } + + /** + * API명별 요약 통계 전체 조회 (Excel용) + */ + public List selectSummaryListForExcel(ApiStatsSearch search) { + QApiStatsDay q = QApiStatsDay.apiStatsDay; + BooleanBuilder builder = buildSearchConditions(q, search); + + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return results; + } + private BooleanBuilder buildSearchConditions(QApiStatsDay q, ApiStatsSearch search) { BooleanBuilder builder = new BooleanBuilder(); diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourService.java index 74816c8..91524a7 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsHourService.java @@ -1,11 +1,14 @@ package com.eactive.eai.rms.data.entity.onl.kjb.statistics; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -13,8 +16,10 @@ import org.springframework.transaction.annotation.Transactional; import com.eactive.eai.data.jpa.AbstractDataService; import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; @Service @Transactional @@ -148,6 +153,173 @@ public class ApiStatsHourService .fetch(); } + /** + * API명별 요약 통계 조회 (GROUP BY apiName) + */ + public Page selectSummaryList(ApiStatsSearch search, Pageable pageable) { + QApiStatsHour q = QApiStatsHour.apiStatsHour; + BooleanBuilder builder = new BooleanBuilder(); + + calculateDateRange(search); + if (search.getParsedStartDateTime() != null) { + builder.and(q.statTime.goe(search.getParsedStartDateTime())); + builder.and(q.statTime.loe(search.getParsedEndDateTime())); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + builder.and(q.apiName.containsIgnoreCase(search.getSearchApiName())); + } + + if (StringUtils.isNotBlank(search.getSearchGwInstanceId())) { + builder.and(q.gwInstanceId.containsIgnoreCase(search.getSearchGwInstanceId())); + } + + if (StringUtils.isNotBlank(search.getSearchBizDivCode())) { + builder.and(q.bizDivCode.containsIgnoreCase(search.getSearchBizDivCode())); + } + + if (StringUtils.isNotBlank(search.getSearchClientId())) { + builder.and(q.clientId.containsIgnoreCase(search.getSearchClientId())); + } + + if (StringUtils.isNotBlank(search.getSearchInboundAdapter())) { + builder.and(q.inboundAdapter.containsIgnoreCase(search.getSearchInboundAdapter())); + } + + if (StringUtils.isNotBlank(search.getSearchOutboundAdapter())) { + builder.and(q.outboundAdapter.containsIgnoreCase(search.getSearchOutboundAdapter())); + } + + // 전체 건수 조회 + Long total = getJPAQueryFactory() + .select(q.apiName.countDistinct()) + .from(q) + .where(builder) + .fetchOne(); + + // 페이징 적용하여 조회 + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + // 가중 평균: SUM(avg * successCnt) / SUM(successCnt) + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return new PageImpl<>(results, pageable, total != null ? total.longValue() : 0); + } + + /** + * API명별 요약 통계 전체 조회 (Excel용) + */ + public List selectSummaryListForExcel(ApiStatsSearch search) { + QApiStatsHour q = QApiStatsHour.apiStatsHour; + BooleanBuilder builder = new BooleanBuilder(); + + calculateDateRange(search); + if (search.getParsedStartDateTime() != null) { + builder.and(q.statTime.goe(search.getParsedStartDateTime())); + builder.and(q.statTime.loe(search.getParsedEndDateTime())); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + builder.and(q.apiName.containsIgnoreCase(search.getSearchApiName())); + } + + if (StringUtils.isNotBlank(search.getSearchGwInstanceId())) { + builder.and(q.gwInstanceId.containsIgnoreCase(search.getSearchGwInstanceId())); + } + + if (StringUtils.isNotBlank(search.getSearchBizDivCode())) { + builder.and(q.bizDivCode.containsIgnoreCase(search.getSearchBizDivCode())); + } + + if (StringUtils.isNotBlank(search.getSearchClientId())) { + builder.and(q.clientId.containsIgnoreCase(search.getSearchClientId())); + } + + if (StringUtils.isNotBlank(search.getSearchInboundAdapter())) { + builder.and(q.inboundAdapter.containsIgnoreCase(search.getSearchInboundAdapter())); + } + + if (StringUtils.isNotBlank(search.getSearchOutboundAdapter())) { + builder.and(q.outboundAdapter.containsIgnoreCase(search.getSearchOutboundAdapter())); + } + + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return results; + } + /** * 조회 기간 계산 (최대 24시간 제한) */ diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteService.java index 9a35b35..7466422 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMinuteService.java @@ -1,11 +1,15 @@ package com.eactive.eai.rms.data.entity.onl.kjb.statistics; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; 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; @@ -13,8 +17,10 @@ import org.springframework.transaction.annotation.Transactional; import com.eactive.eai.data.jpa.AbstractDataService; import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; @Service @Transactional @@ -150,6 +156,173 @@ public class ApiStatsMinuteService .fetch(); } + /** + * API명별 요약 통계 조회 (GROUP BY apiName) + */ + public Page selectSummaryList(ApiStatsSearch search, Pageable pageable) { + QApiStatsMinute q = QApiStatsMinute.apiStatsMinute; + BooleanBuilder builder = new BooleanBuilder(); + + calculateDateRange(search); + if (search.getParsedStartDateTime() != null) { + builder.and(q.statTime.goe(search.getParsedStartDateTime())); + builder.and(q.statTime.loe(search.getParsedEndDateTime())); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + builder.and(q.apiName.containsIgnoreCase(search.getSearchApiName())); + } + + if (StringUtils.isNotBlank(search.getSearchGwInstanceId())) { + builder.and(q.gwInstanceId.containsIgnoreCase(search.getSearchGwInstanceId())); + } + + if (StringUtils.isNotBlank(search.getSearchBizDivCode())) { + builder.and(q.bizDivCode.containsIgnoreCase(search.getSearchBizDivCode())); + } + + if (StringUtils.isNotBlank(search.getSearchClientId())) { + builder.and(q.clientId.containsIgnoreCase(search.getSearchClientId())); + } + + if (StringUtils.isNotBlank(search.getSearchInboundAdapter())) { + builder.and(q.inboundAdapter.containsIgnoreCase(search.getSearchInboundAdapter())); + } + + if (StringUtils.isNotBlank(search.getSearchOutboundAdapter())) { + builder.and(q.outboundAdapter.containsIgnoreCase(search.getSearchOutboundAdapter())); + } + + // 전체 건수 조회 + Long total = getJPAQueryFactory() + .select(q.apiName.countDistinct()) + .from(q) + .where(builder) + .fetchOne(); + + // 페이징 적용하여 조회 + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + // 가중 평균: SUM(avg * successCnt) / SUM(successCnt) + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return new PageImpl<>(results, pageable, total != null ? total.longValue() : 0); + } + + /** + * API명별 요약 통계 전체 조회 (Excel용) + */ + public List selectSummaryListForExcel(ApiStatsSearch search) { + QApiStatsMinute q = QApiStatsMinute.apiStatsMinute; + BooleanBuilder builder = new BooleanBuilder(); + + calculateDateRange(search); + if (search.getParsedStartDateTime() != null) { + builder.and(q.statTime.goe(search.getParsedStartDateTime())); + builder.and(q.statTime.loe(search.getParsedEndDateTime())); + } + + if (StringUtils.isNotBlank(search.getSearchApiName())) { + builder.and(q.apiName.containsIgnoreCase(search.getSearchApiName())); + } + + if (StringUtils.isNotBlank(search.getSearchGwInstanceId())) { + builder.and(q.gwInstanceId.containsIgnoreCase(search.getSearchGwInstanceId())); + } + + if (StringUtils.isNotBlank(search.getSearchBizDivCode())) { + builder.and(q.bizDivCode.containsIgnoreCase(search.getSearchBizDivCode())); + } + + if (StringUtils.isNotBlank(search.getSearchClientId())) { + builder.and(q.clientId.containsIgnoreCase(search.getSearchClientId())); + } + + if (StringUtils.isNotBlank(search.getSearchInboundAdapter())) { + builder.and(q.inboundAdapter.containsIgnoreCase(search.getSearchInboundAdapter())); + } + + if (StringUtils.isNotBlank(search.getSearchOutboundAdapter())) { + builder.and(q.outboundAdapter.containsIgnoreCase(search.getSearchOutboundAdapter())); + } + + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return results; + } + /** * 조회 기간 계산 (최대 1시간 제한) */ diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMonthService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMonthService.java index 928ce4a..4545eca 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMonthService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsMonthService.java @@ -1,11 +1,14 @@ package com.eactive.eai.rms.data.entity.onl.kjb.statistics; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -13,8 +16,10 @@ import org.springframework.transaction.annotation.Transactional; import com.eactive.eai.data.jpa.AbstractDataService; import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; @Service @Transactional @@ -87,6 +92,113 @@ public class ApiStatsMonthService .fetch(); } + /** + * API명별 요약 통계 조회 (GROUP BY apiName) + */ + public Page selectSummaryList(ApiStatsSearch search, Pageable pageable) { + QApiStatsMonth q = QApiStatsMonth.apiStatsMonth; + BooleanBuilder builder = buildSearchConditions(q, search); + + // 전체 건수 조회 + Long total = getJPAQueryFactory() + .select(q.apiName.countDistinct()) + .from(q) + .where(builder) + .fetchOne(); + + // 페이징 적용하여 조회 + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + // 가중 평균: SUM(avg * successCnt) / SUM(successCnt) + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return new PageImpl<>(results, pageable, total != null ? total.longValue() : 0); + } + + /** + * API명별 요약 통계 전체 조회 (Excel용) + */ + public List selectSummaryListForExcel(ApiStatsSearch search) { + QApiStatsMonth q = QApiStatsMonth.apiStatsMonth; + BooleanBuilder builder = buildSearchConditions(q, search); + + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return results; + } + private BooleanBuilder buildSearchConditions(QApiStatsMonth q, ApiStatsSearch search) { BooleanBuilder builder = new BooleanBuilder(); diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsYearService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsYearService.java index 64145eb..65842b0 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsYearService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/kjb/statistics/ApiStatsYearService.java @@ -1,10 +1,13 @@ package com.eactive.eai.rms.data.entity.onl.kjb.statistics; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.Year; import java.util.List; import org.apache.commons.lang3.StringUtils; 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; @@ -12,8 +15,10 @@ import org.springframework.transaction.annotation.Transactional; import com.eactive.eai.data.jpa.AbstractDataService; import com.eactive.ext.kjb.statistics.ui.ApiStatsChartUI; import com.eactive.ext.kjb.statistics.ui.ApiStatsSearch; +import com.eactive.ext.kjb.statistics.ui.ApiStatsUI; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; @Service @Transactional @@ -84,6 +89,113 @@ public class ApiStatsYearService .fetch(); } + /** + * API명별 요약 통계 조회 (GROUP BY apiName) + */ + public Page selectSummaryList(ApiStatsSearch search, Pageable pageable) { + QApiStatsYear q = QApiStatsYear.apiStatsYear; + BooleanBuilder builder = buildSearchConditions(q, search); + + // 전체 건수 조회 + Long total = getJPAQueryFactory() + .select(q.apiName.countDistinct()) + .from(q) + .where(builder) + .fetchOne(); + + // 페이징 적용하여 조회 + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + // 가중 평균: SUM(avg * successCnt) / SUM(successCnt) + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return new PageImpl<>(results, pageable, total != null ? total.longValue() : 0); + } + + /** + * API명별 요약 통계 전체 조회 (Excel용) + */ + public List selectSummaryListForExcel(ApiStatsSearch search) { + QApiStatsYear q = QApiStatsYear.apiStatsYear; + BooleanBuilder builder = buildSearchConditions(q, search); + + List results = getJPAQueryFactory() + .select(Projections.fields(ApiStatsUI.class, + q.apiName, + q.totalCnt.sum().as("totalCnt"), + q.successCnt.sum().as("successCnt"), + q.timeoutCnt.sum().as("timeoutCnt"), + q.systemErrCnt.sum().as("systemErrCnt"), + q.bizErrCnt.sum().as("bizErrCnt"), + q.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), + q.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), + q.seq900BizErrCnt.sum().as("seq900BizErrCnt"), + q.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), + q.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), + Expressions.cases() + .when(q.successCnt.sum().gt(0)) + .then(q.avgRespTime.multiply(q.successCnt).sum() + .divide(q.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"))) + .from(q) + .where(builder) + .groupBy(q.apiName) + .orderBy(q.apiName.asc()) + .fetch(); + + // 소수점 3자리로 반올림 + results.forEach(ui -> { + if (ui.getAvgRespTime() != null) { + ui.setAvgRespTime(ui.getAvgRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMinRespTime() != null) { + ui.setMinRespTime(ui.getMinRespTime().setScale(3, RoundingMode.HALF_UP)); + } + if (ui.getMaxRespTime() != null) { + ui.setMaxRespTime(ui.getMaxRespTime().setScale(3, RoundingMode.HALF_UP)); + } + }); + + return results; + } + private BooleanBuilder buildSearchConditions(QApiStatsYear q, ApiStatsSearch search) { BooleanBuilder builder = new BooleanBuilder(); diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsDayController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsDayController.java index 3221419..01bba22 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsDayController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsDayController.java @@ -63,6 +63,12 @@ public class ApiStatsDayController { return ResponseEntity.ok(chartData); } + @PostMapping(value = "/onl/kjb/statistics/apiStatsDayMan.json", params = "cmd=LIST_SUMMARY") + public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { + Page page = service.selectSummaryList(search, pageable); + return ResponseEntity.ok(new GridResponse<>(page)); + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsDayMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); @@ -106,10 +112,55 @@ public class ApiStatsDayController { } } + @PostMapping(value = "/onl/kjb/statistics/apiStatsDayMan.json", params = "cmd=EXCEL_EXPORT_SUMMARY") + public void exportSummaryToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { + log.info("Excel export (summary) started - search: {}", search); + + try { + List dataList = service.selectSummaryListForExcel(search); + log.info("Summary data retrieved - count: {}", dataList.size()); + + if (dataList.isEmpty()) { + log.warn("No data found for export"); + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"조회된 데이터가 없습니다.\"}"); + response.getWriter().flush(); + return; + } + + String fileName = generateSummaryFileName(search); + log.info("Generating Excel file: {}", fileName); + + excelExportService.exportApiStats(dataList, fileName, response); + log.info("Excel export (summary) completed successfully"); + + } catch (Exception e) { + log.error("Excel export (summary) failed - error: {}", e.getMessage(), e); + + if (!response.isCommitted()) { + response.reset(); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"Excel 파일 생성 중 오류가 발생했습니다: " + e.getMessage() + "\"}"); + response.getWriter().flush(); + } else { + log.error("Cannot send error response - response already committed"); + } + } + } + private String generateFileName(ApiStatsSearch search) { String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) ? search.getSearchStartDateTime() : LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); return "API일별통계_" + timeRange + ".xlsx"; } + + private String generateSummaryFileName(ApiStatsSearch search) { + String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) + ? search.getSearchStartDateTime() + : LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + return "API일별통계_요약_" + timeRange + ".xlsx"; + } } diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java index 1869eba..20b2a9b 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsHourController.java @@ -63,6 +63,12 @@ public class ApiStatsHourController { return ResponseEntity.ok(chartData); } + @PostMapping(value = "/onl/kjb/statistics/apiStatsHourMan.json", params = "cmd=LIST_SUMMARY") + public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { + Page page = service.selectSummaryList(search, pageable); + return ResponseEntity.ok(new GridResponse<>(page)); + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsHourMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); @@ -106,10 +112,55 @@ public class ApiStatsHourController { } } + @PostMapping(value = "/onl/kjb/statistics/apiStatsHourMan.json", params = "cmd=EXCEL_EXPORT_SUMMARY") + public void exportSummaryToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { + log.info("Excel export (summary) started - search: {}", search); + + try { + List dataList = service.selectSummaryListForExcel(search); + log.info("Summary data retrieved - count: {}", dataList.size()); + + if (dataList.isEmpty()) { + log.warn("No data found for export"); + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"조회된 데이터가 없습니다.\"}"); + response.getWriter().flush(); + return; + } + + String fileName = generateSummaryFileName(search); + log.info("Generating Excel file: {}", fileName); + + excelExportService.exportApiStats(dataList, fileName, response); + log.info("Excel export (summary) completed successfully"); + + } catch (Exception e) { + log.error("Excel export (summary) failed - error: {}", e.getMessage(), e); + + if (!response.isCommitted()) { + response.reset(); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"Excel 파일 생성 중 오류가 발생했습니다: " + e.getMessage() + "\"}"); + response.getWriter().flush(); + } else { + log.error("Cannot send error response - response already committed"); + } + } + } + private String generateFileName(ApiStatsSearch search) { String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) ? search.getSearchStartDateTime() : LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHH")); return "API시간별통계_" + timeRange + ".xlsx"; } + + private String generateSummaryFileName(ApiStatsSearch search) { + String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) + ? search.getSearchStartDateTime() + : LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHH")); + return "API시간별통계_요약_" + timeRange + ".xlsx"; + } } diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java index baf46a5..a153172 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMinuteController.java @@ -63,6 +63,12 @@ public class ApiStatsMinuteController { return ResponseEntity.ok(chartData); } + @PostMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.json", params = "cmd=LIST_SUMMARY") + public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { + Page page = service.selectSummaryList(search, pageable); + return ResponseEntity.ok(new GridResponse<>(page)); + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); @@ -106,10 +112,55 @@ public class ApiStatsMinuteController { } } + @PostMapping(value = "/onl/kjb/statistics/apiStatsMinuteMan.json", params = "cmd=EXCEL_EXPORT_SUMMARY") + public void exportSummaryToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { + log.info("Excel export (summary) started - search: {}", search); + + try { + List dataList = service.selectSummaryListForExcel(search); + log.info("Summary data retrieved - count: {}", dataList.size()); + + if (dataList.isEmpty()) { + log.warn("No data found for export"); + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"조회된 데이터가 없습니다.\"}"); + response.getWriter().flush(); + return; + } + + String fileName = generateSummaryFileName(search); + log.info("Generating Excel file: {}", fileName); + + excelExportService.exportApiStats(dataList, fileName, response); + log.info("Excel export (summary) completed successfully"); + + } catch (Exception e) { + log.error("Excel export (summary) failed - error: {}", e.getMessage(), e); + + if (!response.isCommitted()) { + response.reset(); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"Excel 파일 생성 중 오류가 발생했습니다: " + e.getMessage() + "\"}"); + response.getWriter().flush(); + } else { + log.error("Cannot send error response - response already committed"); + } + } + } + private String generateFileName(ApiStatsSearch search) { String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) ? search.getSearchStartDateTime() : LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")); return "API분단위통계_" + timeRange + ".xlsx"; } + + private String generateSummaryFileName(ApiStatsSearch search) { + String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) + ? search.getSearchStartDateTime() + : LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")); + return "API분단위통계_요약_" + timeRange + ".xlsx"; + } } diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMonthController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMonthController.java index 55e474e..5f18d85 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMonthController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsMonthController.java @@ -63,6 +63,12 @@ public class ApiStatsMonthController { return ResponseEntity.ok(chartData); } + @PostMapping(value = "/onl/kjb/statistics/apiStatsMonthMan.json", params = "cmd=LIST_SUMMARY") + public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { + Page page = service.selectSummaryList(search, pageable); + return ResponseEntity.ok(new GridResponse<>(page)); + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsMonthMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); @@ -106,10 +112,55 @@ public class ApiStatsMonthController { } } + @PostMapping(value = "/onl/kjb/statistics/apiStatsMonthMan.json", params = "cmd=EXCEL_EXPORT_SUMMARY") + public void exportSummaryToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { + log.info("Excel export (summary) started - search: {}", search); + + try { + List dataList = service.selectSummaryListForExcel(search); + log.info("Summary data retrieved - count: {}", dataList.size()); + + if (dataList.isEmpty()) { + log.warn("No data found for export"); + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"조회된 데이터가 없습니다.\"}"); + response.getWriter().flush(); + return; + } + + String fileName = generateSummaryFileName(search); + log.info("Generating Excel file: {}", fileName); + + excelExportService.exportApiStats(dataList, fileName, response); + log.info("Excel export (summary) completed successfully"); + + } catch (Exception e) { + log.error("Excel export (summary) failed - error: {}", e.getMessage(), e); + + if (!response.isCommitted()) { + response.reset(); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"Excel 파일 생성 중 오류가 발생했습니다: " + e.getMessage() + "\"}"); + response.getWriter().flush(); + } else { + log.error("Cannot send error response - response already committed"); + } + } + } + private String generateFileName(ApiStatsSearch search) { String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) ? search.getSearchStartDateTime() : YearMonth.now().format(DateTimeFormatter.ofPattern("yyyyMM")); return "API월별통계_" + timeRange + ".xlsx"; } + + private String generateSummaryFileName(ApiStatsSearch search) { + String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) + ? search.getSearchStartDateTime() + : YearMonth.now().format(DateTimeFormatter.ofPattern("yyyyMM")); + return "API월별통계_요약_" + timeRange + ".xlsx"; + } } diff --git a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsYearController.java b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsYearController.java index c2e587a..8b2fb2a 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsYearController.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/ApiStatsYearController.java @@ -62,6 +62,12 @@ public class ApiStatsYearController { return ResponseEntity.ok(chartData); } + @PostMapping(value = "/onl/kjb/statistics/apiStatsYearMan.json", params = "cmd=LIST_SUMMARY") + public ResponseEntity> selectSummaryList(ApiStatsSearch search, Pageable pageable) { + Page page = service.selectSummaryList(search, pageable); + return ResponseEntity.ok(new GridResponse<>(page)); + } + @PostMapping(value = "/onl/kjb/statistics/apiStatsYearMan.json", params = "cmd=EXCEL_EXPORT") public void exportToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { log.info("Excel export started - search: {}", search); @@ -105,10 +111,55 @@ public class ApiStatsYearController { } } + @PostMapping(value = "/onl/kjb/statistics/apiStatsYearMan.json", params = "cmd=EXCEL_EXPORT_SUMMARY") + public void exportSummaryToExcel(ApiStatsSearch search, HttpServletResponse response) throws IOException { + log.info("Excel export (summary) started - search: {}", search); + + try { + List dataList = service.selectSummaryListForExcel(search); + log.info("Summary data retrieved - count: {}", dataList.size()); + + if (dataList.isEmpty()) { + log.warn("No data found for export"); + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"조회된 데이터가 없습니다.\"}"); + response.getWriter().flush(); + return; + } + + String fileName = generateSummaryFileName(search); + log.info("Generating Excel file: {}", fileName); + + excelExportService.exportApiStats(dataList, fileName, response); + log.info("Excel export (summary) completed successfully"); + + } catch (Exception e) { + log.error("Excel export (summary) failed - error: {}", e.getMessage(), e); + + if (!response.isCommitted()) { + response.reset(); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"message\":\"Excel 파일 생성 중 오류가 발생했습니다: " + e.getMessage() + "\"}"); + response.getWriter().flush(); + } else { + log.error("Cannot send error response - response already committed"); + } + } + } + private String generateFileName(ApiStatsSearch search) { String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) ? search.getSearchStartDateTime() : String.valueOf(Year.now().getValue()); return "API연간통계_" + timeRange + ".xlsx"; } + + private String generateSummaryFileName(ApiStatsSearch search) { + String timeRange = StringUtils.isNotBlank(search.getSearchStartDateTime()) + ? search.getSearchStartDateTime() + : String.valueOf(Year.now().getValue()); + return "API연간통계_요약_" + timeRange + ".xlsx"; + } } diff --git a/src/main/java/com/eactive/ext/kjb/statistics/job/DailyToMonthlyAggregationJob.java b/src/main/java/com/eactive/ext/kjb/statistics/job/DailyToMonthlyAggregationJob.java index 4f5e8f1..149b554 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/job/DailyToMonthlyAggregationJob.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/job/DailyToMonthlyAggregationJob.java @@ -25,6 +25,7 @@ import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDayService; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsDay; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsMonth; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQueryFactory; /** @@ -129,11 +130,26 @@ public class DailyToMonthlyAggregationJob implements Job { qd.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), qd.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), qd.seq900BizErrCnt.sum().as("seq900BizErrCnt"), - qd.avgRespTime.avg().castToNum(BigDecimal.class).as("avgRespTime"), + Expressions.cases() + .when(qd.successCnt.sum().gt(0)) + .then(qd.avgRespTime.multiply(qd.successCnt).sum() + .divide(qd.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"), qd.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), qd.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), - qd.p50RespTime.avg().castToNum(BigDecimal.class).as("p50RespTime"), - qd.p95RespTime.avg().castToNum(BigDecimal.class).as("p95RespTime"))) + Expressions.cases() + .when(qd.successCnt.sum().gt(0)) + .then(qd.p50RespTime.multiply(qd.successCnt).sum() + .divide(qd.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("p50RespTime"), + Expressions.cases() + .when(qd.successCnt.sum().gt(0)) + .then(qd.p95RespTime.multiply(qd.successCnt).sum() + .divide(qd.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("p95RespTime"))) .from(qd) .where(qd.statTime.goe(startDate) .and(qd.statTime.loe(endDate))) diff --git a/src/main/java/com/eactive/ext/kjb/statistics/job/HourlyToDailyAggregationJob.java b/src/main/java/com/eactive/ext/kjb/statistics/job/HourlyToDailyAggregationJob.java index 2495d2d..feb20ee 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/job/HourlyToDailyAggregationJob.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/job/HourlyToDailyAggregationJob.java @@ -24,6 +24,7 @@ import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsHourService; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsDay; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsHour; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQueryFactory; /** @@ -125,11 +126,26 @@ public class HourlyToDailyAggregationJob implements Job { qh.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), qh.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), qh.seq900BizErrCnt.sum().as("seq900BizErrCnt"), - qh.avgRespTime.avg().castToNum(BigDecimal.class).as("avgRespTime"), + Expressions.cases() + .when(qh.successCnt.sum().gt(0)) + .then(qh.avgRespTime.multiply(qh.successCnt).sum() + .divide(qh.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"), qh.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), qh.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), - qh.p50RespTime.avg().castToNum(BigDecimal.class).as("p50RespTime"), - qh.p95RespTime.avg().castToNum(BigDecimal.class).as("p95RespTime"))) + Expressions.cases() + .when(qh.successCnt.sum().gt(0)) + .then(qh.p50RespTime.multiply(qh.successCnt).sum() + .divide(qh.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("p50RespTime"), + Expressions.cases() + .when(qh.successCnt.sum().gt(0)) + .then(qh.p95RespTime.multiply(qh.successCnt).sum() + .divide(qh.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("p95RespTime"))) .from(qh) .where(qh.statTime.goe(startDateTime) .and(qh.statTime.loe(endDateTime))) diff --git a/src/main/java/com/eactive/ext/kjb/statistics/job/MonthlyToYearlyAggregationJob.java b/src/main/java/com/eactive/ext/kjb/statistics/job/MonthlyToYearlyAggregationJob.java index 0e4f932..b0450ee 100644 --- a/src/main/java/com/eactive/ext/kjb/statistics/job/MonthlyToYearlyAggregationJob.java +++ b/src/main/java/com/eactive/ext/kjb/statistics/job/MonthlyToYearlyAggregationJob.java @@ -23,6 +23,7 @@ import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMonthService; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsMonth; import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsYear; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQueryFactory; /** @@ -127,11 +128,26 @@ public class MonthlyToYearlyAggregationJob implements Job { qm.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"), qm.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"), qm.seq900BizErrCnt.sum().as("seq900BizErrCnt"), - qm.avgRespTime.avg().castToNum(BigDecimal.class).as("avgRespTime"), + Expressions.cases() + .when(qm.successCnt.sum().gt(0)) + .then(qm.avgRespTime.multiply(qm.successCnt).sum() + .divide(qm.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("avgRespTime"), qm.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"), qm.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"), - qm.p50RespTime.avg().castToNum(BigDecimal.class).as("p50RespTime"), - qm.p95RespTime.avg().castToNum(BigDecimal.class).as("p95RespTime"))) + Expressions.cases() + .when(qm.successCnt.sum().gt(0)) + .then(qm.p50RespTime.multiply(qm.successCnt).sum() + .divide(qm.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("p50RespTime"), + Expressions.cases() + .when(qm.successCnt.sum().gt(0)) + .then(qm.p95RespTime.multiply(qm.successCnt).sum() + .divide(qm.successCnt.sum()).castToNum(BigDecimal.class)) + .otherwise((BigDecimal) null) + .as("p95RespTime"))) .from(qm) .where(qm.statTime.goe(startMonth) .and(qm.statTime.loe(endMonth)))