系统操作日志:集成 mzt-biz-log

This commit is contained in:
puhui999
2023-12-12 22:29:40 +08:00
parent 72cebfca14
commit c5cc818a49
14 changed files with 399 additions and 6 deletions

View File

@ -0,0 +1,144 @@
package cn.iocoder.yudao.module.system.dal.dataobject.logger;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
import java.util.Map;
/**
* 操作日志表
*
* @author 芋道源码
*/
@TableName(value = "system_operate_log_v2", autoResultMap = true)
@KeySequence("system_operate_log_seq_v2") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
public class OperateLogV2DO extends BaseDO {
/**
* {@link #javaMethodArgs} 的最大长度
*/
public static final Integer JAVA_METHOD_ARGS_MAX_LENGTH = 8000;
/**
* {@link #resultData} 的最大长度
*/
public static final Integer RESULT_MAX_LENGTH = 4000;
/**
* 日志主键
*/
@TableId
private Long id;
/**
* 链路追踪编号
*
* 一般来说通过链路追踪编号可以将访问日志错误日志链路追踪日志logger 打印日志等,结合在一起,从而进行排错。
*/
private String traceId;
/**
* 用户编号
*
* 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
*/
private Long userId;
/**
* 用户类型
*
* 关联 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 操作模块
*/
private String module;
/**
* 操作名
*/
private String name;
/**
* 操作分类
*
* 枚举 {@link OperateTypeEnum}
*/
private Integer type;
/**
* 操作内容,记录整个操作的明细
* 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
*/
private String content;
/**
* 拓展字段,有些复杂的业务,需要记录一些字段
* 例如说,记录订单编号,则可以添加 key 为 "orderId"value 为订单编号
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> exts;
/**
* 请求方法名
*/
private String requestMethod;
/**
* 请求地址
*/
private String requestUrl;
/**
* 用户 IP
*/
private String userIp;
/**
* 浏览器 UA
*/
private String userAgent;
/**
* Java 方法名
*/
private String javaMethod;
/**
* Java 方法的参数
*
* 实际格式为 Map<String, Object>
* 不使用 @TableField(typeHandler = FastjsonTypeHandler.class) 注解的原因是,数据库存储有长度限制,会进行裁剪,会导致 JSON 反序列化失败
* 其中key 为参数名value 为参数值
*/
private String javaMethodArgs;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 执行时长,单位:毫秒
*/
private Integer duration;
/**
* 结果码
*
* 目前使用的 {@link CommonResult#getCode()} 属性
*/
private Integer resultCode;
/**
* 结果提示
*
* 目前使用的 {@link CommonResult#getMsg()} 属性
*/
private String resultMsg;
/**
* 结果数据
*
* 如果是对象,则使用 JSON 格式化
*/
private String resultData;
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.system.dal.mysql.logger;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
@Mapper
public interface OperateLogV2Mapper extends BaseMapperX<OperateLogV2DO> {
default PageResult<OperateLogV2DO> selectPage(OperateLogPageReqVO reqVO, Collection<Long> userIds) {
LambdaQueryWrapperX<OperateLogV2DO> query = new LambdaQueryWrapperX<OperateLogV2DO>()
.likeIfPresent(OperateLogV2DO::getModule, reqVO.getModule())
.inIfPresent(OperateLogV2DO::getUserId, userIds)
.eqIfPresent(OperateLogV2DO::getType, reqVO.getType())
.betweenIfPresent(OperateLogV2DO::getStartTime, reqVO.getStartTime());
if (Boolean.TRUE.equals(reqVO.getSuccess())) {
query.eq(OperateLogV2DO::getResultCode, GlobalErrorCodeConstants.SUCCESS.getCode());
} else if (Boolean.FALSE.equals(reqVO.getSuccess())) {
query.gt(OperateLogV2DO::getResultCode, GlobalErrorCodeConstants.SUCCESS.getCode());
}
query.orderByDesc(OperateLogV2DO::getId); // 降序
return selectPage(reqVO, query);
}
}

View File

@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.system.framework.bizlog.config;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.framework.bizlog.service.AdminUserParseFunction;
import cn.iocoder.yudao.module.system.framework.bizlog.service.ILogRecordServiceImpl;
import com.mzt.logapi.beans.Operator;
import com.mzt.logapi.service.IOperatorGetService;
import com.mzt.logapi.starter.annotation.EnableLogRecord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Optional;
/**
* 使用 @Configuration 是因为 mzt-biz-log 的配置类是 @Configuration 的
*
* @author HUIHUI
*/
@Configuration(proxyBeanMethods = false)
@EnableLogRecord(tenant = "${yudao.info.base-package}")
@Slf4j
public class YudaoOperateLogV2Configuration {
//======================= mzt-biz-log =======================
@Bean
public ILogRecordServiceImpl iLogRecordServiceImpl(OperateLogApi operateLogApi) {
log.info("ILogRecordServiceImpl 初始化");
return new ILogRecordServiceImpl(operateLogApi);
}
@Bean
public IOperatorGetService operatorGetLoginUserIdService() {
// 获取操作用户编号
return () -> Optional.of(WebFrameworkUtils.getLoginUserId())
.map(a -> {
Operator operator = new Operator();
operator.setOperatorId(a.toString());
return operator;
})
.orElseThrow(() -> new IllegalArgumentException("user is null"));
}
@Bean
public AdminUserParseFunction adminUserParseFunction(AdminUserApi adminUserApi) {
return new AdminUserParseFunction(adminUserApi);
}
}

View File

@ -0,0 +1 @@
package cn.iocoder.yudao.module.system.framework.bizlog;

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.system.framework.bizlog.service;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.mzt.logapi.service.IParseFunction;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* 自定义函数-通过用户编号获取用户信息
*
* @author HUIHUI
*/
@Slf4j
@RequiredArgsConstructor
public class AdminUserParseFunction implements IParseFunction {
private final AdminUserApi adminUserApi;
@Override
public boolean executeBefore() {
return true;
}
@Override
public String functionName() {
return "getAdminUserById";
}
@Override
public String apply(Object value) {
if (value == null) {
log.warn("(getAdminUserById) 解析异常参数为 null");
return "";
}
if (StrUtil.isEmpty(value.toString())) {
log.warn("(getAdminUserById) 解析异常参数为空");
return "";
}
// 获取用户信息
AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString()));
if (user == null) {
log.warn("(getAdminUserById) 获取用户信息失败,参数为:{}", value);
return "";
}
// 返回格式 芋道源码(13888888888)
String nickname = user.getNickname();
if (ObjUtil.isNotEmpty(user.getMobile())) {
return nickname.concat("(").concat(user.getMobile()).concat(")");
}
return nickname;
}
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.system.framework.bizlog.service;
import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
import com.mzt.logapi.beans.LogRecord;
import com.mzt.logapi.service.ILogRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Collections;
import java.util.List;
/**
* 操作日志 ILogRecordService 实现类
*
* 基于 {@link OperateLogApi} 实现,记录操作日志
*
* @author HUIHUI
*/
@Slf4j
@RequiredArgsConstructor
public class ILogRecordServiceImpl implements ILogRecordService {
private final OperateLogApi operateLogApi;
@Override
public void record(LogRecord logRecord) {
log.info("【logRecord】log={}", logRecord);
}
@Override
public List<LogRecord> queryLog(String bizNo, String type) {
return Collections.emptyList();
}
@Override
public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {
return Collections.emptyList();
}
}

View File

@ -19,6 +19,13 @@ public interface OperateLogService {
*/
void createOperateLog(OperateLogCreateReqDTO createReqDTO);
/**
* 记录操作日志 V2
*
* @param createReqDTO 操作日志请求
*/
void createOperateLogV2(OperateLogCreateReqDTO createReqDTO);
/**
* 获得操作日志分页列表
*

View File

@ -8,14 +8,16 @@ import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper;
import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogV2Mapper;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.Collection;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@ -34,6 +36,8 @@ public class OperateLogServiceImpl implements OperateLogService {
@Resource
private OperateLogMapper operateLogMapper;
@Resource
private OperateLogV2Mapper operateLogV2Mapper;
@Resource
private AdminUserService userService;
@ -46,6 +50,14 @@ public class OperateLogServiceImpl implements OperateLogService {
operateLogMapper.insert(log);
}
@Override
public void createOperateLogV2(OperateLogCreateReqDTO createReqDTO) {
OperateLogV2DO log = BeanUtils.toBean(createReqDTO, OperateLogV2DO.class);
log.setJavaMethodArgs(StrUtils.maxLength(log.getJavaMethodArgs(), JAVA_METHOD_ARGS_MAX_LENGTH));
log.setResultData(StrUtils.maxLength(log.getResultData(), RESULT_MAX_LENGTH));
operateLogV2Mapper.insert(log);
}
@Override
public PageResult<OperateLogDO> getOperateLogPage(OperateLogPageReqVO pageReqVO) {
// 处理基于用户昵称的查询