日志代码提交

This commit is contained in:
liang.chao 2025-09-09 12:09:52 +08:00
parent deafebfc26
commit d66a47eec9
11 changed files with 1507 additions and 6 deletions

View File

@ -0,0 +1,191 @@
package com.bonus.web.controller.system;
import com.bonus.common.annotation.SysLog;
import com.bonus.common.core.controller.BaseController;
import com.bonus.common.core.domain.AjaxResult;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.page.TableDataInfo;
import com.bonus.common.enums.OperaType;
import com.bonus.common.utils.FileUtils;
import com.bonus.common.utils.global.SystemGlobal;
import com.bonus.system.domain.SysLogsVo;
import com.bonus.system.service.ExportSqlService;
import com.bonus.system.service.ISysLogService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @authorcwchen
* @date2024-02-28-14:01
* @version1.0
* @description系统日志
*/
@RestController
@RequestMapping("/sys/sysLog")
@Slf4j
public class SysLogController extends BaseController {
@Resource(name = "ISysLogService")
private ISysLogService service;
/**
* 存储的路径
*/
@Value("${sql.filePath}")
private String filePath;
@Autowired
private ExportSqlService exportSqlService;
/**
* 保存系统日志业务日志和其他日常日志
*
* @param sysLog 日志对象
*/
@ApiOperation(value = "保存系统日志")
@PostMapping("saveLogs")
public AjaxResult saveLogs(@RequestBody SysLogsVo sysLog) {
return service.saveLogs(sysLog);
}
/**
* 保存越权日志
*
* @param sysLog
* @param request
*/
@PostMapping("addLogs")
public void addLogs(@RequestBody SysLogsVo sysLog, HttpServletRequest request) {
service.saveLogs(sysLog, request);
}
@ApiOperation(value = "查询系统日志")
@GetMapping("getSystemLogs")
@SysLog(title = "审计日志", module = "审计日志->系统日志", businessType = OperaType.QUERY, details = "查询系统日志列表", type = "系统日志", logType = 0)
public TableDataInfo getSystemLogs(SysLogsVo dto) {
try {
dto.setLogType(0);
startPage();
List<SysLogsVo> list = service.getSystemLogs(dto);
return getDataTable(list);
} catch (Exception e) {
log.error(e.toString(), e);
return getDataTableBad(new ArrayList<>(), "请求出错了");
}
}
@ApiOperation(value = "查询业务日志")
@GetMapping("getYwLogs")
@SysLog(title = "业务日志", module = "审计日志->业务日志", businessType = OperaType.QUERY, details = "查询业务日志列表", type = "系统日志", logType = 0)
public TableDataInfo getYwLogs(SysLogsVo dto) {
try {
dto.setLogType(1);
startPage();
List<SysLogsVo> list = service.getSystemLogs(dto);
return getDataTable(list);
} catch (Exception e) {
log.error(e.toString(), e);
return getDataTableBad(new ArrayList<>(), "请求出错了");
}
}
@ApiOperation(value = "查询异常日志")
@GetMapping("getErrLogs")
@SysLog(title = "异常日志", module = "审计日志->异常日志", businessType = OperaType.QUERY, logType = 0, details = "查询系统异常日志", type = "系统日志")
public TableDataInfo getErrLogs(SysLogsVo dto) {
try {
dto.setLogType(2);
startPage();
List<SysLogsVo> list = service.getSystemLogs(dto);
return getDataTable(list);
} catch (Exception e) {
log.error(e.toString(), e);
return getDataTableBad(new ArrayList<>(), "请求出错了");
}
}
@ApiOperation(value = "日志备份")
@GetMapping("downloadErrLogs")
@SysLog(title = "审计日志", module = "审计日志->异常日志", businessType = OperaType.COPY_LOG, logType = 0, details = "异常日志备份", type = "系统日志")
public void downloadErrLogs(HttpServletRequest request, HttpServletResponse response) {
try {
String dateTimeNow = exportSqlService.export("2");
String path = filePath + ExportSqlService.TABLE_NAME + dateTimeNow + SystemGlobal.TEXT_FIX;
FileUtils.fileDownload(path, "sys_log" + dateTimeNow + ".sql", response);
} catch (Exception e) {
logger.error("文件下载失败", e);
}
}
@ApiOperation(value = "日志备份")
@GetMapping("downloadYwLogs")
@SysLog(title = "审计日志", module = "审计日志-->业务日志", businessType = OperaType.COPY_LOG, logType = 0, details = "业务日志备份", type = "系统日志")
public void downloadYwLogs(HttpServletRequest request, HttpServletResponse response) {
try {
String dateTimeNow = exportSqlService.export("1");
String path = filePath + ExportSqlService.TABLE_NAME + dateTimeNow + SystemGlobal.TEXT_FIX;
FileUtils.fileDownload(path, "sys_log" + dateTimeNow + ".sql", response);
} catch (Exception e) {
logger.error("文件下载失败", e);
}
}
@ApiOperation(value = "日志备份")
@GetMapping("downloadSysLogs")
@SysLog(title = "审计日志", module = "审计日志->系统日志", businessType = OperaType.COPY_LOG, details = "系统日志备份", type = "系统日志", logType = 0)
public void downloadSysLogs(HttpServletRequest request, HttpServletResponse response) {
try {
String dateTimeNow = exportSqlService.export("0");
String path = filePath + ExportSqlService.TABLE_NAME + dateTimeNow + SystemGlobal.TEXT_FIX;
FileUtils.fileDownload(path, "sys_log" + dateTimeNow + ".sql", response);
} catch (Exception e) {
logger.error("文件下载失败", e);
}
}
@ApiOperation(value = "查询日志容量")
@PostMapping("getLogsSet")
@SysLog(title = "审计日志", module = "审计日志->日志容量配置", businessType = OperaType.QUERY, details = "查询日志容量", type = "系统日志", logType = 0)
public R<String> getLogsSet() {
return service.getLogsSet();
}
@ApiOperation(value = "设置日志容量")
@PostMapping("setLogsSet")
@SysLog(title = "审计日志", module = "审计日志->日志容量配置", businessType = OperaType.QUERY, details = "修改日志容量", type = "系统日志", logType = 0)
public R<String> setLogsSet(@RequestBody SysLogsVo dto) {
return service.setLogsSet(dto.getCapacity());
}
@ApiOperation(value = "查询日志统计分析")
@PostMapping("getLogStatistics")
@SysLog(title = "审计日志", module = "审计日志->日志分析", businessType = OperaType.QUERY, details = "查询日志分析", type = "系统日志", logType = 0)
public R<Map<String, Object>> getLogStatistics(@RequestBody SysLogsVo dto) {
return service.getLogStatistics(dto);
}
@ApiOperation(value = "查询日志告警")
@GetMapping("logWarn")
public AjaxResult logWarn() {
service.handleWarningLog();
return success();
}
}

