Commit 82a25474f60be3a89186e53e4ee46ccd4b3e016d

Authored by YunaiV
2 parents 7542b6a7 525d41ba

Merge remote-tracking branch 'origin/develop' into develop

Showing 70 changed files with 1135 additions and 408 deletions
yudao-dependencies/pom.xml
@@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
26 <mybatis-plus.version>3.5.4.1</mybatis-plus.version> 26 <mybatis-plus.version>3.5.4.1</mybatis-plus.version>
27 <mybatis-plus-generator.version>3.5.4.1</mybatis-plus-generator.version> 27 <mybatis-plus-generator.version>3.5.4.1</mybatis-plus-generator.version>
28 <dynamic-datasource.version>4.2.0</dynamic-datasource.version> 28 <dynamic-datasource.version>4.2.0</dynamic-datasource.version>
29 - <mybatis-plus-join.version>1.4.7.2</mybatis-plus-join.version> 29 + <mybatis-plus-join.version>1.4.8.1</mybatis-plus-join.version>
30 <redisson.version>3.25.0</redisson.version> 30 <redisson.version>3.25.0</redisson.version>
31 <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version> 31 <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
32 <!-- 消息队列 --> 32 <!-- 消息队列 -->
yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml
@@ -46,6 +46,13 @@ @@ -46,6 +46,13 @@
46 <groupId>com.google.guava</groupId> 46 <groupId>com.google.guava</groupId>
47 <artifactId>guava</artifactId> 47 <artifactId>guava</artifactId>
48 </dependency> 48 </dependency>
  49 + <!-- Springboot-注解-通用操作日志组件 -->
  50 + <!-- 此组件解决的问题是: 「谁」在「什么时间」对「什么」做了「什么事」 -->
  51 + <dependency>
  52 + <groupId>io.github.mouzt</groupId>
  53 + <artifactId>bizlog-sdk</artifactId>
  54 + </dependency>
  55 +
