文章目录
- 操作日志介绍
- 自动保存操作日志
- 基本实现思路
- 定义注解
- 枚举
- 业务类型枚举
- 操作人员类型枚举
- AOP具体实现
- 方法上添加注解
- 日志增删改查
- 日志表sql
- 实体类
- Service
- Controller
- Vo
操作日志介绍
操作日志是对系统或应用程序中所有用户操作、系统事件、后台任务等进行详细记录的文本文件或数据库条目。它是系统运行过程中的“黑匣子”,详尽地记载了每一次交互、每一次状态变更以及每一次异常情况。在数字化时代,操作日志的作用与重要性不容忽视,主要体现在以下几个方面:
-
故障排查与问题定位
:当系统出现故障或异常行为时,操作日志是首要的诊断工具。通过查阅相关时间段的日志记录,工程师可以快速定位到问题发生的精确时间点、涉及的功能模块以及可能触发问题的操作步骤,大大缩短了故障排查的时间,提高了问题解决效率。日志中的错误代码、异常堆栈信息等详细数据,更是为精准定位问题根源提供了关键线索 -
审计追踪与合规性要求
:对于许多行业(如金融、医疗、政府等),法律法规往往要求对关键业务操作进行详细的记录和长期保存,以满足审计需求和合规性监管。操作日志能够完整记录用户的操作行为、操作时间、操作结果等信息,确保了业务流程的透明度和可追溯性。在发生争议或安全事件时,操作日志可以作为重要的证据材料,帮助厘清责任归属,保障各方权益 -
性能分析与优化
:操作日志不仅记录错误和异常,也包含系统正常运行时的各项指标和状态变化。通过对日志数据进行深度分析,可以揭示系统的性能瓶颈、资源使用情况、用户访问模式等信息,为系统优化提供数据支持。例如,通过分析请求响应时间、并发量等指标,可以发现并优化慢查询、资源争抢等问题,提升系统整体性能 -
安全监控与威胁检测
:在网络安全领域,操作日志是实时监控系统安全状态、及时发现并响应潜在威胁的重要手段。通过对登录尝试、权限变更、敏感数据访问等操作的记录与分析,可以及时发现异常行为,如暴力破解、未授权访问、数据泄露等安全事件,从而启动应急预案,防止或减轻损失 -
业务洞察与决策支持
:对于业务运营人员而言,操作日志中蕴含的用户行为数据是了解产品使用情况、用户偏好、功能受欢迎程度等关键信息的重要来源。通过对日志进行统计分析和数据挖掘,可以得出诸如活跃用户数、功能使用频率、转化率等业务指标,为产品优化、市场策略制定提供数据驱动的决策支持
自动保存操作日志
基本实现思路
定义注解,将注解添加到需要记录日志的方法上,当方法执行完成或者抛异常后,通过AOP获取方法的参数、响应等信息记录到数据库中。
定义注解
import com.dam.enums.log.BusinessTypeEnum;
import com.dam.enums.log.OperatorTypeEnum;
import java.lang.annotation.*;
/**
* @author dam
*/
// 表示注解可以用在参数、方法上面
@Target({ElementType.PARAMETER, ElementType.METHOD})
// 设置注解的保留策略为 RUNTIME,这意味着该注解信息将在编译后的字节码中保留,并能在运行时通过反射获取
@Retention(RetentionPolicy.RUNTIME)
// 标记该注解将被包含在生成的 JavaDoc 文档中,便于开发者查阅和理解
@Documented
public @interface OperationLog {
/**
* 模块名称
*/
public String title() default "";
/**
* 方法名称
*/
public String detail() default "";
/**
* 业务类型
*/
public BusinessTypeEnum businessType() default BusinessTypeEnum.OTHER;
/**
* 操作人类别(手机用户、网页用户)
*/
public OperatorTypeEnum operatorType() default OperatorTypeEnum.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
}
枚举
业务类型枚举
/**
* 业务操作类型
*/
public enum BusinessTypeEnum {
/**
* 其它
*/
OTHER(0,"其他"),
/**
* 新增
*/
INSERT(1,"新增"),
/**
* 修改
*/
UPDATE(2,"修改"),
/**
* 删除
*/
DELETE(3,"删除"),
/**
* 授权
*/
ASSGIN(4,"授权"),
/**
* 导出
*/
EXPORT(5,"导出"),
/**
* 导入
*/
IMPORT(6,"导入"),
/**
* 强退
*/
FORCE(7,"强退"),
/**
* 更新状态
*/
STATUS(8,"更新状态"),
/**
* 清空数据
*/
CLEAN(9,"清空数据"),
PASS(10,"通过"),
REJECT(11,"拒绝"),
;
private Integer code;
private String name;
BusinessTypeEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
public Integer getCode() {
return code;
}
public String getName() {
return name;
}
}
操作人员类型枚举
public enum OperatorTypeEnum {
/**
* 其它
*/
OTHER(0,"其他"),
/**
* 后台用户
*/
MANAGE(1,"后台用户"),
/**
* 手机端用户
*/
MOBILE(2,"手机端用户");
private Integer code;
private String name;
OperatorTypeEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
public Integer getCode() {
return code;
}
public String getName() {
return name;
}
}
AOP具体实现
这个类是一个使用 Spring AOP(面向切面编程)技术实现的操作日志记录处理器。
- 当应用中某个方法执行前后(成功返回或抛出异常)触发相应的通知方法(doAfterReturning 和 doAfterThrowing),这两个方法都会调用通用的日志处理方法 handleLog。
- handleLog 方法负责收集日志所需的各种信息,如请求方法、URL、IP 地址、地理位置、用户身份(从 JWT 令牌中解析)、企业及门店信息、操作状态(成功或异常)、异常消息(如果有)等。
- 收集到的日志信息封装在 OperationLogEntity 对象中,然后调用 OperationLogService 的 save 方法将日志数据持久化到数据库。
package com.dam.aop;
import com.alibaba.fastjson.JSON;
import com.dam.annotation.OperationLog;
import com.dam.model.entity.system.OperationLogEntity;
import com.dam.service.OperationLogService;
import com.dam.utils.JwtUtil;
import com.dam.utils.ip.IpAddressUtils;
import com.dam.utils.ip.IpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
/**
* 操作日志记录处理
*/
// 使用 AOP(面向切面编程)技术实现操作日志记录
@Aspect
// 将该类作为 Spring 管理的 Bean
@Component
public class OperationLogAspect {
private static final Logger log = LoggerFactory.getLogger(OperationLogAspect.class);
/**
* 注入 OperationLogService 依赖,用于持久化操作日志到数据库
*/
@Resource
private OperationLogService operationLogService;
/**
* 处理完请求后执行
* 在带有 @OperationLog 注解的方法执行成功并返回后触发
* pointcut:切入点
*
* @param joinPoint 切点
* @param controllerLog 注解实例
* @param jsonResult 返回结果对象(如果有的话)
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, OperationLog controllerLog, Object jsonResult) {
// 调用通用日志处理方法,记录正常操作日志
handleLog(joinPoint, controllerLog, null, jsonResult);
}
/**
* 拦截异常操作
* 在带有 @OperationLog 注解的方法抛出异常时触发
*
* @param joinPoint 切点
* @param controllerLog 注解实例
* @param e 异常对象
*/
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, OperationLog controllerLog, Exception e) {
// 调用通用日志处理方法,记录异常操作日志
handleLog(joinPoint, controllerLog, e, null);
}
/**
* 处理日志,填充日志信息,并将日志存储到数据库
*
* @param joinPoint 切点信息
* @param controllerLog 操作日志注解实例
* @param e 异常对象(如果有的话)
* @param jsonResult 返回结果对象(如果有的话)
*/
protected void handleLog(final JoinPoint joinPoint, OperationLog controllerLog, final Exception e, Object jsonResult) {
try {
根据请求上下文获取 HttpServletRequest 相关信息
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
// 获取request之后,请求的内容都可以知道
HttpServletRequest request = sra.getRequest();
操作日志对象信息存储
OperationLogEntity operationLog = new OperationLogEntity();
// 操作状态
operationLog.setStatus(0);
// 请求的地址
String ip = IpUtil.getIpAddress(request);
operationLog.setOperIp(ip);
// 设置请求的 URL
operationLog.setOperUrl(request.getRequestURI());
// 设置请求的地理位置信息(根据 IP 解析)
operationLog.setOperLocation(IpAddressUtils.getRealAddressByIP(ip));
// 从请求头中提取 JWT 令牌,获取用户名和企业、店铺 ID
String token = request.getHeader("token");
String userName = JwtUtil.getUsername(token);
operationLog.setOperName(userName);
// 设置企业信息
operationLog.setEnterpriseId(Long.parseLong(JwtUtil.getEnterpriseId(token)));
// 设置门店信息
operationLog.setStoreId(Long.parseLong(JwtUtil.getStoreId(token)));
// 如果存在异常,更新操作状态并记录异常信息
if (e != null) {
// 异常
operationLog.setStatus(1);
operationLog.setErrorMsg(e.getMessage());
}
// 获取类名和方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operationLog.setMethod(className + "." + methodName + "()");
// 设置请求方法(GET、POST、PUT 等)
operationLog.setRequestMethod(request.getMethod());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operationLog, jsonResult);
// 将操作数据保存数据库
operationLogService.save(operationLog);
} catch (Exception exp) {
// 记录本地异常日志
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param log 日志
* @param operationLog 操作日志
* @throws Exception
*/
public void getControllerMethodDescription(JoinPoint joinPoint, OperationLog log, OperationLogEntity operationLog, Object jsonResult) throws Exception {
// 设置业务类型(从注解中获取)
operationLog.setBusinessType(log.businessType().getCode());
// 设置操作标题(从注解中获取)
operationLog.setTitle(log.title());
// 设置操作详情(从注解中获取)
operationLog.setDetail(log.detail());
// 设置操作人类型(从注解中获取)
operationLog.setOperatorType(log.operatorType().getCode());
// 如果需要保存请求数据,提取并设置请求参数
if (log.isSaveRequestData()) {
this.setRequestValue(joinPoint, operationLog);
}
// 如果需要保存响应数据且有返回结果,将其序列化并设置到日志中
if (log.isSaveResponseData() && !StringUtils.isEmpty(jsonResult)) {
operationLog.setJsonResult(JSON.toJSONString(jsonResult));
}
}
/**
* 获取请求的参数,放到操作日志中
*
* @param joinPoint 切点信息
* @param operationLog 操作日志实体对象
*/
private void setRequestValue(JoinPoint joinPoint, OperationLogEntity operationLog) {
String requestMethod = operationLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
String params = argsArrayToString(joinPoint.getArgs());
operationLog.setOperParam(params);
}
}
/**
* 将方法参数数组转化为字符串表示形式
*
* @param paramArr 方法参数数组
* @return 参数字符串
*/
private String argsArrayToString(Object[] paramArr) {
String params = "";
if (paramArr != null && paramArr.length > 0) {
for (Object param : paramArr) {
// 每个param包含了 健值
if (!StringUtils.isEmpty(param) && !isFilterObject(param)) {
try {
Object jsonObj = JSON.toJSON(param);
params += jsonObj.toString() + " ";
} catch (Exception e) {
}
}
}
}
return params.trim();
}
/**
* 判断是否需要过滤的对象
*
* @param o 对象信息
* @return 如果是需要过滤的对象,则返回true;否则返回false
*/
// @SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
// 如果是数组,检查其组件类型是否为 MultipartFile
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
// 如果是集合,检查其中是否有 MultipartFile 类型的元素
Collection collection = (Collection) o;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
// 如果是 Map,检查其中是否有 MultipartFile 类型的值
Map map = (Map) o;
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
// 检查对象是否为 MultipartFile、HttpServletRequest、HttpServletResponse 或 BindingResult 类型
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
}
注:isFilterObject 方法的作用是判断给定的 Object 是否属于需要在操作日志记录过程中进行过滤的类型。也就是说,如果一个对象通过了这个方法的检查,那么在生成操作日志时,不应将其具体内容包含在日志记录中。该方法主要服务于日志记录的精细化控制,避免某些特定类型或敏感信息被无差别地写入日志,可能造成日志冗余
- MultipartFile:代表上传的文件对象,通常包含文件内容等大量二进制数据,不适合记录在日志中
- HttpServletRequest、HttpServletResponse:分别代表 HTTP 请求和响应对象,它们通常包含大量的请求/响应头、Cookie、Session 等信息,记录全部内容既没有必要,也可能包含敏感信息
- BindingResult:Spring MVC 中用于绑定和验证表单数据的结果对象,通常包含详细的验证错误信息,可能不适合全部写入日志
方法上添加注解
通过在方法上面添加@OperationLog注解,即可为其添加日志记录功能,注意,并不是所有方法都适合做日志记录,例如一些非关键业务的查询方法,都记录反而造成数据库的日志数据较多。应该对一些关键业务做日志记录,例如排班计算(计算出错时方便找到方法参数进行bug修复)、数据增加、数据修改(当数据被错误修改时、可以看到是谁修改的,方便追责和数据修复、数据删除)
/**
* 保存
*/
@RequestMapping("/save")
@PreAuthorize("hasAuthority('bnt.task.add')")
@OperationLog(title = SchedulingTaskController.title, businessType = BusinessTypeEnum.INSERT, detail = "新增排班任务")
public R save(@RequestBody SchedulingTaskEntity schedulingTask, HttpServletRequest httpServletRequest) throws SSSException {
long storeId = Long.parseLong(JwtUtil.getStoreId(httpServletRequest.getHeader("token")));
schedulingTask.setStoreId(storeId);
//默认复制门店的排班规则
R r = enterpriseFeignService.copySchedulingRule(storeId);
if (r.getCode() == ResultCodeEnum.SUCCESS.getCode().intValue()) {
Long ruleId = r.getData("ruleId", new TypeReference<Long>() {
});
if (ruleId == null) {
throw new SSSException(ResultCodeEnum.FAIL.getCode(), "门店规则还没有设置,请先设置规则再添加任务");
}
schedulingTask.setSchedulingRuleId(ruleId);
}
schedulingTaskService.save(schedulingTask);
return R.ok();
}
日志增删改查
日志表sql
DROP TABLE IF EXISTS `operation_log`;
CREATE TABLE `operation_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除(0-未删, 1-已删)',
`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '模块标题',
`business_type` tinyint DEFAULT NULL COMMENT '业务类型 (0其它 1新增 2修改 3删除)',
`method` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '方法名称',
`detail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '说明',
`request_method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求方式',
`operator_type` tinyint DEFAULT NULL COMMENT '操作类别(0其它 1后台用户 2手机端用户)',
`oper_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '操作人员',
`enterprise_id` bigint NOT NULL COMMENT '企业id',
`store_id` bigint DEFAULT NULL COMMENT '门店名称',
`oper_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求URL',
`oper_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '主机地址',
`oper_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '操作地点',
`oper_param` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin COMMENT '请求参数',
`json_result` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin COMMENT '返回参数',
`status` tinyint DEFAULT NULL COMMENT '操作状态 (0正常 1异常)',
`error_msg` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '错误消息',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1776520698685394945 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='操作日志表';
SET FOREIGN_KEY_CHECKS = 1;
实体类
import com.baomidou.mybatisplus.annotation.TableName;
import com.dam.model.entity.BaseEntity;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serializable;
/**
* 操作日志表
*
* @author dam
* @email 1782067308@qq.com
* @date 2023-03-13 16:42:08
*/
@Data
@TableName("operation_log")
public class OperationLogEntity extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 模块标题
*/
private String title;
/**
* 业务类型 (0其它 1新增 2修改 3删除)
*/
private Integer businessType;
/**
* 方法名称
*/
private String method;
/**
* 说明
*/
private String detail;
/**
* 请求方式
*/
private String requestMethod;
/**
* 操作类别(0其它 1后台用户 2手机端用户)
*/
private Integer operatorType;
/**
* 操作人员
*/
private String operName;
/**
* 企业id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long enterpriseId;
/**
* 门店名称
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long storeId;
/**
* 请求URL
*/
private String operUrl;
/**
* 主机地址
*/
private String operIp;
/**
* 操作地点
*/
private String operLocation;
/**
* 请求参数
*/
private String operParam;
/**
* 返回参数
*/
private String jsonResult;
/**
* 操作状态 (0正常 1异常)
*/
private Integer status;
/**
* 错误消息
*/
private String errorMsg;
}
Service
import com.baomidou.mybatisplus.extension.service.IService;
import com.dam.model.entity.system.OperationLogEntity;
import com.dam.utils.PageUtils;
import java.util.Map;
/**
* 操作日志表
*
* @author dam
* @email 1782067308@qq.com
* @date 2023-03-13 16:42:08
*/
public interface OperationLogService extends IService<OperationLogEntity> {
PageUtils queryPage(Map<String, Object> params, String token);
OperationLogEntity getById(Long id);
}
实现类
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dam.dao.OperationLogDao;
import com.dam.model.entity.system.OperationLogEntity;
import com.dam.model.entity.system.UserEntity;
import com.dam.model.enums.system.UserCodeEnum;
import com.dam.service.OperationLogService;
import com.dam.service.UserService;
import com.dam.utils.JwtUtil;
import com.dam.utils.PageUtils;
import com.dam.utils.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("operationLogService")
class OperationLogServiceImpl extends ServiceImpl<OperationLogDao, OperationLogEntity> implements OperationLogService {
@Autowired
private OperationLogDao OperationLogDao;
@Autowired
private UserService userService;
@Override
public PageUtils queryPage(Map<String, Object> params, String token) {
Long userId = Long.parseLong(JwtUtil.getUserId(token));
UserEntity user = userService.getById(userId);
String enterpriseId = JwtUtil.getEnterpriseId(token);
String storeId = JwtUtil.getStoreId(token);
QueryWrapper<OperationLogEntity> queryWrapper = new QueryWrapper<>();
if (user.getType() == UserCodeEnum.TYPE_SYSTEM_MANAGER.getCode().intValue()) {
//--if--系统管理员,可以查询所有日志
} else if (user.getType() == UserCodeEnum.TYPE_ENTERPRISE_MANAGER.getCode().intValue()) {
//--if--企业管理员,只能查询企业的日志
queryWrapper.eq("enterprise_id", enterpriseId);
} else if (user.getType() == UserCodeEnum.TYPE_STORE_MANAGER.getCode().intValue()) {
//--if--门店管理员,只能查询门店的日志
queryWrapper.eq("store_id", storeId);
} else {
//--if--普通用户,什么都查不出来
queryWrapper.eq("id", -1);
}
queryWrapper.orderByDesc("create_time");
IPage<OperationLogEntity> page = this.page(
new Query<OperationLogEntity>().getPage(params),
queryWrapper
);
return new PageUtils(page);
}
@Override
public OperationLogEntity getById(Long id) {
return OperationLogDao.selectById(id);
}
}
Controller
只讲日志查询和删除开放给用户
import com.alibaba.fastjson.TypeReference;
import com.dam.feign.EnterpriseFeignService;
import com.dam.model.entity.enterprise.EnterpriseEntity;
import com.dam.model.entity.enterprise.StoreEntity;
import com.dam.model.entity.system.OperationLogEntity;
import com.dam.model.enums.ResultCodeEnum;
import com.dam.model.result.R;
import com.dam.model.vo.system.OperationLogVo;
import com.dam.service.OperationLogService;
import com.dam.utils.PageUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
* 操作日志表
*
* @author dam
* @email 1782067308@qq.com
* @date 2023-03-13 16:42:08
*/
@RestController
@RequestMapping("system/operationLog")
public class OperationLogController {
@Autowired
private OperationLogService operationLogService;
@Autowired
private EnterpriseFeignService enterpriseFeignService;
private static final String title = "操作日志管理";
/**
* 列表
*/
@RequestMapping("/list")
@PreAuthorize("hasAuthority('bnt.operLog.list')")
public R list(@RequestParam Map<String, Object> params, HttpServletRequest httpRequest) {
String token = httpRequest.getHeader("token");
查询数据
PageUtils page = operationLogService.queryPage(params,token);
封装数据给前端展示
List<OperationLogEntity> operationLogEntityList = (List<OperationLogEntity>) page.getList();
List<OperationLogVo> operationLogVoList = new ArrayList<>();
Set<Long> storeIdList = new HashSet<>();
Set<Long> enterpriseIdList = new HashSet<>();
for (OperationLogEntity operationLogEntity : operationLogEntityList) {
OperationLogVo operationLogVo = new OperationLogVo();
BeanUtils.copyProperties(operationLogEntity, operationLogVo);
if (operationLogEntity.getStoreId() != null) {
enterpriseIdList.add(operationLogEntity.getEnterpriseId());
storeIdList.add(operationLogEntity.getStoreId());
operationLogVoList.add(operationLogVo);
}
}
//设置企业名称
R r1 = enterpriseFeignService.getEnterpriseMapByIdList(new ArrayList<>(enterpriseIdList));
if (r1.getCode() == ResultCodeEnum.SUCCESS.getCode().intValue()) {
Map<Long, EnterpriseEntity> idAndEnterpriseEntityMap = r1.getData("idAndEnterpriseEntityMap",
new TypeReference<Map<Long, EnterpriseEntity>>() {
});
for (OperationLogVo operationLogVo : operationLogVoList) {
Long enterpriseId = operationLogVo.getEnterpriseId();
operationLogVo.setEnterpriseName(idAndEnterpriseEntityMap.get(enterpriseId).getName());
}
}
//设置门店名称
R r2 = enterpriseFeignService.getStoreMapByIdList(new ArrayList<>(storeIdList));
if (r2.getCode() == ResultCodeEnum.SUCCESS.getCode().intValue()) {
Map<Long, StoreEntity> idAndStoreEntityMap = r2.getData("idAndStoreEntityMap",
new TypeReference<Map<Long, StoreEntity>>() {
});
for (OperationLogVo operationLogVo : operationLogVoList) {
Long storeId = operationLogVo.getStoreId();
operationLogVo.setStoreName(idAndStoreEntityMap.get(storeId).getName());
}
}
page.setList(operationLogVoList);
return R.ok().addData("page", page);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
@PreAuthorize("hasAuthority('bnt.operLog.list')")
public R info(@PathVariable("id") Long id) {
OperationLogEntity operationLog = operationLogService.getById(id);
return R.ok().addData("operationLog", operationLog);
}
/**
* 保存
*/
// @RequestMapping("/save")
// @PreAuthorize("hasAuthority('bnt.operLog.add')")
// public R save(@RequestBody OperationLogEntity operationLog) {
// operationLogService.save(operationLog);
//
// return R.ok();
// }
/**
* 修改
*/
// @RequestMapping("/update")
// @PreAuthorize("hasAuthority('bnt.operLog.update')")
// public R update(@RequestBody OperationLogEntity operationLog) {
// operationLogService.updateById(operationLog);
//
// return R.ok();
// }
/**
* 删除
*/
@RequestMapping("/deleteBatch")
@PreAuthorize("hasAuthority('bnt.operLog.delete')")
public R delete(@RequestBody Long[] ids) {
operationLogService.removeByIds(Arrays.asList(ids));
return R.ok();
}
}
Vo
import com.baomidou.mybatisplus.annotation.TableName;
import com.dam.model.entity.BaseEntity;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serializable;
/**
* 操作日志表
*
* @author dam
* @email 1782067308@qq.com
* @date 2023-03-13 16:42:08
*/
@Data
@TableName("operation_log")
public class OperationLogVo extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 模块标题
*/
private String title;
/**
* 业务类型 (0其它 1新增 2修改 3删除)
*/
private Integer businessType;
/**
* 方法名称
*/
private String method;
/**
* 说明
*/
private String detail;
/**
* 请求方式
*/
private String requestMethod;
/**
* 操作类别(0其它 1后台用户 2手机端用户)
*/
private Integer operatorType;
/**
* 操作人员
*/
private String operName;
/**
* 企业id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long enterpriseId;
/**
* 企业名称
*/
private String enterpriseName;
/**
* 门店id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long storeId;
/**
* 门店名称
*/
private String storeName;
/**
* 请求URL
*/
private String operUrl;
/**
* 主机地址
*/
private String operIp;
/**
* 操作地点
*/
private String operLocation;
/**
* 请求参数
*/
private String operParam;
/**
* 返回参数
*/
private String jsonResult;
/**
* 操作状态 (0正常 1异常)
*/
private Integer status;
/**
* 错误消息
*/
private String errorMsg;
}