View File

@ -7,12 +7,16 @@ bonus:
# 版权年份
copyrightYear: 2025
# 文件路径 示例( Windows配置D:/bonus/uploadPathLinux配置 /home/bonus/uploadPath
profile: D:/bonus/uploadPath
profile: E:/bonus/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
sql:
filePath: E:/bonus/filePath
schemaName: smart_archives_dev
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080

View File

@ -23,7 +23,7 @@ import com.bonus.common.utils.sql.SqlUtil;
/**
* web层通用数据处理
*
*
* @author bonus
*/
public class BaseController
@ -46,7 +46,15 @@ public class BaseController
}
});
}
protected TableDataInfo getDataTableBad(List<?> list,String msg)
{
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS);
rspData.setRows(list);
rspData.setMsg(msg);
rspData.setTotal(new PageInfo(list).getTotal());
return rspData;
}
/**
* 设置请求分页数据
*/
@ -113,7 +121,7 @@ public class BaseController
{
return AjaxResult.success(message);
}
/**
* 返回成功消息
*/
@ -140,7 +148,7 @@ public class BaseController
/**
* 响应返回结果
*
*
* @param rows 影响行数
* @return 操作结果
*/
@ -151,7 +159,7 @@ public class BaseController
/**
* 响应返回结果
*
*
* @param result 结果
* @return 操作结果
*/

View File

@ -0,0 +1,39 @@
package com.bonus.common.exception;
import lombok.Data;
@Data
public class SysWarning {
public SysWarning(String warningId, String warningEvent, String warningIp, String operaUserName, String operaTime, String warningStatus){
this.warningId = warningId;
this.warningEvent = warningEvent;
this.warningIp = warningIp;
this.operaUserName = operaUserName;
this.operaTime = operaTime;
this.warningStatus = warningStatus;
}
private String warningId;
private String warningEvent = "";
private String warningIp = "";
private String operaUserName = "";
private String operaTime;
private String warningStatus = "0";
@Override
public String toString() {
return "SysWarning{" +
"warningId='" + warningId + '\'' +
", warningEvent='" + warningEvent + '\'' +
", warningIp='" + warningIp + '\'' +
", operaUserName='" + operaUserName + '\'' +
", warningTime=" + operaTime +
", warningStatus='" + warningStatus + '\'' +
'}';
}
}

View File

@ -0,0 +1,20 @@
package com.bonus.common.exception;
import org.springframework.context.ApplicationEvent;
public class WaringLogEvent extends ApplicationEvent {
private final SysWarning sysWarning;
public WaringLogEvent(SysWarning logEvent) {
super(logEvent);
this.sysWarning = logEvent;
}
public SysWarning getSysWarning(){
return this.sysWarning;
}
}

View File