49 </dependencies> 56 </dependencies>
50 57
51 </project> 58 </project>
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogAutoConfiguration.java
1 package cn.iocoder.yudao.framework.operatelog.config; 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;  
7 import org.springframework.boot.autoconfigure.AutoConfiguration; 3 import org.springframework.boot.autoconfigure.AutoConfiguration;
8 -import org.springframework.context.annotation.Bean;  
9 4
10 @AutoConfiguration 5 @AutoConfiguration
11 public class YudaoOperateLogAutoConfiguration { 6 public class YudaoOperateLogAutoConfiguration {
12 7
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 - } 8 + //@Bean
  9 + //public OperateLogAspect operateLogAspect() {
  10 + // return new OperateLogAspect();
  11 + //}
  12 + //
  13 + //@Bean
  14 + //public OperateLogFrameworkService operateLogFrameworkService(OperateLogApi operateLogApi) {
  15 + // return new OperateLogFrameworkServiceImpl(operateLogApi);
  16 + //}
22 17
23 } 18 }
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/config/YudaoOperateLogV2Configuration.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.operatelogv2.config;
  2 +
  3 +import cn.iocoder.yudao.framework.operatelogv2.core.aop.OperateLogV2Aspect;
  4 +import cn.iocoder.yudao.framework.operatelogv2.core.service.ILogRecordServiceImpl;
  5 +import com.mzt.logapi.service.ILogRecordService;
  6 +import com.mzt.logapi.starter.annotation.EnableLogRecord;
  7 +import lombok.extern.slf4j.Slf4j;
  8 +import org.springframework.boot.autoconfigure.AutoConfiguration;
  9 +import org.springframework.context.annotation.Bean;
  10 +import org.springframework.context.annotation.Primary;
  11 +
  12 +/**
  13 + * mzt-biz-log 配置类
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦
  18 +@AutoConfiguration
  19 +@Slf4j
  20 +public class YudaoOperateLogV2Configuration {
  21 +
  22 + @Bean
  23 + @Primary
  24 + public ILogRecordService iLogRecordServiceImpl() {
  25 + return new ILogRecordServiceImpl();
  26 + }
  27 +
  28 + @Bean
  29 + public OperateLogV2Aspect operateLogV2Aspect() {
  30 + return new OperateLogV2Aspect();
  31 + }
  32 +
  33 +}
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/aop/OperateLogV2Aspect.java 0 → 100644
  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 +/**
  48 + * 拦截使用 @Operation 注解, 获取操作类型、开始时间、持续时间、方法相关信息、执行结果等信息
  49 + * 对 mzt-biz-log 日志信息进行增强
  50 + *
  51 + * @author HUIHUI
  52 + */
  53 +@Aspect
  54 +@Slf4j
  55 +public class OperateLogV2Aspect {
  56 +
  57 + /**
  58 + * 用于记录操作内容的上下文
  59 + *
  60 + * @see OperateLogV2CreateReqDTO#getContent()
  61 + */
  62 + private static final ThreadLocal<LogRecord> CONTENT = new ThreadLocal<>();
  63 + /**
  64 + * 用于记录拓展字段的上下文
  65 + *
  66 + * @see OperateLogV2CreateReqDTO#getExtra()
  67 + */
  68 + private static final ThreadLocal<Map<String, Object>> EXTRA = new ThreadLocal<>();
  69 +
  70 + @Resource
  71 + private OperateLogApi operateLogApi;
  72 +
  73 + @Around("@annotation(operation)")
  74 + public Object around(ProceedingJoinPoint joinPoint, Operation operation) throws Throwable {
  75 + RequestMethod requestMethod = obtainFirstMatchRequestMethod(obtainRequestMethod(joinPoint));
  76 + if (requestMethod == RequestMethod.GET) { // 跳过 get 方法
  77 + return joinPoint.proceed();
  78 + }
  79 +
  80 + // 目前,只有管理员,才记录操作日志!所以非管理员,直接调用,不进行记录
  81 + Integer userType = WebFrameworkUtils.getLoginUserType();
  82 + if (ObjUtil.notEqual(userType, UserTypeEnum.ADMIN.getValue())) {
  83 + return joinPoint.proceed();
  84 + }
  85 +
  86 + // 记录开始时间
  87 + LocalDateTime startTime = LocalDateTime.now();
  88 + try {
  89 + // 执行原有方法
  90 + Object result = joinPoint.proceed();
  91 + // 记录正常执行时的操作日志
  92 + this.log(joinPoint, operation, startTime, result, null);
  93 + return result;
  94 + } catch (Throwable exception) {
  95 + this.log(joinPoint, operation, startTime, null, exception);
  96 + throw exception;
  97 + } finally {
  98 + clearThreadLocal();
  99 + }
  100 + }
  101 +
  102 + public static void setContent(LogRecord content) {
  103 + CONTENT.set(content);
  104 + }
  105 +
  106 + public static void addExtra(String key, Object value) {
  107 + if (EXTRA.get() == null) {
  108 + EXTRA.set(new HashMap<>());
  109 + }
  110 + EXTRA.get().put(key, value);
  111 + }
  112 +
  113 + public static void addExtra(Map<String, Object> extra) {
  114 + if (EXTRA.get() == null) {
  115 + EXTRA.set(new HashMap<>());
  116 + }
  117 + EXTRA.get().putAll(extra);
  118 + }
  119 +
  120 + private static void clearThreadLocal() {
  121 + CONTENT.remove();
  122 + EXTRA.remove();
  123 + }
  124 +
  125 + private void log(ProceedingJoinPoint joinPoint, Operation operation,
  126 + LocalDateTime startTime, Object result, Throwable exception) {
  127 + try {
  128 + // 判断不记录的情况(默认没有值就是记录)
  129 + if (EXTRA.get() != null && EXTRA.get().get(ENABLE) != null) {
  130 + return;
  131 + }
  132 + if (CONTENT.get() == null) { // 没有值说明没有日志需要记录
  133 + return;
  134 + }
  135 +
  136 + // 真正记录操作日志
  137 + this.log0(joinPoint, operation, startTime, result, exception);
  138 + } catch (Throwable ex) {
  139 + log.error("[log][记录操作日志时,发生异常,其中参数是 joinPoint({}) apiOperation({}) result({}) exception({}) ]",
  140 + joinPoint, operation, result, exception, ex);
  141 + }
  142 + }
  143 +
  144 + private void log0(ProceedingJoinPoint joinPoint, Operation operation,
  145 + LocalDateTime startTime, Object result, Throwable exception) {
  146 + OperateLogV2CreateReqDTO reqDTO = new OperateLogV2CreateReqDTO();
  147 + // 补全通用字段
  148 + reqDTO.setTraceId(TracerUtils.getTraceId());
  149 + reqDTO.setStartTime(startTime);
  150 + // 补充用户信息
  151 + fillUserFields(reqDTO);
  152 + // 补全模块信息
  153 + fillModuleFields(reqDTO, operation);
  154 + // 补全请求信息
  155 + fillRequestFields(reqDTO);
  156 + // 补全方法信息
  157 + fillMethodFields(reqDTO, joinPoint, startTime, result, exception);
  158 +
  159 + // 异步记录日志
  160 + operateLogApi.createOperateLogV2(reqDTO);
  161 + }
  162 +
  163 + private static void fillUserFields(OperateLogV2CreateReqDTO reqDTO) {
  164 + reqDTO.setUserId(WebFrameworkUtils.getLoginUserId());
  165 + reqDTO.setUserType(WebFrameworkUtils.getLoginUserType());
  166 + }
  167 +
  168 + private static void fillModuleFields(OperateLogV2CreateReqDTO reqDTO, Operation operation) {
  169 + LogRecord logRecord = CONTENT.get();
  170 + reqDTO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号
  171 + reqDTO.setContent(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  172 +
  173 + // type 属性
  174 + reqDTO.setType(logRecord.getType()); // 大模块类型如 crm 客户
  175 + // subType 属性
  176 + if (logRecord.getSubType() != null) {
  177 + reqDTO.setSubType(logRecord.getSubType());// 操作名称如 转移客户
  178 + }
  179 + if (StrUtil.isEmpty(reqDTO.getSubType()) && operation != null) {
  180 + reqDTO.setSubType(operation.summary());
  181 + }
  182 +
  183 + // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"}
  184 + Map<String, Object> objectMap = EXTRA.get();
  185 + if (objectMap != null) {
  186 + Object object = objectMap.get(EXTRA_KEY);
  187 + if (object instanceof Map<?, ?> extraMap) {
  188 + if (extraMap.keySet().stream().allMatch(String.class::isInstance)) {
  189 + @SuppressWarnings("unchecked")
  190 + Map<String, Object> extra = (Map<String, Object>) extraMap;
  191 + reqDTO.setExtra(extra);
  192 + return;
  193 + }
  194 + }
  195 + // 激进一点不是 map 直接当 value 处理
  196 + Map<String, Object> extra = Maps.newHashMapWithExpectedSize(1);
  197 + extra.put(EXTRA_KEY, object);
  198 + reqDTO.setExtra(extra);
  199 + }
  200 +
  201 + }
  202 +
  203 + private static void fillRequestFields(OperateLogV2CreateReqDTO reqDTO) {
  204 + // 获得 Request 对象
  205 + HttpServletRequest request = ServletUtils.getRequest();
  206 + if (request == null) {
  207 + return;
  208 + }
  209 + // 补全请求信息
  210 + reqDTO.setRequestMethod(request.getMethod());
  211 + reqDTO.setRequestUrl(request.getRequestURI());
  212 + reqDTO.setUserIp(ServletUtils.getClientIP(request));
  213 + reqDTO.setUserAgent(ServletUtils.getUserAgent(request));
  214 + }
  215 +
  216 + private static void fillMethodFields(OperateLogV2CreateReqDTO reqDTO, ProceedingJoinPoint joinPoint, LocalDateTime startTime,
  217 + Object result, Throwable exception) {
  218 + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  219 + reqDTO.setJavaMethod(methodSignature.toString());
  220 + if (EXTRA.get().get(IS_LOG_ARGS) == null) {
  221 + reqDTO.setJavaMethodArgs(obtainMethodArgs(joinPoint));
  222 + }
  223 + if (EXTRA.get().get(IS_LOG_RESULT_DATA) == null) {
  224 + reqDTO.setResultData(obtainResultData(result));
  225 + }
  226 + reqDTO.setDuration((int) (LocalDateTimeUtil.between(startTime, LocalDateTime.now()).toMillis()));
  227 + // (正常)处理 resultCode 和 resultMsg 字段
  228 + if (result instanceof CommonResult) {
  229 + CommonResult<?> commonResult = (CommonResult<?>) result;
  230 + reqDTO.setResultCode(commonResult.getCode());
  231 + reqDTO.setResultMsg(commonResult.getMsg());
  232 + } else {
  233 + reqDTO.setResultCode(SUCCESS.getCode());
  234 + }
  235 + // (异常)处理 resultCode 和 resultMsg 字段
  236 + if (exception != null) {
  237 + reqDTO.setResultCode(INTERNAL_SERVER_ERROR.getCode());
  238 + reqDTO.setResultMsg(ExceptionUtil.getRootCauseMessage(exception));
  239 + }
  240 + }
  241 +
  242 + @SuppressWarnings("SameParameterValue")
  243 + private static <T extends Annotation> T getClassAnnotation(ProceedingJoinPoint joinPoint, Class<T> annotationClass) {
  244 + return ((MethodSignature) joinPoint.getSignature()).getMethod().getDeclaringClass().getAnnotation(annotationClass);
  245 + }
  246 +
  247 + private static String obtainMethodArgs(ProceedingJoinPoint joinPoint) {
  248 + // TODO 提升:参数脱敏和忽略
  249 + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  250 + String[] argNames = methodSignature.getParameterNames();
  251 + Object[] argValues = joinPoint.getArgs();
  252 + // 拼接参数
  253 + Map<String, Object> args = Maps.newHashMapWithExpectedSize(argValues.length);
  254 + for (int i = 0; i < argNames.length; i++) {
  255 + String argName = argNames[i];
  256 + Object argValue = argValues[i];
  257 + // 被忽略时,标记为 ignore 字符串,避免和 null 混在一起
  258 + args.put(argName, !isIgnoreArgs(argValue) ? argValue : "[ignore]");
  259 + }
  260 + return JsonUtils.toJsonString(args);
  261 + }
  262 +
  263 + private static String obtainResultData(Object result) {
  264 + // TODO 提升:结果脱敏和忽略
  265 + if (result instanceof CommonResult) {
  266 + result = ((CommonResult<?>) result).getData();
  267 + }
  268 + return JsonUtils.toJsonString(result);
  269 + }
  270 +
  271 + private static boolean isIgnoreArgs(Object object) {
  272 + Class<?> clazz = object.getClass();
  273 + // 处理数组的情况
  274 + if (clazz.isArray()) {
  275 + return IntStream.range(0, Array.getLength(object))
  276 + .anyMatch(index -> isIgnoreArgs(Array.get(object, index)));
  277 + }
  278 + // 递归,处理数组、Collection、Map 的情况
  279 + if (Collection.class.isAssignableFrom(clazz)) {
  280 + return ((Collection<?>) object).stream()
  281 + .anyMatch((Predicate<Object>) OperateLogV2Aspect::isIgnoreArgs);
  282 + }
  283 + if (Map.class.isAssignableFrom(clazz)) {
  284 + return isIgnoreArgs(((Map<?, ?>) object).values());
  285 + }
  286 + // obj
  287 + return object instanceof MultipartFile
  288 + || object instanceof HttpServletRequest
  289 + || object instanceof HttpServletResponse
  290 + || object instanceof BindingResult;
  291 + }
  292 +
  293 + private static RequestMethod obtainFirstMatchRequestMethod(RequestMethod[] requestMethods) {
  294 + if (ArrayUtil.isEmpty(requestMethods)) {
  295 + return null;
  296 + }
  297 + // 优先,匹配最优的 POST、PUT、DELETE
  298 + RequestMethod result = obtainFirstLogRequestMethod(requestMethods);
  299 + if (result != null) {
  300 + return result;
  301 + }
  302 + // 然后,匹配次优的 GET
  303 + result = Arrays.stream(requestMethods).filter(requestMethod -> requestMethod == RequestMethod.GET)
  304 + .findFirst().orElse(null);
  305 + if (result != null) {
  306 + return result;
  307 + }
  308 + // 兜底,获得第一个
  309 + return requestMethods[0];
  310 + }
  311 +
  312 + private static RequestMethod[] obtainRequestMethod(ProceedingJoinPoint joinPoint) {
  313 + RequestMapping requestMapping = AnnotationUtils.getAnnotation( // 使用 Spring 的工具类,可以处理 @RequestMapping 别名注解
  314 + ((MethodSignature) joinPoint.getSignature()).getMethod(), RequestMapping.class);
  315 + return requestMapping != null ? requestMapping.method() : new RequestMethod[]{};
  316 + }
  317 +
  318 + private static RequestMethod obtainFirstLogRequestMethod(RequestMethod[] requestMethods) {
  319 + if (ArrayUtil.isEmpty(requestMethods)) {
  320 + return null;
  321 + }
  322 + return Arrays.stream(requestMethods).filter(requestMethod ->
  323 + requestMethod == RequestMethod.POST
  324 + || requestMethod == RequestMethod.PUT
  325 + || requestMethod == RequestMethod.DELETE)
  326 + .findFirst().orElse(null);
  327 + }
  328 +
  329 +}
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/enums/OperateLogV2Constants.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.operatelogv2.core.enums;
  2 +
  3 +/**
  4 + * 操作日志常量接口
  5 + *
  6 + * @author HUIHUI
  7 + */
  8 +public interface OperateLogV2Constants {
  9 +
  10 + // ========== 开关字段-如果没有值默认就是记录 ==========
  11 +
  12 + /**
  13 + * 是否记录日志
  14 + */
  15 + String ENABLE = "enable";
  16 +
  17 + /**
  18 + * 是否记录方法参数
  19 + */
  20 + String IS_LOG_ARGS = "isLogArgs";
  21 +
  22 + /**
  23 + * 是否记录方法结果的数据
  24 + */
  25 + String IS_LOG_RESULT_DATA = "isLogResultData";
  26 +
  27 + // ========== 扩展 ==========
  28 +
  29 + /**
  30 + * 扩展信息
  31 + */
  32 + String EXTRA_KEY = "extra";
  33 +
  34 +}
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.operatelogv2.core;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/service/ILogRecordServiceImpl.java 0 → 100644
  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 + }
  27 +
  28 + @Override
  29 + public List<LogRecord> queryLog(String bizNo, String type) {
  30 + return Collections.emptyList();
  31 + }
  32 +
  33 + @Override
  34 + public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {
  35 + return Collections.emptyList();
  36 + }
  37 +
  38 +}
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/core/vo/OperateLogV2PageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.operatelogv2.core.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - 操作日志分页 Request VO")
  8 +@Data
  9 +public class OperateLogV2PageReqVO extends PageParam {
  10 +
  11 + @Schema(description = "模块数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + private Long bizId;
  13 +
  14 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  15 + private Long userId;
  16 +
  17 + @Schema(description = "模块类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  18 + private String bizType;
  19 +
  20 +}
yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelogv2/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.operatelogv2;
0 \ No newline at end of file 2 \ 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 \ No newline at end of file 1 \ No newline at end of file
  2 +cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogAutoConfiguration
  3 +cn.iocoder.yudao.framework.operatelogv2.config.YudaoOperateLogV2Configuration
3 \ No newline at end of file 4 \ No newline at end of file
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
@@ -22,6 +22,5 @@ public interface LogRecordConstants { @@ -22,6 +22,5 @@ public interface LogRecordConstants {
22 //======================= 客户转移操作日志 ======================= 22 //======================= 客户转移操作日志 =======================
23 23
24 String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; 24 String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
25 - String TRANSFER_CUSTOMER_LOG_FAIL = ""; // TODO @puhui999:这个可以删除哈,一般不搞失败的日志  
26 25
27 } 26 }
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.enums.permission;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * Crm 数据权限角色枚举
  8 + *
  9 + * @author HUIHUI
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum CrmPermissionRoleCodeEnum {
  14 +
  15 + CRM_ADMIN("crm_admin", "CRM 管理员");
  16 +
  17 + /**
  18 + * 角色标识
  19 + */
  20 + private String code;
  21 + /**
  22 + * 角色名称
  23 + */
  24 + private String name;
  25 +
  26 +}
  27 +
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
1 ### 请求 /transfer 1 ### 请求 /transfer
2 PUT {{baseUrl}}/crm/customer/transfer 2 PUT {{baseUrl}}/crm/customer/transfer
3 -Content-Type: application/json 3 +Content-Type: application/-id: {{adminTenentId}}json
4 Authorization: Bearer {{token}} 4 Authorization: Bearer {{token}}
5 -tenant-id: {{adminTenentId}} 5 +tenant
6 6
7 { 7 {
8 "id": 10, 8 "id": 10,
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -3,8 +3,10 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer; @@ -3,8 +3,10 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 7 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
7 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 8 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  9 +import cn.iocoder.yudao.framework.operatelogv2.core.vo.OperateLogV2PageReqVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; 10 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
9 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; 11 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@@ -12,6 +14,7 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; @@ -12,6 +14,7 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
12 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 14 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
13 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 15 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
14 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; 16 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
  17 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
15 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; 18 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
16 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 19 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
17 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 20 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -64,7 +67,7 @@ public class CrmCustomerController { @@ -64,7 +67,7 @@ public class CrmCustomerController {
64 } 67 }
65 68
66 @PutMapping("/update") 69 @PutMapping("/update")
67 - //@Operation(summary = "更新客户") 70 + @Operation(summary = "更新客户")
68 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 71 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
69 public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) { 72 public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
70 customerService.updateCustomer(updateReqVO); 73 customerService.updateCustomer(updateReqVO);
@@ -128,30 +131,21 @@ public class CrmCustomerController { @@ -128,30 +131,21 @@ public class CrmCustomerController {
128 } 131 }
129 132
130 @PutMapping("/transfer") 133 @PutMapping("/transfer")
131 - //@Operation(summary = "客户转移") 134 + @Operation(summary = "客户转移")
132 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 135 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
133 public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { 136 public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
134 customerService.transferCustomer(reqVO, getLoginUserId()); 137 customerService.transferCustomer(reqVO, getLoginUserId());
135 return success(true); 138 return success(true);
136 } 139 }
137 140
138 - // TODO @puhui999:operate-log-list 或者 operate-log-page 如果分页  
139 - @GetMapping("/operate-log") 141 + @GetMapping("/operate-log-page")
140 @Operation(summary = "获得客户操作日志") 142 @Operation(summary = "获得客户操作日志")
141 @Parameter(name = "id", description = "编号", required = true, example = "1024") 143 @Parameter(name = "id", description = "编号", required = true, example = "1024")
142 @PreAuthorize("@ss.hasPermission('crm:customer:query')") 144 @PreAuthorize("@ss.hasPermission('crm:customer:query')")
143 - // TODO @puhui999:最好有读权限;方法名改成 getCustomerOperateLog  
144 - public CommonResult<List<OperateLogV2RespDTO>> getOperateLog(@RequestParam("id") Long id) {  
145 - // 1. 获取客户  
146 - // TODO @puhui999:这个校验可以去掉哈;  
147 - CrmCustomerDO customer = customerService.getCustomer(id);  
148 - if (customer == null) {  
149 - return success(null);  
150 - }  
151 -  
152 - // 2. 获取操作日志  
153 - // TODO @puhui999:操作日志,返回可能要分页哈;  
154 - return success(operateLogApi.getOperateLogByModuleAndBizId(CRM_CUSTOMER, id)); 145 + public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(OperateLogV2PageReqVO reqVO) {
  146 + reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
  147 + reqVO.setBizType(CRM_CUSTOMER);
  148 + return success(operateLogApi.getOperateLogPage(BeanUtils.toBean(reqVO, OperateLogV2PageReqDTO.class)));
155 } 149 }
156 150
157 // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了; 151 // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java
@@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionR @@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionR
8 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
9 import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert; 9 import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; 10 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
11 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;  
12 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 11 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  12 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
13 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 13 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
14 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 14 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
15 import cn.iocoder.yudao.module.system.api.dept.PostApi; 15 import cn.iocoder.yudao.module.system.api.dept.PostApi;
@@ -21,12 +21,12 @@ import io.swagger.v3.oas.annotations.Operation; @@ -21,12 +21,12 @@ import io.swagger.v3.oas.annotations.Operation;
21 import io.swagger.v3.oas.annotations.Parameter; 21 import io.swagger.v3.oas.annotations.Parameter;
22 import io.swagger.v3.oas.annotations.Parameters; 22 import io.swagger.v3.oas.annotations.Parameters;
23 import io.swagger.v3.oas.annotations.tags.Tag; 23 import io.swagger.v3.oas.annotations.tags.Tag;
  24 +import jakarta.annotation.Resource;
  25 +import jakarta.validation.Valid;
24 import org.springframework.security.access.prepost.PreAuthorize; 26 import org.springframework.security.access.prepost.PreAuthorize;
25 import org.springframework.validation.annotation.Validated; 27 import org.springframework.validation.annotation.Validated;
26 import org.springframework.web.bind.annotation.*; 28 import org.springframework.web.bind.annotation.*;
27 29
28 -import jakarta.annotation.Resource;  
29 -import jakarta.validation.Valid;  
30 import java.util.*; 30 import java.util.*;
31 31
32 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 32 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
@@ -38,11 +38,8 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; { @@ -38,11 +38,8 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; {
38 default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) { 38 default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
39 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>(); 39 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>();
40 // 拼接数据权限的查询条件 40 // 拼接数据权限的查询条件
41 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), 41 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(),
42 CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); 42 CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
43 - if (!condition) {  
44 - return PageResult.empty();  
45 - }  
46 // 拼接自身的查询条件 43 // 拼接自身的查询条件
47 query.selectAll(CrmBusinessDO.class) 44 query.selectAll(CrmBusinessDO.class)
48 .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName()) 45 .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
@@ -30,11 +30,8 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; { @@ -30,11 +30,8 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; {
30 default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO pageReqVO, Long userId) { 30 default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO pageReqVO, Long userId) {
31 MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>(); 31 MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>();
32 // 拼接数据权限的查询条件 32 // 拼接数据权限的查询条件
33 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), 33 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(),
34 CrmClueDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool()); 34 CrmClueDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
35 - if (!condition) {  
36 - return PageResult.empty();  
37 - }  
38 // 拼接自身的查询条件 35 // 拼接自身的查询条件
39 query.selectAll(CrmClueDO.class) 36 query.selectAll(CrmClueDO.class)
40 .likeIfPresent(CrmClueDO::getName, pageReqVO.getName()) 37 .likeIfPresent(CrmClueDO::getName, pageReqVO.getName())
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
@@ -43,11 +43,8 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; { @@ -43,11 +43,8 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; {
43 default PageResult<CrmContactDO> selectPage(CrmContactPageReqVO pageReqVO, Long userId) { 43 default PageResult<CrmContactDO> selectPage(CrmContactPageReqVO pageReqVO, Long userId) {
44 MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>(); 44 MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>();
45 // 拼接数据权限的查询条件 45 // 拼接数据权限的查询条件
46 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), 46 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(),
47 CrmContactDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); 47 CrmContactDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
48 - if (!condition) {  
49 - return PageResult.empty();  
50 - }  
51 // 拼接自身的查询条件 48 // 拼接自身的查询条件
52 query.selectAll(CrmContactDO.class) 49 query.selectAll(CrmContactDO.class)
53 .likeIfPresent(CrmContactDO::getName, pageReqVO.getName()) 50 .likeIfPresent(CrmContactDO::getName, pageReqVO.getName())
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
@@ -41,11 +41,8 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; { @@ -41,11 +41,8 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; {
41 default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) { 41 default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) {
42 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); 42 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
43 // 拼接数据权限的查询条件 43 // 拼接数据权限的查询条件
44 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), 44 + CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(),
45 CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); 45 CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
46 - if (!condition) {  
47 - return PageResult.empty();  
48 - }  
49 // 拼接自身的查询条件 46 // 拼接自身的查询条件
50 mpjLambdaWrapperX.selectAll(CrmContractDO.class) 47 mpjLambdaWrapperX.selectAll(CrmContractDO.class)
51 .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo()) 48 .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -30,11 +30,8 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -30,11 +30,8 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
30 default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) { 30 default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
31 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); 31 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
32 // 拼接数据权限的查询条件 32 // 拼接数据权限的查询条件
33 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), 33 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
34 CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool()); 34 CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
35 - if (!condition) {  
36 - return PageResult.empty();  
37 - }  
38 // 拼接自身的查询条件 35 // 拼接自身的查询条件
39 query.selectAll(CrmCustomerDO.class) 36 query.selectAll(CrmCustomerDO.class)
40 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) 37 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
@@ -39,11 +39,8 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; { @@ -39,11 +39,8 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; {
39 default PageResult<CrmReceivableDO> selectPage(CrmReceivablePageReqVO pageReqVO, Long userId) { 39 default PageResult<CrmReceivableDO> selectPage(CrmReceivablePageReqVO pageReqVO, Long userId) {
40 MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>(); 40 MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
41 // 拼接数据权限的查询条件 41 // 拼接数据权限的查询条件
42 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(), 42 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
43 CrmReceivableDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); 43 CrmReceivableDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
44 - if (!condition) {  
45 - return PageResult.empty();  
46 - }  
47 // 拼接自身的查询条件 44 // 拼接自身的查询条件
48 query.selectAll(CrmReceivableDO.class) 45 query.selectAll(CrmReceivableDO.class)
49 .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo()) 46 .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo())
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
@@ -38,11 +38,8 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO @@ -38,11 +38,8 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO
38 default PageResult<CrmReceivablePlanDO> selectPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) { 38 default PageResult<CrmReceivablePlanDO> selectPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {
39 MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>(); 39 MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();
40 // 拼接数据权限的查询条件 40 // 拼接数据权限的查询条件
41 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), 41 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),
42 CrmReceivablePlanDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); 42 CrmReceivablePlanDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
43 - if (!condition) {  
44 - return PageResult.empty();  
45 - }  
46 // 拼接自身的查询条件 43 // 拼接自身的查询条件
47 query.selectAll(CrmReceivablePlanDO.class) 44 query.selectAll(CrmReceivablePlanDO.class)
48 .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId()) 45 .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId())
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/package-info.java deleted
1 -package cn.iocoder.yudao.module.crm.framework.bizlog;  
2 \ No newline at end of file 0 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/core/package-info.java deleted
1 -package cn.iocoder.yudao.module.crm.framework.core;  
2 \ No newline at end of file 0 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.operatelog;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmIndustryParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/parse/CrmIndustryParseFunction.java
1 -package cn.iocoder.yudao.module.crm.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.crm.framework.operatelog.parse;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; 4 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
@@ -8,7 +8,6 @@ import org.springframework.stereotype.Component; @@ -8,7 +8,6 @@ import org.springframework.stereotype.Component;
8 8
9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY; 9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
10 10
11 -// TODO @puhui999:包名使用 operatelog 更合适哈;  
12 /** 11 /**
13 * 自定义函数-通过行业编号获取行业信息 12 * 自定义函数-通过行业编号获取行业信息
14 * 13 *
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmLevelParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/parse/CrmLevelParseFunction.java
1 -package cn.iocoder.yudao.module.crm.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.crm.framework.operatelog.parse;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; 4 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmSourceParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/parse/CrmSourceParseFunction.java
1 -package cn.iocoder.yudao.module.crm.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.crm.framework.operatelog.parse;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; 4 import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/core/annotations/CrmPermission.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/annotations/CrmPermission.java
1 -package cn.iocoder.yudao.module.crm.framework.core.annotations; 1 +package cn.iocoder.yudao.module.crm.framework.permission.core.annotations;
2 2
3 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 3 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
4 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 4 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/core/aop/CrmPermissionAspect.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/aop/CrmPermissionAspect.java
1 -package cn.iocoder.yudao.module.crm.framework.core.aop; 1 +package cn.iocoder.yudao.module.crm.framework.permission.core.aop;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
@@ -6,27 +6,27 @@ import cn.hutool.core.util.StrUtil; @@ -6,27 +6,27 @@ import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; 6 import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
7 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 7 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
9 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;  
10 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
11 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 10 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  11 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
  12 +import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils;
12 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 13 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
  14 +import jakarta.annotation.Resource;
13 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
14 import org.aspectj.lang.JoinPoint; 16 import org.aspectj.lang.JoinPoint;
15 import org.aspectj.lang.annotation.Aspect; 17 import org.aspectj.lang.annotation.Aspect;
16 import org.aspectj.lang.annotation.Before; 18 import org.aspectj.lang.annotation.Before;
17 import org.springframework.stereotype.Component; 19 import org.springframework.stereotype.Component;
18 20
19 -import jakarta.annotation.Resource;  
20 import java.util.ArrayList; 21 import java.util.ArrayList;
21 import java.util.List; 22 import java.util.List;
22 import java.util.Map; 23 import java.util.Map;
23 24
24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
25 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 27 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
26 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED; 28 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED;
27 -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_MODEL_NOT_EXISTS;  
28 29
29 -// TODO 这个包,改成 permission,然后搞 config 和 core 包,这个类在 core 包里;目的是:framework 最好分类下  
30 /** 30 /**
31 * Crm 数据权限校验 AOP 切面 31 * Crm 数据权限校验 AOP 切面
32 * 32 *
@@ -42,10 +42,6 @@ public class CrmPermissionAspect { @@ -42,10 +42,6 @@ public class CrmPermissionAspect {
42 42
43 @Before("@annotation(crmPermission)") 43 @Before("@annotation(crmPermission)")
44 public void doBefore(JoinPoint joinPoint, CrmPermission crmPermission) { 44 public void doBefore(JoinPoint joinPoint, CrmPermission crmPermission) {
45 - // TODO 芋艿:临时,方便大家调试  
46 - //if (true) {  
47 - // return;  
48 - //}  
49 // 获取相关属性值 45 // 获取相关属性值
50 Map<String, Object> expressionValues = parseExpressions(joinPoint, crmPermission); 46 Map<String, Object> expressionValues = parseExpressions(joinPoint, crmPermission);
51 Integer bizType = StrUtil.isEmpty(crmPermission.bizTypeValue()) ? 47 Integer bizType = StrUtil.isEmpty(crmPermission.bizTypeValue()) ?
@@ -53,16 +49,28 @@ public class CrmPermissionAspect { @@ -53,16 +49,28 @@ public class CrmPermissionAspect {
53 Long bizId = (Long) expressionValues.get(crmPermission.bizId()); // 模块数据编号 49 Long bizId = (Long) expressionValues.get(crmPermission.bizId()); // 模块数据编号
54 Integer permissionLevel = crmPermission.level().getLevel(); // 需要的权限级别 50 Integer permissionLevel = crmPermission.level().getLevel(); // 需要的权限级别
55 51
56 - // TODO 如果是超级管理员则直接通过  
57 - //if (superAdmin){  
58 - // return;  
59 - //}  
60 -  
61 - // 1. 获取数据权限 52 + // 1.1 如果是超级管理员则直接通过
  53 + if (CrmPermissionUtils.validateAdminUser()) {
  54 + return;
  55 + }
  56 + // 1.2 获取数据权限
62 List<CrmPermissionDO> bizPermissions = crmPermissionService.getPermissionListByBiz(bizType, bizId); 57 List<CrmPermissionDO> bizPermissions = crmPermissionService.getPermissionListByBiz(bizType, bizId);
63 - if (CollUtil.isEmpty(bizPermissions)) { // 数据权限不存存那么数据也不存在  
64 - throw exception(CRM_PERMISSION_MODEL_NOT_EXISTS, CrmBizTypeEnum.getNameByType(bizType)); 58 + if (CollUtil.isEmpty(bizPermissions)) { // 没有数据权限的情况
  59 + // 公海数据如果没有团队成员大家也因该有读权限才对
  60 + if (CrmPermissionLevelEnum.isRead(permissionLevel)) {
  61 + return;
  62 + }
  63 +
  64 + // 没有数据权限的情况下超出了读权限直接报错,避免后面校验空指针
  65 + throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType));
  66 + } else { // 有数据权限但是没有负责人的情况
  67 + if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel()))) {
  68 + if (CrmPermissionLevelEnum.isRead(permissionLevel)) {
  69 + return;
  70 + }
  71 + }
65 } 72 }
  73 +
66 // 2.1 情况一:如果自己是负责人,则默认有所有权限 74 // 2.1 情况一:如果自己是负责人,则默认有所有权限
67 CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), getUserId())); 75 CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), getUserId()));
68 if (userPermission != null) { 76 if (userPermission != null) {
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.permission.core;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.permission.core.util;
  2 +
  3 +import cn.hutool.extra.spring.SpringUtil;
  4 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
  5 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum;
  6 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  7 +
  8 +/**
  9 + * 数据权限工具类
  10 + *
  11 + * @author HUIHUI
  12 + */
  13 +public class CrmPermissionUtils {
  14 +
  15 + /**
  16 + * 校验用户是否是 CRM 管理员
  17 + *
  18 + * @return 是/否
  19 + */
  20 + public static boolean validateAdminUser() {
  21 + return SingletonManager.getPermissionApi().hasAnyRoles(getUserId(), CrmPermissionRoleCodeEnum.CRM_ADMIN.getCode());
  22 + }
  23 +
  24 + /**
  25 + * 获得用户编号
  26 + *
  27 + * @return 用户编号
  28 + */
  29 + private static Long getUserId() {
  30 + return WebFrameworkUtils.getLoginUserId();
  31 + }
  32 +
  33 + /**
  34 + * 静态内部类实现单例获取
  35 + *
  36 + * @author HUIHUI
  37 + */
  38 + private static class SingletonManager {
  39 +
  40 + private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class);
  41 +
  42 + public static PermissionApi getPermissionApi() {
  43 + return PERMISSION_API;
  44 + }
  45 +
  46 + }
  47 +
  48 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.permission;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
