Commit 69503689912abe6e565fd71895bc0245bd72fc2c

Authored by puhui999
1 parent 5de6a8bd

操作日志:完善 code review 提到的问题

Showing 27 changed files with 183 additions and 649 deletions
yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml
... ... @@ -47,13 +47,6 @@
47 47 <artifactId>guava</artifactId>
48 48 </dependency>
49 49  
50   - <dependency>
51   - <!-- Spring Boot 通用操作日志组件,基于注解实现 -->
52   - <!-- 此组件解决的问题是:「谁」在「什么时间」对「什么」做了「什么事」 -->
53   - <groupId>io.github.mouzt</groupId>
54   - <artifactId>bizlog-sdk</artifactId>
55   - </dependency>
56   -
57 50 </dependencies>
58 51  
59 52 </project>
... ...
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogAutoConfiguration.java
1 1 package cn.iocoder.yudao.framework.operatelog.config;
2 2  
  3 +import cn.iocoder.yudao.framework.operatelog.core.aop.OperateLogAspect;
  4 +import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkService;
  5 +import cn.iocoder.yudao.framework.operatelog.core.service.OperateLogFrameworkServiceImpl;
  6 +import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
3 7 import org.springframework.boot.autoconfigure.AutoConfiguration;
  8 +import org.springframework.context.annotation.Bean;
