이력관리 추가

main
이범준 6 months ago
parent e72e31d4d5
commit 071b4106cb

@ -0,0 +1,119 @@
package cokr.xit.fims.base;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
import cokr.xit.base.docs.xls.CellDef;
import cokr.xit.base.docs.xls.XLSWriter;
import cokr.xit.base.syslog.LogQuery;
import cokr.xit.base.syslog.service.LoggingService;
import cokr.xit.fims.task.TaskRequestMappingHandlerMapping;
import cokr.xit.foundation.data.DataFormat;
import cokr.xit.foundation.data.DataObject;
import cokr.xit.foundation.web.RequestHandlerReader;
@Controller
public class LoggingController extends cokr.xit.base.syslog.web.LoggingController {
@Resource(name = "loggingService")
private LoggingService loggingService;
@Autowired
private TaskRequestMappingHandlerMapping requestHandlers;
private RequestHandlerReader requestReader = new RequestHandlerReader();
@Override
public ModelAndView getLogs(LogQuery req) {
boolean download = !isEmpty(req.getDownload());
if (!download)
setFetchSize(req);
else
req.setPageNum(0).setFetchSize(0);
String
toDate = req.getToDate(),
fromDate = req.getFromDate();
if (isEmpty(toDate)) {
Date date = new Date();
toDate = DataFormat.yyyy_mm_dd(date);
req.setToDate(toDate.replace("-", ""));
if (isEmpty(fromDate)) {
fromDate = DataFormat.yyyy_mm_dd(new Date(date.getTime() - 1000L * 60L * 60L * 24L * 30L));
req.setFromDate(fromDate.replace("-", ""));
}
}
List<DataObject>
list = loggingService.getLogs(req),
web = list.stream()
.filter(row -> "web".equals(row.get("LOG_TYPE")))
.toList();
if (!list.isEmpty()) {
Map<String, DataObject> reqs = requestReader.asTree(requestHandlers).getIndex();
list.forEach(row -> {
Object obj = row.get("REG_DT");
row.set("REG_DT", DataFormat.yyyy_mm_dd_hh_mm_ss(obj));
obj = row.get("URL");
if (isEmpty(obj)) return;
DataObject reqInfo = reqs.get(obj);
row.set("DSCRP", reqInfo != null ? reqInfo.get("name") : "");
});
}
if (!download)
return setCollectionInfo(
new ModelAndView("jsonView")
.addObject("fromDate", fromDate)
.addObject("toDate", toDate),
list,
"syslog"
);
else
return download(list);
}
private ModelAndView download(List<DataObject> list) {
List<CellDef> cellDefs = List.of(
new CellDef().setLabel("계정").setField("USER_ACNT"),
new CellDef().setLabel("사용자이름").setField("USER_NM"),
new CellDef().setLabel("IP 주소").setField("IP_ADDR"),
new CellDef().setLabel("로그유형").setField("LOG_TYPE_NM"),
new CellDef().setLabel("접속일시").setField("REG_DT"),
new CellDef().setLabel("URL").setField("URL"),
new CellDef().setLabel("설명").setField("DSCRP"),
new CellDef().setLabel("파일이름").setField("FILE_NM"),
new CellDef().setLabel("민감정보").setField("PSNL_INFO")
);
XLSWriter xlsx = new XLSWriter()
.worksheet(0)
.trackWidth(IntStream.range(0, cellDefs.size()).toArray())
.cell(0, 0)
.value("시스템 로그 목록").merge(0, cellDefs.size() - 1)
.cell(2, 0)
.rowValues(CellDef.header(cellDefs, null))
.cell(3, 0)
.values(
list,
CellDef.values(cellDefs)
)
.autoWidth();
return new ModelAndView("downloadView")
.addObject("download",
xlsx.getDownloadable()
.setFilename("시스템 로그 목록.xlsx")
);
}
}