@@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
12 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper; 12 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
15 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 15 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
16 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 16 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
17 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 17 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
18 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 18 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
@@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
12 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; 12 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
15 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 15 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
16 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 16 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
17 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 17 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
18 import jakarta.annotation.Resource; 18 import jakarta.annotation.Resource;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
@@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; @@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
9 import cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactMapper; 9 import cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactMapper;
10 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 10 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
11 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 11 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
12 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 12 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
13 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 13 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
14 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 14 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
15 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 15 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contactbusinesslink/CrmContactBusinessLinkServiceImpl.java
@@ -14,7 +14,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink.CrmContact @@ -14,7 +14,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink.CrmContact
14 import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessLinkMapper; 14 import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessLinkMapper;
15 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 15 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
16 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 16 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
17 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 17 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
18 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; 18 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
19 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
20 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
12 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper; 12 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
15 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 15 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
16 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 16 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
17 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 17 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
18 import jakarta.annotation.Resource; 18 import jakarta.annotation.Resource;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.service.customer; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.service.customer;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.framework.operatelogv2.core.enums.OperateLogV2Constants;
6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
@@ -12,7 +13,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; @@ -12,7 +13,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
12 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; 13 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 14 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 15 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
15 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 16 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
16 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 17 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
17 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 18 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 19 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -52,6 +53,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -52,6 +53,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
52 53
53 @Override 54 @Override
54 @Transactional(rollbackFor = Exception.class) 55 @Transactional(rollbackFor = Exception.class)
  56 + @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#customerId}}", success = "创建了客户")