4 9  
5 10 @AutoConfiguration
6 11 public class YudaoOperateLogAutoConfiguration {
7 12  
8   - // TODO @puhui999:这个是不是留着哈?因为老版本还是会留着一段时间的
9   - //@Bean
10   - //public OperateLogAspect operateLogAspect() {
11   - // return new OperateLogAspect();
12   - //}
13   - //
14   - //@Bean
15   - //public OperateLogFrameworkService operateLogFrameworkService(OperateLogApi operateLogApi) {
16   - // return new OperateLogFrameworkServiceImpl(operateLogApi);
17   - //}
  13 + @Bean
  14 + public OperateLogAspect operateLogAspect() {
  15 + return new OperateLogAspect();
  16 + }
  17 +
  18 + @Bean
  19 + public OperateLogFrameworkService operateLogFrameworkService(OperateLogApi operateLogApi) {
  20 + return new OperateLogFrameworkServiceImpl(operateLogApi);
  21 + }
18 22  
19 23 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/aop/OperateLogV2Aspect.java deleted
1   -package cn.iocoder.yudao.framework.operatelogv2.core.aop;
2   -
3   -import cn.hutool.core.date.LocalDateTimeUtil;
4   -import cn.hutool.core.exceptions.ExceptionUtil;
5   -import cn.hutool.core.util.ArrayUtil;
6   -import cn.hutool.core.util.ObjUtil;
7   -import cn.hutool.core.util.StrUtil;
8   -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
9   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
10   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
11   -import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
12   -import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
13   -import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
14   -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
15   -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
16   -import com.google.common.collect.Maps;
17   -import com.mzt.logapi.beans.LogRecord;
18   -import io.swagger.v3.oas.annotations.Operation;
19   -import jakarta.annotation.Resource;
20   -import jakarta.servlet.http.HttpServletRequest;
21   -import jakarta.servlet.http.HttpServletResponse;
22   -import lombok.extern.slf4j.Slf4j;
23   -import org.aspectj.lang.ProceedingJoinPoint;
24   -import org.aspectj.lang.annotation.Around;
25   -import org.aspectj.lang.annotation.Aspect;
26   -import org.aspectj.lang.reflect.MethodSignature;
27   -import org.springframework.core.annotation.AnnotationUtils;
28   -import org.springframework.validation.BindingResult;
29   -import org.springframework.web.bind.annotation.RequestMapping;
30   -import org.springframework.web.bind.annotation.RequestMethod;
31   -import org.springframework.web.multipart.MultipartFile;
32   -
33   -import java.lang.annotation.Annotation;
34   -import java.lang.reflect.Array;
35   -import java.time.LocalDateTime;
36   -import java.util.Arrays;
37   -import java.util.Collection;
38   -import java.util.HashMap;
39   -import java.util.Map;
40   -import java.util.function.Predicate;
41   -import java.util.stream.IntStream;
42   -
43   -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
44   -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.SUCCESS;
45   -import static cn.iocoder.yudao.framework.operatelogv2.core.enums.OperateLogV2Constants.*;
46   -
47   -// TODO @puhui999:这个类,是不是可以删除哈;简化简化
48   -/**
49   - * 拦截使用 @Operation 注解, 获取操作类型、开始时间、持续时间、方法相关信息、执行结果等信息
50   - * 对 mzt-biz-log 日志信息进行增强
51   - *
52   - * @author HUIHUI
53   - */
54   -@Aspect
55   -@Slf4j
56   -public class OperateLogV2Aspect {
57   -
58   - /**
59   - * 用于记录操作内容的上下文
60   - *
61   - * @see OperateLogV2CreateReqDTO#getContent()
62   - */
63   - private static final ThreadLocal<LogRecord> CONTENT = new ThreadLocal<>();
64   - /**
65   - * 用于记录拓展字段的上下文
66   - *
67   - * @see OperateLogV2CreateReqDTO#getExtra()
68   - */
69   - private static final ThreadLocal<Map<String, Object>> EXTRA = new ThreadLocal<>();
70   -
71   - @Resource
72   - private OperateLogApi operateLogApi;
73   -
74   - @Around("@annotation(operation)")
75   - public Object around(ProceedingJoinPoint joinPoint, Operation operation) throws Throwable {
76   - RequestMethod requestMethod = obtainFirstMatchRequestMethod(obtainRequestMethod(joinPoint));
77   - if (requestMethod == RequestMethod.GET) { // 跳过 get 方法
78   - return joinPoint.proceed();
79   - }
80   -
81   - // 目前,只有管理员,才记录操作日志!所以非管理员,直接调用,不进行记录
82   - Integer userType = WebFrameworkUtils.getLoginUserType();
83   - if (ObjUtil.notEqual(userType, UserTypeEnum.ADMIN.getValue())) {
84   - return joinPoint.proceed();
85   - }
86   -
87   - // 记录开始时间
88   - LocalDateTime startTime = LocalDateTime.now();
89   - try {
90   - // 执行原有方法
91   - Object result = joinPoint.proceed();
92   - // 记录正常执行时的操作日志
93   - this.log(joinPoint, operation, startTime, result, null);
94   - return result;
95   - } catch (Throwable exception) {
96   - this.log(joinPoint, operation, startTime, null, exception);
97   - throw exception;
98   - } finally {
99   - clearThreadLocal();
100   - }
101   - }
102   -
103   - public static void setContent(LogRecord content) {
104   - CONTENT.set(content);
105   - }
106   -
107   - public static void addExtra(String key, Object value) {
108   - if (EXTRA.get() == null) {
109   - EXTRA.set(new HashMap<>());
110   - }
111   - EXTRA.get().put(key, value);
112   - }
113   -
114   - public static void addExtra(Map<String, Object> extra) {
115   - if (EXTRA.get() == null) {
116   - EXTRA.set(new HashMap<>());
117   - }
118   - EXTRA.get().putAll(extra);
119   - }
120   -
121   - private static void clearThreadLocal() {
122   - CONTENT.remove();
123   - EXTRA.remove();
124   - }
125   -
126   - private void log(ProceedingJoinPoint joinPoint, Operation operation,
127   - LocalDateTime startTime, Object result, Throwable exception) {
128   - try {
129   - // 判断不记录的情况(默认没有值就是记录)
130   - if (EXTRA.get() != null && EXTRA.get().get(ENABLE) != null) {
131   - return;
132   - }
133   - if (CONTENT.get() == null) { // 没有值说明没有日志需要记录
134   - return;
135   - }
136   -
137   - // 真正记录操作日志
138   - this.log0(joinPoint, operation, startTime, result, exception);
139   - } catch (Throwable ex) {
140   - log.error("[log][记录操作日志时,发生异常,其中参数是 joinPoint({}) apiOperation({}) result({}) exception({}) ]",
141   - joinPoint, operation, result, exception, ex);
142   - }
143   - }
144   -
145   - private void log0(ProceedingJoinPoint joinPoint, Operation operation,
146   - LocalDateTime startTime, Object result, Throwable exception) {
147   - OperateLogV2CreateReqDTO reqDTO = new OperateLogV2CreateReqDTO();
148   - // 补全通用字段
149   - reqDTO.setTraceId(TracerUtils.getTraceId());
150   - reqDTO.setStartTime(startTime);
151   - // 补充用户信息
152   - fillUserFields(reqDTO);
153   - // 补全模块信息
154   - fillModuleFields(reqDTO, operation);
155   - // 补全请求信息
156   - fillRequestFields(reqDTO);
157   - // 补全方法信息
158   - fillMethodFields(reqDTO, joinPoint, startTime, result, exception);
159   -
160   - // 异步记录日志
161   - operateLogApi.createOperateLogV2(reqDTO);
162   - }
163   -
164   - private static void fillUserFields(OperateLogV2CreateReqDTO reqDTO) {
165   - reqDTO.setUserId(WebFrameworkUtils.getLoginUserId());
166   - reqDTO.setUserType(WebFrameworkUtils.getLoginUserType());
167   - }
168   -
169   - private static void fillModuleFields(OperateLogV2CreateReqDTO reqDTO, Operation operation) {
170   - LogRecord logRecord = CONTENT.get();
171   - reqDTO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号
172   - reqDTO.setContent(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
173   -
174   - // type 属性
175   - reqDTO.setType(logRecord.getType()); // 大模块类型如 crm 客户
176   - // subType 属性
177   - if (logRecord.getSubType() != null) {
178   - reqDTO.setSubType(logRecord.getSubType());// 操作名称如 转移客户
179   - }
180   - if (StrUtil.isEmpty(reqDTO.getSubType()) && operation != null) {
181   - reqDTO.setSubType(operation.summary());
182   - }
183   -
184   - // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"}
185   - Map<String, Object> objectMap = EXTRA.get();
186   - if (objectMap != null) {
187   - Object object = objectMap.get(EXTRA_KEY);
188   - if (object instanceof Map<?, ?> extraMap) {
189   - if (extraMap.keySet().stream().allMatch(String.class::isInstance)) {
190   - @SuppressWarnings("unchecked")
191   - Map<String, Object> extra = (Map<String, Object>) extraMap;
192   - reqDTO.setExtra(extra);
193   - return;
194   - }
195   - }
196   - // 激进一点不是 map 直接当 value 处理
197   - Map<String, Object> extra = Maps.newHashMapWithExpectedSize(1);
198   - extra.put(EXTRA_KEY, object);
199   - reqDTO.setExtra(extra);
200   - }
201   -
202   - }
203   -
204   - private static void fillRequestFields(OperateLogV2CreateReqDTO reqDTO) {
205   - // 获得 Request 对象
206   - HttpServletRequest request = ServletUtils.getRequest();
207   - if (request == null) {
208   - return;
209   - }
210   - // 补全请求信息
211   - reqDTO.setRequestMethod(request.getMethod());
212   - reqDTO.setRequestUrl(request.getRequestURI());
213   - reqDTO.setUserIp(ServletUtils.getClientIP(request));
214   - reqDTO.setUserAgent(ServletUtils.getUserAgent(request));
215   - }
216   -
217   - private static void fillMethodFields(OperateLogV2CreateReqDTO reqDTO, ProceedingJoinPoint joinPoint, LocalDateTime startTime,
218   - Object result, Throwable exception) {
219   - MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
220   - reqDTO.setJavaMethod(methodSignature.toString());
221   - if (EXTRA.get().get(IS_LOG_ARGS) == null) {
222   - reqDTO.setJavaMethodArgs(obtainMethodArgs(joinPoint));
223   - }
224   - if (EXTRA.get().get(IS_LOG_RESULT_DATA) == null) {
225   - reqDTO.setResultData(obtainResultData(result));
226   - }
227   - reqDTO.setDuration((int) (LocalDateTimeUtil.between(startTime, LocalDateTime.now()).toMillis()));
228   - // (正常)处理 resultCode 和 resultMsg 字段
229   - if (result instanceof CommonResult) {
230   - CommonResult<?> commonResult = (CommonResult<?>) result;
231   - reqDTO.setResultCode(commonResult.getCode());
232   - reqDTO.setResultMsg(commonResult.getMsg());
233   - } else {
234   - reqDTO.setResultCode(SUCCESS.getCode());
235   - }
236   - // (异常)处理 resultCode 和 resultMsg 字段
237   - if (exception != null) {
238   - reqDTO.setResultCode(INTERNAL_SERVER_ERROR.getCode());
239   - reqDTO.setResultMsg(ExceptionUtil.getRootCauseMessage(exception));
240   - }
241   - }
242   -
243   - @SuppressWarnings("SameParameterValue")
244   - private static <T extends Annotation> T getClassAnnotation(ProceedingJoinPoint joinPoint, Class<T> annotationClass) {
245   - return ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaringClass().getAnnotation(annotationClass);
246   - }
247   -
248   - private static String obtainMethodArgs(ProceedingJoinPoint joinPoint) {
249   - // TODO 提升:参数脱敏和忽略
250   - MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
251   - String[] argNames = methodSignature.getParameterNames();
252   - Object[] argValues = joinPoint.getArgs();
253   - // 拼接参数
254   - Map<String, Object> args = Maps.newHashMapWithExpectedSize(argValues.length);
255   - for (int i = 0; i < argNames.length; i++) {
256   - String argName = argNames[i];
257   - Object argValue = argValues[i];
258   - // 被忽略时,标记为 ignore 字符串,避免和 null 混在一起
259   - args.put(argName, !isIgnoreArgs(argValue) ? argValue : "[ignore]");
260   - }
261   - return JsonUtils.toJsonString(args);
262   - }
263   -
264   - private static String obtainResultData(Object result) {
265   - // TODO 提升:结果脱敏和忽略
266   - if (result instanceof CommonResult) {
267   - result = ((CommonResult<?>) result).getData();
268   - }
269   - return JsonUtils.toJsonString(result);
270   - }
271   -
272   - private static boolean isIgnoreArgs(Object object) {
273   - Class<?> clazz = object.getClass();
274   - // 处理数组的情况
275   - if (clazz.isArray()) {
276   - return IntStream.range(0, Array.getLength(object))
277   - .anyMatch(index -> isIgnoreArgs(Array.get(object, index)));
278   - }
279   - // 递归,处理数组、Collection、Map 的情况
280   - if (Collection.class.isAssignableFrom(clazz)) {
281   - return ((Collection<?>) object).stream()
282   - .anyMatch((Predicate<Object>) OperateLogV2Aspect::isIgnoreArgs);
283   - }
284   - if (Map.class.isAssignableFrom(clazz)) {
285   - return isIgnoreArgs(((Map<?, ?>) object).values());
286   - }
287   - // obj
288   - return object instanceof MultipartFile
289   - || object instanceof HttpServletRequest
290   - || object instanceof HttpServletResponse
291   - || object instanceof BindingResult;
292   - }
293   -
294   - private static RequestMethod obtainFirstMatchRequestMethod(RequestMethod[] requestMethods) {
295   - if (ArrayUtil.isEmpty(requestMethods)) {
296   - return null;
297   - }
298   - // 优先,匹配最优的 POST、PUT、DELETE
299   - RequestMethod result = obtainFirstLogRequestMethod(requestMethods);
300   - if (result != null) {
301   - return result;
302   - }
303   - // 然后,匹配次优的 GET
304   - result = Arrays.stream(requestMethods).filter(requestMethod -> requestMethod == RequestMethod.GET)
305   - .findFirst().orElse(null);
306   - if (result != null) {
307   - return result;
308   - }
309   - // 兜底,获得第一个
310   - return requestMethods[0];
311   - }
312   -
313   - private static RequestMethod[] obtainRequestMethod(ProceedingJoinPoint joinPoint) {
314   - RequestMapping requestMapping = AnnotationUtils.getAnnotation( // 使用 Spring 的工具类,可以处理 @RequestMapping 别名注解
315   - ((MethodSignature) joinPoint.getSignature()).getMethod(), RequestMapping.class);
316   - return requestMapping != null ? requestMapping.method() : new RequestMethod[]{};
317   - }
318   -
319   - private static RequestMethod obtainFirstLogRequestMethod(RequestMethod[] requestMethods) {
320   - if (ArrayUtil.isEmpty(requestMethods)) {
321   - return null;
322   - }
323   - return Arrays.stream(requestMethods).filter(requestMethod ->
324   - requestMethod == RequestMethod.POST
325   - || requestMethod == RequestMethod.PUT
326   - || requestMethod == RequestMethod.DELETE)
327   - .findFirst().orElse(null);
328   - }
329   -
330   -}
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/enums/OperateLogV2Constants.java deleted
1   -package cn.iocoder.yudao.framework.operatelogv2.core.enums;
2   -
3   -// TODO @puhui999:这个类,是不是可以删除哈;
4   -/**
5   - * 操作日志常量接口
6   - *
7   - * @author HUIHUI
8   - */
9   -public interface OperateLogV2Constants {
10   -
11   - // ========== 开关字段-如果没有值默认就是记录 ==========
12   -
13   - /**
14   - * 是否记录日志
15   - */
16   - String ENABLE = "enable";
17   -
18   - /**
19   - * 是否记录方法参数
20   - */
21   - String IS_LOG_ARGS = "isLogArgs";
22   -
23   - /**
24   - * 是否记录方法结果的数据
25   - */
26   - String IS_LOG_RESULT_DATA = "isLogResultData";
27   -
28   - // ========== 扩展 ==========
29   -
30   - /**
31   - * 扩展信息
32   - */
33   - String EXTRA_KEY = "extra";
34   -
35   -}
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/service/ILogRecordServiceImpl.java deleted
1   -package cn.iocoder.yudao.framework.operatelogv2.core.service;
2   -
3   -import cn.iocoder.yudao.framework.operatelogv2.core.aop.OperateLogV2Aspect;
4   -import com.mzt.logapi.beans.LogRecord;
5   -import com.mzt.logapi.context.LogRecordContext;
6   -import com.mzt.logapi.service.ILogRecordService;
7   -import lombok.extern.slf4j.Slf4j;
8   -
9   -import java.util.Collections;
10   -import java.util.List;
11   -
12   -/**
13   - * 操作日志 ILogRecordService 实现类
14   - *
15   - * 基于 {@link OperateLogV2Aspect} 实现,记录操作日志
16   - *
17   - * @author HUIHUI
18   - */
19   -@Slf4j
20   -public class ILogRecordServiceImpl implements ILogRecordService {
21   -
22   - @Override
23   - public void record(LogRecord logRecord) {
24   - OperateLogV2Aspect.setContent(logRecord); // 操作日志
25   - OperateLogV2Aspect.addExtra(LogRecordContext.getVariables()); // 扩展信息
26   - // TODO @puhui999:这里是不是调用 operateLogApi 进行记录哈
27   - }
28   -
29   - @Override
30   - public List<LogRecord> queryLog(String bizNo, String type) {
31   - throw new UnsupportedOperationException("不支持该操作,请使用 OperateLogApi 查询!");
32   - }
33   -
34   - @Override
35   - public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {
36   - throw new UnsupportedOperationException("不支持该操作,请使用 OperateLogApi 查询!");
37   - }
38   -
39   -}
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/package-info.java deleted
1   -package cn.iocoder.yudao.framework.operatelogv2;
2 0 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
1   -cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogAutoConfiguration
2   -cn.iocoder.yudao.framework.operatelogv2.config.YudaoOperateLogV2Configuration
3 1 \ No newline at end of file
  2 +cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogAutoConfiguration
4 3 \ No newline at end of file
... ...
yudao-framework/yudao-spring-boot-starter-security/pom.xml
... ... @@ -50,6 +50,13 @@
50 50 <artifactId>guava</artifactId>
51 51 </dependency>
52 52  
  53 + <dependency>
  54 + <!-- Spring Boot 通用操作日志组件,基于注解实现 -->
  55 + <!-- 此组件解决的问题是:「谁」在「什么时间」对「什么」做了「什么事」 -->
  56 + <groupId>io.github.mouzt</groupId>
  57 + <artifactId>bizlog-sdk</artifactId>
  58 + </dependency>
  59 +
53 60 <!-- 业务组件 -->
54 61 <dependency>
55 62 <groupId>cn.iocoder.boot</groupId>
... ...
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/config/YudaoOperateLogV2Configuration.java renamed to yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogV2Configuration.java
1   -package cn.iocoder.yudao.framework.operatelogv2.config;
  1 +package cn.iocoder.yudao.framework.operatelog.config;
2 2  
3   -import cn.iocoder.yudao.framework.operatelogv2.core.aop.OperateLogV2Aspect;
4   -import cn.iocoder.yudao.framework.operatelogv2.core.service.ILogRecordServiceImpl;
  3 +import cn.iocoder.yudao.framework.operatelog.core.service.ILogRecordServiceImpl;
5 4 import com.mzt.logapi.service.ILogRecordService;
6 5 import com.mzt.logapi.starter.annotation.EnableLogRecord;
7 6 import lombok.extern.slf4j.Slf4j;
... ... @@ -9,7 +8,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
9 8 import org.springframework.context.annotation.Bean;
10 9 import org.springframework.context.annotation.Primary;
11 10  
12   -// TODO @puhui999:思考了下,为了减少 starter 数量,在 security 组件里,增加一个 core/operatelog 包,然后 YudaoOperateLogV2Configuration 搞一个过去;
  11 +
13 12 /**
14 13 * mzt-biz-log 配置类
15 14 *
... ... @@ -26,9 +25,4 @@ public class YudaoOperateLogV2Configuration {
26 25 return new ILogRecordServiceImpl();
27 26 }
28 27  
29   - @Bean
30   - public OperateLogV2Aspect operateLogV2Aspect() {
31   - return new OperateLogV2Aspect();
32   - }
33   -
34 28 }
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.operatelog.core;
0 2 \ No newline at end of file
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/ILogRecordServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.operatelog.core.service;
  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;
  6 +import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
  7 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
  8 +import com.mzt.logapi.beans.LogRecord;
  9 +import com.mzt.logapi.service.ILogRecordService;
  10 +import jakarta.annotation.Resource;
  11 +import jakarta.servlet.http.HttpServletRequest;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +
  14 +import java.util.Collections;
  15 +import java.util.List;
  16 +
  17 +/**
  18 + * 操作日志 ILogRecordService 实现类
  19 + *
  20 + * 基于 {@link OperateLogApi} 实现,记录操作日志
  21 + *
  22 + * @author HUIHUI
  23 + */
  24 +@Slf4j
  25 +public class ILogRecordServiceImpl implements ILogRecordService {
  26 +
  27 + @Resource
  28 + private OperateLogApi operateLogApi;
  29 +
  30 + @Override
  31 + public void record(LogRecord logRecord) {
  32 + OperateLogV2CreateReqDTO reqDTO = new OperateLogV2CreateReqDTO();
  33 + // 补全通用字段
  34 + reqDTO.setTraceId(TracerUtils.getTraceId());
  35 + // 补充用户信息
  36 + fillUserFields(reqDTO);
  37 + // 补全模块信息
  38 + fillModuleFields(reqDTO, logRecord);
  39 + // 补全请求信息
  40 + fillRequestFields(reqDTO);
  41 + // 异步记录日志
  42 + operateLogApi.createOperateLogV2(reqDTO);
  43 + // TODO 测试结束删除或搞个开关
  44 + log.info("操作日志 ===> {}", reqDTO);
  45 + }
  46 +
  47 + private static void fillUserFields(OperateLogV2CreateReqDTO reqDTO) {
  48 + reqDTO.setUserId(WebFrameworkUtils.getLoginUserId());
  49 + reqDTO.setUserType(WebFrameworkUtils.getLoginUserType());
  50 + }
  51 +
  52 + public static void fillModuleFields(OperateLogV2CreateReqDTO reqDTO, LogRecord logRecord) {
  53 + reqDTO.setType(logRecord.getType()); // 大模块类型如 crm 客户
  54 + reqDTO.setSubType(logRecord.getSubType());// 操作名称如 转移客户
  55 + reqDTO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号
  56 + reqDTO.setAction(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  57 + reqDTO.setExtra(logRecord.getExtra()); // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"}
  58 + }
  59 +
  60 + private static void fillRequestFields(OperateLogV2CreateReqDTO reqDTO) {
  61 + // 获得 Request 对象
  62 + HttpServletRequest request = ServletUtils.getRequest();
  63 + if (request == null) {
  64 + return;
  65 + }
  66 + // 补全请求信息
  67 + reqDTO.setRequestMethod(request.getMethod());
  68 + reqDTO.setRequestUrl(request.getRequestURI());
  69 + reqDTO.setUserIp(ServletUtils.getClientIP(request));
  70 + reqDTO.setUserAgent(ServletUtils.getUserAgent(request));
  71 + }
  72 +
  73 + @Override
  74 + public List<LogRecord> queryLog(String bizNo, String type) {
  75 + return Collections.emptyList();
  76 + }
  77 +
  78 + @Override
  79 + public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {
  80 + return Collections.emptyList();
  81 + }
  82 +
  83 +}
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
1 1 cn.iocoder.yudao.framework.security.config.YudaoSecurityAutoConfiguration
2   -cn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter
3 2 \ No newline at end of file
  3 +cn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter
  4 +cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogV2Configuration
4 5 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
... ... @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 7 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
8 8 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
9   -import cn.iocoder.yudao.framework.operatelogv2.core.vo.OperateLogV2PageReqVO;
10 9 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
11 10 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
12 11 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
... ... @@ -61,6 +60,7 @@ public class CrmCustomerController {
61 60  
62 61 @PostMapping("/create")
63 62 @Operation(summary = "创建客户")
  63 + @OperateLog(enable = false) // TODO 关闭原有日志记录
64 64 @PreAuthorize("@ss.hasPermission('crm:customer:create')")
65 65 public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerCreateReqVO createReqVO) {
66 66 return success(customerService.createCustomer(createReqVO, getLoginUserId()));
... ... @@ -68,6 +68,7 @@ public class CrmCustomerController {
68 68  
69 69 @PutMapping("/update")
70 70 @Operation(summary = "更新客户")
  71 + @OperateLog(enable = false) // TODO 关闭原有日志记录
71 72 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
72 73 public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
73 74 customerService.updateCustomer(updateReqVO);
... ... @@ -76,6 +77,7 @@ public class CrmCustomerController {
76 77  
77 78 @DeleteMapping("/delete")
78 79 @Operation(summary = "删除客户")
  80 + @OperateLog(enable = false) // TODO 关闭原有日志记录
79 81 @Parameter(name = "id", description = "编号", required = true)
80 82 @PreAuthorize("@ss.hasPermission('crm:customer:delete')")
81 83 public CommonResult<Boolean> deleteCustomer(@RequestParam("id") Long id) {
... ... @@ -132,6 +134,7 @@ public class CrmCustomerController {
132 134  
133 135 @PutMapping("/transfer")
134 136 @Operation(summary = "客户转移")
  137 + @OperateLog(enable = false) // TODO 关闭原有日志记录
135 138 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
136 139 public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
137 140 customerService.transferCustomer(reqVO, getLoginUserId());
... ... @@ -140,9 +143,8 @@ public class CrmCustomerController {
140 143  
141 144 @GetMapping("/operate-log-page")
142 145 @Operation(summary = "获得客户操作日志")
143   - @Parameter(name = "id", description = "编号", required = true, example = "1024")
144 146 @PreAuthorize("@ss.hasPermission('crm:customer:query')")
145   - public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(OperateLogV2PageReqVO reqVO) {
  147 + public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(CrmCustomerOperateLogPageReqVO reqVO) {
146 148 reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
147 149 reqVO.setBizType(CRM_CUSTOMER);
148 150 return success(operateLogApi.getOperateLogPage(BeanUtils.toBean(reqVO, OperateLogV2PageReqDTO.class)));
... ... @@ -151,6 +153,7 @@ public class CrmCustomerController {
151 153 // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了;
152 154 @PutMapping("/lock")
153 155 @Operation(summary = "锁定/解锁客户")
  156 + @OperateLog(enable = false) // TODO 关闭原有日志记录
154 157 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
155 158 public CommonResult<Boolean> lockCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
156 159 customerService.lockCustomer(updateReqVO);
... ... @@ -161,6 +164,7 @@ public class CrmCustomerController {
161 164  
162 165 @PutMapping("/put-pool")
163 166 @Operation(summary = "数据放入公海")
  167 + @OperateLog(enable = false) // TODO 关闭原有日志记录
164 168 @Parameter(name = "id", description = "客户编号", required = true, example = "1024")
165 169 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
166 170 public CommonResult<Boolean> putCustomerPool(@RequestParam("id") Long id) {
... ...
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/vo/OperateLogV2PageReqVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerOperateLogPageReqVO.java
1   -package cn.iocoder.yudao.framework.operatelogv2.core.vo;
  1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 4 import io.swagger.v3.oas.annotations.media.Schema;
5 5 import lombok.Data;
6 6  
7   -// TODO @puhui999:这个最好每个模块自己弄哈,放这里有点怪可能
8   -@Schema(description = "管理后台 - 操作日志分页 Request VO")
  7 +@Schema(description = "管理后台 - crm 客户操作日志分页 Request VO")
9 8 @Data
10   -public class OperateLogV2PageReqVO extends PageParam {
  9 +public class CrmCustomerOperateLogPageReqVO extends PageParam {
11 10  
12 11 @Schema(description = "模块数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
13 12 private Long bizId;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/parse/CrmIndustryParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmIndustryParseFunction.java
1   -package cn.iocoder.yudao.module.crm.framework.operatelog.parse;
  1 +package cn.iocoder.yudao.module.crm.framework.operatelog.core;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
5 6 import com.mzt.logapi.service.IParseFunction;
... ... @@ -8,7 +9,6 @@ import org.springframework.stereotype.Component;
8 9  
9 10 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
10 11  
11   -// TODO @puhui999:还是放在 core 包下哈;
12 12 /**
13 13 * 自定义函数-通过行业编号获取行业信息
14 14 *
... ... @@ -30,8 +30,7 @@ public class CrmIndustryParseFunction implements IParseFunction {
30 30  
31 31 @Override
32 32 public String apply(Object value) {
33   -// if (ObjUtil.isEmpty(value)) TODO @puhui999 可以直接替代哈;
34   - if (value == null) {
  33 + if (ObjUtil.isEmpty(value)) {
35 34 return "";
36 35 }
37 36 if (StrUtil.isEmpty(value.toString())) {
... ... @@ -39,12 +38,7 @@ public class CrmIndustryParseFunction implements IParseFunction {
39 38 }
40 39  
41 40 // 获取行业信息
42   - // TODO @puhui999:这里可以不用 try catch 哇?
43   - try {
44   - return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, value.toString());
45   - } catch (Exception ignored) {
46   - }
47   - return "";
  41 + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, value.toString());
48 42 }
49 43  
50 44 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/parse/CrmLevelParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmLevelParseFunction.java
1   -package cn.iocoder.yudao.module.crm.framework.operatelog.parse;
  1 +package cn.iocoder.yudao.module.crm.framework.operatelog.core;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
5 6 import com.mzt.logapi.service.IParseFunction;
... ... @@ -29,7 +30,7 @@ public class CrmLevelParseFunction implements IParseFunction {
29 30  
30 31 @Override
31 32 public String apply(Object value) {
32   - if (value == null) {
  33 + if (ObjUtil.isEmpty(value)) {
33 34 return "";
34 35 }
35 36 if (StrUtil.isEmpty(value.toString())) {
... ... @@ -37,10 +38,7 @@ public class CrmLevelParseFunction implements IParseFunction {
37 38 }
38 39  
39 40 // 获取客户等级信息
40   - try {
41   - return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_LEVEL, value.toString());
42   - } catch (Exception ignored) {
43   - }
44   - return "";
  41 + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_LEVEL, value.toString());
45 42 }
  43 +
46 44 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/parse/CrmSourceParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmSourceParseFunction.java
1   -package cn.iocoder.yudao.module.crm.framework.operatelog.parse;
  1 +package cn.iocoder.yudao.module.crm.framework.operatelog.core;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
5 6 import com.mzt.logapi.service.IParseFunction;
... ... @@ -29,7 +30,7 @@ public class CrmSourceParseFunction implements IParseFunction {
29 30  
30 31 @Override
31 32 public String apply(Object value) {
32   - if (value == null) {
  33 + if (ObjUtil.isEmpty(value)) {
33 34 return "";
34 35 }
35 36 if (StrUtil.isEmpty(value.toString())) {
... ... @@ -37,10 +38,7 @@ public class CrmSourceParseFunction implements IParseFunction {
37 38 }
38 39  
39 40 // 获取客户来源信息
40   - try {
41   - return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_SOURCE, value.toString());
42   - } catch (Exception ignored) {
43   - }
44   - return "";
  41 + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_SOURCE, value.toString());
45 42 }
  43 +
46 44 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.service.customer;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.framework.operatelogv2.core.enums.OperateLogV2Constants;
7 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO;
8 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
9 8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
... ... @@ -53,7 +52,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
53 52  
54 53 @Override
55 54 @Transactional(rollbackFor = Exception.class)
56   - @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#customerId}}", success = "创建了客户")
  55 + @LogRecord(type = CRM_CUSTOMER, subType = "创建客户", bizNo = "{{#customerId}}", success = "创建了客户")
57 56 public Long createCustomer(CrmCustomerCreateReqVO createReqVO, Long userId) {
58 57 // 插入
59 58 CrmCustomerDO customer = CrmCustomerConvert.INSTANCE.convert(createReqVO);
... ... @@ -71,7 +70,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
71 70 // TODO @puhui999:测试下,能不能打出用户数据的变更。啊哈,可以打完微信发我下;
72 71 @Override
73 72 @Transactional(rollbackFor = Exception.class)
74   - @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#updateReqVO.id}}", success = "更新了客户{_DIFF{#updateReqVO}}")
  73 + @LogRecord(type = CRM_CUSTOMER, subType = "更新客户", bizNo = "{{#updateReqVO.id}}", success = "更新了客户{_DIFF{#updateReqVO}}", extra = "{{#extra}}")
75 74 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
76 75 public void updateCustomer(CrmCustomerUpdateReqVO updateReqVO) {
77 76 // 校验存在
... ... @@ -83,14 +82,15 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
83 82  
84 83 // __DIFF 函数传递了一个参数,传递的参数是修改之后的对象,这种方式需要在方法内部向 LogRecordContext 中 put 一个变量,代表是之前的对象,这个对象可以是null
85 84 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomerDO, CrmCustomerUpdateReqVO.class));
  85 + // TODO 扩展信息测试
86 86 HashMap<String, Object> extra = new HashMap<>();
87 87 extra.put("tips", "随便记录一点啦");
88   - LogRecordContext.putVariable(OperateLogV2Constants.EXTRA_KEY, extra);
  88 + LogRecordContext.putVariable("extra", extra);
89 89 }
90 90  
91 91 @Override
92 92 @Transactional(rollbackFor = Exception.class)
93   - @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#id}}", success = "删除了客户")
  93 + @LogRecord(type = CRM_CUSTOMER, subType = "删除客户", bizNo = "{{#id}}", success = "删除了客户")
94 94 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
95 95 public void deleteCustomer(Long id) {
96 96 // 校验存在
... ... @@ -141,7 +141,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
141 141  
142 142 @Override
143 143 @Transactional(rollbackFor = Exception.class)
144   - @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#reqVO.id}}", success = TRANSFER_CUSTOMER_LOG_SUCCESS)
  144 + @LogRecord(type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}", success = TRANSFER_CUSTOMER_LOG_SUCCESS)
145 145 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
146 146 public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {
147 147 // 1. 校验客户是否存在
... ... @@ -158,7 +158,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
158 158 }
159 159  
160 160 @Override
161   - @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#updateReqVO.id}}", success = "锁定了客户")
  161 + @LogRecord(type = CRM_CUSTOMER, subType = "锁定/解锁客户", bizNo = "{{#updateReqVO.id}}", success = "锁定了客户")
162 162 public void lockCustomer(CrmCustomerUpdateReqVO updateReqVO) {
163 163 // 校验存在
164 164 validateCustomerExists(updateReqVO.getId());
... ... @@ -172,7 +172,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
172 172  
173 173 @Override
174 174 @Transactional(rollbackFor = Exception.class)
175   - @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#id}}", success = "将客户放入了公海")
  175 + @LogRecord(type = CRM_CUSTOMER, subType = "客户放入公海", bizNo = "{{#id}}", success = "将客户放入了公海")
176 176 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
177 177 public void putCustomerPool(Long id) {
178 178 // 1. 校验存在
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2CreateReqDTO.java
... ... @@ -5,9 +5,6 @@ import jakarta.validation.constraints.NotEmpty;
5 5 import jakarta.validation.constraints.NotNull;
6 6 import lombok.Data;
7 7  
8   -import java.time.LocalDateTime;
9   -import java.util.Map;
10   -
11 8 /**
12 9 * 系统操作日志 Create Req BO
13 10 *
... ... @@ -56,12 +53,12 @@ public class OperateLogV2CreateReqDTO {
56 53 * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
57 54 */
58 55 @NotEmpty(message = "操作内容不能为空")
59   - private String content;
  56 + private String action;
60 57 /**
61 58 * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
62 59 * 例如说,记录订单编号,{ orderId: "1"}
63 60 */
64   - private Map<String, Object> extra;
  61 + private String extra;
65 62  
66 63 /**
67 64 * 请求方法名
... ... @@ -84,38 +81,4 @@ public class OperateLogV2CreateReqDTO {
84 81 @NotEmpty(message = "浏览器 UA 不能为空")
85 82 private String userAgent;
86 83  
87   - /**
88   - * Java 方法名
89   - */
90   - private String javaMethod;
91   - /**
92   - * Java 方法的参数
93   - */
94   - private String javaMethodArgs;
95   -
96   - /**
97   - * 开始时间
98   - */
99   - private LocalDateTime startTime;
100   -
101   - /**
102   - * 执行时长,单位:毫秒
103   - */
104   - private Integer duration;
105   -
106   - /**
107   - * 结果码
108   - */
109   - private Integer resultCode;
110   -
111   - /**
112   - * 结果提示
113   - */
114   - private String resultMsg;
115   -
116   - /**
117   - * 结果数据
118   - */
119   - private String resultData;
120   -
121 84 }
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java
... ... @@ -2,9 +2,6 @@ package cn.iocoder.yudao.module.system.api.logger.dto;
2 2  
3 3 import lombok.Data;
4 4  
5   -import java.time.LocalDateTime;
6   -import java.util.Map;
7   -
8 5 /**
9 6 * 系统操作日志 Resp DTO
10 7 *
... ... @@ -44,11 +41,11 @@ public class OperateLogV2RespDTO {
44 41 /**
45 42 * 操作内容
46 43 */
47   - private String content;
  44 + private String action;
48 45 /**
49 46 * 拓展字段
50 47 */
51   - private Map<String, Object> extra;
  48 + private String extra;
52 49  
53 50 /**
54 51 * 请求方法名
... ... @@ -67,43 +64,4 @@ public class OperateLogV2RespDTO {
67 64 */
68 65 private String userAgent;
69 66  
70   - /**
71   - * Java 方法名
72   - */
73   - private String javaMethod;
74   - /**
75   - * Java 方法的参数
76   - */
77   - private String javaMethodArgs;
78   -
79   - /**
80   - * 开始时间
81   - */
82   - private LocalDateTime startTime;
83   -
84   - /**
85   - * 执行时长,单位:毫秒
86   - */
87   - private Integer duration;
88   -
89   - /**
90   - * 结果码
91   - */
92   - private Integer resultCode;
93   -
94   - /**
95   - * 结果提示
96   - */
97   - private String resultMsg;
98   -
99   - /**
100   - * 结果数据
101   - */
102   - private String resultData;
103   -
104   - /**
105   - * 创建时间
106   - */
107   - private LocalDateTime createTime;
108   -
109 67 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java
... ... @@ -2,11 +2,11 @@ package cn.iocoder.yudao.module.system.api.logger;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 5 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
7 6 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
8 7 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
9 8 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
  9 +import cn.iocoder.yudao.module.system.convert.logger.OperateLogConvert;
10 10 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
11 11 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
12 12 import cn.iocoder.yudao.module.system.service.logger.OperateLogService;
... ... @@ -17,10 +17,8 @@ import org.springframework.stereotype.Service;
17 17 import org.springframework.validation.annotation.Validated;
18 18  
19 19 import java.util.List;
20   -import java.util.Map;
21 20  
22   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
23   -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
24 22  
25 23 /**
26 24 * 操作日志 API 实现类
... ... @@ -56,17 +54,7 @@ public class OperateLogApiImpl implements OperateLogApi {
56 54  
57 55 // 获取用户
58 56 List<AdminUserDO> userList = adminUserService.getUserList(convertSet(operateLogPage.getList(), OperateLogV2DO::getUserId));
59   - return BeanUtils.toBean(operateLogPage, OperateLogV2RespDTO.class).setList(setUserInfo(operateLogPage.getList(), userList));
60   - }
61   -
62   - // TODO @puhui999:这种 convert 还是放到 convert 类里,
63   - private static List<OperateLogV2RespDTO> setUserInfo(List<OperateLogV2DO> logList, List<AdminUserDO> userList) {
64   - Map<Long, AdminUserDO> userMap = convertMap(userList, AdminUserDO::getId);
65   - return convertList(logList, item -> {
66   - OperateLogV2RespDTO respDTO = BeanUtils.toBean(item, OperateLogV2RespDTO.class);
67   - findAndThen(userMap, item.getUserId(), user -> respDTO.setUserName(user.getNickname()));
68   - return respDTO;
69   - });
  57 + return OperateLogConvert.INSTANCE.convertPage(operateLogPage, userList);
70 58 }
71 59  
72 60 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/logger/OperateLogConvert.java
1 1 package cn.iocoder.yudao.module.system.convert.logger;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
6 8 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO;
7 9 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
  10 +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
8 11 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
9 12 import org.mapstruct.Mapper;
10 13 import org.mapstruct.factory.Mappers;
... ... @@ -12,6 +15,9 @@ import org.mapstruct.factory.Mappers;
12 15 import java.util.List;
13 16 import java.util.Map;
14 17  
  18 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  19 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  20 +
15 21 @Mapper
16 22 public interface OperateLogConvert {
17 23  
... ... @@ -25,4 +31,17 @@ public interface OperateLogConvert {
25 31 });
26 32 }
27 33  
  34 + default PageResult<OperateLogV2RespDTO> convertPage(PageResult<OperateLogV2DO> operateLogPage, List<AdminUserDO> userList) {
  35 + return BeanUtils.toBean(operateLogPage, OperateLogV2RespDTO.class).setList(setUserInfo(operateLogPage.getList(), userList));
  36 + }
  37 +
  38 + private static List<OperateLogV2RespDTO> setUserInfo(List<OperateLogV2DO> logList, List<AdminUserDO> userList) {
  39 + Map<Long, AdminUserDO> userMap = convertMap(userList, AdminUserDO::getId);
  40 + return CollectionUtils.convertList(logList, item -> {
  41 + OperateLogV2RespDTO respDTO = BeanUtils.toBean(item, OperateLogV2RespDTO.class);
  42 + findAndThen(userMap, item.getUserId(), user -> respDTO.setUserName(user.getNickname()));
  43 + return respDTO;
  44 + });
  45 + }
  46 +
28 47 }
... ...
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 5 import com.baomidou.mybatisplus.annotation.KeySequence;
7   -import com.baomidou.mybatisplus.annotation.TableField;
8 6 import com.baomidou.mybatisplus.annotation.TableId;
9 7 import com.baomidou.mybatisplus.annotation.TableName;
10   -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
11 8 import lombok.Data;
12 9 import lombok.EqualsAndHashCode;
13 10  
14   -import java.time.LocalDateTime;
15   -import java.util.Map;
16   -
17 11 /**
18 12 * 操作日志表 V2
19 13 *
... ... @@ -26,16 +20,6 @@ import java.util.Map;
26 20 public class OperateLogV2DO extends BaseDO {
27 21  
28 22 /**
29   - * {@link #javaMethodArgs} 的最大长度
30   - */
31   - public static final Integer JAVA_METHOD_ARGS_MAX_LENGTH = 8000;
32   -
33   - /**
34   - * {@link #resultData} 的最大长度
35   - */
36   - public static final Integer RESULT_MAX_LENGTH = 4000;
37   -
38   - /**
39 23 * 日志主键
40 24 */
41 25 @TableId
... ... @@ -70,20 +54,18 @@ public class OperateLogV2DO extends BaseDO {
70 54 * 操作模块业务编号
71 55 */
72 56 private Long bizId;
73   - // TODO @puhui999:content 改成 action,extra 换成 String。注释就直接用 mzt,和它完全对应好了。
74 57 /**
75   - * 操作内容,记录整个操作的明细
  58 + * 日志内容,记录整个操作的明细
76 59 *
77 60 * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
78 61 */
79   - private String content;
  62 + private String action;
80 63 /**
81 64 * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
82 65 *
83 66 * 例如说,记录订单编号,{ orderId: "1"}
84 67 */
85   - @TableField(typeHandler = JacksonTypeHandler.class)
86   - private Map<String, Object> extra;
  68 + private String extra;
87 69  
88 70 /**
89 71 * 请求方法名
... ... @@ -102,44 +84,4 @@ public class OperateLogV2DO extends BaseDO {
102 84 */
103 85 private String userAgent;
104 86  
105   - // TODO @puhui999:微信已经讨论,下面的字段都不要哈;
106   - /**
107   - * Java 方法名
108   - */
109   - private String javaMethod;
110   - /**
111   - * Java 方法的参数
112   - *
113   - * 实际格式为 Map<String, Object>
114   - * 不使用 @TableField(typeHandler = FastjsonTypeHandler.class) 注解的原因是,数据库存储有长度限制,会进行裁剪,会导致 JSON 反序列化失败
115   - * 其中,key 为参数名,value 为参数值
116   - */
117   - private String javaMethodArgs;
118   - /**
119   - * 开始时间
120   - */
121   - private LocalDateTime startTime;
122   - /**
123   - * 执行时长,单位:毫秒
124   - */
125   - private Integer duration;
126   - /**
127   - * 结果码
128   - *
129   - * 目前使用的 {@link CommonResult#getCode()} 属性
130   - */
131   - private Integer resultCode;
132   - /**
133   - * 结果提示
134   - *
135   - * 目前使用的 {@link CommonResult#getMsg()} 属性
136   - */
137   - private String resultMsg;
138   - /**
139   - * 结果数据
140   - *
141   - * 如果是对象,则使用 JSON 格式化
142   - */
143   - private String resultData;
144   -
145 87 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/parse/AdminUserParseFunction.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java
1   -package cn.iocoder.yudao.module.system.framework.operatelog.parse;
  1 +package cn.iocoder.yudao.module.system.framework.operatelog.core;
2 2  
3 3 import cn.hutool.core.util.ObjUtil;
4 4 import cn.hutool.core.util.StrUtil;
... ... @@ -9,7 +9,6 @@ import jakarta.annotation.Resource;
9 9 import lombok.extern.slf4j.Slf4j;
10 10 import org.springframework.stereotype.Component;
11 11  
12   -// TODO @puhui999:这个微信讨论下,function 叫啥好哈;
13 12 /**
14 13 * 自定义函数-通过用户编号获取用户信息
15 14 *
... ... @@ -27,15 +26,12 @@ public class AdminUserParseFunction implements IParseFunction {
27 26 return "getAdminUserById";
28 27 }
29 28  
30   - // TODO @puhui999:这个方法的实现优化下哈;
31 29 @Override
32 30 public String apply(Object value) {
33   - if (value == null) {
34   - //log.warn("(getAdminUserById) 解析异常参数为 null");
  31 + if (ObjUtil.isEmpty(value)) {
35 32 return "";
36 33 }
37 34 if (StrUtil.isEmpty(value.toString())) {
38   - //log.warn("(getAdminUserById) 解析异常参数为空");
39 35 return "";
40 36 }
41 37  
... ... @@ -52,4 +48,5 @@ public class AdminUserParseFunction implements IParseFunction {
52 48 }
53 49 return nickname;
54 50 }
  51 +
55 52 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/parse/AreaParseFunction.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java
1   -package cn.iocoder.yudao.module.system.framework.operatelog.parse;
  1 +package cn.iocoder.yudao.module.system.framework.operatelog.core;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
5 6 import com.mzt.logapi.service.IParseFunction;
6 7 import lombok.extern.slf4j.Slf4j;
7 8 import org.springframework.stereotype.Component;
8 9  
9   -// TODO @puhui999:还是放在 core 包下哈;
10 10 /**
11 11 * 自定义函数-通过区域编号获取区域信息
12 12 *
... ... @@ -28,7 +28,7 @@ public class AreaParseFunction implements IParseFunction {
28 28  
29 29 @Override
30 30 public String apply(Object value) {
31   - if (value == null) {
  31 + if (ObjUtil.isEmpty(value)) {
32 32 return "";
33 33 }
34 34 if (StrUtil.isEmpty(value.toString())) {
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
... ... @@ -71,8 +71,6 @@ public class OperateLogServiceImpl implements OperateLogService {
71 71 @Override
72 72 public void createOperateLogV2(OperateLogV2CreateReqDTO createReqBO) {
73 73 OperateLogV2DO log = BeanUtils.toBean(createReqBO, OperateLogV2DO.class);
74   - log.setJavaMethodArgs(StrUtils.maxLength(log.getJavaMethodArgs(), JAVA_METHOD_ARGS_MAX_LENGTH));
75   - log.setResultData(StrUtils.maxLength(log.getResultData(), RESULT_MAX_LENGTH));
76 74 operateLogV2Mapper.insert(log);
77 75 }
78 76  
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java
... ... @@ -21,15 +21,14 @@ import cn.iocoder.yudao.module.system.service.social.SocialUserService;
21 21 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
22 22 import com.xingyuv.captcha.model.common.ResponseModel;
23 23 import com.xingyuv.captcha.service.CaptchaService;
24   -import org.junit.jupiter.api.BeforeEach;
25   -import org.junit.jupiter.api.Test;
26   -import org.springframework.boot.test.mock.mockito.MockBean;
27   -import org.springframework.context.annotation.Import;
28   -
29 24 import jakarta.annotation.Resource;
30 25 import jakarta.validation.ConstraintViolationException;
31 26 import jakarta.validation.Validation;
32 27 import jakarta.validation.Validator;
  28 +import org.junit.jupiter.api.BeforeEach;
  29 +import org.junit.jupiter.api.Test;
  30 +import org.springframework.boot.test.mock.mockito.MockBean;
  31 +import org.springframework.context.annotation.Import;
33 32  
34 33 import static cn.hutool.core.util.RandomUtil.randomEle;
35 34 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
... ... @@ -237,7 +236,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
237 236 // mock 方法(绑定的用户编号)
238 237 Long userId = 1L;
239 238 when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
240   - eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), userId));
  239 + eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId));
241 240 // mock(用户)
242 241 AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
243 242 when(userService.getUser(eq(userId))).thenReturn(user);
... ...