mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-23 23:45:08 +08:00
系统操作日志:集成 mzt-biz-log
This commit is contained in:
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
package cn.iocoder.yudao.module.system.framework.bizlog;
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -19,6 +19,13 @@ public interface OperateLogService {
|
||||
*/
|
||||
void createOperateLog(OperateLogCreateReqDTO createReqDTO);
|
||||
|
||||
/**
|
||||
* 记录操作日志 V2
|
||||
*
|
||||
* @param createReqDTO 操作日志请求
|
||||
*/
|
||||
void createOperateLogV2(OperateLogCreateReqDTO createReqDTO);
|
||||
|
||||
/**
|
||||
* 获得操作日志分页列表
|
||||
*
|
||||
|
@ -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) {
|
||||
// 处理基于用户昵称的查询
|
||||
|
Reference in New Issue
Block a user