55 public Long createCustomer(CrmCustomerCreateReqVO createReqVO, Long userId) { 57 public Long createCustomer(CrmCustomerCreateReqVO createReqVO, Long userId) {
56 // 插入 58 // 插入
57 CrmCustomerDO customer = CrmCustomerConvert.INSTANCE.convert(createReqVO); 59 CrmCustomerDO customer = CrmCustomerConvert.INSTANCE.convert(createReqVO);
@@ -60,26 +62,34 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -60,26 +62,34 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
60 // 创建数据权限 62 // 创建数据权限
61 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) 63 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
62 .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 64 .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
  65 +
  66 + // 添加日志上下文所需
  67 + LogRecordContext.putVariable("customerId", customer.getId());
63 return customer.getId(); 68 return customer.getId();
64 } 69 }
65 70
66 @Override 71 @Override
67 @Transactional(rollbackFor = Exception.class) 72 @Transactional(rollbackFor = Exception.class)
68 - @LogRecord(success = "更新了客户{_DIFF{#updateReqVO}}", type = CRM_CUSTOMER, subType = "更新客户", bizNo = "{{#updateReqVO.id}}") 73 + @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#updateReqVO.id}}", success = "更新了客户{_DIFF{#updateReqVO}}")
69 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 74 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
70 public void updateCustomer(CrmCustomerUpdateReqVO updateReqVO) { 75 public void updateCustomer(CrmCustomerUpdateReqVO updateReqVO) {
71 // 校验存在 76 // 校验存在
72 CrmCustomerDO oldCustomerDO = validateCustomerExists(updateReqVO.getId()); 77 CrmCustomerDO oldCustomerDO = validateCustomerExists(updateReqVO.getId());
73 78
74 - // __DIFF 函数传递了一个参数,传递的参数是修改之后的对象,这种方式需要在方法内部向 LogRecordContext 中 put 一个变量,代表是之前的对象,这个对象可以是null  
75 - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomerDO, CrmCustomerUpdateReqVO.class));  
76 // 更新 79 // 更新
77 CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO); 80 CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO);
78 customerMapper.updateById(updateObj); 81 customerMapper.updateById(updateObj);
  82 +
  83 + // __DIFF 函数传递了一个参数,传递的参数是修改之后的对象,这种方式需要在方法内部向 LogRecordContext 中 put 一个变量,代表是之前的对象,这个对象可以是null
  84 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomerDO, CrmCustomerUpdateReqVO.class));
  85 + HashMap<String, Object> extra = new HashMap<>();
  86 + extra.put("tips", "随便记录一点啦");
  87 + LogRecordContext.putVariable(OperateLogV2Constants.EXTRA_KEY, extra);
79 } 88 }
80 89
81 @Override 90 @Override
82 @Transactional(rollbackFor = Exception.class) 91 @Transactional(rollbackFor = Exception.class)
  92 + @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#id}}", success = "删除了客户")
83 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 93 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
84 public void deleteCustomer(Long id) { 94 public void deleteCustomer(Long id) {
85 // 校验存在 95 // 校验存在
@@ -125,27 +135,17 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -125,27 +135,17 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
125 */ 135 */
126 @Override 136 @Override
127 public void validateCustomer(Long customerId) { 137 public void validateCustomer(Long customerId) {
128 - // 校验客户是否存在  
129 - if (customerId == null) {  
130 - throw exception(CUSTOMER_NOT_EXISTS);  
131 - }  
132 - CrmCustomerDO customer = customerMapper.selectById(customerId);  
133 - if (Objects.isNull(customer)) {  
134 - throw exception(CUSTOMER_NOT_EXISTS);  
135 - } 138 + validateCustomerExists(customerId);
136 } 139 }
137 140
138 @Override 141 @Override
139 @Transactional(rollbackFor = Exception.class) 142 @Transactional(rollbackFor = Exception.class)
140 - // TODO @puhui999:@LogRecord(type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}", success = TRANSFER_CUSTOMER_LOG_SUCCESS)  
141 - @LogRecord(success = TRANSFER_CUSTOMER_LOG_SUCCESS, type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}") 143 + @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#reqVO.id}}", success = TRANSFER_CUSTOMER_LOG_SUCCESS)
142 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) 144 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
143 public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) { 145 public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {
144 // 1. 校验客户是否存在 146 // 1. 校验客户是否存在
145 - validateCustomer(reqVO.getId());  
146 - // 添加 crmCustomer 到日志上下文 TODO 日志记录放在 service 里是因为已经过了权限校验查询时不用走两次校验  
147 - // TODO @puhui999:customer 不用查询,从 1. 拿到哈;然后 put这个动作,可以放到 3.;这样逻辑结构就是,校验、逻辑、日志,更加清晰  
148 - LogRecordContext.putVariable("crmCustomer", customerMapper.selectById(reqVO.getId())); 147 + CrmCustomerDO customerDO = validateCustomerExists(reqVO.getId());
  148 +
149 // 2.1 数据权限转移 149 // 2.1 数据权限转移
150 crmPermissionService.transferPermission( 150 crmPermissionService.transferPermission(
151 CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())); 151 CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()));
@@ -153,9 +153,11 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -153,9 +153,11 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
153 customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 153 customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
154 154
155 // 3. TODO 记录转移日志 155 // 3. TODO 记录转移日志
  156 + LogRecordContext.putVariable("crmCustomer", customerDO);
156 } 157 }
157 158
158 @Override 159 @Override
  160 + @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#updateReqVO.id}}", success = "锁定了客户")
159 public void lockCustomer(CrmCustomerUpdateReqVO updateReqVO) { 161 public void lockCustomer(CrmCustomerUpdateReqVO updateReqVO) {
160 // 校验存在 162 // 校验存在
161 validateCustomerExists(updateReqVO.getId()); 163 validateCustomerExists(updateReqVO.getId());
@@ -169,6 +171,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -169,6 +171,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
169 171
170 @Override 172 @Override
171 @Transactional(rollbackFor = Exception.class) 173 @Transactional(rollbackFor = Exception.class)
  174 + @LogRecord(type = CRM_CUSTOMER, bizNo = "{{#id}}", success = "将客户放入了公海")
172 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 175 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
173 public void putCustomerPool(Long id) { 176 public void putCustomerPool(Long id) {
174 // 1. 校验存在 177 // 1. 校验存在
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
8 import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmPermissionMapper; 8 import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmPermissionMapper;
9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
10 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 10 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  11 +import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils;
11 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 12 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
12 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; 13 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
13 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 14 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -89,8 +90,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { @@ -89,8 +90,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
89 CrmPermissionDO oldPermission = crmPermissionMapper.selectByBizTypeAndBizIdByUserId( 90 CrmPermissionDO oldPermission = crmPermissionMapper.selectByBizTypeAndBizIdByUserId(
90 transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId()); 91 transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId());
91 String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType()); 92 String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType());
92 - // TODO 校验是否为超级管理员 || 1  
93 - if (oldPermission == null || !isOwner(oldPermission.getLevel())) { 93 + if (oldPermission == null || !isOwner(oldPermission.getLevel()) || !CrmPermissionUtils.validateAdminUser()) {
94 throw exception(CRM_PERMISSION_DENIED, bizTypeName); 94 throw exception(CRM_PERMISSION_DENIED, bizTypeName);
95 } 95 }
96 // 1.1 校验转移对象是否已经是该负责人 96 // 1.1 校验转移对象是否已经是该负责人
@@ -138,7 +138,6 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { @@ -138,7 +138,6 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
138 } 138 }
139 139
140 @Override 140 @Override
141 - @Transactional(rollbackFor = Exception.class) // TODO @puhui999:这里不用加的,就一个操作哈;  
142 public void deletePermission(Integer bizType, Long bizId) { 141 public void deletePermission(Integer bizType, Long bizId) {
143 int deletedCount = crmPermissionMapper.deletePermission(bizType, bizId); 142 int deletedCount = crmPermissionMapper.deletePermission(bizType, bizId);
144 if (deletedCount == 0) { 143 if (deletedCount == 0) {
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
@@ -15,7 +15,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO @@ -15,7 +15,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO
15 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper; 15 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper;
16 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 16 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
17 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 17 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
18 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 18 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
19 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 19 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
20 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 20 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
21 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 21 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
@@ -18,7 +18,7 @@ import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper; @@ -18,7 +18,7 @@ import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;
18 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; 18 import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
19 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 19 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
20 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 20 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
21 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 21 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
22 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 22 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
23 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 23 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
24 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 24 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java
@@ -7,9 +7,11 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; @@ -7,9 +7,11 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
7 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 7 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
8 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; 8 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
9 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 9 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  10 +import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils;
10 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 11 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
11 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 12 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
12 import com.baomidou.mybatisplus.core.toolkit.support.SFunction; 13 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
  14 +import com.github.yulichang.autoconfigure.MybatisPlusJoinProperties;
13 import com.github.yulichang.wrapper.MPJLambdaWrapper; 15 import com.github.yulichang.wrapper.MPJLambdaWrapper;
14 16
15 import java.util.Collection; 17 import java.util.Collection;
@@ -33,46 +35,41 @@ public class CrmQueryWrapperUtils { @@ -33,46 +35,41 @@ public class CrmQueryWrapperUtils {
33 * @param userId 用户编号 35 * @param userId 用户编号
34 * @param sceneType 场景类型 36 * @param sceneType 场景类型
35 * @param pool 公海 37 * @param pool 公海
36 - * @return 是否 (是:需要执行查询,否:不需要查询调用方法直接返回空)  
37 */ 38 */
38 - // TODO @puhui999:bizId 直接传递会不会简单点 回复:还是需要 SFunction 因为分页连表时不知道 bizId 是多少;是不是把 bizId 传入就好啦?  
39 - public static <T extends MPJLambdaWrapper<?>, S> boolean appendPermissionCondition(T query, Integer bizType, SFunction<S, ?> bizId,  
40 - Long userId, Integer sceneType, Boolean pool) { 39 + public static <T extends MPJLambdaWrapper<?>, S> void appendPermissionCondition(T query, Integer bizType, SFunction<S, ?> bizId,
  40 + Long userId, Integer sceneType, Boolean pool) {
  41 + final String ownerUserIdField = SingletonManager.getMybatisPlusJoinProperties().getTableAlias() + ".owner_user_id";
41 // 1. 构建数据权限连表条件 42 // 1. 构建数据权限连表条件
42 - if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE)) { // 管理员不需要数据权限 43 + if (ObjUtil.notEqual(CrmPermissionUtils.validateAdminUser(), Boolean.TRUE) && ObjUtil.notEqual(pool, Boolean.TRUE)) { // 管理员,公海不需要数据权限
43 query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType) 44 query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType)
44 - .eq(CrmPermissionDO::getBizId, bizId) 45 + .eq(CrmPermissionDO::getBizId, bizId) // 只能使用 SFunction 如果传 id 解析出来的 sql 不对
45 .eq(CrmPermissionDO::getUserId, userId)); 46 .eq(CrmPermissionDO::getUserId, userId));
46 } 47 }
47 // 2.1 场景一:我负责的数据 48 // 2.1 场景一:我负责的数据
48 if (CrmSceneTypeEnum.isOwner(sceneType)) { 49 if (CrmSceneTypeEnum.isOwner(sceneType)) {
49 - query.eq("owner_user_id", userId); 50 + query.eq(ownerUserIdField, userId);
50 } 51 }
51 // 2.2 场景二:我参与的数据 52 // 2.2 场景二:我参与的数据
52 if (CrmSceneTypeEnum.isInvolved(sceneType)) { 53 if (CrmSceneTypeEnum.isInvolved(sceneType)) {
53 - query.ne("owner_user_id", userId)  
54 - // TODO @puhui999:IN 是不是更合适哈;  
55 - .and(q -> q.eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel())  
56 - .or()  
57 - .eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.WRITE.getLevel())); 54 + query.ne(ownerUserIdField, userId)
  55 + .in(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel(), CrmPermissionLevelEnum.WRITE.getLevel());
58 } 56 }
59 // 2.3 场景三:下属负责的数据 57 // 2.3 场景三:下属负责的数据
60 if (CrmSceneTypeEnum.isSubordinate(sceneType)) { 58 if (CrmSceneTypeEnum.isSubordinate(sceneType)) {
61 - // TODO @puhui999:要不如果没有下属,拼一个 owner_user_id in null,不返回结果就好啦;  
62 - List<AdminUserRespDTO> subordinateUsers = getAdminUserApi().getUserListBySubordinate(userId); 59 + List<AdminUserRespDTO> subordinateUsers = SingletonManager.getAdminUserApi().getUserListBySubordinate(userId);
63 if (CollUtil.isEmpty(subordinateUsers)) { 60 if (CollUtil.isEmpty(subordinateUsers)) {
64 - return false; 61 + query.eq(ownerUserIdField, -1); // 不返回任何结果
  62 + } else {
  63 + query.in(ownerUserIdField, convertSet(subordinateUsers, AdminUserRespDTO::getId));
65 } 64 }
66 - query.in("owner_user_id", convertSet(subordinateUsers, AdminUserRespDTO::getId));  
67 } 65 }
68 66
69 // 3. 拼接公海的查询条件 67 // 3. 拼接公海的查询条件
70 if (ObjUtil.equal(pool, Boolean.TRUE)) { // 情况一:公海 68 if (ObjUtil.equal(pool, Boolean.TRUE)) { // 情况一:公海
71 - query.isNull("owner_user_id"); 69 + query.isNull(ownerUserIdField);
72 } else { // 情况二:不是公海 70 } else { // 情况二:不是公海
73 - query.isNotNull("owner_user_id"); 71 + query.isNotNull(ownerUserIdField);
74 } 72 }
75 - return true;  
76 } 73 }
77 74
78 /** 75 /**
@@ -84,7 +81,7 @@ public class CrmQueryWrapperUtils { @@ -84,7 +81,7 @@ public class CrmQueryWrapperUtils {
84 * @param userId 用户编号 81 * @param userId 用户编号
85 */ 82 */
86 public static <T extends MPJLambdaWrapper<?>> void appendPermissionCondition(T query, Integer bizType, Collection<Long> bizIds, Long userId) { 83 public static <T extends MPJLambdaWrapper<?>> void appendPermissionCondition(T query, Integer bizType, Collection<Long> bizIds, Long userId) {
87 - if (ObjUtil.equal(validateAdminUser(userId), Boolean.TRUE)) {// 管理员不需要数据权限 84 + if (ObjUtil.equal(CrmPermissionUtils.validateAdminUser(), Boolean.TRUE)) {// 管理员不需要数据权限
88 return; 85 return;
89 } 86 }
90 87
@@ -93,38 +90,23 @@ public class CrmQueryWrapperUtils { @@ -93,38 +90,23 @@ public class CrmQueryWrapperUtils {
93 .in(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId)); 90 .in(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId));
94 } 91 }
95 92
96 - private static AdminUserApi getAdminUserApi() {  
97 - return AdminUserApiHolder.ADMIN_USER_API;  
98 - }  
99 -  
100 /** 93 /**
101 - * 校验用户是否是管理员  
102 - *  
103 - * @param userId 用户编号  
104 - * @return 是/否  
105 - */  
106 - private static boolean validateAdminUser(Long userId) {  
107 - // TODO 查询权限配置表用户的角色信息  
108 - // TODO @puhui999:查询用户的角色;CRM_ADMIN("crm_admin", "CRM 管理员"),  
109 - //CrmPermissionConfig permissionConfig = crmPermissionConfigService.getPermissionConfigByUserId(userId);  
110 - //if (permissionConfig == null) {  
111 - // return false;  
112 - //}  
113 - //// 校验是否为管理员  
114 - //if (permissionConfig.getIsAdmin()){  
115 - // return true;  
116 - //}  
117 - return false;  
118 - }  
119 -  
120 - /**  
121 - * 静态内部类实现 AdminUserApi 单例获取 94 + * 静态内部类实现单例获取
122 * 95 *
123 * @author HUIHUI 96 * @author HUIHUI
124 */ 97 */
125 - private static class AdminUserApiHolder { 98 + private static class SingletonManager {
126 99
127 private static final AdminUserApi ADMIN_USER_API = SpringUtil.getBean(AdminUserApi.class); 100 private static final AdminUserApi ADMIN_USER_API = SpringUtil.getBean(AdminUserApi.class);
  101 + private static final MybatisPlusJoinProperties MYBATIS_PLUS_JOIN_PROPERTIES = SpringUtil.getBean(MybatisPlusJoinProperties.class);
  102 +
  103 + public static AdminUserApi getAdminUserApi() {
  104 + return ADMIN_USER_API;
  105 + }
  106 +
  107 + public static MybatisPlusJoinProperties getMybatisPlusJoinProperties() {
  108 + return MYBATIS_PLUS_JOIN_PROPERTIES;
  109 + }
128 110
129 } 111 }
130 112
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java
@@ -7,26 +7,33 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -7,26 +7,33 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 import cn.iocoder.yudao.module.promotion.controller.app.activity.vo.AppActivityRespVO; 7 import cn.iocoder.yudao.module.promotion.controller.app.activity.vo.AppActivityRespVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
  10 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
  11 +import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
  12 +import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO; 13 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
  14 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
