From cb4a07007901b7f4c57bcd11cf79f15bd902c0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=A7=80=EC=9D=B8?= Date: Fri, 14 Nov 2025 10:47:38 +0900 Subject: [PATCH] =?UTF-8?q?API=20=ED=98=B8=EC=B6=9C=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=20(=EC=99=B8=EB=B6=80/=EB=82=B4=EB=B6=80)=20=EB=B6=84=EB=A6=AC?= =?UTF-8?q?=20=EC=99=B8=EB=B6=80=20=EC=9A=94=EC=B2=AD=20API=20=ED=86=B5?= =?UTF-8?q?=EC=A0=9C=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApiInDataManagementController.java | 39 ++ .../service/ApiInDataManagementMapper.java | 6 + .../service/ApiInDataManagementService.java | 6 + .../impl/ApiInDataManagementServiceImpl.java | 15 + .../admins/mgmt/Mgmt_Api_Indata_SQL.xml | 65 +- .../admins/mgmt/Mgmt_Api_Outdata_SQL.xml | 40 +- .../constructionProjectManagement/left.jsp | 3 + .../mgmtApi/api-request-statistics-index.jsp | 9 +- .../views/admins/mgmtApi/mgmt-api-outdata.jsp | 594 +++++++++++++++--- 9 files changed, 652 insertions(+), 125 deletions(-) diff --git a/src/main/java/geoinfo/admins/apiManagement/ApiInDataManagementController.java b/src/main/java/geoinfo/admins/apiManagement/ApiInDataManagementController.java index c404258..0cef203 100644 --- a/src/main/java/geoinfo/admins/apiManagement/ApiInDataManagementController.java +++ b/src/main/java/geoinfo/admins/apiManagement/ApiInDataManagementController.java @@ -1,5 +1,6 @@ package geoinfo.admins.apiManagement; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -70,6 +71,44 @@ public class ApiInDataManagementController { return result; } + + /** + * API 관리 > API 호출 통계 + * @param params + * @param model + * @param response + * @param request + * @return + * @throws Exception + */ + @ResponseBody + @RequestMapping(value = "/admins/mgmtApi/i_counts.do", method = RequestMethod.POST) + public HashMap getMgmtApiInDataCounts(@RequestParam HashMap params, ModelMap model, HttpServletResponse response, HttpServletRequest request) throws Exception { + strUtil sUtil = new strUtil(); + HashMap result = new HashMap(); + String STAT_TYPE = sUtil.checkNull((String)params.get("statType")); + String COUNT_FROM_DT = sUtil.checkNull((String)params.get("countFromDt")); + String COUNT_TO_DT = sUtil.checkNull((String)params.get("countToDt")); + params.put("COUNT_FROM_DT", COUNT_FROM_DT); + params.put("COUNT_TO_DT", COUNT_TO_DT); + List countList = new ArrayList<>(); + + if("monthly".equals(STAT_TYPE)) { + countList = apiInDataManagementService.selectWebApiLogMonthlyCount(params); + } else if("yearly".equals(STAT_TYPE)) { + countList = apiInDataManagementService.selectWebApiLogYearlyCount(params); + } else { // daily(기본) + countList = apiInDataManagementService.selectWebApiLogDailyCount(params); + + } + + + result.put("code", "SUCCESS"); + result.put("msg", "일일접속량 데이터 조회를 성공했습니다."); + result.put("data", countList); + + return result; + } /** * API 관리 > 관리 API 목록 diff --git a/src/main/java/geoinfo/admins/apiManagement/service/ApiInDataManagementMapper.java b/src/main/java/geoinfo/admins/apiManagement/service/ApiInDataManagementMapper.java index 8469234..b5fee9a 100644 --- a/src/main/java/geoinfo/admins/apiManagement/service/ApiInDataManagementMapper.java +++ b/src/main/java/geoinfo/admins/apiManagement/service/ApiInDataManagementMapper.java @@ -10,6 +10,12 @@ import egovframework.rte.psl.dataaccess.util.EgovMap; public interface ApiInDataManagementMapper { public EgovMap selectDailyAccessCountByHour(HashMap params) throws Exception; + + public List selectWebApiLogDailyCount(HashMap params) throws Exception; + + public List selectWebApiLogMonthlyCount(HashMap params) throws Exception; + + public List selectWebApiLogYearlyCount(HashMap params) throws Exception; public List selectInfo(HashMap params) throws Exception; diff --git a/src/main/java/geoinfo/admins/apiManagement/service/ApiInDataManagementService.java b/src/main/java/geoinfo/admins/apiManagement/service/ApiInDataManagementService.java index 9ff5c1e..806c2ec 100644 --- a/src/main/java/geoinfo/admins/apiManagement/service/ApiInDataManagementService.java +++ b/src/main/java/geoinfo/admins/apiManagement/service/ApiInDataManagementService.java @@ -10,6 +10,12 @@ import egovframework.rte.psl.dataaccess.util.EgovMap; public interface ApiInDataManagementService { public EgovMap selectDailyAccessCountByHour(HashMap params) throws Exception; + + public List selectWebApiLogDailyCount(HashMap params) throws Exception; + + public List selectWebApiLogMonthlyCount(HashMap params) throws Exception; + + public List selectWebApiLogYearlyCount(HashMap params) throws Exception; public List selectInfo(HashMap params) throws Exception; diff --git a/src/main/java/geoinfo/admins/apiManagement/service/impl/ApiInDataManagementServiceImpl.java b/src/main/java/geoinfo/admins/apiManagement/service/impl/ApiInDataManagementServiceImpl.java index 9cf2082..1b1903a 100644 --- a/src/main/java/geoinfo/admins/apiManagement/service/impl/ApiInDataManagementServiceImpl.java +++ b/src/main/java/geoinfo/admins/apiManagement/service/impl/ApiInDataManagementServiceImpl.java @@ -22,6 +22,21 @@ public class ApiInDataManagementServiceImpl implements ApiInDataManagementServic return masterMapper.selectDailyAccessCountByHour(params); } + @Override + public List selectWebApiLogDailyCount(HashMap params) throws Exception { + return masterMapper.selectWebApiLogDailyCount(params); + } + + @Override + public List selectWebApiLogMonthlyCount(HashMap params) throws Exception { + return masterMapper.selectWebApiLogMonthlyCount(params); + } + + @Override + public List selectWebApiLogYearlyCount(HashMap params) throws Exception { + return masterMapper.selectWebApiLogYearlyCount(params); + } + @Override public List selectInfo(HashMap params) throws Exception { return masterMapper.selectInfo(params); diff --git a/src/main/resources/geoinfo/sqlmap/mappers/admins/mgmt/Mgmt_Api_Indata_SQL.xml b/src/main/resources/geoinfo/sqlmap/mappers/admins/mgmt/Mgmt_Api_Indata_SQL.xml index 7e3ea91..1ba8a6e 100644 --- a/src/main/resources/geoinfo/sqlmap/mappers/admins/mgmt/Mgmt_Api_Indata_SQL.xml +++ b/src/main/resources/geoinfo/sqlmap/mappers/admins/mgmt/Mgmt_Api_Indata_SQL.xml @@ -30,12 +30,73 @@ NVL(SUM(CASE WHEN TO_CHAR(ACCESS_DT, 'HH24') = '21' THEN 1 END), 0) AS COUNT_21, NVL(SUM(CASE WHEN TO_CHAR(ACCESS_DT, 'HH24') = '22' THEN 1 END), 0) AS COUNT_22, NVL(SUM(CASE WHEN TO_CHAR(ACCESS_DT, 'HH24') = '23' THEN 1 END), 0) AS COUNT_23 - FROM GEOINFO.WEB_API_LOG + FROM GEOINFO.WEB_API_INBOUND_LOG WHERE ACCESS_DT >= TRUNC(TO_DATE(#{CHART_DATE}, 'YYYY-MM-DD')) AND ACCESS_DT TRUNC(TO_DATE(#{CHART_DATE}, 'YYYY-MM-DD')) + 1 + + + + + + + + diff --git a/src/main/resources/geoinfo/sqlmap/mappers/admins/mgmt/Mgmt_Api_Outdata_SQL.xml b/src/main/resources/geoinfo/sqlmap/mappers/admins/mgmt/Mgmt_Api_Outdata_SQL.xml index 2ddbe8e..331650b 100644 --- a/src/main/resources/geoinfo/sqlmap/mappers/admins/mgmt/Mgmt_Api_Outdata_SQL.xml +++ b/src/main/resources/geoinfo/sqlmap/mappers/admins/mgmt/Mgmt_Api_Outdata_SQL.xml @@ -30,7 +30,7 @@ NVL(SUM(CASE WHEN TO_CHAR(ACCESS_DT, 'HH24') = '21' THEN 1 END), 0) AS COUNT_21, NVL(SUM(CASE WHEN TO_CHAR(ACCESS_DT, 'HH24') = '22' THEN 1 END), 0) AS COUNT_22, NVL(SUM(CASE WHEN TO_CHAR(ACCESS_DT, 'HH24') = '23' THEN 1 END), 0) AS COUNT_23 - FROM GEOINFO.WEB_API_LOG + FROM GEOINFO.WEB_API_OUTBOUND_LOG WHERE ACCESS_DT >= TRUNC(TO_DATE(#{CHART_DATE}, 'YYYY-MM-DD')) AND ACCESS_DT TRUNC(TO_DATE(#{CHART_DATE}, 'YYYY-MM-DD')) + 1 @@ -38,19 +38,19 @@ @@ -65,13 +65,13 @@ ) + 1 ) SELECT TO_CHAR(MR.MONTH_START, 'YYYY-MM') AS period - ,wai.name AS api_name + ,WAO.name AS api_name ,NVL(COUNT(WAL.ACCESS_DT), 0) AS access_count FROM MONTH_RANGE MR - LEFT JOIN GEOINFO.WEB_API_LOG WAL ON TRUNC(WAL.ACCESS_DT, 'MM') = MR.MONTH_START - INNER JOIN GEOINFO.WEB_API_INBOUND WAI ON WAL.API_SEQ = WAI.IDX - GROUP BY MR.MONTH_START, wai.name, wai.idx - ORDER BY MR.MONTH_START DESC, wai.idx ASC, wai.name + LEFT JOIN GEOINFO.WEB_API_OUTBOUND_LOG WAL ON TRUNC(WAL.ACCESS_DT, 'MM') = MR.MONTH_START + INNER JOIN GEOINFO.WEB_API_OUTBOUND WAO ON WAL.API_SEQ = WAO.IDX + GROUP BY MR.MONTH_START, WAO.name, WAO.idx + ORDER BY MR.MONTH_START DESC, WAO.idx ASC, WAO.name @@ -88,13 +88,13 @@ ) + 1 ) SELECT TO_CHAR(YR.YEAR_START, 'YYYY') AS period - ,wai.name AS api_name + ,WAO.name AS api_name ,NVL(COUNT(WAL.ACCESS_DT), 0) AS access_count FROM YEAR_RANGE YR - LEFT JOIN GEOINFO.WEB_API_LOG WAL ON TRUNC(WAL.ACCESS_DT, 'YYYY') = YR.YEAR_START - INNER JOIN GEOINFO.WEB_API_INBOUND WAI ON WAL.API_SEQ = WAI.IDX - GROUP BY YR.YEAR_START, wai.name, wai.idx - ORDER BY YR.YEAR_START DESC, wai.idx ASC, wai.name + LEFT JOIN GEOINFO.WEB_API_OUTBOUND_LOG WAL ON TRUNC(WAL.ACCESS_DT, 'YYYY') = YR.YEAR_START + INNER JOIN GEOINFO.WEB_API_OUTBOUND WAO ON WAL.API_SEQ = WAO.IDX + GROUP BY YR.YEAR_START, WAO.name, WAO.idx + ORDER BY YR.YEAR_START DESC, WAO.idx ASC, WAO.name - UPDATE WEB_API_INBOUND + UPDATE WEB_API_OUTBOUND SET ACTIVE_YN = #{activeYn} diff --git a/src/main/webapp/WEB-INF/views/admins/constructionProjectManagement/left.jsp b/src/main/webapp/WEB-INF/views/admins/constructionProjectManagement/left.jsp index e4bbf32..011ee34 100644 --- a/src/main/webapp/WEB-INF/views/admins/constructionProjectManagement/left.jsp +++ b/src/main/webapp/WEB-INF/views/admins/constructionProjectManagement/left.jsp @@ -80,6 +80,9 @@ img { border:0; } + diff --git a/src/main/webapp/WEB-INF/views/admins/mgmtApi/api-request-statistics-index.jsp b/src/main/webapp/WEB-INF/views/admins/mgmtApi/api-request-statistics-index.jsp index 964aa4a..358c620 100644 --- a/src/main/webapp/WEB-INF/views/admins/mgmtApi/api-request-statistics-index.jsp +++ b/src/main/webapp/WEB-INF/views/admins/mgmtApi/api-request-statistics-index.jsp @@ -599,7 +599,6 @@ format: "yyyy", language: "ko", autoclose: true, -// minViewMode: 1, // 월 단위 todayHighlight: true, container: $("#count_from_dt").closest(".datepicker-wrapper") }); @@ -619,12 +618,12 @@ animateValue("delayRate", 0, 0, 1500); animateValue("errorRate", 0, 0, 1500); + // API 호출 통계 > 일별, 월별, 연도별 통계 + getApiStat(); + // API 호출 통제 목록 조회 getMgmtApiList(); - // API 호출 통계 > 일별 통계 - getApiStat(); - // API 호출로그 목록 조회 getMgmtApiLogList(); @@ -753,7 +752,7 @@ } $.ajax({ type : "POST", - url : "/admins/mgmtApi/o_counts.do" , + url : "/admins/mgmtApi/i_counts.do" , data : { statType : statType, countFromDt: $('#count_from_dt').val(), diff --git a/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-outdata.jsp b/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-outdata.jsp index 1db5e79..ddf8e2d 100644 --- a/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-outdata.jsp +++ b/src/main/webapp/WEB-INF/views/admins/mgmtApi/mgmt-api-outdata.jsp @@ -3,11 +3,10 @@ <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui"%> - - + + - - + @@ -15,14 +14,127 @@ @@ -189,9 +312,78 @@

📈 일일 접속량

- + +
+ +
+ + +
+
+

국토지반 API 호출 통계

+ +
+ + + +
+
+ ~ + +
+
+

일별 API 호출 현황

+ + + + + + + + + + + +
날짜API 명호출 건수
표시 할 통계데이타가 없습니다.
+
+
+

월별 API 호출 현황

+ + + + + + + + + + + +
API 명총 호출 건수
표시 할 통계데이타가 없습니다.
+
+
+

연도별 API 호출 현황

+ + + + + + + + + + + +
연도API 명총 호출 건수
표시 할 통계데이타가 없습니다.
+
+
+
@@ -245,17 +437,17 @@
- +
-

API 목록

- - - - +

API 호출 통제

+
-
+
표시 할 API 목록이 존재하지 않습니다.
@@ -313,22 +505,124 @@ requestAnimationFrame(animation); } + // --------- 일일 접속량 차트 관련 변수 -------------------- let trafficChart; // 전역 변수 + // --------- 일일 접속량 차트 관련 변수 -------------------- + + // --------- API 호출 통계 기간 입력 관련 변수 -------------------- + var today = new Date(); // (기준)오늘 날짜 + var thisYear = today.getFullYear(); // (기준)올해연도 + var thisMonth = today.getMonth+1; // (기준)이번달 + var toDate = new Date(today.getFullYear(), today.getMonth(), 1); // (기준)이번달1일 + + var weekAgo = new Date(); // 7일 전 날짜 + weekAgo.setDate(today.getDate() - 7); + + var fvMonthAgo = new Date(); + fvMonthAgo.setMonth(toDate.getMonth() -5); + // --------- API 호출 통계 기간 입력 관련 변수 -------------------- + + // --------- API 호출 통계 CSV 내보내기 관련 변수 -------------------- + let processedStats = { + daily: new Map(), + monthly: new Map(), + yearly: new Map() + }; + // --------- API 호출 통계 CSV 내보내기 관련 변수 -------------------- + $(document).ready(function(){ + // ---------------------------------------------------- + // 1. 날짜 선택기 초기화 + // ---------------------------------------------------- + // datepicker - $("#chartDate").datepicker({ - format: "yyyy-mm-dd", + $("#chartDate, #count_from_dt, #count_to_dt").datepicker({ + format: "yyyy-mm-dd", language: "ko", - autoclose: true + autoclose: true, + todayHighlight: true, + container: $("#chartDate").closest(".datepicker-wrapper") }).datepicker("setDate", new Date()); + + $("#count_from_dt").datepicker("setDate", weekAgo); // 국토지반 API 호출 통계 시작일 셋팅(저번주) - // API 호출 현황 + // ---------------------------------------------------- + // 2. 탭 전환 처리 + // ---------------------------------------------------- + $('.tab-link').on('click', function() { + const targetId = $(this).data('tab'); + + $('.tab-link').removeClass('active'); + $(this).addClass('active'); + + $('.tab-content').removeClass('active'); + $('#' + targetId).addClass('active'); + + $("#count_from_dt, #count_to_dt").datepicker('destroy'); // datepicker 재선언(format 변경) + // 일별 탭 클릭 시 + if (targetId === 'daily') { + // 월 단위 datepicker 재초기화 + $("#count_from_dt, #count_to_dt").datepicker({ + format: "yyyy-mm-dd", + language: "ko", + autoclose: true, + minViewMode: 0, // 월 단위 + todayHighlight: true, + container: $("#count_from_dt").closest(".datepicker-wrapper") + }); + $("#count_to_dt").datepicker("setDate", today); // 국토지반 API 호출 통계 시작일 셋팅(저번주) + $("#count_from_dt").datepicker("setDate", weekAgo); // 국토지반 API 호출 통계 시작일 셋팅(저번주) + } + // 월별 탭 클릭 시 + if (targetId === 'monthly') { + // 월 단위 datepicker 재초기화 + $("#count_from_dt, #count_to_dt").datepicker({ + format: "yyyy-mm", + language: "ko", + autoclose: true, + minViewMode: 1, // 월 단위 + todayHighlight: true, + container: $("#count_from_dt").closest(".datepicker-wrapper") + }); + + $("#count_from_dt").datepicker("setDate", fvMonthAgo); + $("#count_to_dt").datepicker("setDate", thisMonth); + + $("#count_from_dt").val($("#count_from_dt").val().substr(0,7)); + $("#count_to_dt").val($("#count_to_dt").val().substr(0,7)); + } + + // 연도별 탭 클릭 시 + if (targetId === 'yearly') { + // 월 단위 datepicker 재초기화 + $("#count_from_dt, #count_to_dt").datepicker({ + minViewMode: 'years', + format: "yyyy", + language: "ko", + autoclose: true, + todayHighlight: true, + container: $("#count_from_dt").closest(".datepicker-wrapper") + }); + + $("#count_to_dt").datepicker("setDate", new Date(thisYear, 0, 1)); + $("#count_from_dt").datepicker("setDate", new Date((thisYear -5), 0, 1)); + $("#count_to_dt").val($("#count_to_dt").val().substr(0,4)) + $("#count_from_dt").val($("#count_from_dt").val().substr(0,4)) + } + }); + + // ---------------------------------------------------- + // 3. API 호출 현황 초기 로드 + // ---------------------------------------------------- animateValue("successRate", 0, 100, 1500); animateValue("failRate", 0, 0, 1500); animateValue("delayRate", 0, 0, 1500); animateValue("errorRate", 0, 0, 1500); - // API 목록 목록 조회 + // API 호출 통계 > 일별, 월별, 연도별 통계 + getApiStat(); + + // 외부요청 API 호출 통제 목록 조회 getMgmtApiList(); // API 호출로그 목록 조회 @@ -341,9 +635,22 @@ $(document).on('change', '#chartDate', function() { getMgmtApiChart(); }); + + // API 호출 현황 건수 - 날짜 변경 시 차트 다시 로드 + $(document).on('change', '#count_from_dt, #count_to_dt', function() { + if(!validDate()){ + return; + } + getApiStat(); + }); + + // API 호출 통계 건수 - CSV 내보내기 클릭 + $(document).on('click', '#export-csv-btn', function() { + exportToCSV(); + }) }); - // 외부요청 API 목록 조회 + // 외부요청 API 호출 통제 목록 조회 function getMgmtApiList(){ $.ajax({ type : "POST", @@ -353,7 +660,7 @@ success : function(res){ // res.code, res.msg, res.data if (res.code == "SUCCESS") { let procList = res.data; //Array List - const $listContainer = $("#apiSwitchList"); + const $listContainer = $("#outApiSwitchList"); $listContainer.empty(); // 기존 내용 제거 // 데이터 반복 $.each(res.data, function(i, item) { @@ -377,7 +684,7 @@ let idxArr = []; let active = $(ele).is(":checked") ? "Y" : "N" if (btnType == 'globalSwitch') { - $('#apiSwitchList .api-switch').each(function(i, el) { + $('#outApiSwitchList .api-switch').each(function(i, el) { const idx = $(el).data('idx'); idxArr.push(idx); }); @@ -387,14 +694,14 @@ $.ajax({ type : "POST", - url : "/admins/mgmtApi/toggleSts.do" , + url : "/admins/mgmtApi/o_toggleSts.do" , data : {idxArr:idxArr, activeYn: active}, traditional: true, dataType :"json", success : function(res){ // res.code, res.msg, res.data if (res.code == "SUCCESS") { let procList = res.data; //Array List - const $listContainer = $("#apiSwitchList"); + const $listContainer = $("#outApiSwitchList"); $listContainer.empty(); // 기존 내용 제거 // 데이터 반복 $.each(res.data, function(i, item) { @@ -422,15 +729,110 @@
\${item.idx} \${item.name}
\${item.desc.replace(/\\n/g, "
")}
- <%--
`; return html; } + // API 호출 통계 > 일별, 월별, 연도별 통계 + function getApiStat(){ + var statType = $('.stats-container > .tab-nav').find('.active').data("tab"); + var fromDt = $('#count_from_dt').val(); + var toDt = $('#count_to_dt').val(); + // 날짜데이터가 올바르게 셋팅되지 않았을때는 요청하지 않는다. + if (statType === "monthly") { + if (fromDt.length < 7 || toDt.length < 7 ) return; + } else if (statType === "yearly") { + if (fromDt.length < 4 || toDt.length < 4 ) return; + } else { // statType === "daily" + if (fromDt.length < 10 || toDt.length < 10 ) return; + } + $.ajax({ + type : "POST", + url : "/admins/mgmtApi/o_counts.do" , + data : { + statType : statType, + countFromDt: $('#count_from_dt').val(), + countToDt: $('#count_to_dt').val() + }, + dataType :"json", + success : function(res){ // res.code, res.msg, res.data + if (res.code == "SUCCESS") { + let countList = res.data; + let $listContainer; + // 활성화 된 탭의 tbody를 컨테이너로 지정 + if (statType === "monthly") { + $listContainer = $("#monthly-tbody"); + } else if (statType === "yearly") { + $listContainer = $("#yearly-tbody"); + } else { + $listContainer = $("#daily-tbody"); + } + + // 활성화 된 탭의 csv 양식으로 지정 + if (statType === "monthly") { + $listContainer = $("#monthly-tbody"); + processedStats.monthly = countList; + } else if (statType === "yearly") { + $listContainer = $("#yearly-tbody"); + processedStats.yearly = countList; + } else { + $listContainer = $("#daily-tbody"); + processedStats.daily = countList; + } + // DOM에 추가 + drawApiCountList(res.data, $listContainer); + } else { + alert("API 호출 로그 목록 조회중 오류가 발생하였습니다. 다시 시도해주세요."); + } + }, + error : function(response){ + alert("API 호출 로그 목록 조회중 내부 오류가 발생하였습니다. 다시 시도해주세요."); + } + }); + }; + + function drawApiCountList(countList, $target) { + $target.empty(); + if (!countList || countList.length === 0) { + $target.append('표시 할 통계데이타가 없습니다.'); + return; + } + + const grouped = {}; + let totalCount = 0; // 합계 초기화 + + countList.forEach(item => { + if (!grouped[item.period]) grouped[item.period] = []; + grouped[item.period].push(item); + totalCount += parseInt(item.accessCount); + }); + + let html = ''; + Object.keys(grouped).sort((a, b) => b.localeCompare(a)).forEach(period => { + const items = grouped[period]; + items.forEach((item, idx) => { + html += ` + \${idx === 0 ? `\${period}` : ''} + \${item.apiName} + \${item.accessCount} 건 + `; + }); + }); + + // 합계 row 추가 + html += ` + 합계 + \${totalCount} 건 + `; + + $target.append(html); + } + // API 호출 로그 목록 조회 function getMgmtApiLogList(){ $.ajax({ @@ -474,7 +876,6 @@ return html; } - // API 호출 로그 목록 조회 function getMgmtApiChart(){ var chartDate = $('#chartDate').val(); @@ -547,80 +948,77 @@ } }); } + + function exportToCSV() { + // 1. 현재 활성화된 탭 찾기 + const activeTab = document.querySelector('.tab-link.active'); + if (!activeTab) return; + + const tabId = activeTab.getAttribute('data-tab'); // 'daily', 'monthly', 'yearly' + const dataList = processedStats[tabId]; // 전역 변수에서 배열 데이터 가져오기 - /** - * 날짜형식 YYYYMMDDHHMI -> YYYY-MM-DD HH:MI - */ - function formatDateTime(raw) { - if (!raw || raw.length !== 12) return ""; - return ( - raw.slice(0, 4) + "-" + - raw.slice(4, 6) + "-" + - raw.slice(6, 8) + " " + - raw.slice(8, 10) + ":" + - raw.slice(10, 12) - ); + if (!Array.isArray(dataList) || dataList.length === 0) { + alert('내보낼 데이터가 없습니다.'); + return; + } + + let headers = []; + let filename = ''; + + // 2. 탭에 맞는 헤더와 파일명 설정 + if (tabId === 'daily') { + headers = ['날짜', 'API 명', '호출 건수']; + filename = 'api_stats_daily.csv'; + } else if (tabId === 'monthly') { + headers = ['월', 'API 명', '총 호출 건수']; + filename = 'api_stats_monthly.csv'; + } else { // 'yearly' + headers = ['연도', 'API 명', '총 호출 건수']; + filename = 'api_stats_yearly.csv'; + } + + // 3. 날짜 기준으로 내림차순 정렬 + const sortedData = [...dataList].sort((a, b) => b.period.localeCompare(a.period)); + + // 4. CSV 헤더 + 데이터 행 구성 + const rows = [headers]; + sortedData.forEach(item => { + const { period, apiName, accessCount } = item; + rows.push([period, apiName, accessCount]); + }); + + // 5. CSV 문자열로 변환 + const csvContent = rows.map(row => row.join(',')).join('\r\n'); + + // 6. UTF-8 BOM 추가 및 파일 다운로드 + const bom = '\uFEFF'; + const blob = new Blob([bom + csvContent], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = filename; + + document.body.appendChild(a); + a.click(); + + // 6. 리소스 정리 + document.body.removeChild(a); + URL.revokeObjectURL(url); + } + /** + * API 호출 통계 날짜 유효검사 + */ + function validDate(){ + var statType = $('.stats-container > .tab-nav').find('.active').data("tab"); + var from_dt_val = $('#count_from_dt').val(); + var to_dt_val = $('#count_to_dt').val(); + if ("daily" === statType && (from_dt_val.length != 10 || to_dt_val.length != 10)) return false; // YYYY-MM-DD + if ("monthly" === statType && (from_dt_val.length != 7 || to_dt_val.length != 7)) return false; // YYYY-MM + if ("yearly" === statType && (from_dt_val.length != 4 || to_dt_val.length != 4)) return false; // YYYY + return true; } - - // 예시 데이터 (100개 생성) -// const apiLogs = Array.from({ length: 100 }, (_, i) => ({ -// api: i % 2 === 0 ? "/API_CHA_085/request" : "/API_BH_020/dc332", -// method: ["GET", "POST", "PUT"][Math.floor(Math.random() * 3)], -// params: `subCode=test${i}&prvcCode=x${i}`, -// status: [ "200 OK", "404 NOT FOUND", "500 INTERNAL ERROR" ][Math.floor(Math.random() * 3)], -// time: Math.floor(Math.random() * 500), -// result: ["성공", "지연", "실패"][Math.floor(Math.random() * 3)] -// })); - -// const rowsPerPage = 10; -// let currentPage = 1; - -// function renderTable(page) { -// const tableBody = document.getElementById("apiLogBody"); -// tableBody.innerHTML = ""; - -// const start = (page - 1) * rowsPerPage; -// const end = start + rowsPerPage; -// const pageData = apiLogs.slice(start, end); - -// pageData.forEach(row => { -// const tr = document.createElement("tr"); -// tr.innerHTML += '' + row.api + '' + row.method + '' + row.params + '' + row.status + '' + row.time + '' + getResultIcon(row.result) + ' ' +row.result + ''; -// tableBody.appendChild(tr); -// }); -// } - -// function renderPagination() { -// const totalPages = Math.ceil(apiLogs.length / rowsPerPage); -// const pagination = document.getElementById("pagination"); -// pagination.innerHTML = ""; - -// for (let i = 1; i <= totalPages; i++) { -// const btn = document.createElement("button"); -// btn.textContent = i; -// if (i === currentPage) btn.classList.add("active"); -// btn.addEventListener("click", () => { -// currentPage = i; -// renderTable(currentPage); -// renderPagination(); -// }); -// pagination.appendChild(btn); -// } -// } - -// function getResultIcon(result) { -// if (result === "성공") -// return ``; -// if (result === "실패") -// return ``; -// if (result === "지연") -// return ``; -// return ""; -// } - - // 초기 렌더링 -// renderTable(currentPage); -// renderPagination();