@ -61,7 +61,7 @@ SELECT A.*, FILE_PATH URL
<selectKey keyProperty="file.id,file.path" keyColumn="NEW_ID,PATH" resultType="map" order="BEFORE">
SELECT NEW_ID, CONCAT(DIR, NEW_ID, '.', #{file.extension}) PATH
FROM (
SELECT IFNULL(MAX(FILE_ID) + 1, CONCAT(THIS_DAY, '00001')) NEW_ID
SELECT NVL(MAX(FILE_ID) + 1, CONCAT(THIS_DAY, '00001')) NEW_ID
FROM TB_FILE A, (<include refid="utility.selectThisDay" />) B
WHERE FILE_ID LIKE CONCAT(THIS_DAY, '%')
) T1, (

@ -31,7 +31,7 @@ SELECT A.*
<insert id="insertMenu" parameterType="map">/* 메뉴 등록(menuMapper.insertMenu) */
<selectKey order="BEFORE" resultType="map" keyColumn="NEW_NO,NEW_ORD" keyProperty="menu.id,menu.sortOrder">
SELECT NEW_NO, NEW_ORD
FROM (SELECT IFNULL(MAX(MENU_NO) + 1, 0) NEW_NO FROM TB_MENU) A,
FROM (SELECT NVL(MAX(MENU_NO) + 1, 0) NEW_NO FROM TB_MENU) A,
(<include refid="newSortOrder" />) B</selectKey>
INSERT INTO TB_MENU (
MENU_NO
@ -69,7 +69,7 @@ UPDATE TB_MENU SET
, IMG_CNF = #{menu.imageConf}
WHERE MENU_NO = #{menu.id}</update>
<sql id="newSortOrder">SELECT IFNULL(MAX(SRT_ORD) + 1, 0) NEW_ORD FROM TB_MENU WHERE PRNT_NO = IFNULL(#{parentID}, IFNULL(#{menu.parentID}, 0))</sql>
<sql id="newSortOrder">SELECT NVL(MAX(SRT_ORD) + 1, 0) NEW_ORD FROM TB_MENU WHERE PRNT_NO = NVL(#{parentID}, NVL(#{menu.parentID}, 0))</sql>
<update id="moveMenus" parameterType="map">/* 메뉴 이동(menuMapper.moveMenus) */
UPDATE TB_MENU SET

@ -16,8 +16,49 @@
<result property="ipAddress" column="IP_ADDR"/> <!-- 등록 일시 -->
</resultMap>
<select id="getLogs" parameterType="map" resultType="dataobject">/* 시스템 로그 조회(loggingMapper.getLogs) */
<include refid="utility.paging-prefix" />
SELECT LOG_ID
, A.LOG_TYPE
, CASE MTD_NM WHEN 'onLogin' THEN '로그인'
WHEN 'onLogout' THEN '로그아웃'
WHEN 'onFailure' THEN '로그인 실패'
ELSE LOG_TYPE_NM
END LOG_TYPE_NM
, URL
, CLS_NM
, MTD_NM
, NULL DSCRP
, FILE_NM
, DATA_CNT
, DATA_NM
, PSNL_INFO
, A.USER_ID
, USER_ACNT
, USER_NM
, NSTT_CD
, IP_ADDR
, A.REG_DT
FROM TB_SYS_LOG A LEFT OUTER JOIN
TB_USER B ON A.USER_ID = B.USER_ID,
(SELECT 'log-inout' LOG_TYPE, '로그인아웃' LOG_TYPE_NM FROM DUAL UNION
SELECT 'download' LOG_TYPE, '다운로드' LOG_TYPE_NM FROM DUAL UNION
SELECT 'service' LOG_TYPE, '서비스' LOG_TYPE_NM FROM DUAL UNION
SELECT 'web' LOG_TYPE, '웹' LOG_TYPE_NM FROM DUAL
) C
<where><if test="fromDate != null"> AND LOG_ID >= CONCAT(#{fromDate}, LPAD(0, 16, '0'))</if>
<if test="toDate != null"> AND LOG_ID &lt;= CONCAT(#{toDate}, LPAD(9, 16, '9'))</if>
<if test="logTypes != null"> AND A.LOG_TYPE IN (<foreach collection="logTypes" item="logType" separator=",">#{logType}</foreach>)</if>
<if test="userIDs != null"> AND USER_ID IN (<foreach collection="userIDs" item="userID" separator=",">#{userID}</foreach>)</if>
<if test="userAccounts != null"> AND USER_ACNT IN (<foreach collection="userAccounts" item="userAccount" separator=",">#{userAccount}</foreach>)</if>
<if test="userName != null"> AND USER_NM LIKE CONCAT('%', #{userName}, '%')</if>
AND A.LOG_TYPE = C.LOG_TYPE</where>
<include refid="utility.sortBy" />
<include refid="utility.paging-suffix" /></select>
<insert id="insertLog" parameterType="cokr.xit.base.syslog.ServiceLog">/* 시스템 로그 등록(loggingMapper.insertLog) */
<selectKey keyProperty="id" keyColumn="NEW_ID" resultType="string" order="BEFORE">SELECT CONCAT(TODAY, LPAD(NVL(SUBSTR(MAX(LOG_ID), 9) + 1, 1), 16, '0')) NEW_ID
<selectKey keyProperty="id" keyColumn="NEW_ID" resultType="string" order="BEFORE">
SELECT CONCAT(TODAY, LPAD(NVL(SUBSTR(MAX(LOG_ID), 9) + 1, 1), 16, '0')) NEW_ID
FROM TB_SYS_LOG A, (<include refid="utility.selectToday" />) B
WHERE LOG_ID LIKE CONCAT(TODAY, '%')</selectKey>
INSERT INTO TB_SYS_LOG (

@ -30,8 +30,8 @@ SELECT FOUND_ROWS() TOT_CNT</select>
<sql id="selectToday">SELECT<include refid="utility.today" />TODAY FROM DUAL</sql>
<sql id="thisDay">IFNULL(#{thisDay},<include refid="utility.today" />)</sql>
<sql id="thisDay">NVL(#{thisDay},<include refid="utility.today" />)</sql>
<sql id="selectThisDay">SELECT<include refid="utility.thisDay" />THIS_DAY</sql>
<sql id="selectThisDay">SELECT<include refid="utility.thisDay" />THIS_DAY FROM DUAL</sql>
</mapper>

@ -1,6 +1,6 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" session="false"%>
<%@ include file="/WEB-INF/jsp/include/taglib.jsp"%>
<link rel="stylesheet" href="<c:url value="/resources/3rd-party/sneat/libs/jstree/jstree.css"/>" /--%>
<c:set var="prefixName" scope="request">메뉴</c:set>
<!-- Page Body -->
<div class="d-flex flex-column flex-grow-1">

@ -0,0 +1,155 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" session="false"%>
<%@ include file="/WEB-INF/jsp/include/taglib.jsp"%>
<c:set var="prefixName" scope="request">시스템 로그</c:set>
<!-- Page Body -->
<div class="d-flex flex-column flex-grow-1">
<div class="dataTables_wrapper dt-bootstrap5 no-footer">
<div id="${infoPrefix}-query" class="d-flex flex-row justify-content-between p-3">
<div class="d-flex flex-row">
<div class="input-group">
<select id="logType" class="form-select">
<option value="">로그유형</option>
<option value="log-inout">로그인/아웃</option>
<option value="download">다운로드</option>
<option value="web">웹</option>
</select>
</div>
<div class="input-group" style="margin: 0 1rem 0 1rem">
<select id="by" onchange="document.getElementById('term').focus();" class="form-select">
<option value="userName">사용자 이름</option>
<option value="userAccount">사용자 계정</option>
<option value="userID">사용자 아이디</option>
</select>
<input id="term" type="text" placeholder="조회 조건을 입력하십시오." class="form-control">
</div>
<div class="input-group">
<input id="fromDate" type="text" value="${fromDate}" required placeholder="시작일자" class="form-control">
<span style="margin: 0 .5rem 0 .5rem"> ~ </span>
<input id="toDate" type="text" value="${toDate}" required placeholder="종료일자" class="form-control">
</div>
</div>
<div>
<button onclick="search${infoPrefix}s();" class="btn btn-primary">찾기</button>
<button onclick="${infoPrefix}Control.download();" class="btn btn-primary not-empty">다운로드</button>
</div>
</div>
<div class="table-responsive">
<table class="datatables-ajax table table-bordered dataTable no-footer" id="DataTables_Table_0" aria-describedby="DataTables_Table_0_info">
<thead>
<tr><th style="width: auto;">계정</th>
<th style="width: auto;">사용자이름</th>
<th style="width: auto;">IP 주소</th>
<th style="width: max-content;">로그유형</th>
<th style="width: max-content;">접속일시</th>
<th style="width: auto;">URL</th>
<th style="width: auto;">설명</th>
<th style="width: auto;">파일이름</th>
<th style="width: auto;">민감정보</th>
</tr>
</thead>
<tbody id="${infoPrefix}List">
</tbody>
<template id="${infoPrefix}Row">
<tr data-key="{LOG_ID}" {onclick}>
<td>{USER_ACNT}</td>
<td>{USER_NM}</td>
<td>{IP_ADDR}</td>
<td>{LOG_TYPE_NM}</td>
<td>{REG_DT}</td>
<td>{URL}</td>
<td>{DSCRP}</td>
<td>{FILE_NM}</td>
<td>{PSNL_INFO}</td>
</tr>
</template>
<template id="${infoPrefix}NotFound">
<tr class="odd">
<td valign="top" colspan="8" class="dataTables_empty text-center">${prefixName} 정보를 찾지 못했습니다.</td>
</tr>
</template>
</table>
</div>
<div class="d-flex flex-row p-3 justify-content-between">
<label id="${infoPrefix}PagingInfo" class="dataTables_info" role="status" aria-live="polite"></label>
<ul id="${infoPrefix}Paging" class="pagination pagination-primary">
</ul>
</div>
</div>
</div>
<!--/ Page Body -->
<script >
var ${infoPrefix}Control = new DatasetControl({
prefix: "${infoPrefix}",
prefixName: "${prefixName}",
keymapper: row => row.LOG_ID,
dataGetter: obj => obj.${infoPrefix}List,
query: {}
});
function search${infoPrefix}s() {
let logType = $("#logType").val(),
params = {
logTypes: logType ? [logType] : [],
fromDate: $("#fromDate").val().replace(/-/gi, ""),
toDate: $("#toDate").val().replace(/-/gi, "")
},
terms = $("#${infoPrefix}-query #term").val();
if (terms) {
switch ($("#${infoPrefix}-query #by").val()) {
case "userName": params.userName = terms; break;
case "userAccount": params.userAccounts = [terms]; break;
case "userID": params.userIDs = [terms]; break;
default: break;
}
}
${infoPrefix}Control.query = params;
${infoPrefix}Control.load(1);
}
${infoPrefix}Control.onDatasetChange = obj => {
let ${infoPrefix}List = ${infoPrefix}Control.dataset,
empty = ${infoPrefix}List.empty,
trs = empty ?
[document.getElementById("${infoPrefix}NotFound").innerHTML] : <%-- from template#${infoPrefix}NotFound --%>
${infoPrefix}List.inStrings(
document.getElementById("${infoPrefix}Row").innerHTML, <%-- from template#${infoPrefix}Row --%>
(str, dataItem) => str
.replace(/{onclick}=""/gi, 'onclick="syslogControl.setCurrent(\'' + dataItem.getValue("LOG_ID") + '\');"')
);
$("#${infoPrefix}List").html(trs.join());
$("#${infoPrefix}-query button.not-empty").prop("disabled", empty);
$("#${infoPrefix}Paging").setPaging({
list:${infoPrefix}Control.dataset,
prefix:${infoPrefix}Control.prefix,
start:obj.${infoPrefix}Start,
totalSize:obj.${infoPrefix}Total,
fetchSize:obj.${infoPrefix}Fetch,
func:"${infoPrefix}Control.load({index})"
});
};
${infoPrefix}Control.onCurrentChange = item => {
if (!item) return;
let key = item.data.LOG_ID;
$("#${infoPrefix}List").setCurrentRow(key);
};
$("#term").onEnterPress(search${infoPrefix}s);
$(function(){
${onload}
${infoPrefix}Control.setData({
${infoPrefix}List:${syslogList},
${infoPrefix}Start:${syslogStart},
${infoPrefix}Fetch:${syslogFetch},
${infoPrefix}Total:${syslogTotal}
});
$("#${infoPrefix}-query #fromDate, #${infoPrefix}-query #toDate").datePicker();
});
//# sourceURL=${infoPrefix}-main.jsp
</script>
Loading…
Cancel
Save