11 import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; 15 import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
12 import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService; 16 import cn.iocoder.yudao.module.promotion.service.bargain.BargainActivityService;
13 import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService; 17 import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
  18 +import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
  19 +import cn.iocoder.yudao.module.promotion.service.reward.RewardActivityService;
14 import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService; 20 import cn.iocoder.yudao.module.promotion.service.seckill.SeckillActivityService;
15 import io.swagger.v3.oas.annotations.Operation; 21 import io.swagger.v3.oas.annotations.Operation;
16 import io.swagger.v3.oas.annotations.Parameter; 22 import io.swagger.v3.oas.annotations.Parameter;
17 import io.swagger.v3.oas.annotations.tags.Tag; 23 import io.swagger.v3.oas.annotations.tags.Tag;
  24 +import jakarta.annotation.Resource;
18 import org.springframework.validation.annotation.Validated; 25 import org.springframework.validation.annotation.Validated;
19 import org.springframework.web.bind.annotation.GetMapping; 26 import org.springframework.web.bind.annotation.GetMapping;
20 import org.springframework.web.bind.annotation.RequestMapping; 27 import org.springframework.web.bind.annotation.RequestMapping;
21 import org.springframework.web.bind.annotation.RequestParam; 28 import org.springframework.web.bind.annotation.RequestParam;
22 import org.springframework.web.bind.annotation.RestController; 29 import org.springframework.web.bind.annotation.RestController;
23 30
24 -import jakarta.annotation.Resource;  
25 import java.time.LocalDateTime; 31 import java.time.LocalDateTime;
26 import java.util.*; 32 import java.util.*;
  33 +import java.util.stream.Collectors;
27 34
28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 35 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
29 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; 36 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
30 37
31 @Tag(name = "用户 APP - 营销活动") // 用于提供跨多个活动的 HTTP 接口 38 @Tag(name = "用户 APP - 营销活动") // 用于提供跨多个活动的 HTTP 接口
32 @RestController 39 @RestController
@@ -40,6 +47,10 @@ public class AppActivityController { @@ -40,6 +47,10 @@ public class AppActivityController {
40 private SeckillActivityService seckillActivityService; 47 private SeckillActivityService seckillActivityService;
41 @Resource 48 @Resource
42 private BargainActivityService bargainActivityService; 49 private BargainActivityService bargainActivityService;
  50 + @Resource
  51 + private DiscountActivityService discountActivityService;
  52 + @Resource
  53 + private RewardActivityService rewardActivityService;
43 54
44 @GetMapping("/list-by-spu-id") 55 @GetMapping("/list-by-spu-id")
45 @Operation(summary = "获得单个商品,近期参与的每个活动") 56 @Operation(summary = "获得单个商品,近期参与的每个活动")
@@ -64,45 +75,105 @@ public class AppActivityController { @@ -64,45 +75,105 @@ public class AppActivityController {
64 if (CollUtil.isEmpty(spuIds)) { 75 if (CollUtil.isEmpty(spuIds)) {
65 return new ArrayList<>(); 76 return new ArrayList<>();
66 } 77 }
  78 +
67 LocalDateTime now = LocalDateTime.now(); 79 LocalDateTime now = LocalDateTime.now();
  80 + // 获取开启的且开始的且没有结束的活动
68 List<AppActivityRespVO> activityList = new ArrayList<>(); 81 List<AppActivityRespVO> activityList = new ArrayList<>();
  82 + // 1. 拼团活动
  83 + getCombinationActivities(spuIds, now, activityList);
  84 + // 2. 秒杀活动
  85 + getSeckillActivities(spuIds, now, activityList);
  86 + // 3. 砍价活动
  87 + getBargainActivities(spuIds, now, activityList);
  88 + // 4. 限时折扣活动
  89 + getDiscountActivities(spuIds, now, activityList);
  90 + // 5. 满减送活动
  91 + getRewardActivities(spuIds, now, activityList);
  92 + return activityList;
  93 + }
69 94
70 - // 1. 拼团活动 - 获取开启的且开始的且没有结束的活动 95 + private void getCombinationActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
71 List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatusAndDateTimeLt( 96 List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(
72 spuIds, CommonStatusEnum.ENABLE.getStatus(), now); 97 spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
73 - if (CollUtil.isNotEmpty(combinationActivities)) {  
74 - combinationActivities.forEach(item -> {  
75 - activityList.add(new AppActivityRespVO().setId(item.getId())  
76 - .setType(PromotionTypeEnum.COMBINATION_ACTIVITY.getType()).setName(item.getName())  
77 - .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));  
78 - }); 98 + if (CollUtil.isEmpty(combinationActivities)) {
  99 + return;
79 } 100 }
80 101
81 - // 2. 秒杀活动 - 获取开启的且开始的且没有结束的活动 102 + combinationActivities.forEach(item -> {
  103 + activityList.add(new AppActivityRespVO().setId(item.getId())
  104 + .setType(PromotionTypeEnum.COMBINATION_ACTIVITY.getType()).setName(item.getName())
  105 + .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
  106 + });
  107 + }
  108 +
  109 + private void getSeckillActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
82 List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatusAndDateTimeLt( 110 List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(
83 spuIds, CommonStatusEnum.ENABLE.getStatus(), now); 111 spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
84 - if (CollUtil.isNotEmpty(seckillActivities)) {  
85 - seckillActivities.forEach(item -> {  
86 - activityList.add(new AppActivityRespVO().setId(item.getId())  
87 - .setType(PromotionTypeEnum.SECKILL_ACTIVITY.getType()).setName(item.getName())  
88 - .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));  
89 - }); 112 + if (CollUtil.isEmpty(seckillActivities)) {
  113 + return;
90 } 114 }
91 115
92 - // 3. 砍价活动 - 获取开启的且开始的且没有结束的活动 116 + seckillActivities.forEach(item -> {
  117 + activityList.add(new AppActivityRespVO().setId(item.getId())
  118 + .setType(PromotionTypeEnum.SECKILL_ACTIVITY.getType()).setName(item.getName())
  119 + .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
  120 + });
  121 + }
  122 +
  123 + private void getBargainActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
93 List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatusAndDateTimeLt( 124 List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatusAndDateTimeLt(
94 spuIds, CommonStatusEnum.ENABLE.getStatus(), now); 125 spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
95 if (CollUtil.isNotEmpty(bargainActivities)) { 126 if (CollUtil.isNotEmpty(bargainActivities)) {
96 - bargainActivities.forEach(item -> {  
97 - activityList.add(new AppActivityRespVO().setId(item.getId())  
98 - .setType(PromotionTypeEnum.BARGAIN_ACTIVITY.getType()).setName(item.getName())  
99 - .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));  
100 - }); 127 + return;
101 } 128 }
102 129
103 - // TODO 芋艿:满减送活动  
104 - // TODO 芋艿:限时折扣活动  
105 - return activityList; 130 + bargainActivities.forEach(item -> {
  131 + activityList.add(new AppActivityRespVO().setId(item.getId())
  132 + .setType(PromotionTypeEnum.BARGAIN_ACTIVITY.getType()).setName(item.getName())
  133 + .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
  134 + });
  135 + }
  136 +
  137 + private void getDiscountActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
  138 + List<DiscountActivityDO> discountActivities = discountActivityService.getDiscountActivityBySpuIdsAndStatusAndDateTimeLt(
  139 + spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
  140 + if (CollUtil.isEmpty(discountActivities)) {
  141 + return;
  142 + }
  143 +
  144 + List<DiscountProductDO> products = discountActivityService.getDiscountProductsByActivityId(
  145 + convertSet(discountActivities, DiscountActivityDO::getId));
  146 + Map<Long, Long> productMap = convertMap(products, DiscountProductDO::getActivityId, DiscountProductDO::getSpuId);
  147 + discountActivities.forEach(item -> {
  148 + activityList.add(new AppActivityRespVO().setId(item.getId())
  149 + .setType(PromotionTypeEnum.DISCOUNT_ACTIVITY.getType()).setName(item.getName())
  150 + .setSpuId(productMap.get(item.getId())).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
  151 + });
  152 + }
  153 +
  154 + private void getRewardActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
  155 + List<RewardActivityDO> rewardActivityList = rewardActivityService.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(
  156 + spuIds, PromotionActivityStatusEnum.RUN.getStatus(), now);
  157 + if (CollUtil.isEmpty(rewardActivityList)) {
  158 + return;
  159 + }
  160 +
  161 + Map<Long, Optional<RewardActivityDO>> spuIdAndActivityMap = spuIds.stream()
  162 + .collect(Collectors.toMap(
  163 + spuId -> spuId,
  164 + spuId -> rewardActivityList.stream()
  165 + .filter(activity -> activity.getProductSpuIds().contains(spuId))
  166 + .max(Comparator.comparing(RewardActivityDO::getCreateTime))));
  167 + for (Long supId : spuIdAndActivityMap.keySet()) {
  168 + if (spuIdAndActivityMap.get(supId).isEmpty()) {
  169 + continue;
  170 + }
  171 +
  172 + RewardActivityDO rewardActivityDO = spuIdAndActivityMap.get(supId).get();
  173 + activityList.add(new AppActivityRespVO().setId(rewardActivityDO.getId())
  174 + .setType(PromotionTypeEnum.REWARD_ACTIVITY.getType()).setName(rewardActivityDO.getName())
  175 + .setSpuId(supId).setStartTime(rewardActivityDO.getStartTime()).setEndTime(rewardActivityDO.getEndTime()));
  176 + }
106 } 177 }
107 178
108 } 179 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountActivityMapper.java
@@ -7,9 +7,9 @@ import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountAc @@ -7,9 +7,9 @@ import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountAc
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
  10 +import java.time.LocalDateTime;
10 import java.util.Collection; 11 import java.util.Collection;
11 import java.util.List; 12 import java.util.List;
12 -import java.util.Set;  
13 13
14 /** 14 /**
15 * 限时折扣活动 Mapper 15 * 限时折扣活动 Mapper
@@ -27,4 +27,20 @@ public interface DiscountActivityMapper extends BaseMapperX&lt;DiscountActivityDO&gt; @@ -27,4 +27,20 @@ public interface DiscountActivityMapper extends BaseMapperX&lt;DiscountActivityDO&gt;
27 .orderByDesc(DiscountActivityDO::getId)); 27 .orderByDesc(DiscountActivityDO::getId));
28 } 28 }
29 29
  30 + /**
  31 + * 获取指定活动编号的活动列表且
  32 + * 开始时间和结束时间小于给定时间 dateTime 的活动列表
  33 + *
  34 + * @param ids 活动编号
  35 + * @param dateTime 指定日期
  36 + * @return 活动列表
  37 + */
  38 + default List<DiscountActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {
  39 + return selectList(new LambdaQueryWrapperX<DiscountActivityDO>()
  40 + .in(DiscountActivityDO::getId, ids)
  41 + .lt(DiscountActivityDO::getStartTime, dateTime)
  42 + .gt(DiscountActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
  43 + .orderByDesc(DiscountActivityDO::getCreateTime));
  44 + }
  45 +
30 } 46 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/discount/DiscountProductMapper.java
@@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.promotion.dal.mysql.discount; @@ -2,11 +2,13 @@ package cn.iocoder.yudao.module.promotion.dal.mysql.discount;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO; 4 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
  5 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
