건설현장 관리 > 발주기관 로그인 내역 - 일별 접속량 차트 추가

main
유지인 2026-01-14 15:07:36 +09:00
parent dbb58e393c
commit 61be76c392
6 changed files with 249 additions and 97 deletions

View File

@ -995,6 +995,31 @@ public class ConstructionProjectManagementController {
return "admins/constructionProjectManagement/construction-user-login-history"; return "admins/constructionProjectManagement/construction-user-login-history";
} }
/**
* API >
* @param params
* @param model
* @param response
* @param request
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/admins/constructionProjectManagement/o_charts.do", method = RequestMethod.POST)
public HashMap<String, Object> getMgmtApiOutDataAccessChart(@RequestParam HashMap<String, Object> params, ModelMap model, HttpServletResponse response, HttpServletRequest request) throws Exception {
strUtil sUtil = new strUtil();
HashMap<String, Object> result = new HashMap<String, Object>();
String CHART_DATE = sUtil.checkNull((String)params.get("chartDate"));
params.put("CHART_DATE", CHART_DATE);
List<EgovMap> listData = masterService.selectUserLoginHistoryChart(params);
result.put("code", "SUCCESS");
result.put("msg", "일별 접속량 데이터 조회를 성공했습니다.");
result.put("data", listData);
return result;
}
public void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { public void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {
List<?> selectInfoListExcel = (List<?>) model.get("selectInfoListExcel"); List<?> selectInfoListExcel = (List<?>) model.get("selectInfoListExcel");

View File

@ -54,6 +54,8 @@ public interface GeneralUserMngMapper {
public List<EgovMap> selectUserLoginHistory(HashMap<String, Object> params) throws Exception; public List<EgovMap> selectUserLoginHistory(HashMap<String, Object> params) throws Exception;
public List<EgovMap> selectUserLoginHistoryChart(HashMap<String, Object> params) throws Exception;
String findProjectMasterCompanyNameByUserid(String userId); String findProjectMasterCompanyNameByUserid(String userId);
List<EgovMap> getUserGDisList(HashMap<String, Object> params) throws Exception; List<EgovMap> getUserGDisList(HashMap<String, Object> params) throws Exception;

View File

@ -52,6 +52,8 @@ public interface GeneralUserMngService {
public List<EgovMap> selectUserLoginHistory(HashMap<String, Object> params) throws Exception; public List<EgovMap> selectUserLoginHistory(HashMap<String, Object> params) throws Exception;
public List<EgovMap> selectUserLoginHistoryChart(HashMap<String, Object> params) throws Exception;
public List<EgovMap> getUserGDisList(HashMap<String, Object> params) throws Exception; public List<EgovMap> getUserGDisList(HashMap<String, Object> params) throws Exception;
} }

View File

@ -128,6 +128,11 @@ public class GeneralUserMngServiceImpl implements GeneralUserMngService {
return masterMapper.selectUserLoginHistory(params); return masterMapper.selectUserLoginHistory(params);
} }
@Override
public List<EgovMap> selectUserLoginHistoryChart(HashMap<String, Object> params) throws Exception {
return masterMapper.selectUserLoginHistoryChart(params);
}
@Override @Override
public List<EgovMap> getUserGDisList(HashMap<String, Object> params) throws Exception { public List<EgovMap> getUserGDisList(HashMap<String, Object> params) throws Exception {
return masterMapper.getUserGDisList(params); return masterMapper.getUserGDisList(params);

View File

@ -485,6 +485,34 @@
]]> --> ]]> -->
</select> </select>
<select id="selectUserLoginHistoryChart" parameterType="map" resultType="egovMap">
<!-- SELECT TO_CHAR(d.visit_date, 'YYYY-MM-DD') AS visit_date
,COUNT(wmi.USERID) AS visit_count
FROM (SELECT TRUNC(TO_DATE(#{chartDate})) - LEVEL + 1 AS visit_date
FROM DUAL
CONNECT BY LEVEL <![CDATA[<=]]> 10) d
LEFT JOIN WEB_REQUEST_LOG wrl ON wrl.DATETIME <![CDATA[>=]]> d.visit_date
AND wrl.DATETIME <![CDATA[<]]> d.visit_date + 1
INNER JOIN WEB_MEMBER_IN wmi ON wrl.USERID = wmi.USERID
AND wmi.CLS = 2
GROUP BY d.visit_date
ORDER BY d.visit_date -->
SELECT TO_CHAR(d.visit_date, 'YYYY-MM-DD') AS visit_date
,NVL(COUNT(wrl.DATETIME), 0) AS visit_count
FROM (SELECT TRUNC(TO_DATE(#{chartDate})) - LEVEL + 1 AS visit_date
FROM DUAL
CONNECT BY LEVEL <![CDATA[<=]]> 10) d
LEFT JOIN WEB_REQUEST_LOG wrl ON wrl.DATETIME <![CDATA[>=]]> d.visit_date
AND wrl.DATETIME <![CDATA[<]]> d.visit_date + 1
AND EXISTS (SELECT 1
FROM WEB_MEMBER_IN WMI
WHERE WMI.USERID = wrl.USERID
AND WMI.CLS = 2)
GROUP BY d.visit_date
ORDER BY d.visit_date
</select>
<select id="findProjectMasterCompanyNameByUserid" parameterType="String" resultType="String"> <select id="findProjectMasterCompanyNameByUserid" parameterType="String" resultType="String">
<![CDATA[ <![CDATA[
SELECT SELECT

View File

@ -7,75 +7,105 @@
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="${pageContext.request.contextPath}/js/jquery/jquery-1.10.2.min.js"></script> <script src="${pageContext.request.contextPath}/js/jquery/jquery-1.10.2.min.js"></script>
<!-- Bootstrap Datepicker -->
<link rel="stylesheet" href="${pageContext.request.contextPath}/js/bootstrap-datepicker-1.9.0-dist/css/bootstrap-datepicker.min.css">
<script type="text/javascript" src="${pageContext.request.contextPath}/js/bootstrap-datepicker-1.9.0-dist/js/bootstrap-datepicker.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/bootstrap-datepicker-1.9.0-dist/locales/bootstrap-datepicker.ko.min.js"></script>
<!-- MUI + Chart.js + jQuery -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="${pageContext.request.contextPath}/js/admins/common.js"></script> <script src="${pageContext.request.contextPath}/js/admins/common.js"></script>
<link rel="stylesheet" HREF="${pageContext.request.contextPath}/css/admins/style.css" type="text/css"> <link rel="stylesheet" HREF="${pageContext.request.contextPath}/css/admins/style.css" type="text/css">
<style>
.datepicker-wrapper > * {
margin-right: 6px;
}
.datepicker-wrapper .date-next,
.datepicker-wrapper .date-prev {
border: 1px solid #4285f4;
height: 18px;
line-height: 18px;
padding: 0 2px;
margin-right: 1px;
border-radius: 3px;
background: #ecf3fe;
color: #337bed;
transition: .3s;
}
.date-prev:hover, .date-next:hover {
cursor: pointer;
background: #337bed;
color: #ecf3fe;
}
.title-container, .chart-container {
background: #fff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);
padding: 20px; margin-bottom: 20px;
}
.datepicker-dropdown {
z-index: 99999 !important;
position: absolute;
left: 0 !important;
background: #fff;
border: 1px solid #e6e6e6;
}
</style>
<script> <script>
// --------- 일일 접속량 차트 관련 변수 --------------------
let trafficChart; // 전역 변수
// --------- 일일 접속량 차트 관련 변수 --------------------
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
var searchTitle = document.getElementById('searchTitle'); // ----------------------------------------------------
var searchValue = document.getElementById('searchValue'); // 1. 날짜 선택기 초기화
var searchContainer = searchValue.parentNode; // ----------------------------------------------------
var tildeText = document.createElement('span');
tildeText.innerHTML = '~';
var searchBgndt = document.createElement('input');
var searchEnddt = document.createElement('input');
function createInput(input, id) { // datepicker
input.type = 'text'; $("#chartDate, #count_from_dt, #count_to_dt").datepicker({
input.id = id; format: "yyyy-mm-dd",
input.name = id; language: "ko",
input.className = 'search'; autoclose: true,
input.style.display = 'inline-block'; todayHighlight: true,
input.style.width = '12ch'; container: 'body'
input.placeholder = 'YYYYMMDD'; }).datepicker("setDate", new Date());
input.style.color = '#000'; // Text color
input.maxLength = 8; // Limit input length
input.onfocus = function() {
this.placeholder = '';
this.style.color = '#000'; // Text color
};
input.onblur = function() {
if (this.value === '') {
this.placeholder = 'YYYYMMDD';
}
};
}
createInput(searchBgndt, 'searchBgndt'); // 페이지 로드시 차트 먼저 로드
createInput(searchEnddt, 'searchEnddt'); getMgmtApiChart();
searchBgndt.style.marginRight = '2px';
searchEnddt.style.marginLeft = '2px';
searchBgndt.value = "${params.searchBgndt}";
searchEnddt.value = "${params.searchEnddt}";
function addInputs() { // 날짜 변경 시 차트 다시 로드
searchValue.style.display = 'none'; $(document).on('change', '#chartDate', function() {
searchContainer.insertBefore(searchBgndt, searchValue); getMgmtApiChart();
searchContainer.insertBefore(tildeText, searchValue);
searchContainer.insertBefore(searchEnddt, searchValue);
}
function removeInputs() {
if (document.getElementById('searchBgndt')) {
searchBgndt.remove();
tildeText.remove();
searchEnddt.remove();
}
searchValue.style.display = 'inline-block';
}
if (searchTitle.value == '7') {
addInputs();
}
searchTitle.addEventListener('change', function() {
if (this.value == '7') {
addInputs();
} else {
removeInputs();
}
}); });
});
// <> 버튼으로 날짜 변경
$(document).on('click', '.date-prev, .date-next', function() {
const $input = $('#chartDate');
// 현재 날짜 가져오기 (yyyy-mm-dd)
const currentVal = $input.val();
if (!currentVal) return;
// 문자열 → Date 객체
const dateParts = currentVal.split('-');
const currentDate = new Date(
dateParts[0],
dateParts[1] - 1,
dateParts[2]
);
// 이전 / 다음 구분
if ($(this).hasClass('date-prev')) {
currentDate.setDate(currentDate.getDate() - 10);
} else {
currentDate.setDate(currentDate.getDate() + 10);
}
// datepicker 날짜 변경 (이게 핵심)
$input.datepicker('setDate', currentDate);
// setDate 하면 change 이벤트가 자동으로 발생함
});
})
var context = "${pageContext.request.contextPath}"; var context = "${pageContext.request.contextPath}";
function linkPage(index){ function linkPage(index){
@ -83,43 +113,92 @@ function linkPage(index){
$("#searchForm").attr("action", "${pageContext.request.contextPath}/admins/user/02.do").submit(); $("#searchForm").attr("action", "${pageContext.request.contextPath}/admins/user/02.do").submit();
} }
/* function excelDownload(){ // API 호출 로그 목록 조회
$("#searchForm").attr("action", "${pageContext.request.contextPath}/admins/user/02_excel.do").submit(); function getMgmtApiChart(){
$("#searchForm").attr("action", "${pageContext.request.contextPath}/admins/user/02.do"); var chartDate = $('#chartDate').val();
$.ajax({
type : "POST",
url : "/admins/constructionProjectManagement/o_charts.do" ,
data : {chartDate: $('#chartDate').val()},
dataType :"json",
success : function(res){ // res.code, res.msg, res.data
if (res.code == "SUCCESS") {
const labels = res.data.map(d => d.visitDate.substring(5));
const data = res.data.map(d => d.visitCount);
drawTrafficChart(labels, data);
} else {
alert("API 호출 로그 목록 조회중 오류가 발생하였습니다. 다시 시도해주세요.");
}
},
error : function(response){
alert("API 호출 로그 목록 조회중 내부 오류가 발생하였습니다. 다시 시도해주세요.");
}
});
}
// Chart.js: 일일 접속량
function drawTrafficChart(labels, dataArr) {
const ctx = document.getElementById('trafficChart').getContext('2d');
// 이미 차트가 있으면 갱신
if (trafficChart) {
trafficChart.data.labels = labels;
trafficChart.data.datasets[0].data = dataArr;
trafficChart.update();
return;
}
// 최초 생성
trafficChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
data: dataArr,
borderColor: '#4285f4',
backgroundColor: 'rgba(66,133,244,0.1)',
tension: 0.4,
fill: true,
pointRadius: 3
}]
},
options: {
plugins: {
legend: { display: false }
},
scales: {
y: {
beginAtZero: true,
ticks: { stepSize: 1 }
}
}
}
});
} }
*/
$(function(){
var searchTitle = "${params.searchTitle}";
searchTitle = searchTitle == "" ? "0" : searchTitle;
$("#searchTitle").val(searchTitle);
});
// 엑셀 다운로드
function clickExcelDownload(){
const params = new URLSearchParams();
params.append("constTag", "");
params.append("constName", "");
params.append("constStartDate", "");
params.append("constEndDate", "");
params.append("constStateCode", "");
params.append("projectStateCode", "");
params.append("constCompanyName", "");
params.append("constCompanyAdmin", "");
params.append("constCompanyTel", "");
params.append("excelDownload", "Y");
// 엑셀 다운로드 // 페이지 정보
function clickExcelDownload(){ const pagingEle = document.getElementById('paging');
const params = new URLSearchParams(); params.append("nPage", "0");
params.append("nCount", "10000");
params.append("constTag", ""); // AJAX가 아닌 직접 다운로드 요청
params.append("constName", ""); window.location.href = "/admins/constructionProjectManagement/excel.do?" + params.toString();
params.append("constStartDate", ""); $('#excelDownload').val("");
params.append("constEndDate", ""); }
params.append("constStateCode", "");
params.append("projectStateCode", "");
params.append("constCompanyName", "");
params.append("constCompanyAdmin", "");
params.append("constCompanyTel", "");
params.append("excelDownload", "Y");
// 페이지 정보
const pagingEle = document.getElementById('paging');
params.append("nPage", "0");
params.append("nCount", "10000");
// AJAX가 아닌 직접 다운로드 요청
window.location.href = "/admins/constructionProjectManagement/excel.do?" + params.toString();
$('#excelDownload').val("");
}
</script> </script>
</head> </head>
<body> <body>
@ -128,10 +207,21 @@ $(function(){
<input type="hidden" id="cls" name="cls" value="2" /> <input type="hidden" id="cls" name="cls" value="2" />
<table id="Table_Main" width="100%" border="0" cellpadding="0" cellspacing="0"> <table id="Table_Main" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr> <tr>
<td colspan=2><div style="background: #fff; border-radius: 12px; padding: 5px 10px; <td colspan=2><div class="title-container" style="padding: 5px 10px;"><h3>발주기관 로그인 내역</h3></div></td>
box-shadow: 0 2px 8px rgba(0,0,0,0.05);"><h3>발주기관 로그인 내역</h3></div></td> </tr>
<tr height=20 colspan=2>
<td>
<div class="chart-container">
<div class="datepicker-wrapper" style="position: relative;display: flex;align-items: center;">
<h3>📈 일별 접속량</h3>
<input type="text" name="chartDate" id="chartDate" class="input" placeholder="클릭하여 날짜 선택">
<div class="date-prev"> &lt;</div>
<div class="date-next"> &gt;</div>
</div>
<canvas id="trafficChart" height="100"></canvas>
</div>
</td>
</tr> </tr>
<tr height=20 colspan=2><td>&nbsp;</td></tr>
<%-- <tr height=25> <%-- <tr height=25>
<!-- START : 엑셀 다운로드 -------------------------------------------------------------------------> <!-- START : 엑셀 다운로드 ------------------------------------------------------------------------->
<td> <td>