@ -0,0 +1,320 @@
package com.bonus.common.utils;
import com.bonus.common.utils.file.FileTypeUtils;
import com.bonus.common.utils.file.MimeTypeUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
/**
* 文件处理工具类
*
* @author bonus
*/
public class FileUtils {
/**
* 字符常量斜杠 {@code '/'}
*/
public static final char SLASH = '/';
/**
* 字符常量反斜杠 {@code '\\'}
*/
public static final char BACKSLASH = '\\';
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
private static final String PARENT_DIRECTORY_INDICATOR = "..";
private static final String MSIE_IDENTIFIER = "MSIE";
private static final String FIREFOX_IDENTIFIER = "Firefox";
private static final String CHROME_IDENTIFIER = "Chrome";
/**
* 输出指定文件的byte数组
*
* @param filePath 文件路径
* @param os 输出流
*/
public static void writeBytes(String filePath, OutputStream os) throws IOException {
FileInputStream fis = null;
try {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
}
fis = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fis.read(b)) > 0) {
os.write(b, 0, length);
}
} catch (IOException e) {
throw e;
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/**
* 删除文件
*
* @param filePath 文件
* @return
*/
public static boolean deleteFile(String filePath) {
boolean flag = false;
File file = new File(filePath);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists()) {
flag = file.delete();
}
return flag;
}
/**
* 文件名称验证
*
* @param filename 文件名称
* @return true 正常 false 非法
*/
public static boolean isValidFilename(String filename) {
return filename.matches(FILENAME_PATTERN);
}
/**
* 检查文件是否可下载
*
* @param resource 需要下载的文件
* @return true 正常 false 非法
*/
public static boolean checkAllowDownload(String resource) {
// 禁止目录上跳级别
if (StringUtils.contains(resource, PARENT_DIRECTORY_INDICATOR)) {
return false;
}
// 判断是否在允许下载的文件规则内
return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource));
}
/**
* 下载文件名重新编码
*
* @param request 请求对象
* @param fileName 文件名
* @return 编码后的文件名
*/
public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
final String agent = request.getHeader("USER-AGENT");
String filename = fileName;
if (agent.contains(MSIE_IDENTIFIER)) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains(FIREFOX_IDENTIFIER)) {
// 火狐浏览器
filename = new String(fileName.getBytes(), "ISO8859-1");
} else if (agent.contains(CHROME_IDENTIFIER)) {
// google浏览器
filename = URLEncoder.encode(filename, "utf-8");
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
/**
* 返回文件名
*
* @param filePath 文件
* @return 文件名
*/
public static String getName(String filePath) {
if (null == filePath) {
return null;
}
int len = filePath.length();
if (0 == len) {
return filePath;
}
if (isFileSeparator(filePath.charAt(len - 1))) {
// 以分隔符结尾的去掉结尾分隔符
len--;
}
int begin = 0;
char c;
for (int i = len - 1; i > -1; i--) {
c = filePath.charAt(i);
if (isFileSeparator(c)) {
// 查找最后一个路径分隔符/或者\
begin = i + 1;
break;
}
}
return filePath.substring(begin, len);
}
/**
* 是否为Windows或者LinuxUnix文件分隔符<br>
* Windows平台下分隔符为\LinuxUnix/
*
* @param c 字符
* @return 是否为Windows或者LinuxUnix文件分隔符
*/
public static boolean isFileSeparator(char c) {
return SLASH == c || BACKSLASH == c;
}
/**
* 下载文件名重新编码
*
* @param response 响应对象
* @param realFileName 真实文件名
*/
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
String percentEncodedFileName = percentEncode(realFileName);
StringBuilder contentDispositionValue = new StringBuilder();
contentDispositionValue.append("attachment; filename=")
.append(percentEncodedFileName)
.append(";")
.append("filename*=")
.append("utf-8''")
.append(percentEncodedFileName);
response.setHeader("Content-disposition", contentDispositionValue.toString());
response.setHeader("download-filename", percentEncodedFileName);
}
/**
* 百分号编码工具方法
*
* @param s 需要百分号编码的字符串
* @return 百分号编码后的字符串
*/
public static String percentEncode(String s) throws UnsupportedEncodingException {
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
return encode.replaceAll("\\+", "%20");
}
/**
* 生成统一格式的文件名
* 格式为uploads/yyyy/MM/dd/fileName
*
* @param fileName 原始文件名
* @return 生成的文件名
*/
public static String generateObjectName(String fileName) {
// 生成安全的文件名例如使用UUID加上原始文件扩展名
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String datePath = sdf.format(new Date());
return "uploads/" + datePath + "/" + fileName;
}
/**
* 从文件路径中提取文件名
*
* @param filePath 文件路径
* @return 文件名
*/
public static String getFileNameFromPath(String filePath) {
Path path = Paths.get(filePath);
return path.getFileName().toString();
}
/**
* 将MultipartFile转换为File对象
* 在临时目录中创建文件并将上传文件内容写入
*
* @param multiFile MultipartFile对象
* @return 转换后的File对象如果转换失败返回null
*/
public static File multipartFileToFile(MultipartFile multiFile) {
try {
String fileName = multiFile.getOriginalFilename();
if (fileName == null) {
return null;
}
String prefix = UUID.randomUUID().toString().replace("-", "");
String suffix = fileName.substring(fileName.lastIndexOf("."));
File file = File.createTempFile(prefix, suffix);
multiFile.transferTo(file);
return file;
} catch (Exception e) {
return null;
}
}
/**
* 下载指定路径的文件
* @param outputDir 要下载的文件的绝对路径
* @param fileName 文件名
* @param response HttpServletResponse对象
* @throws IOException 可能抛出的IO异常
*/
public static void fileDownload(String outputDir, String fileName, HttpServletResponse response) throws IOException {
try (FileInputStream is = new FileInputStream(new File(outputDir))) {
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.setHeader("content-disposition", "attachment;filename=\"" + URLEncoder.encode(fileName, "utf-8") + "\"");
ServletOutputStream os = response.getOutputStream();
IOUtils.copy(is, os);
}
}
/**
* MultipartFile 转换为带前缀的 Base64 编码
*
* @param file 上传的文件
* @return 带前缀的 Base64 编码的字符串
* @throws IOException 如果读取文件时出错
*/
public static String convertToBase64WithPrefix(MultipartFile file) throws IOException {
// 获取文件的字节数组
byte[] fileBytes = file.getBytes();
// 获取文件的内容类型 (MIME Type)
String contentType = file.getContentType();
// 根据文件的 MIME 类型生成前缀
String prefix = "data:" + contentType + ";base64,";
// 将文件字节数组转换为 Base64 字符串
String base64Encoded = Base64.getEncoder().encodeToString(fileBytes);
// 返回带前缀的 Base64 字符串
return prefix + base64Encoded;
}
}

View File

@ -0,0 +1,133 @@
package com.bonus.system.mapper;
import com.bonus.system.domain.SysLogsVo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @authorbonus
* @version1.0
* @description系统日志-数据库访问层
*/
public interface SysLogMapper {
/**
* 保存系统日志
*
* @param sysLog
* @description 保存系统日志
* @author cwchen
* @date 2024/2/28 14:35
*/
void saveLogs(SysLogsVo sysLog);
/**
* 查询日志
* @param dto
* @return
*/
List<SysLogsVo> getSystemLogs(SysLogsVo dto);
/**
* 查询日志容量
* @return
*/
String getLogsSet();
/**
* 数据插入
* @param capacity
*/
void setLogsSet(String capacity);
/**
* 更新数据
* @param capacity
*/
void updateLogsSet(String capacity);
/**
* 采茶戏
* @param dto
* @return
*/
int getLogStatistics(SysLogsVo dto);
/**
* 按照操作类型查询
* @param dto
* @return
*/
List<SysLogsVo> getLogsListByOperType(SysLogsVo dto);
/**
* 按照操作人查询
* @param dto
* @return
*/
List<SysLogsVo> getLogsListByOperUserName(SysLogsVo dto);
/**
* 通过 方法查找模块
* @param operUri
* @return
*/
SysLogsVo getModule(@Param("module") String operUri);
/**
* 查询当日异常告警数量
* @return
*/
int getErrorLogs();
/**
* 查询日志容量
* @return
*/
String getLogsRl(String schemaName);
/**
* 查询全部日志细腻系
* @param dto
* @return
*/
List<SysLogsVo> getAllLogs(SysLogsVo dto);
/**
* 查询
* @param sysLog
* @return
*/
String getModuleName(SysLogsVo sysLog);
/**
* 检查模块是否存在
* @param sysLog
* @return
*/
String getModuleIsc(SysLogsVo sysLog);
/**
* 查询全部日志
* @param logType
* @return
*/
List<SysLogsVo> getLogsLists(@Param("logType") String logType);
/**
* 查询未处理告警日志
* @return
*/
List<SysLogsVo> getNotHandleWarningLog();
/**
* 修改指定日志为已处理状态
* @param logId 日志id
* @return
*/
void updateLogsWithHandledStatus(String logId);
}

View File