5 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
6 import org.apache.ibatis.annotations.Param; 7 import org.apache.ibatis.annotations.Param;
7 8
8 import java.util.Collection; 9 import java.util.Collection;
9 import java.util.List; 10 import java.util.List;
  11 +import java.util.Map;
10 12
11 /** 13 /**
12 * 限时折扣商城 Mapper 14 * 限时折扣商城 Mapper
@@ -30,4 +32,20 @@ public interface DiscountProductMapper extends BaseMapperX&lt;DiscountProductDO&gt; { @@ -30,4 +32,20 @@ public interface DiscountProductMapper extends BaseMapperX&lt;DiscountProductDO&gt; {
30 32
31 // TODO @zhangshuai:逻辑里,尽量避免写 join 语句哈,你可以看看这个查询,有什么办法优化?目前的一个思路,是分 2 次查询,性能也是 ok 的 33 // TODO @zhangshuai:逻辑里,尽量避免写 join 语句哈,你可以看看这个查询,有什么办法优化?目前的一个思路,是分 2 次查询,性能也是 ok 的
32 List<DiscountProductDO> getMatchDiscountProductList(@Param("skuIds") Collection<Long> skuIds); 34 List<DiscountProductDO> getMatchDiscountProductList(@Param("skuIds") Collection<Long> skuIds);
  35 +
  36 + /**
  37 + * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
  38 + *
  39 + * @param spuIds spu 编号
  40 + * @param status 状态
  41 + * @return 包含 spuId 和 activityId 的 map 对象列表
  42 + */
  43 + default List<Map<String, Object>> selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
  44 + return selectMaps(new QueryWrapper<DiscountProductDO>()
  45 + .select("spu_id AS spuId, MAX(DISTINCT(activity_id)) AS activityId")
  46 + .in("spu_id", spuIds)
  47 + .eq("activity_status", status)
  48 + .groupBy("spu_id"));
  49 + }
  50 +
33 } 51 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.reward; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.reward;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
  9 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
8 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
9 11
  12 +import java.time.LocalDateTime;
10 import java.util.Collection; 13 import java.util.Collection;
11 import java.util.List; 14 import java.util.List;
  15 +import java.util.function.Function;
  16 +import java.util.stream.Collectors;
12 17
13 /** 18 /**
14 * 满减送活动 Mapper 19 * 满减送活动 Mapper
@@ -35,4 +40,30 @@ public interface RewardActivityMapper extends BaseMapperX&lt;RewardActivityDO&gt; { @@ -35,4 +40,30 @@ public interface RewardActivityMapper extends BaseMapperX&lt;RewardActivityDO&gt; {
35 .eq(RewardActivityDO::getStatus, status)); 40 .eq(RewardActivityDO::getStatus, status));
36 } 41 }
37 42
  43 + default List<RewardActivityDO> selectListBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
  44 + Function<Collection<Long>, String> productScopeValuesFindInSetFunc = ids -> ids.stream()
  45 + .map(id -> StrUtil.format("FIND_IN_SET({}, product_spu_ids) ", id))
  46 + .collect(Collectors.joining(" OR "));
  47 + return selectList(new QueryWrapper<RewardActivityDO>()
  48 + .eq("status", status)
  49 + .apply(productScopeValuesFindInSetFunc.apply(spuIds)));
  50 + }
  51 +
  52 + /**
  53 + * 获取指定活动编号的活动列表且
  54 + * 开始时间和结束时间小于给定时间 dateTime 的活动列表
  55 + *
  56 + * @param ids 活动编号
  57 + * @param dateTime 指定日期
  58 + * @return 活动列表
  59 + */
  60 + default List<RewardActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {
  61 + return selectList(new LambdaQueryWrapperX<RewardActivityDO>()
  62 + .in(RewardActivityDO::getId, ids)
  63 + .lt(RewardActivityDO::getStartTime, dateTime)
  64 + .gt(RewardActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
  65 + .orderByDesc(RewardActivityDO::getCreateTime)
  66 + );
  67 + }
  68 +
38 } 69 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java
@@ -6,8 +6,9 @@ import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountAc @@ -6,8 +6,9 @@ import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountAc
6 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
9 -  
10 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
  10 +
  11 +import java.time.LocalDateTime;
11 import java.util.Collection; 12 import java.util.Collection;
12 import java.util.List; 13 import java.util.List;
13 14
@@ -89,4 +90,14 @@ public interface DiscountActivityService { @@ -89,4 +90,14 @@ public interface DiscountActivityService {
89 */ 90 */
90 List<DiscountProductDO> getDiscountProductsByActivityId(Collection<Long> activityIds); 91 List<DiscountProductDO> getDiscountProductsByActivityId(Collection<Long> activityIds);
91 92
  93 + /**
  94 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
  95 + *
  96 + * @param spuIds spu 编号
  97 + * @param status 状态
  98 + * @param dateTime 当前日期时间
  99 + * @return 折扣活动列表
  100 + */
  101 + List<DiscountActivityDO> getDiscountActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
  102 +
92 } 103 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.service.discount; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.service.discount;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
  5 +import cn.hutool.core.map.MapUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
@@ -15,16 +16,20 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountActivityMapp @@ -15,16 +16,20 @@ import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountActivityMapp
15 import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper; 16 import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper;
16 import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; 17 import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
17 import cn.iocoder.yudao.module.promotion.util.PromotionUtils; 18 import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
  19 +import jakarta.annotation.Resource;
18 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
19 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
20 22
21 -import jakarta.annotation.Resource; 23 +import java.time.LocalDateTime;
22 import java.util.Collection; 24 import java.util.Collection;
  25 +import java.util.Collections;
23 import java.util.List; 26 import java.util.List;
  27 +import java.util.Map;
24 import java.util.stream.Collectors; 28 import java.util.stream.Collectors;
25 29
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 31 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  32 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
28 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 33 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
29 34
30 /** 35 /**
@@ -109,7 +114,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService { @@ -109,7 +114,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
109 /** 114 /**
110 * 校验商品是否冲突 115 * 校验商品是否冲突
111 * 116 *
112 - * @param id 编号 117 + * @param id 编号
113 * @param products 商品列表 118 * @param products 商品列表
114 */ 119 */
115 private void validateDiscountActivityProductConflicts(Long id, List<DiscountActivityBaseVO.Product> products) { 120 private void validateDiscountActivityProductConflicts(Long id, List<DiscountActivityBaseVO.Product> products) {
@@ -184,4 +189,17 @@ public class DiscountActivityServiceImpl implements DiscountActivityService { @@ -184,4 +189,17 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
184 return discountProductMapper.selectList("activity_id", activityIds); 189 return discountProductMapper.selectList("activity_id", activityIds);
185 } 190 }
186 191
  192 + @Override
  193 + public List<DiscountActivityDO> getDiscountActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
  194 + // 1. 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
  195 + List<Map<String, Object>> spuIdAndActivityIdMaps = discountProductMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
  196 + if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
  197 + return Collections.emptyList();
  198 + }
  199 +
  200 + // 2. 查询活动详情
  201 + return discountActivityMapper.selectListByIdsAndDateTimeLt(
  202 + convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), dateTime);
  203 + }
  204 +
187 } 205 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java
@@ -6,8 +6,9 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi @@ -6,8 +6,9 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi
6 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityPageReqVO;
7 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivityUpdateReqVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
9 -  
10 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
  10 +
  11 +import java.time.LocalDateTime;
