Commit c74881c8f0c9214515cce672ed62a60ade91a968

Authored by puhui999
1 parent 13d6c42a

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

Showing 11 changed files with 145 additions and 94 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
... ... @@ -7,12 +7,21 @@ package cn.iocoder.yudao.module.crm.enums;
7 7 */
8 8 public interface LogRecordConstants {
9 9  
10   - String WHO = "【{getAdminUserById{#userId}}】";
  10 + //======================= 客户模块类型 =======================
  11 + // TODO puhui999: 确保模块命名方式为 module + 子模块名称的方式。统一定义模块名称是为了方便查询各自记录的操作日志,列如说:查询客户【张三的操作日志】就可以 module + bizId
  12 + String CRM_LEADS = "CRM-线索";
  13 + String CRM_CUSTOMER = "CRM-客户";
  14 + String CRM_CONTACT = "CRM-联系人";
  15 + String CRM_BUSINESS = "CRM-商机";
  16 + String CRM_CONTRACT = "CRM-合同";
  17 + String CRM_PRODUCT = "CRM-产品";
  18 + String CRM_RECEIVABLE = "CRM-回款";
  19 + String CRM_RECEIVABLE_PLAN = "CRM-回款计划";
11 20  
12 21 //======================= 客户转移操作日志 =======================
13 22  
14   - String TRANSFER_CUSTOMER_LOG_TYPE = "客户转移";
15   - String TRANSFER_CUSTOMER_LOG_SUCCESS = WHO + "把客户【{{#crmCustomer.name}}】负责人【{getAdminUserById{#crmCustomer.ownerUserId}}】转移给了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
  23 + String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer == null ? '' : #crmCustomer.name}}】负责人从" +
  24 + "【{getAdminUserById{#crmCustomer == null ? '' : #crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
16 25 String TRANSFER_CUSTOMER_LOG_FAIL = "";
17 26  
18 27 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
... ... @@ -5,9 +5,8 @@ Authorization: Bearer {{token}}
5 5 tenant-id: {{adminTenentId}}
6 6  
7 7 {
8   - "id": 11,
9   - "newOwnerUserId": 127,
10   - "oldOwnerPermissionLevel": 2
  8 + "id": 10,
  9 + "newOwnerUserId": 127
11 10 }
12 11  
13 12  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
... ... @@ -123,7 +123,6 @@ public class CrmCustomerController {
123 123 }
124 124  
125 125 @PutMapping("/transfer")
126   - @Operation(summary = "客户转移")
127 126 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
128 127 public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
129 128 customerService.transferCustomer(reqVO, getLoginUserId());
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
... ... @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerUpdat
9 9 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
10 10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
11 11 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
12   -import cn.iocoder.yudao.module.crm.enums.LogRecordConstants;
13 12 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 13 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
15 14 import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
... ... @@ -27,6 +26,8 @@ import java.util.*;
27 26  
28 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
29 28 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
  29 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER;
  30 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS;
30 31 import static java.util.Collections.singletonList;
31 32  
32 33 /**
... ... @@ -129,8 +130,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
129 130  
130 131 @Override
131 132 @Transactional(rollbackFor = Exception.class)
132   - @LogRecord(success = LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS,
133   - type = LogRecordConstants.TRANSFER_CUSTOMER_LOG_TYPE, bizNo = "{{#reqVO.id}}")
  133 + @LogRecord(success = TRANSFER_CUSTOMER_LOG_SUCCESS, type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}")
134 134 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
135 135 public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {
136 136 // 1. 校验客户是否存在
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java
1 1 package cn.iocoder.yudao.module.system.dal.dataobject.logger;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
6   -import cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum;
7 5 import com.baomidou.mybatisplus.annotation.KeySequence;
8   -import com.baomidou.mybatisplus.annotation.TableField;
9 6 import com.baomidou.mybatisplus.annotation.TableId;
10 7 import com.baomidou.mybatisplus.annotation.TableName;
11   -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
12 8 import lombok.Data;
13 9 import lombok.EqualsAndHashCode;
14 10  
15   -import java.time.LocalDateTime;
16   -import java.util.Map;
17   -
18 11 /**
19   - * 操作日志表
  12 + * 操作日志表 V2
20 13 *
21 14 * @author 芋道源码
22 15 */
... ... @@ -27,16 +20,6 @@ import java.util.Map;
27 20 public class OperateLogV2DO extends BaseDO {
28 21  
29 22 /**
30   - * {@link #javaMethodArgs} 的最大长度
31   - */
32   - public static final Integer JAVA_METHOD_ARGS_MAX_LENGTH = 8000;
33   -
34   - /**
35   - * {@link #resultData} 的最大长度
36   - */
37   - public static final Integer RESULT_MAX_LENGTH = 4000;
38   -
39   - /**
40 23 * 日志主键
41 24 */
42 25 @TableId
... ... @@ -68,22 +51,14 @@ public class OperateLogV2DO extends BaseDO {
68 51 */
69 52 private String name;
70 53 /**
71   - * 操作分类
72   - *
73   - * 枚举 {@link OperateTypeEnum}
  54 + * 操作模块业务编号
74 55 */
75   - private Integer type;
  56 + private Long bizId;
76 57 /**
77 58 * 操作内容,记录整个操作的明细
78 59 * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
79 60 */
80 61 private String content;
81   - /**
82   - * 拓展字段,有些复杂的业务,需要记录一些字段
83   - * 例如说,记录订单编号,则可以添加 key 为 "orderId",value 为订单编号
84   - */
85   - @TableField(typeHandler = JacksonTypeHandler.class)
86   - private Map<String, Object> exts;
87 62  
88 63 /**
89 64 * 请求方法名
... ... @@ -102,43 +77,4 @@ public class OperateLogV2DO extends BaseDO {
102 77 */
103 78 private String userAgent;
104 79  
105   - /**
106   - * Java 方法名
107   - */
108   - private String javaMethod;
109   - /**
110   - * Java 方法的参数
111   - *
112   - * 实际格式为 Map<String, Object>
113   - * 不使用 @TableField(typeHandler = FastjsonTypeHandler.class) 注解的原因是,数据库存储有长度限制,会进行裁剪,会导致 JSON 反序列化失败
114   - * 其中,key 为参数名,value 为参数值
115   - */
116   - private String javaMethodArgs;
117   - /**
118   - * 开始时间
119   - */
120   - private LocalDateTime startTime;
121   - /**
122   - * 执行时长,单位:毫秒
123   - */
124   - private Integer duration;
125   - /**
126   - * 结果码
127   - *
128   - * 目前使用的 {@link CommonResult#getCode()} 属性
129   - */
130   - private Integer resultCode;
131   - /**
132   - * 结果提示
133   - *
134   - * 目前使用的 {@link CommonResult#getMsg()} 属性
135   - */
136   - private String resultMsg;
137   - /**
138   - * 结果数据
139   - *
140   - * 如果是对象,则使用 JSON 格式化
141   - */
142   - private String resultData;
143   -
144 80 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java
1 1 package cn.iocoder.yudao.module.system.dal.mysql.logger;
2 2  
3   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
4 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
... ... @@ -16,14 +15,7 @@ public interface OperateLogV2Mapper extends BaseMapperX&lt;OperateLogV2DO&gt; {
16 15 default PageResult<OperateLogV2DO> selectPage(OperateLogPageReqVO reqVO, Collection<Long> userIds) {
17 16 LambdaQueryWrapperX<OperateLogV2DO> query = new LambdaQueryWrapperX<OperateLogV2DO>()
18 17 .likeIfPresent(OperateLogV2DO::getModule, reqVO.getModule())
19   - .inIfPresent(OperateLogV2DO::getUserId, userIds)
20   - .eqIfPresent(OperateLogV2DO::getType, reqVO.getType())
21   - .betweenIfPresent(OperateLogV2DO::getStartTime, reqVO.getStartTime());
22   - if (Boolean.TRUE.equals(reqVO.getSuccess())) {
23   - query.eq(OperateLogV2DO::getResultCode, GlobalErrorCodeConstants.SUCCESS.getCode());
24   - } else if (Boolean.FALSE.equals(reqVO.getSuccess())) {
25   - query.gt(OperateLogV2DO::getResultCode, GlobalErrorCodeConstants.SUCCESS.getCode());
26   - }
  18 + .inIfPresent(OperateLogV2DO::getUserId, userIds);
27 19 query.orderByDesc(OperateLogV2DO::getId); // 降序
28 20 return selectPage(reqVO, query);
29 21 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/config/YudaoOperateLogV2Configuration.java
... ... @@ -3,14 +3,13 @@ package cn.iocoder.yudao.module.system.framework.bizlog.config;
3 3 import com.mzt.logapi.starter.annotation.EnableLogRecord;
4 4 import org.springframework.context.annotation.Configuration;
5 5  
6   -
7 6 /**
8   - *
  7 + * mzt-biz-log 配置类
9 8 *
10 9 * @author HUIHUI
11 10 */
12 11 @Configuration(proxyBeanMethods = false)
13   -@EnableLogRecord(tenant = "${yudao.info.base-package}")
  12 +@EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦
14 13 public class YudaoOperateLogV2Configuration {
15 14  
16 15 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AdminUserParseFunction.java
... ... @@ -34,11 +34,11 @@ public class AdminUserParseFunction implements IParseFunction {
34 34 @Override
35 35 public String apply(Object value) {
36 36 if (value == null) {
37   - log.warn("(getAdminUserById) 解析异常参数为 null");
  37 + //log.warn("(getAdminUserById) 解析异常参数为 null");
38 38 return "";
39 39 }
40 40 if (StrUtil.isEmpty(value.toString())) {
41   - log.warn("(getAdminUserById) 解析异常参数为空");
  41 + //log.warn("(getAdminUserById) 解析异常参数为空");
42 42 return "";
43 43 }
44 44  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.framework.bizlog.service;
2 2  
  3 +import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
  4 +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
  5 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
3 6 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
  7 +import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;
4 8 import com.mzt.logapi.beans.LogRecord;
5 9 import com.mzt.logapi.service.ILogRecordService;
6 10 import jakarta.annotation.Resource;
  11 +import jakarta.servlet.http.HttpServletRequest;
7 12 import lombok.extern.slf4j.Slf4j;
8 13 import org.springframework.stereotype.Service;
9 14  
... ... @@ -26,7 +31,42 @@ public class ILogRecordServiceImpl implements ILogRecordService {
26 31  
27 32 @Override
28 33 public void record(LogRecord logRecord) {
29   - log.info("【logRecord】log={}", logRecord);
  34 + OperateLogV2CreateReqBO reqBO = new OperateLogV2CreateReqBO();
  35 + // 补全通用字段
  36 + reqBO.setTraceId(TracerUtils.getTraceId());
  37 + // 补充用户信息
  38 + fillUserFields(reqBO);
  39 + // 补全模块信息
  40 + fillModuleFields(reqBO, logRecord);
  41 + // 补全请求信息
  42 + fillRequestFields(reqBO);
  43 + // 异步记录日志
  44 + log.info("操作日志 ===> {}", reqBO);
  45 + }
  46 +
  47 + private static void fillUserFields(OperateLogV2CreateReqBO reqBO) {
  48 + reqBO.setUserId(WebFrameworkUtils.getLoginUserId());
  49 + reqBO.setUserType(WebFrameworkUtils.getLoginUserType());
  50 + }
  51 +
  52 + public static void fillModuleFields(OperateLogV2CreateReqBO reqBO, LogRecord logRecord) {
  53 + reqBO.setModule(logRecord.getType()); // 大模块类型如 crm-客户
  54 + reqBO.setName(logRecord.getSubType());// 操作名称如 转移客户
  55 + reqBO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号
  56 + reqBO.setContent(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  57 + }
  58 +
  59 + private static void fillRequestFields(OperateLogV2CreateReqBO reqBO) {
  60 + // 获得 Request 对象
  61 + HttpServletRequest request = ServletUtils.getRequest();
  62 + if (request == null) {
  63 + return;
  64 + }
  65 + // 补全请求信息
  66 + reqBO.setRequestMethod(request.getMethod());
  67 + reqBO.setRequestUrl(request.getRequestURI());
  68 + reqBO.setUserIp(ServletUtils.getClientIP(request));
  69 + reqBO.setUserAgent(ServletUtils.getUserAgent(request));
30 70 }
31 71  
32 72 @Override
... ... @@ -38,4 +78,5 @@ public class ILogRecordServiceImpl implements ILogRecordService {
38 78 public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {
39 79 return Collections.emptyList();
40 80 }
  81 +
41 82 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
... ... @@ -53,8 +53,6 @@ public class OperateLogServiceImpl implements OperateLogService {
53 53 @Override
54 54 public void createOperateLogV2(OperateLogCreateReqDTO createReqDTO) {
55 55 OperateLogV2DO log = BeanUtils.toBean(createReqDTO, OperateLogV2DO.class);
56   - log.setJavaMethodArgs(StrUtils.maxLength(log.getJavaMethodArgs(), JAVA_METHOD_ARGS_MAX_LENGTH));
57   - log.setResultData(StrUtils.maxLength(log.getResultData(), RESULT_MAX_LENGTH));
58 56 operateLogV2Mapper.insert(log);
59 57 }
60 58  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.logger.bo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import lombok.Data;
  6 +
  7 +/**
  8 + * 系统操作日志 Create Req BO
  9 + *
  10 + * @author HUIHUI
  11 + */
  12 +@Data
  13 +public class OperateLogV2CreateReqBO {
  14 +
  15 + /**
  16 + * 链路追踪编号
  17 + *
  18 + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
  19 + */
  20 + private String traceId;
  21 + /**
  22 + * 用户编号
  23 + *
  24 + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
  25 + */
  26 + @NotEmpty(message = "用户编号不能为空")
  27 + private Long userId;
  28 + /**
  29 + * 用户类型
  30 + *
  31 + * 关联 {@link UserTypeEnum}
  32 + */
  33 + @NotEmpty(message = "用户类型不能为空")
  34 + private Integer userType;
  35 + /**
  36 + * 操作模块
  37 + */
  38 + @NotEmpty(message = "操作模块不能为空")
  39 + private String module;
  40 + /**
  41 + * 操作名
  42 + */
  43 + @NotEmpty(message = "操作名不能为空")
  44 + private String name;
  45 + /**
  46 + * 操作模块业务编号
  47 + */
  48 + @NotEmpty(message = "操作模块业务编号不能为空")
  49 + private Long bizId;
  50 + /**
  51 + * 操作内容,记录整个操作的明细
  52 + * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  53 + */
  54 + @NotEmpty(message = "操作内容不能为空")
  55 + private String content;
  56 +
  57 + /**
  58 + * 请求方法名
  59 + */
  60 + @NotEmpty(message = "请求方法名不能为空")
  61 + private String requestMethod;
  62 + /**
  63 + * 请求地址
  64 + */
  65 + @NotEmpty(message = "请求地址不能为空")
  66 + private String requestUrl;
  67 + /**
  68 + * 用户 IP
  69 + */
  70 + @NotEmpty(message = "用户 IP 不能为空")
  71 + private String userIp;
  72 + /**
  73 + * 浏览器 UA
  74 + */
  75 + @NotEmpty(message = "浏览器 UA 不能为空")
  76 + private String userAgent;
  77 +
  78 +}
... ...