@ -0,0 +1,93 @@
package com.bonus.system.service;
import com.bonus.common.utils.DateUtils;
import com.bonus.common.utils.global.SystemGlobal;
import com.bonus.system.domain.SysLogsVo;
import com.bonus.system.mapper.SysLogMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
/**
* @author 黑子
*/
@Configuration
@Slf4j
public class ExportSqlService {
public static String TABLE_NAME = "sys_logs";
/**
* 存储的路径
*/
@Value("${sql.filePath}")
private String filePath;
@Resource
private SysLogMapper mapper;
@SneakyThrows
public String export(String logType) {
String dateTimeNow = DateUtils.dateTimeNow();
String path = filePath + TABLE_NAME + dateTimeNow;
List<SysLogsVo> list = mapper.getLogsLists(logType);
// 创建文件并写入
try {
FileWriter writer = new FileWriter(path + SystemGlobal.TEXT_FIX);
list.forEach(logs -> {
try {
// 根据你的表结构生成你需要的SQL行
String rowSql = "INSERT INTO sys_logs (log_id, opera_user_name,ip,user_id,model" +
",oper_time,method,params,operate_detail,oper_type,oper_uri,log_type,result,times,failure_reason,grade,err_type,method_type,title,result_data) VALUES ('"
+ isNotNull(logs.getLogId()) + "', '"
+ isNotNull(logs.getOperaUserName()) + "', '"
+ isNotNull(logs.getIp()) + "', '"
+ isNotNull(logs.getUserId()) + "', '"
+ isNotNull(logs.getModel()) + "', '"
+ isNotNull(logs.getOperTime()) + "', '"
+ isNotNull(logs.getMethod()) + "', '"
+ isNotNull(logs.getParams()) + "', '"
+ isNotNull(logs.getOperateDetail()) + "', '"
+ isNotNull(logs.getOperType()) + "', '"
+ isNotNull(logs.getOperUri()) + "', '"
+ isNotNull(logs.getLogType()) + "', '"
+ isNotNull(logs.getFailureReason()) + "', '"
+ isNotNull(logs.getTimes()) + "', '"
+ isNotNull(logs.getFailureReason()) + "', '"
+ isNotNull(logs.getGrade()) + "', '"
+ isNotNull(logs.getErrType()) + "', '"
+ isNotNull(logs.getMethodType()) + "', '"
+ isNotNull(logs.getTitle()) + "', '" +
isNotNull(logs.getResultData()) + "');";
writer.write(rowSql + System.lineSeparator());
} catch (IOException e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
log.error(e.toString(), e);
}
return dateTimeNow;
}
public String isNotNull(Object object) {
if (object == null) {
return "";
}
return object.toString();
}
}

View File

@ -0,0 +1,80 @@
package com.bonus.system.service;
import com.bonus.common.core.domain.AjaxResult;
import com.bonus.common.core.domain.R;
import com.bonus.system.domain.SysLogsVo;
import org.springframework.scheduling.annotation.Async;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
/**
* @authorcwchen
* @date2024-02-28-14:01
* @version1.0
* @description系统日志-业务层
*/
public interface ISysLogService {
/**
* 保存系统日志
*
* @param sysLog
* @return AjaxResult
* @description
* @author cwchen
* @date 2024/2/28 14:33
*/
AjaxResult saveLogs(SysLogsVo sysLog);
/**
* 查询系统日志
* @param dto
* @return
*/
List<SysLogsVo> getSystemLogs(SysLogsVo dto);
/**
* 查询日志容量信息
* @return
*/
R<String> getLogsSet();
/**
* 审计日志容量配置
* @param capacity
* @return
*/
R<String> setLogsSet(String capacity);
/**
* 查询·日志统计
* @param dto
* @return
*/
R<Map<String, Object>> getLogStatistics(SysLogsVo dto);
/**
*日志容量告警
* @return
*/
void logWarn( );
/**
* 保存日志
* @param sysLog
* @param request
* @return
*/
void saveLogs(SysLogsVo sysLog, HttpServletRequest request);
/**
* 获取未处理的告警日志
* @return
*/
@Async
public void handleWarningLog();
@Async
public void updateLogsWithHandledStatus(String logId);
}

View File

@ -0,0 +1,330 @@
package com.bonus.system.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.bonus.common.core.domain.AjaxResult;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.domain.model.LoginUser;
import com.bonus.common.exception.SysWarning;
import com.bonus.common.exception.WaringLogEvent;
import com.bonus.common.utils.DateUtils;
import com.bonus.common.utils.SecurityUtils;
import com.bonus.common.utils.global.SystemGlobal;
import com.bonus.common.utils.ip.IpUtils;
import com.bonus.common.utils.uuid.IdUtils;
import com.bonus.system.domain.SysLogsVo;
import com.bonus.system.mapper.SysLogMapper;
import com.bonus.system.service.ISysLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @authorcwchen
* @date2024-02-28-14:02
* @version1.0
* @description系统日志-业务逻辑层
*/
@Service(value = "ISysLogService")
@Slf4j
public class SysLogServiceImpl implements ISysLogService {
@Value("${sql.schemaName}")
private String schemaName;
@Resource
private SysLogMapper mapper;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Override
@Transactional(rollbackFor = Exception.class)
public AjaxResult saveLogs(SysLogsVo sysLog) {
try {
//如果是异常日志
if (SystemGlobal.LOG_ERR.equals(sysLog.getErrType()) && StringUtils.isEmpty(sysLog.getModel())) {
SysLogsVo sysLog1 = mapper.getModule(sysLog.getOperUri());
if (sysLog1 != null) {
sysLog.setModel(sysLog1.getModel());
sysLog.setOperateDetail(sysLog1.getOperateDetail());
} else {
if (StringUtils.isEmpty(sysLog1.getModel())) {
sysLog.setModel("未知的请求模块");
}
}
}
if (sysLog.getLogType() == 2) {
sysLog.setWarningStatus("0");
}
if (sysLog.getOperaUserName() != null) {
String str = sysLog.getOperaUserName().replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_");
sysLog.setOperaUserName(str);
}
if (sysLog.getIp() != null) {
String str = sysLog.getIp().replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_");
sysLog.setIp(str);
}
mapper.saveLogs(sysLog);
if (sysLog.getLogType() == 2) {
eventPublisher.publishEvent(new WaringLogEvent(new SysWarning(sysLog.getLogId(), sysLog.getErrType(), sysLog.getIp(), sysLog.getOperaUserName(), sysLog.getOperTime(), "0")));
}
} catch (Exception e) {
log.error("保存系统日志");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return null;
}
@Override
public void saveLogs(SysLogsVo sysLog, HttpServletRequest request) {
try {
String loginUuid = IdUtils.fastUUID();
String ip = IpUtils.getIpAddr(request);
sysLog.setIp(ip);
sysLog.setLogId(loginUuid);
sysLog.setGrade("");
sysLog.setErrType("越权访问");
sysLog.setFailureReason("页面未授权");
sysLog.setOperType("查询");
sysLog.setOperateDetail("查看页面");
String module = mapper.getModuleName(sysLog);
sysLog.setLogType(2);
sysLog.setResult("失败");
sysLog.setOperTime(DateUtils.getTime());
sysLog.setModel(module);
LoginUser user = SecurityUtils.getLoginUser();
sysLog.setUserId(String.valueOf(user.getUserId()));
sysLog.setOperaUserName(user.getUsername());
String modules = mapper.getModuleIsc(sysLog);
if (StringUtils.isNotEmpty(module)) {
if (StringUtils.isEmpty(module)) {
sysLog.setModel(modules);
}
}
if (sysLog.getLogType() == 2) {
sysLog.setWarningStatus("0");
}
if (sysLog.getOperaUserName() != null) {
String str = sysLog.getOperaUserName().replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_");
sysLog.setOperaUserName(str);
}
if (sysLog.getIp() != null) {
String str = sysLog.getIp().replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_");
sysLog.setIp(str);
}
mapper.saveLogs(sysLog);
if (sysLog.getLogType() == 2) {
eventPublisher.publishEvent(new WaringLogEvent(new SysWarning(loginUuid, "越权访问", ip, user.getUsername(), DateUtils.getTime(), "0")));
}
} catch (Exception e) {
log.error(e.toString(), e);
}
}
@Override
public List<SysLogsVo> getSystemLogs(SysLogsVo dto) {
try {
if (StringUtils.isEmpty(dto.getOperTime())) {
dto.setStartTime(DateUtils.dateTimeNow());
dto.setEndTime(DateUtils.dateTimeNow());
} else {
dto.setStartTime(dto.getOperTime().split(" - ")[0].trim());
dto.setEndTime(dto.getOperTime().split(" - ")[1].trim());
}
return mapper.getSystemLogs(dto);
} catch (Exception e) {
log.error(e.toString(), e);
}
return new ArrayList<>();
}
/**
* @return
*/
@Override
public R<String> getLogsSet() {
try {
String capacity = mapper.getLogsSet();
if (StringUtils.isEmpty(capacity)) {
return R.ok("1");
}
return R.ok(capacity);
} catch (Exception e) {
log.error(e.toString(), e);
}
return R.ok(SystemGlobal.LOG_DEFEAT_SIZE + "");
}
/**
* 系统日志 调试
*
* @param capacity
* @return
*/
@Override
public R<String> setLogsSet(String capacity) {
try {
if (StringUtils.isNotEmpty(capacity)) {
if (SystemGlobal.ERR_NUM.equals(capacity.toUpperCase())) {
return R.fail("请输入数字");
}
} else {
return R.fail("日志容量最低是" + SystemGlobal.LOG_DEFEAT_SIZE + "GB");
}
Double cap = Double.parseDouble(capacity);
if (cap < SystemGlobal.LOG_DEFEAT_SIZE) {
return R.fail("日志容量最低是" + SystemGlobal.LOG_DEFEAT_SIZE + "GB");
}
} catch (Exception e) {
log.error(e.toString(), e);
return R.fail("请输入数字");
}
try {
String city = mapper.getLogsSet();
if (StringUtils.isEmpty(city)) {
mapper.setLogsSet(capacity);
} else {
mapper.updateLogsSet(capacity);
}
} catch (Exception e) {
log.error(e.toString(), e);
return R.fail("系统异常");
}
return R.ok("设置成功", "设置成功");
}
public final static String SUCCESS = "成功";
/**
* @param dto
* @return
*/
@Override
public R<Map<String, Object>> getLogStatistics(SysLogsVo dto) {
Map<String, Object> map = new HashMap<>();
try {
if (StringUtils.isNotEmpty(dto.getOperTime())) {
dto.setStartTime(dto.getOperTime().split(" - ")[0].trim());
dto.setEndTime(dto.getOperTime().split(" - ")[1].trim());
}
List<SysLogsVo> all = mapper.getAllLogs(dto);
//先按操作结果查询
if (CollectionUtil.isNotEmpty(all)) {
int allNum = all.stream().mapToInt(SysLogsVo::getNum).sum();
map.put("allNum", allNum);
all.forEach(vo -> {
if (vo.getResult().contains("成功")) {
map.put("sNum", vo.getNum());
} else {
map.put("eNum", vo.getNum());
}
});
}
if (SystemGlobal.LOG_TYPE.equals(dto.getType())) {
//按日志类型查询
List<Integer> type = new ArrayList<>();
List<String> typeList = new ArrayList<>();
typeList.add("系统日志");
typeList.add("业务日志");
typeList.add("异常日志");
type.add(0);
type.add(1);
type.add(2);
List<Integer> nums = new ArrayList<>();
type.forEach(str -> {
dto.setLogType(str);
int num = mapper.getLogStatistics(dto);
nums.add(num);
});
map.put("list", typeList);
map.put("nums", nums);
} else if ("2".equals(dto.getType())) {
//按操作类型查询
List<String> typeList = new ArrayList<>();
List<Integer> nums = new ArrayList<>();
List<SysLogsVo> list = mapper.getLogsListByOperType(dto);
list.forEach(vo -> {
typeList.add(vo.getOperType());
nums.add(vo.getNum());
});
map.put("list", typeList);
map.put("nums", nums);
} else if ("3".equals(dto.getType())) {
List<String> typeList = new ArrayList<>();
List<Integer> nums = new ArrayList<>();
List<SysLogsVo> list = mapper.getLogsListByOperUserName(dto);
list.forEach(vo -> {
typeList.add(vo.getOperaUserName());
nums.add(vo.getNum());
});
map.put("list", typeList);
map.put("nums", nums);
}
return R.ok(map);
} catch (Exception e) {
log.error(e.toString(), e);
}
return R.ok(map);
}
/**
* 日志容量告警
*
* @return
*/
@Override
public void logWarn() {
try {
double bfb = 0.9;
String rl = mapper.getLogsRl(schemaName);
if (rl == null) {
rl = "0";
}
String city = mapper.getLogsSet();
Double d = Double.parseDouble(rl);
Double max = Double.parseDouble(city) * bfb;
if (d >= max) {
String warningEvent = "日志容量告警,当日日志内存为" + d + "GB,日志内存超过总内存的90%,请及时处理!";
eventPublisher.publishEvent(new WaringLogEvent(new SysWarning("0", warningEvent, "", null, null, "1")));
}
} catch (Exception e) {
log.error(e.toString(), e);
}
}
@Override
@Async
public void handleWarningLog() {
List<SysLogsVo> list = mapper.getNotHandleWarningLog();
// 使用for-each循环遍历List
for (SysLogsVo item : list) {
eventPublisher.publishEvent(new WaringLogEvent(new SysWarning(item.getLogId(), item.getErrType(), item.getIp(), item.getOperaUserName(), item.getOperTime(), "0")));
log.info("*****系统管理员和审计管理员处理异常日志*******");
}
}
@Override
@Async
public void updateLogsWithHandledStatus(String logId) {
mapper.updateLogsWithHandledStatus(logId);
}
}

View File

@ -0,0 +1,283 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.bonus.system.mapper.SysLogMapper">
<!--保存系统日志-->
<insert id="saveLogs" parameterType="SysLogsVo">
insert into da_ky_sys_logs(
<if test=" logId!= null and logId != ''">log_id,</if>
<if test=" operaUserName!= null">opera_user_name,</if>
<if test=" ip!= null">ip,</if>
<if test=" userId!= null and userId != 0">user_id,</if>
<if test=" model!= null">model,</if>
<if test=" operTime!= null">oper_time,</if>
<if test=" method!= null">method,</if>
<if test=" params!= null">params,</if>
<if test=" operateDetail!= null">operate_detail,</if>
<if test=" operaType!= null">oper_type,</if>
<if test=" operUri!= null">oper_uri,</if>
<if test=" logType!= null">log_type,</if>
<if test=" result!= null">result,</if>
<if test=" times!= null">times,</if>
<if test=" failureReason!= null">failure_reason,</if>
<if test=" grade!= null">grade,</if>
<if test=" errType!= null">err_type,</if>
<if test=" methodType!= null">method_type,</if>
<if test=" title!= null">title,</if>
<if test=" resultData!= null">result_data,</if>
<if test=" warningStatus!= null">warning_status,</if>
)values (
<if test=" logId!= null and logId != ''">{logId},</if>
<if test=" operaUserName!= null">{operaUserName},</if>
<if test=" ip!= null">{ip},</if>
<if test=" userId!= null and userId != 0">{userId},</if>
<if test=" model!= null">{model},</if>
<if test=" operTime!= null">{operTime},</if>
<if test=" method!= null">{method},</if>
<if test=" params!= null">{params},</if>
<if test=" operateDetail!= null">{operateDetail},</if>
<if test=" operaType!= null">{operaType},</if>
<if test=" operUri!= null">{operUri},</if>
<if test=" logType!= null">{logType},</if>
<if test=" result!= null">{result},</if>
<if test=" times!= null">{times},</if>
<if test=" failureReason!= null">{failureReason},</if>
<if test=" grade!= null">{grade},</if>
<if test=" errType!= null">{errType},</if>
<if test=" methodType!= null">{methodType},</if>
<if test=" title!= null">{title},</if>
<if test=" resultData!= null">{resultData},</if>
<if test=" warningStatus!= null">{warningStatus},</if>
</insert>
<update id="updateWarningStatus" parameterType="SysLogsVo">
update sys_warning
set warning_status = #{warningStatus}
where log_id = #{logId}
</update>
<!-- <insert id="saveLogs">-->
<!-- INSERT INTO sys_logs-->
<!-- <trim prefix="(" suffix=")" suffixOverrides=",">-->
<!-- <if test="logId != null and logId != ''">log_id,</if>-->
<!-- <if test="ip != null and ip!=''">ip,</if>-->
<!-- <if test="userId != null ">user_id,</if>-->
<!-- <if test="model != null and model!=''">model,</if>-->
<!-- <if test="operaUserName != null and operaUserName!=''">opera_user_name,</if>-->
<!-- <if test="operTime != null and operTime!=''">oper_time,</if>-->
<!-- <if test="method != null and method!=''">method,</if>-->
<!-- <if test="params != null and params!=''">params,</if>-->
<!-- <if test="operateDetail != null and operateDetail!=''">operate_detail,</if>-->
<!-- <if test="operType != null and operType!=''">oper_type,</if>-->
<!-- <if test="operUri != null and operUri!=''">oper_uri,</if>-->
<!-- <if test="logType != null">log_type,</if>-->
<!-- <if test="fruit != null">result,</if>-->
<!-- <if test="times != null and times!=''">times,</if>-->
<!-- <if test="failureReason != null and failureReason!=''">failure_reason,</if>-->
<!-- <if test="grade != null and grade!=''">grade,</if>-->
<!-- <if test="errType != null and errType!=''">err_type,</if>-->
<!-- </trim>-->
<!-- <trim prefix="values (" suffix=")" suffixOverrides=",">-->
<!-- <if test="logId != null and logId != ''">#{logId},</if>-->
<!-- <if test="ip != null and ip!=''">#{ip},</if>-->
<!-- <if test="userId != null">#{userId},</if>-->
<!-- <if test="model != null and model!=''">#{model},</if>-->
<!-- <if test="operaUserName != null and operaUserName!=''">#{operaUserName},</if>-->
<!-- <if test="operTime != null and operTime!=''">#{operTime},</if>-->
<!-- <if test="method != null and method!=''">#{method},</if>-->
<!-- <if test="params != null and params!=''">#{params},</if>-->
<!-- <if test="operateDetail != null and operateDetail!=''">#{operateDetail},</if>-->
<!-- <if test="operType != null and operType!=''">#{operType},</if>-->
<!-- <if test="operUri != null and operUri!=''">#{operUri},</if>-->
<!-- <if test="logType != null">#{logType},</if>-->
<!-- <if test="fruit != null">#{fruit},</if>-->
<!-- <if test="times != null and times!=''">#{times},</if>-->
<!-- <if test="failureReason != null and failureReason!=''">#{failureReason},</if>-->
<!-- <if test="grade != null and grade!=''">#{grade},</if>-->
<!-- <if test="errType != null and errType!=''">#{errType},</if>-->
<!-- </trim>-->
<!-- </insert>-->
<insert id="setLogsSet">
insert into da_ky_sys_logs_set(capacity) value(#{capacity})
</insert>
<update id="updateLogsSet">
update da_ky_sys_logs_set set capacity=#{capacity}
</update>
<select id="getSystemLogs" resultType="com.bonus.system.domain.SysLogsVo">
SELECT
log_id,
opera_user_name operaUserName,
ip,
user_id userId,
model,
oper_time operTime,
method,
params,
operate_detail operateDetail,
oper_type operType,
oper_uri operUri,
log_type logType,
result,
grade,
err_type errType,
method_type methodType,
times
from da_ky_sys_logs
where log_type=#{logType}
<if test="operaUserName!=null and operaUserName!=''">
and opera_user_name LIKE concat('%',#{operaUserName},'%')
</if>
<if test="model!=null and model!=''">
and model LIKE concat('%',#{model},'%')
</if>
<if test="operType!=null and operType !=''">
and oper_type LIKE concat('%',#{operType},'%')
</if>
<if test="params!=null and params !=''">
and params LIKE concat('%',#{params},'%')
</if>
<if test="errType!=null and errType!=''">
and err_type=#{errType}
</if>
<if test="grade!=null and grade!=''">
and grade=#{grade}
</if>
<if test="endTime!=null and endTime!=''">
and oper_time BETWEEN concat(#{startTime}, ' 00:00:00') AND concat(#{endTime},' 23:59:59')
</if>
<if test="ip !=null and ip!=''">
and ip LIKE concat('%',#{ip},'%')
</if>
<if test="result !=null and result !=''">
and result LIKE concat('%',#{result},'%')
</if>
<if test="logSort!=null and logDesc !=null and logSort!='' and logDesc!='' ">
order by
<if test='logSort=="1"'>
oper_time
</if>
<if test='logSort=="2"'>
opera_user_name
</if>
<if test='logSort=="3"'>
model
</if>
<if test='logSort=="4"'>
ip
</if>
<if test='logSort=="5"'>
oper_type
</if>
<if test='logDesc=="1"'>
COLLATE utf8mb4_unicode_ci DESC
</if>
<if test='logDesc=="2"'>
COLLATE utf8mb4_unicode_ci ASC
</if>
</if>
</select>
<select id="getLogsSet" resultType="java.lang.String">
select capacity
from da_ky_sys_logs_set
</select>
<select id="getLogStatistics" resultType="java.lang.Integer">
select COUNT(1) num
FROM da_ky_sys_logs
where log_type=#{logType}
<if test="endTime!=null and endTime!=''">
and oper_time BETWEEN concat(#{startTime}, ' 00:00:00') AND CONCAT(#{endTime},' 23:59:59')
</if>
</select>
<select id="getLogsListByOperType" resultType="com.bonus.system.domain.SysLogsVo">
select COUNT(1) num,oper_type operType
FROM da_ky_sys_logs
WHERE oper_type IS NOT NULL
<if test="endTime!=null and endTime!=''">
and oper_time BETWEEN CONCAT(#{startTime}, ' 00:00:00') AND CONCAT(#{endTime},' 23:59:59')
</if>
GROUP by oper_type
</select>
<select id="getLogsListByOperUserName" resultType="com.bonus.system.domain.SysLogsVo">
select COUNT(1) num,opera_user_name operaUserName
FROM da_ky_sys_logs
WHERE opera_user_name IS NOT NULL
<if test="endTime!=null and endTime!=''">
and oper_time BETWEEN CONCAT(#{startTime}, ' 00:00:00') AND CONCAT(#{endTime},' 23:59:59')
</if>
GROUP by opera_user_name
</select>
<!--查询模块-->
<select id="getModule" resultType="com.bonus.system.domain.SysLogsVo">
select CONCAT(IFNULL(sm.menu_name,''),'->',IFNULL(sm2.menu_name,'')) module ,sm3.menu_name operateDetail
from sys_menu sm
left join sys_menu sm2 on sm2.p_id=sm.menu_id and sm2.menu_type=1
left join sys_menu sm3 on sm3.p_id=sm2.menu_id and sm3.menu_type=2
where sm3.menu_auth=#{module}
limit 1
</select>
<select id="getErrorLogs" resultType="java.lang.Integer">
select count(1)
from da_ky_sys_logs
where log_type=2
and DATE_FORMAT(oper_time,'%Y-%m-%d')=CURRENT_DATE
</select>
<select id="getLogsRl" parameterType="java.lang.String" resultType="java.lang.String" >
SELECT
round(((data_length + index_length) / 1024 / 1024 / 1024), 2) AS 'Size in GB'
FROM information_schema.TABLES
WHERE table_schema = #{schemaName} AND table_name = 'da_ky_sys_logs'
</select>
<select id="getModuleName" resultType="java.lang.String">
select CONCAT(IFNULL(sm.menu_name,''),'->',IFNULL(sm2.menu_name,'')) module
from sys_menu sm
left join sys_menu sm2 on sm2.p_id=sm.menu_id and sm2.menu_type=1
where sm2.menu_url=#{operUri}
limit 1
</select>
<select id="getModuleIsc" resultType="java.lang.String">
select menu_name
from sys_menu sm
where sm.menu_url=#{operUri}
limit 1
</select>
<!--查询全部日志-->
<select id="getLogsLists" resultType="com.bonus.system.domain.SysLogsVo">
SELECT
log_id logId, opera_user_name operaUserName,ip,
user_id userId, model, oper_time operTime,
method,params ,operate_detail operateDetail,
oper_type operType,oper_uri operUri, log_type logType,
result fruit,times,failure_reason failureReason,
grade,err_type errType, method_type methodType,
title
FROM da_ky_sys_logs
where log_type=#{logType}
</select>
<select id="getNotHandleWarningLog" resultType="com.bonus.system.domain.SysLogsVo">
SELECT
log_id logId, opera_user_name operaUserName,ip,
user_id userId, oper_time operTime,
oper_type operType, err_type errType
FROM da_ky_sys_logs
where warning_status=0
</select>
<select id="getAllLogs" resultType="com.bonus.system.domain.SysLogsVo">
select count(1) num,result
from da_ky_sys_logs
where oper_time BETWEEN CONCAT(#{startTime}, ' 00:00:00') AND CONCAT(#{endTime},' 23:59:59')
GROUP BY result
</select>
<update id="updateLogsWithHandledStatus">
update da_ky_sys_logs set warning_status=1 where log_id=#{logId}
</update>
</mapper>