11 import java.util.Collection; 12 import java.util.Collection;
12 import java.util.List; 13 import java.util.List;
13 14
@@ -71,4 +72,14 @@ public interface RewardActivityService { @@ -71,4 +72,14 @@ public interface RewardActivityService {
71 */ 72 */
72 List<RewardActivityMatchRespDTO> getMatchRewardActivityList(Collection<Long> spuIds); 73 List<RewardActivityMatchRespDTO> getMatchRewardActivityList(Collection<Long> spuIds);
73 74
  75 + /**
  76 + * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
  77 + *
  78 + * @param spuIds spu 编号
  79 + * @param status 状态
  80 + * @param dateTime 当前日期时间
  81 + * @return 满减送活动列表
  82 + */
  83 + List<RewardActivityDO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
  84 +
74 } 85 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java
@@ -11,14 +11,17 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO; @@ -11,14 +11,17 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
11 import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper; 11 import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper;
12 import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum; 12 import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
13 import cn.iocoder.yudao.module.promotion.util.PromotionUtils; 13 import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
  14 +import jakarta.annotation.Resource;
14 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
15 import org.springframework.validation.annotation.Validated; 16 import org.springframework.validation.annotation.Validated;
16 17
17 -import jakarta.annotation.Resource; 18 +import java.time.LocalDateTime;
18 import java.util.Collection; 19 import java.util.Collection;
  20 +import java.util.Collections;
19 import java.util.List; 21 import java.util.List;
20 22
21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
22 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 25 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
23 import static java.util.Arrays.asList; 26 import static java.util.Arrays.asList;
24 27
@@ -100,10 +103,11 @@ public class RewardActivityServiceImpl implements RewardActivityService { @@ -100,10 +103,11 @@ public class RewardActivityServiceImpl implements RewardActivityService {
100 } 103 }
101 104
102 // TODO @芋艿:逻辑有问题,需要优化;要分成全场、和指定来校验; 105 // TODO @芋艿:逻辑有问题,需要优化;要分成全场、和指定来校验;
  106 +
103 /** 107 /**
104 * 校验商品参加的活动是否冲突 108 * 校验商品参加的活动是否冲突
105 * 109 *
106 - * @param id 活动编号 110 + * @param id 活动编号
107 * @param spuIds 商品 SPU 编号数组 111 * @param spuIds 商品 SPU 编号数组
108 */ 112 */
109 private void validateRewardActivitySpuConflicts(Long id, Collection<Long> spuIds) { 113 private void validateRewardActivitySpuConflicts(Long id, Collection<Long> spuIds) {
@@ -125,7 +129,7 @@ public class RewardActivityServiceImpl implements RewardActivityService { @@ -125,7 +129,7 @@ public class RewardActivityServiceImpl implements RewardActivityService {
125 /** 129 /**
126 * 获得商品参加的满减送活动的数组 130 * 获得商品参加的满减送活动的数组
127 * 131 *
128 - * @param spuIds 商品 SPU 编号数组 132 + * @param spuIds 商品 SPU 编号数组
129 * @param statuses 活动状态数组 133 * @param statuses 活动状态数组
130 * @return 商品参加的满减送活动的数组 134 * @return 商品参加的满减送活动的数组
131 */ 135 */
@@ -163,4 +167,16 @@ public class RewardActivityServiceImpl implements RewardActivityService { @@ -163,4 +167,16 @@ public class RewardActivityServiceImpl implements RewardActivityService {
163 return null; 167 return null;
164 } 168 }
165 169
  170 + @Override
  171 + public List<RewardActivityDO> getRewardActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
  172 + // 1. 查询出指定 spuId 的 spu 参加的活动
  173 + List<RewardActivityDO> rewardActivityList = rewardActivityMapper.selectListBySpuIdsAndStatus(spuIds, status);
  174 + if (CollUtil.isEmpty(rewardActivityList)) {
  175 + return Collections.emptyList();
  176 + }
  177 +
  178 + // 2. 查询活动详情
  179 + return rewardActivityMapper.selectListByIdsAndDateTimeLt(convertSet(rewardActivityList, RewardActivityDO::getId), dateTime);
  180 + }
  181 +
166 } 182 }
yudao-module-system/yudao-module-system-api/pom.xml
@@ -22,32 +22,12 @@ @@ -22,32 +22,12 @@
22 <artifactId>yudao-common</artifactId> 22 <artifactId>yudao-common</artifactId>
23 </dependency> 23 </dependency>
24 24
25 - <!-- TODO @puhui999 & 芋艿:操作日志,要不要这么引入? -->  
26 - <!-- Springboot-注解-通用操作日志组件 -->  
27 - <!-- 此组件解决的问题是: 「谁」在「什么时间」对「什么」做了「什么事」 -->  
28 - <dependency>  
29 - <groupId>io.github.mouzt</groupId>  
30 - <artifactId>bizlog-sdk</artifactId>  
31 - </dependency>  
32 -  
33 - <!-- TODO @puhui999 & 芋艿:要不要移除掉 -->  
34 - <!--工具类相关-->  
35 - <dependency>  
36 - <groupId>com.fasterxml.jackson.core</groupId>  
37 - <artifactId>jackson-databind</artifactId>  
38 - </dependency>  
39 - <dependency>  
40 - <groupId>com.fasterxml.jackson.core</groupId>  
41 - <artifactId>jackson-core</artifactId>  
42 - </dependency>  
43 -  
44 <!-- 参数校验 --> 25 <!-- 参数校验 -->
45 <dependency> 26 <dependency>
46 <groupId>org.springframework.boot</groupId> 27 <groupId>org.springframework.boot</groupId>
47 <artifactId>spring-boot-starter-validation</artifactId> 28 <artifactId>spring-boot-starter-validation</artifactId>
48 <optional>true</optional> 29 <optional>true</optional>
49 </dependency> 30 </dependency>
50 -  
51 </dependencies> 31 </dependencies>
52 32
53 </project> 33 </project>
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java
1 package cn.iocoder.yudao.module.system.api.logger; 1 package cn.iocoder.yudao.module.system.api.logger;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; 4 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
  5 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
  6 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
4 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; 7 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
5 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
6 9
7 -import java.util.List;  
8 -  
9 /** 10 /**
10 * 操作日志 API 接口 11 * 操作日志 API 接口
11 * 12 *
@@ -21,12 +22,18 @@ public interface OperateLogApi { @@ -21,12 +22,18 @@ public interface OperateLogApi {
21 void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO); 22 void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO);
22 23
23 /** 24 /**
24 - * 获取指定模块的指定数据的操作日志 25 + * 创建操作日志
  26 + *
  27 + * @param createReqDTO 请求
  28 + */
  29 + void createOperateLogV2(@Valid OperateLogV2CreateReqDTO createReqDTO);
  30 +
  31 + /**
  32 + * 获取指定模块的指定数据的操作日志分页
25 * 33 *
26 - * @param module 操作模块  
27 - * @param bizId 操作模块编号  
28 - * @return 操作日志 34 + * @param pageReqVO 请求
  35 + * @return 操作日志分页
29 */ 36 */
30 - List<OperateLogV2RespDTO> getOperateLogByModuleAndBizId(String module, Long bizId); 37 + PageResult<OperateLogV2RespDTO> getOperateLogPage(OperateLogV2PageReqDTO pageReqVO);
31 38
32 } 39 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java renamed to yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2CreateReqDTO.java
1 -package cn.iocoder.yudao.module.system.service.logger.bo; 1 +package cn.iocoder.yudao.module.system.api.logger.dto;
2 2
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 import jakarta.validation.constraints.NotEmpty; 4 import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
5 import lombok.Data; 6 import lombok.Data;
6 7
  8 +import java.time.LocalDateTime;
  9 +import java.util.Map;
  10 +
7 /** 11 /**
8 * 系统操作日志 Create Req BO 12 * 系统操作日志 Create Req BO
9 * 13 *
10 * @author HUIHUI 14 * @author HUIHUI
11 */ 15 */
12 @Data 16 @Data
13 -public class OperateLogV2CreateReqBO { 17 +public class OperateLogV2CreateReqDTO {
14 18
15 /** 19 /**
16 * 链路追踪编号 20 * 链路追踪编号
@@ -23,29 +27,29 @@ public class OperateLogV2CreateReqBO { @@ -23,29 +27,29 @@ public class OperateLogV2CreateReqBO {
23 * 27 *
24 * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 28 * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
25 */ 29 */
26 - @NotEmpty(message = "用户编号不能为空") 30 + @NotNull(message = "用户编号不能为空")
27 private Long userId; 31 private Long userId;
28 /** 32 /**
29 * 用户类型 33 * 用户类型
30 * 34 *
31 * 关联 {@link UserTypeEnum} 35 * 关联 {@link UserTypeEnum}
32 */ 36 */
33 - @NotEmpty(message = "用户类型不能为空") 37 + @NotNull(message = "用户类型不能为空")
34 private Integer userType; 38 private Integer userType;
35 /** 39 /**
36 - * 操作模块 40 + * 操作模块类型
37 */ 41 */
38 - @NotEmpty(message = "操作模块不能为空")  
39 - private String module; 42 + @NotEmpty(message = "操作模块类型不能为空")
  43 + private String type;
40 /** 44 /**
41 * 操作名 45 * 操作名
42 */ 46 */
43 @NotEmpty(message = "操作名不能为空") 47 @NotEmpty(message = "操作名不能为空")
44 - private String name; 48 + private String subType;
45 /** 49 /**
46 * 操作模块业务编号 50 * 操作模块业务编号
47 */ 51 */
48 - @NotEmpty(message = "操作模块业务编号不能为空") 52 + @NotNull(message = "操作模块业务编号不能为空")
49 private Long bizId; 53 private Long bizId;
50 /** 54 /**
51 * 操作内容,记录整个操作的明细 55 * 操作内容,记录整个操作的明细
@@ -57,7 +61,7 @@ public class OperateLogV2CreateReqBO { @@ -57,7 +61,7 @@ public class OperateLogV2CreateReqBO {
57 * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) 61 * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
58 * 例如说,记录订单编号,{ orderId: "1"} 62 * 例如说,记录订单编号,{ orderId: "1"}
59 */ 63 */
60 - private String extra; 64 + private Map<String, Object> extra;
61 65
62 /** 66 /**
63 * 请求方法名 67 * 请求方法名
@@ -80,4 +84,38 @@ public class OperateLogV2CreateReqBO { @@ -80,4 +84,38 @@ public class OperateLogV2CreateReqBO {
80 @NotEmpty(message = "浏览器 UA 不能为空") 84 @NotEmpty(message = "浏览器 UA 不能为空")
81 private String userAgent; 85 private String userAgent;
82 86
  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 +
83 } 121 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2PageReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.logger.dto;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import lombok.Data;
  5 +
  6 +/**
  7 + * 操作日志分页 Request DTO
  8 + *
  9 + * @author HUIHUI
  10 + */
  11 +@Data
  12 +public class OperateLogV2PageReqDTO extends PageParam {
  13 +
  14 + /**
  15 + * 模块类型
  16 + */
  17 + private String bizType;
  18 + /**
  19 + * 模块数据编号
  20 + */
  21 + private Long bizId;
  22 +
  23 + /**
  24 + * 用户编号
  25 + */
  26 + private Long userId;
  27 +
  28 +}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java
1 package cn.iocoder.yudao.module.system.api.logger.dto; 1 package cn.iocoder.yudao.module.system.api.logger.dto;
2 2
3 -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;  
4 -import com.fasterxml.jackson.annotation.JsonFormat;  
5 import lombok.Data; 3 import lombok.Data;
6 4
7 import java.time.LocalDateTime; 5 import java.time.LocalDateTime;
8 -  
9 -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;  
10 -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; 6 +import java.util.Map;
11 7
12 /** 8 /**
13 * 系统操作日志 Resp DTO 9 * 系统操作日志 Resp DTO
@@ -26,17 +22,21 @@ public class OperateLogV2RespDTO { @@ -26,17 +22,21 @@ public class OperateLogV2RespDTO {
26 */ 22 */
27 private Long userId; 23 private Long userId;
28 /** 24 /**
  25 + * 用户名称
  26 + */
  27 + private String userName;
  28 + /**
29 * 用户类型 29 * 用户类型
30 */ 30 */
31 private Integer userType; 31 private Integer userType;
32 /** 32 /**
33 - * 操作模块 33 + * 操作模块类型
34 */ 34 */
35 - private String module; 35 + private String type;
36 /** 36 /**
37 * 操作名 37 * 操作名
38 */ 38 */
39 - private String name; 39 + private String subType;
40 /** 40 /**
41 * 操作模块业务编号 41 * 操作模块业务编号
42 */ 42 */
@@ -48,7 +48,7 @@ public class OperateLogV2RespDTO { @@ -48,7 +48,7 @@ public class OperateLogV2RespDTO {
48 /** 48 /**
49 * 拓展字段 49 * 拓展字段
50 */ 50 */
51 - private String extra; 51 + private Map<String, Object> extra;
52 52
53 /** 53 /**
54 * 请求方法名 54 * 请求方法名
@@ -68,20 +68,42 @@ public class OperateLogV2RespDTO { @@ -68,20 +68,42 @@ public class OperateLogV2RespDTO {
68 private String userAgent; 68 private String userAgent;
69 69
70 /** 70 /**
71 - * 创建时间 71 + * Java 方法名
72 */ 72 */
73 - // TODO puhui999: 木得效果怎么肥事  
74 - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)  
75 - private LocalDateTime createTime; 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;
76 93
77 - // TODO @puhui999:下面 2 个字段不用返回;用 userId 哈;返回一个 userName  
78 /** 94 /**
79 - * 创建者 95 + * 结果提示
80 */ 96 */
81 - private String creator; 97 + private String resultMsg;
  98 +
82 /** 99 /**
83 - * 创建者名称 100 + * 结果数据
84 */ 101 */
85 - private String creatorName; 102 + private String resultData;
  103 +
  104 + /**
  105 + * 创建时间
  106 + */
  107 + private LocalDateTime createTime;
86 108
87 } 109 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java
1 package cn.iocoder.yudao.module.system.api.logger; 1 package cn.iocoder.yudao.module.system.api.logger;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; 6 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
  7 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
  8 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
6 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; 9 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
8 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
9 import cn.iocoder.yudao.module.system.service.logger.OperateLogService; 12 import cn.iocoder.yudao.module.system.service.logger.OperateLogService;
10 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 13 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
11 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
  15 +import org.springframework.scheduling.annotation.Async;
12 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
13 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
14 18
15 -import java.util.Collections;  
16 import java.util.List; 19 import java.util.List;
17 import java.util.Map; 20 import java.util.Map;
18 21
@@ -39,21 +42,31 @@ public class OperateLogApiImpl implements OperateLogApi { @@ -39,21 +42,31 @@ public class OperateLogApiImpl implements OperateLogApi {
39 } 42 }
40 43
41 @Override 44 @Override
42 - public List<OperateLogV2RespDTO> getOperateLogByModuleAndBizId(String module, Long bizId) {  
43 - List<OperateLogV2DO> logList = operateLogService.getOperateLogByModuleAndBizId(module, bizId);  
44 - if (CollUtil.isEmpty(logList)) {  
45 - return Collections.emptyList(); 45 + @Async
  46 + public void createOperateLogV2(OperateLogV2CreateReqDTO createReqDTO) {
  47 + operateLogService.createOperateLogV2(createReqDTO);
  48 + }
  49 +
  50 + @Override
  51 + public PageResult<OperateLogV2RespDTO> getOperateLogPage(OperateLogV2PageReqDTO pageReqVO) {
  52 + PageResult<OperateLogV2DO> operateLogPage = operateLogService.getOperateLogPage(pageReqVO);
  53 + if (CollUtil.isEmpty(operateLogPage.getList())) {
  54 + return PageResult.empty();
46 } 55 }
47 56
48 // 获取用户 57 // 获取用户
49 - List<AdminUserDO> userList = adminUserService.getUserList(convertSet(logList, item -> Long.parseLong(item.getCreator()))); 58 + 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 + private static List<OperateLogV2RespDTO> setUserInfo(List<OperateLogV2DO> logList, List<AdminUserDO> userList) {
50 Map<Long, AdminUserDO> userMap = convertMap(userList, AdminUserDO::getId); 63 Map<Long, AdminUserDO> userMap = convertMap(userList, AdminUserDO::getId);
51 return convertList(logList, item -> { 64 return convertList(logList, item -> {
52 - OperateLogV2RespDTO bean = BeanUtils.toBean(item, OperateLogV2RespDTO.class);  
53 - findAndThen(userMap, Long.parseLong(item.getCreator()), user -> {  
54 - bean.setCreatorName(user.getNickname()); 65 + OperateLogV2RespDTO respDTO = BeanUtils.toBean(item, OperateLogV2RespDTO.class);
  66 + findAndThen(userMap, item.getUserId(), user -> {
  67 + respDTO.setUserName(user.getNickname());
55 }); 68 });
56 - return bean; 69 + return respDTO;
57 }); 70 });
58 } 71 }
59 72
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java
1 package cn.iocoder.yudao.module.system.dal.dataobject.logger; 1 package cn.iocoder.yudao.module.system.dal.dataobject.logger;
2 2
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 5 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 import com.baomidou.mybatisplus.annotation.KeySequence; 6 import com.baomidou.mybatisplus.annotation.KeySequence;
  7 +import com.baomidou.mybatisplus.annotation.TableField;
6 import com.baomidou.mybatisplus.annotation.TableId; 8 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 9 import com.baomidou.mybatisplus.annotation.TableName;
  10 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
8 import lombok.Data; 11 import lombok.Data;
9 import lombok.EqualsAndHashCode; 12 import lombok.EqualsAndHashCode;
10 13
  14 +import java.time.LocalDateTime;
  15 +import java.util.Map;
  16 +
11 /** 17 /**
12 * 操作日志表 V2 18 * 操作日志表 V2
13 * 19 *
@@ -20,6 +26,16 @@ import lombok.EqualsAndHashCode; @@ -20,6 +26,16 @@ import lombok.EqualsAndHashCode;
20 public class OperateLogV2DO extends BaseDO { 26 public class OperateLogV2DO extends BaseDO {
21 27
22 /** 28 /**
  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 + /**
23 * 日志主键 39 * 日志主键
24 */ 40 */
25 @TableId 41 @TableId
@@ -42,15 +58,14 @@ public class OperateLogV2DO extends BaseDO { @@ -42,15 +58,14 @@ public class OperateLogV2DO extends BaseDO {
42 * 关联 {@link UserTypeEnum} 58 * 关联 {@link UserTypeEnum}
43 */ 59 */
44 private Integer userType; 60 private Integer userType;
45 - // TODO @puhui999:module 改成 type,name 改成 subType;  
46 /** 61 /**
47 - * 操作模块 62 + * 操作模块类型
48 */ 63 */
49 - private String module; 64 + private String type;
50 /** 65 /**
51 * 操作名 66 * 操作名
52 */ 67 */
53 - private String name; 68 + private String subType;
54 /** 69 /**
55 * 操作模块业务编号 70 * 操作模块业务编号
56 */ 71 */
@@ -66,8 +81,8 @@ public class OperateLogV2DO extends BaseDO { @@ -66,8 +81,8 @@ public class OperateLogV2DO extends BaseDO {
66 * 81 *
67 * 例如说,记录订单编号,{ orderId: "1"} 82 * 例如说,记录订单编号,{ orderId: "1"}
68 */ 83 */
69 - // TODO @puhui999:看看能不能类似 exts 搞 json 格式;  
70 - private String extra; 84 + @TableField(typeHandler = JacksonTypeHandler.class)
  85 + private Map<String, Object> extra;
71 /** 86 /**
72 * 请求方法名 87 * 请求方法名
73 */ 88 */
@@ -85,9 +100,43 @@ public class OperateLogV2DO extends BaseDO { @@ -85,9 +100,43 @@ public class OperateLogV2DO extends BaseDO {
85 */ 100 */
86 private String userAgent; 101 private String userAgent;
87 102
88 - // TODO @芋艿:requestUrl、requestMethod  
89 - // TODO @芋艿:javaMethod、javaMethodArgs  
90 - // TODO @芋艿:startTime、duration  
91 - // TODO @芋艿:resultMsg、resultData 103 + /**
  104 + * Java 方法名
  105 + */
  106 + private String javaMethod;
  107 + /**
  108 + * Java 方法的参数
  109 + *
  110 + * 实际格式为 Map<String, Object>
  111 + * 不使用 @TableField(typeHandler = FastjsonTypeHandler.class) 注解的原因是,数据库存储有长度限制,会进行裁剪,会导致 JSON 反序列化失败
  112 + * 其中,key 为参数名,value 为参数值
  113 + */
  114 + private String javaMethodArgs;
  115 + /**
  116 + * 开始时间
  117 + */
  118 + private LocalDateTime startTime;
  119 + /**
  120 + * 执行时长,单位:毫秒
  121 + */
  122 + private Integer duration;
  123 + /**
  124 + * 结果码
  125 + *
  126 + * 目前使用的 {@link CommonResult#getCode()} 属性
  127 + */
  128 + private Integer resultCode;
  129 + /**
  130 + * 结果提示
  131 + *
  132 + * 目前使用的 {@link CommonResult#getMsg()} 属性
  133 + */
  134 + private String resultMsg;
  135 + /**
  136 + * 结果数据
  137 + *
  138 + * 如果是对象,则使用 JSON 格式化
  139 + */
  140 + private String resultData;
92 141
93 } 142 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java
@@ -3,28 +3,18 @@ package cn.iocoder.yudao.module.system.dal.mysql.logger; @@ -3,28 +3,18 @@ package cn.iocoder.yudao.module.system.dal.mysql.logger;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; 6 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; 7 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
10 -import java.util.Collection;  
11 -import java.util.List;  
12 -  
13 @Mapper 10 @Mapper
14 public interface OperateLogV2Mapper extends BaseMapperX<OperateLogV2DO> { 11 public interface OperateLogV2Mapper extends BaseMapperX<OperateLogV2DO> {
15 12
16 - default PageResult<OperateLogV2DO> selectPage(OperateLogPageReqVO reqVO, Collection<Long> userIds) {  
17 - LambdaQueryWrapperX<OperateLogV2DO> query = new LambdaQueryWrapperX<OperateLogV2DO>()  
18 - .likeIfPresent(OperateLogV2DO::getModule, reqVO.getModule())  
19 - .inIfPresent(OperateLogV2DO::getUserId, userIds);  
20 - query.orderByDesc(OperateLogV2DO::getId); // 降序  
21 - return selectPage(reqVO, query);  
22 - }  
23 -  
24 - default List<OperateLogV2DO> selectListByModuleAndBizId(String module, Long bizId) {  
25 - return selectList(new LambdaQueryWrapperX<OperateLogV2DO>()  
26 - .eq(OperateLogV2DO::getModule, module)  
27 - .eq(OperateLogV2DO::getBizId, bizId) 13 + default PageResult<OperateLogV2DO> selectPage(OperateLogV2PageReqDTO pageReqVO) {
  14 + return selectPage(pageReqVO, new LambdaQueryWrapperX<OperateLogV2DO>()
  15 + .eqIfPresent(OperateLogV2DO::getType, pageReqVO.getBizType())
  16 + .eqIfPresent(OperateLogV2DO::getBizId, pageReqVO.getBizId())
  17 + .eqIfPresent(OperateLogV2DO::getUserId, pageReqVO.getUserId())
28 .orderByDesc(OperateLogV2DO::getCreateTime)); 18 .orderByDesc(OperateLogV2DO::getCreateTime));
29 } 19 }
30 20
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/config/YudaoOperateLogV2Configuration.java deleted
1 -package cn.iocoder.yudao.module.system.framework.bizlog.config;  
2 -  
3 -import com.mzt.logapi.starter.annotation.EnableLogRecord;  
4 -import org.springframework.context.annotation.Configuration;  
5 -  
6 -// TODO @puhui999:挪到 yudao-spring-boot-starter-biz-operatelog 下,搞个 cn.iocoder.yudao.framework.operatelogv2;跑通后,我们直接就删除老的实现了;  
7 -/**  
8 - * mzt-biz-log 配置类  
9 - *  
10 - * @author HUIHUI  
11 - */  
12 -@Configuration(proxyBeanMethods = false)  
13 -@EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦  
14 -public class YudaoOperateLogV2Configuration {  
15 -  
16 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/package-info.java deleted
1 -package cn.iocoder.yudao.module.system.framework.bizlog;  
2 \ No newline at end of file 0 \ No newline at end of file
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.system.framework.bizlog.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.service.logger.OperateLogService;  
7 -import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;  
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 -import org.springframework.stereotype.Service;  
14 -  
15 -import java.util.Collections;  
16 -import java.util.List;  
17 -  
18 -// TODO @puhui999:这个应该搞到 operatelog 组件里哈;  
19 -/**  
20 - * 操作日志 ILogRecordService 实现类  
21 - *  
22 - * 基于 {@link OperateLogService} 实现,记录操作日志  
23 - *  
24 - * @author HUIHUI  
25 - */  
26 -@Slf4j  
27 -@Service  
28 -public class ILogRecordServiceImpl implements ILogRecordService {  
29 -  
30 - @Resource  
31 - private OperateLogService operateLogService;  
32 -  
33 - @Override  
34 - public void record(LogRecord logRecord) {  
35 - OperateLogV2CreateReqBO reqBO = new OperateLogV2CreateReqBO();  
36 - // 补全通用字段  
37 - reqBO.setTraceId(TracerUtils.getTraceId());  
38 - // 补充用户信息  
39 - fillUserFields(reqBO);  
40 - // 补全模块信息  
41 - fillModuleFields(reqBO, logRecord);  
42 - // 补全请求信息  
43 - fillRequestFields(reqBO);  
44 - // 异步记录日志  
45 - operateLogService.createOperateLogV2(reqBO);  
46 - log.info("操作日志 ===> {}", reqBO);  
47 - }  
48 -  
49 - private static void fillUserFields(OperateLogV2CreateReqBO reqBO) {  
50 - reqBO.setUserId(WebFrameworkUtils.getLoginUserId());  
51 - reqBO.setUserType(WebFrameworkUtils.getLoginUserType());  
52 - }  
53 -  
54 - public static void fillModuleFields(OperateLogV2CreateReqBO reqBO, LogRecord logRecord) {  
55 - reqBO.setModule(logRecord.getType()); // 大模块类型如 crm-客户  
56 - reqBO.setName(logRecord.getSubType());// 操作名称如 转移客户  
57 - reqBO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号  
58 - reqBO.setContent(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。  
59 - reqBO.setExtra(logRecord.getExtra()); // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"}  
60 - }  
61 -  
62 - private static void fillRequestFields(OperateLogV2CreateReqBO reqBO) {  
63 - // 获得 Request 对象  
64 - HttpServletRequest request = ServletUtils.getRequest();  
65 - if (request == null) {  
66 - return;  
67 - }  
68 - // 补全请求信息  
69 - reqBO.setRequestMethod(request.getMethod());  
70 - reqBO.setRequestUrl(request.getRequestURI());  
71 - reqBO.setUserIp(ServletUtils.getClientIP(request));  
72 - reqBO.setUserAgent(ServletUtils.getUserAgent(request));  
73 - }  
74 -  
75 - @Override  
76 - public List<LogRecord> queryLog(String bizNo, String type) {  
77 - return Collections.emptyList();  
78 - }  
79 -  
80 - @Override  
81 - public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {  
82 - return Collections.emptyList();  
83 - }  
84 -  
85 -}  
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.framework.operatelog;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AdminUserParseFunction.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/parse/AdminUserParseFunction.java
1 -package cn.iocoder.yudao.module.system.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.system.framework.operatelog.parse;
2 2
3 import cn.hutool.core.util.ObjUtil; 3 import cn.hutool.core.util.ObjUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AreaParseFunction.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/parse/AreaParseFunction.java
1 -package cn.iocoder.yudao.module.system.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.system.framework.operatelog.parse;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; 4 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java
@@ -2,12 +2,11 @@ package cn.iocoder.yudao.module.system.service.logger; @@ -2,12 +2,11 @@ package cn.iocoder.yudao.module.system.service.logger;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; 4 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
  5 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
  6 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
5 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
6 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
8 -import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;  
9 -  
10 -import java.util.List;  
11 10
12 /** 11 /**
13 * 操作日志 Service 接口 12 * 操作日志 Service 接口
@@ -38,16 +37,14 @@ public interface OperateLogService { @@ -38,16 +37,14 @@ public interface OperateLogService {
38 * 37 *
39 * @param createReqBO 创建请求 38 * @param createReqBO 创建请求
40 */ 39 */
41 - void createOperateLogV2(OperateLogV2CreateReqBO createReqBO); 40 + void createOperateLogV2(OperateLogV2CreateReqDTO createReqBO);
42 41
43 - // TODO @puhui999:module 改成 type  
44 /** 42 /**
45 - * 获取指定模块的指定数据的操作日志 43 + * 获得操作日志分页列表
46 * 44 *
47 - * @param module 操作模块  
48 - * @param bizId 操作模块编号  
49 - * @return 操作日志 45 + * @param pageReqVO 分页条件
  46 + * @return 操作日志分页列表
50 */ 47 */
51 - List<OperateLogV2DO> getOperateLogByModuleAndBizId(String module, Long bizId); 48 + PageResult<OperateLogV2DO> getOperateLogPage(OperateLogV2PageReqDTO pageReqVO);
52 49
53 } 50 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
@@ -6,13 +6,14 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -6,13 +6,14 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.framework.common.util.string.StrUtils; 7 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
8 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; 8 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
  9 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
  10 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
9 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; 11 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
10 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; 12 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
11 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO; 13 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
12 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; 14 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
13 import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; 15 import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper;
14 import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogV2Mapper; 16 import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogV2Mapper;
15 -import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;  
16 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 17 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
17 import jakarta.annotation.Resource; 18 import jakarta.annotation.Resource;
18 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
@@ -20,7 +21,6 @@ import org.springframework.stereotype.Service; @@ -20,7 +21,6 @@ import org.springframework.stereotype.Service;
20 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
21 22
22 import java.util.Collection; 23 import java.util.Collection;
23 -import java.util.List;  
24 24
25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
26 import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.JAVA_METHOD_ARGS_MAX_LENGTH; 26 import static cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO.JAVA_METHOD_ARGS_MAX_LENGTH;
@@ -69,14 +69,17 @@ public class OperateLogServiceImpl implements OperateLogService { @@ -69,14 +69,17 @@ public class OperateLogServiceImpl implements OperateLogService {
69 // ======================= LOG V2 ======================= 69 // ======================= LOG V2 =======================
70 70
71 @Override 71 @Override
72 - public void createOperateLogV2(OperateLogV2CreateReqBO createReqBO) { 72 + public void createOperateLogV2(OperateLogV2CreateReqDTO createReqBO) {
73 OperateLogV2DO log = BeanUtils.toBean(createReqBO, OperateLogV2DO.class); 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));
74 operateLogV2Mapper.insert(log); 76 operateLogV2Mapper.insert(log);
75 } 77 }
76 78
  79 +
77 @Override 80 @Override
78 - public List<OperateLogV2DO> getOperateLogByModuleAndBizId(String module, Long bizId) {  
79 - return operateLogV2Mapper.selectListByModuleAndBizId(module, bizId); 81 + public PageResult<OperateLogV2DO> getOperateLogPage(OperateLogV2PageReqDTO pageReqVO) {
  82 + return operateLogV2Mapper.selectPage(pageReqVO);
80 } 83 }
81 84
82 } 85 }
yudao-server/src/main/resources/application-local.yaml
@@ -188,6 +188,7 @@ logging: @@ -188,6 +188,7 @@ logging:
188 cn.iocoder.yudao.module.trade.dal.mysql: debug 188 cn.iocoder.yudao.module.trade.dal.mysql: debug
189 cn.iocoder.yudao.module.promotion.dal.mysql: debug 189 cn.iocoder.yudao.module.promotion.dal.mysql: debug
190 cn.iocoder.yudao.module.statistics.dal.mysql: debug 190 cn.iocoder.yudao.module.statistics.dal.mysql: debug
  191 + cn.iocoder.yudao.module.crm.dal.mysql: debug
191 192
192 debug: false 193 debug: false
193 194
yudao-server/src/main/resources/application.yaml
@@ -79,7 +79,17 @@ mybatis-plus: @@ -79,7 +79,17 @@ mybatis-plus:
79 password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 79 password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
80 80
81 mybatis-plus-join: 81 mybatis-plus-join:
82 - banner: false # 关闭控制台的 Banner 打印 82 + #是否打印 mybatis plus join banner 默认true
  83 + banner: false
  84 + #全局启用副表逻辑删除(默认true) 关闭后关联查询不会加副表逻辑删除
  85 + sub-table-logic: true
  86 + #拦截器MappedStatement缓存(默认true)
  87 + ms-cache: true
  88 + #表别名(默认 t)
  89 + table-alias: t
  90 + #副表逻辑删除条件的位置,支持where、on
  91 + #默认ON (1.4.7.2及之前版本默认为where)
  92 + logic-del-type: on
83 93
84 # Spring Data Redis 配置 94 # Spring Data Redis 配置
85 spring: 95 spring: