【智能排班系统】AOP实现操作日志自动记录

文章目录

  • 操作日志介绍
  • 自动保存操作日志
    • 基本实现思路
    • 定义注解
    • 枚举
      • 业务类型枚举
      • 操作人员类型枚举
    • 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 依赖,用于持久化操作日志到数据库*/@Resourceprivate 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);// 设置请求的 URLoperationLog.setOperUrl(request.getRequestURI());// 设置请求的地理位置信息(根据 IP 解析)operationLog.setOperLocation(IpAddressUtils.getRealAddressByIP(ip));// 从请求头中提取 JWT 令牌,获取用户名和企业、店铺 IDString 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()) {// 如果是数组,检查其组件类型是否为 MultipartFilereturn 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 {@Autowiredprivate OperationLogDao OperationLogDao;@Autowiredprivate UserService userService;@Overridepublic 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);}@Overridepublic 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 {@Autowiredprivate OperationLogService operationLogService;@Autowiredprivate 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/596004.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文心一言指令词宝典之营销文案篇

作者&#xff1a;哈哥撩编程&#xff08;视频号、抖音、公众号同名&#xff09; 新星计划全栈领域优秀创作者博客专家全国博客之星第四名超级个体COC上海社区主理人特约讲师谷歌亚马逊演讲嘉宾科技博主极星会首批签约作者 &#x1f3c6; 推荐专栏&#xff1a; &#x1f3c5;…

【简单讲解下epoll】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

C++设计模式:策略模式(二)

1、定义与动机 定义一系列算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可互相替换&#xff08;变化&#xff09;&#xff0c;该模式使得算法可独立于使用它的客户程序&#xff08;稳定&#xff09;而变化&#xff08;扩展&#xff0c;子类化&#xff09; 在软…

autovacuum

相关查询语句 select relname,reltuples from pg_class where relnamepgbench_accounts; show autovacuum_vacuum_scale_factor ; select count(*) from pgbench_accounts; \dt pgbench_accounts SELECT * FROM pgstattuple(pgbench_accounts); --需要开启插件 SELECT relnam…

企业如何设计和实施有效的网络安全演练?

现实世界中&#xff0c;武装部队一直利用兵棋推演进行实战化训练&#xff0c;为潜在的军事冲突做准备。随着当今的数字化转型&#xff0c;同样的概念正在以网络安全演习的形式在组织中得到应用&#xff0c;很多企业每年都会基于合理的网络攻击场景和事件响应做一些测试和模拟。…

秋招学习数据库LeetCode刷题

数据库基本知识以前学过次数较多&#xff0c;今天看完一遍后都是可以理解的。直接刷Leetcode题吧 牛客上题库刷基础&#xff0c;Leetcode刷 写语句题(争取坚持每日2个sql语句题) 牛客&#xff1a;https://www.nowcoder.com/exam/intelligent?questionJobId10&tagId21015 L…

《QT实用小工具·十五》多种样式的开关控件

1、概述 源码放在文章末尾 目前实现了三种样式的开关控件按钮&#xff0c;如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef IMAGESWITCH_H #define IMAGESWITCH_H/*** 图片开关控件 * 1. 自带三种开关按钮样式。* 2. 可自定义开关图片。*/#include <QWid…

分布式锁实战

4、分布式锁 4.1 、基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#x…

鸿蒙实战开发-如何使用Stage模型卡片

介绍 本示例展示了Stage模型卡片提供方的创建与使用。 用到了卡片扩展模块接口&#xff0c;ohos.app.form.FormExtensionAbility 。 卡片信息和状态等相关类型和枚举接口&#xff0c;ohos.app.form.formInfo 。 卡片提供方相关接口的能力接口&#xff0c;ohos.app.form.for…

STM3定时器输入捕获、超声波测距

1、超声波测距模块介绍 1、HC-SR04共四个引脚&#xff1a;VCC、GND、Trig、Echo&#xff0c;如下图 2、使用 1、通过gpio口向Trig引脚发送一个脉冲信号。 2、HC-SR04接收到脉冲信号后&#xff0c;就会向外发送一段超声波&#xff0c;模块会将echo拉高。 …

Qt Creator 界面

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、认识 Qt Creator 界面 1、总览 2、左边栏 3、代码编辑区 4、UI设计界面 5、构建区 一、认识 …

docker笔记(二):镜像、容器数据卷

四、 docker镜像 4.1 镜像 镜像是一种轻量级、可执行的独立软件包&#xff0c;用来打包软件运行环境和基于运行环境开发的软件&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;包括代码、库、环境变量和配置文件 所有的应用&#xff0c;直接打包docker镜像就可以直…