Commit 9203f485e9fa5e561dadc961ff869cf0e888c1ed

Authored by owen
2 parents 09445840 a974d055

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

# Conflicts:
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleController.java
Showing 203 changed files with 2017 additions and 1410 deletions
README.md
@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 31
32 ![架构图](/.image/common/ruoyi-vue-pro-architecture.png) 32 ![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
33 33
34 -* Java 后端:`master` 分支为 JDK 21 + Spring Boot 3.2.0,`master-jdk8` 分支为 JDK8 + Spring Boot 2.7.18 34 +* Java 后端:`master` 分支为 JDK 8 + Spring Boot 2.7.18,`master-jdk21` 分支为 JDK21 + Spring Boot 3.2.0
35 * 管理后台的电脑端:Vue3 提供 `element-plus`、`vben(ant-design-vue)` 两个版本,Vue2 提供 `element-ui` 版本 35 * 管理后台的电脑端:Vue3 提供 `element-plus`、`vben(ant-design-vue)` 两个版本,Vue2 提供 `element-ui` 版本
36 * 管理后台的移动端:采用 `uni-app` 方案,一份代码多终端适配,同时支持 APP、小程序、H5! 36 * 管理后台的移动端:采用 `uni-app` 方案,一份代码多终端适配,同时支持 APP、小程序、H5!
37 * 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson 37 * 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson
@@ -78,15 +78,21 @@ @@ -78,15 +78,21 @@
78 78
79 【完整版】包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM 等功能 79 【完整版】包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM 等功能
80 80
81 -* JDK 21 + Spring Boot 3.2.0 版本:<https://gitee.com/zhijiantianya/ruoyi-vue-pro> 的 `master` 分支  
82 -* JDK 8 + Spring Boot 2.7.18 版本:<https://gitee.com/zhijiantianya/ruoyi-vue-pro> 的 `master-jdk8` 分支 81 +* JDK 8 + Spring Boot 2.7.18 版本:<https://gitee.com/zhijiantianya/ruoyi-vue-pro> 的 `master` 分支
  82 +* JDK 21 + Spring Boot 3.2.0 版本:<https://gitee.com/zhijiantianya/ruoyi-vue-pro> 的 `master-jdk21` 分支
  83 +
  84 +两个分支的功能是一致的,可以放心使用!
83 85
84 ### ➡️️ 精简版 86 ### ➡️️ 精简版
85 87
86 【精简版】只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM 等功能 88 【精简版】只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM 等功能
87 89
88 -* JDK 21 + Spring Boot 3.2.0 版本:<https://gitee.com/yudaocode/yudao-boot-mini> 的 `master` 分支  
89 -* JDK 8 + Spring Boot 2.7.18 版本:<https://gitee.com/yudaocode/yudao-boot-mini> 的 `master-jdk8` 分支 90 +* JDK 8 + Spring Boot 2.7.18 版本:<https://gitee.com/yudaocode/yudao-boot-mini> 的 `master` 分支
  91 +* JDK 21 + Spring Boot 3.2.0 版本:<https://gitee.com/yudaocode/yudao-boot-mini> 的 `master-jdk21` 分支
  92 +
  93 +如果你想把【完整版】的功能,迁移到【精简版】,可以参考 [《迁移功能到精简版》](https://doc.iocoder.cn/migrate-module/) 文档。
  94 +
  95 +如果你想把【完整版】的功能,迁移到【精简版】,可以参考 [《迁移功能到精简版》](https://doc.iocoder.cn/migrate-module/) 文档。
90 96
91 ## 😎 开源协议 97 ## 😎 开源协议
92 98
sql/mysql/optinal/crm.sql
1 -- `ruoyi-vue-pro`.crm_contact_business_link definition 1 -- `ruoyi-vue-pro`.crm_contact_business_link definition
2 2
3 -CREATE TABLE `crm_contact_business_link` ( 3 +CREATE TABLE `crm_contact_business` (
4 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', 4 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
5 `contact_id` int(11) DEFAULT NULL COMMENT '联系人id', 5 `contact_id` int(11) DEFAULT NULL COMMENT '联系人id',
6 `business_id` int(11) DEFAULT NULL COMMENT '商机id', 6 `business_id` int(11) DEFAULT NULL COMMENT '商机id',
@@ -10,6 +10,5 @@ CREATE TABLE `crm_contact_business_link` ( @@ -10,6 +10,5 @@ CREATE TABLE `crm_contact_business_link` (
10 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 10 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
11 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 11 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
12 `tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '租户编号', 12 `tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '租户编号',
13 - PRIMARY KEY (`id`),  
14 - UNIQUE KEY `crm_contact_business_link_un` (`contact_id`,`business_id`,`deleted`,`tenant_id`) 13 + PRIMARY KEY (`id`)
15 ) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='联系人商机关联表'; 14 ) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='联系人商机关联表';
16 \ No newline at end of file 15 \ No newline at end of file
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 <!-- 消息队列 -->
@@ -51,7 +51,7 @@ @@ -51,7 +51,7 @@
51 <mapstruct.version>1.5.5.Final</mapstruct.version> 51 <mapstruct.version>1.5.5.Final</mapstruct.version>
52 <hutool-5.version>5.8.23</hutool-5.version> 52 <hutool-5.version>5.8.23</hutool-5.version>
53 <hutool-6.version>6.0.0-M8</hutool-6.version> 53 <hutool-6.version>6.0.0-M8</hutool-6.version>
54 - <easyexcel.verion>3.3.2</easyexcel.verion> 54 + <easyexcel.verion>3.3.3</easyexcel.verion>
55 <velocity.version>2.3</velocity.version> 55 <velocity.version>2.3</velocity.version>
56 <screw.version>1.0.5</screw.version> 56 <screw.version>1.0.5</screw.version>
57 <fastjson.version>1.2.83</fastjson.version> 57 <fastjson.version>1.2.83</fastjson.version>
@@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
72 <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version> 72 <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
73 <tencentcloud-sdk-java.version>3.1.880</tencentcloud-sdk-java.version> 73 <tencentcloud-sdk-java.version>3.1.880</tencentcloud-sdk-java.version>
74 <justauth.version>2.0.5</justauth.version> 74 <justauth.version>2.0.5</justauth.version>
75 - <jimureport.version>1.6.1</jimureport.version> 75 + <jimureport.version>1.6.6-beta2</jimureport.version>
76 <xercesImpl.version>2.12.2</xercesImpl.version> 76 <xercesImpl.version>2.12.2</xercesImpl.version>
77 <weixin-java.version>4.5.7.B</weixin-java.version> 77 <weixin-java.version>4.5.7.B</weixin-java.version>
78 <ureport2.version>2.2.9</ureport2.version> 78 <ureport2.version>2.2.9</ureport2.version>
@@ -651,7 +651,7 @@ @@ -651,7 +651,7 @@
651 <!-- 积木报表--> 651 <!-- 积木报表-->
652 <dependency> 652 <dependency>
653 <groupId>org.jeecgframework.jimureport</groupId> 653 <groupId>org.jeecgframework.jimureport</groupId>
654 - <artifactId>jimureport-spring-boot-starter</artifactId> 654 + <artifactId>jimureport-spring-boot3-starter</artifactId>
655 <version>${jimureport.version}</version> 655 <version>${jimureport.version}</version>
656 <exclusions> 656 <exclusions>
657 <exclusion> 657 <exclusion>
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java
@@ -15,6 +15,7 @@ import java.util.Arrays; @@ -15,6 +15,7 @@ import java.util.Arrays;
15 @Getter 15 @Getter
16 public enum TerminalEnum implements IntArrayValuable { 16 public enum TerminalEnum implements IntArrayValuable {
17 17
  18 + UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
18 WECHAT_MINI_PROGRAM(10, "微信小程序"), 19 WECHAT_MINI_PROGRAM(10, "微信小程序"),
19 WECHAT_WAP(11, "微信公众号"), 20 WECHAT_WAP(11, "微信公众号"),
20 H5(20, "H5 网页"), 21 H5(20, "H5 网页"),
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
1 package cn.iocoder.yudao.framework.common.util.cache; 1 package cn.iocoder.yudao.framework.common.util.cache;
2 2
  3 +import com.alibaba.ttl.threadpool.TtlExecutors;
3 import com.google.common.cache.CacheBuilder; 4 import com.google.common.cache.CacheBuilder;
4 import com.google.common.cache.CacheLoader; 5 import com.google.common.cache.CacheLoader;
5 import com.google.common.cache.LoadingCache; 6 import com.google.common.cache.LoadingCache;
6 7
7 import java.time.Duration; 8 import java.time.Duration;
  9 +import java.util.concurrent.Executor;
8 import java.util.concurrent.Executors; 10 import java.util.concurrent.Executors;
9 11
10 /** 12 /**
@@ -15,11 +17,13 @@ import java.util.concurrent.Executors; @@ -15,11 +17,13 @@ import java.util.concurrent.Executors;
15 public class CacheUtils { 17 public class CacheUtils {
16 18
17 public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) { 19 public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) {
  20 + Executor executor = Executors.newCachedThreadPool( // TODO 芋艿:可能要思考下,未来要不要做成可配置
  21 + TtlExecutors.getDefaultDisableInheritableThreadFactory()); // TTL 保证 ThreadLocal 可以透传
18 return CacheBuilder.newBuilder() 22 return CacheBuilder.newBuilder()
19 // 只阻塞当前数据加载线程,其他线程返回旧值 23 // 只阻塞当前数据加载线程,其他线程返回旧值
20 .refreshAfterWrite(duration) 24 .refreshAfterWrite(duration)
21 // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程 25 // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
22 - .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置 26 + .build(CacheLoader.asyncReloading(loader, executor));
23 } 27 }
24 28
25 } 29 }
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java
@@ -88,8 +88,6 @@ public class ServletUtils { @@ -88,8 +88,6 @@ public class ServletUtils {
88 return JakartaServletUtil.getClientIP(request); 88 return JakartaServletUtil.getClientIP(request);
89 } 89 }
90 90
91 - // TODO @疯狂:terminal 还是从 ServletUtils 里拿,更容易全局治理;  
92 -  
93 public static boolean isJsonRequest(ServletRequest request) { 91 public static boolean isJsonRequest(ServletRequest request) {
94 return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE); 92 return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
95 } 93 }
yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/resources/area.csv
@@ -490,6 +490,37 @@ id,name,type,parentId @@ -490,6 +490,37 @@ id,name,type,parentId
490 441700,阳江市,3,440000 490 441700,阳江市,3,440000
491 441800,清远市,3,440000 491 441800,清远市,3,440000
492 441900,东莞市,3,440000 492 441900,东莞市,3,440000
  493 +441901,莞城区,4,441900
  494 +441902,南城区,4,441900
  495 +441904,万江区,4,441900
  496 +441905,石碣镇,4,441900
  497 +441906,石龙镇,4,441900
  498 +441907,茶山镇,4,441900
  499 +441908,石排镇,4,441900
  500 +441909,企石镇,4,441900
  501 +441910,横沥镇,4,441900
  502 +441911,桥头镇,4,441900
  503 +441912,谢岗镇,4,441900
  504 +441913,东坑镇,4,441900
  505 +441914,常平镇,4,441900
  506 +441915,寮步镇,4,441900
  507 +441916,大朗镇,4,441900
  508 +441917,麻涌镇,4,441900
  509 +441918,中堂镇,4,441900
  510 +441919,高埗镇,4,441900
  511 +441920,樟木头镇,4,441900
  512 +441921,大岭山镇,4,441900
  513 +441922,望牛墩镇,4,441900
  514 +441923,黄江镇,4,441900
  515 +441924,洪梅镇,4,441900
  516 +441925,清溪镇,4,441900
  517 +441926,沙田镇,4,441900
  518 +441927,道滘镇,4,441900
  519 +441928,塘厦镇,4,441900
  520 +441929,虎门镇,4,441900
  521 +441930,厚街镇,4,441900
  522 +441931,凤岗镇,4,441900
  523 +441932,长安镇,4,441900
493 442000,中山市,3,440000 524 442000,中山市,3,440000
494 445100,潮州市,3,440000 525 445100,潮州市,3,440000
495 445200,揭阳市,3,440000 526 445200,揭阳市,3,440000
@@ -3605,4 +3636,4 @@ id,name,type,parentId @@ -3605,4 +3636,4 @@ id,name,type,parentId
3605 659008,可克达拉市,4,659000 3636 659008,可克达拉市,4,659000
3606 659009,昆玉市,4,659000 3637 659009,昆玉市,4,659000
3607 659010,胡杨河市,4,659000 3638 659010,胡杨河市,4,659000
3608 -659011,新星市,4,659000 3639 -659011,新星市,4,659000
  3640 +659011,新星市,4,659000
3609 \ No newline at end of file 3641 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-biz-operatelog/pom.xml
@@ -46,6 +46,7 @@ @@ -46,6 +46,7 @@
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 +
49 </dependencies> 50 </dependencies>
50 51
51 </project> 52 </project>
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -71,6 +71,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -71,6 +71,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
71 if (StrUtil.isNotEmpty(config.getPrivateCertContent())) { 71 if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
72 payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath()); 72 payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
73 } 73 }
  74 +// payConfig.setCertSerialNo();
74 75
75 // 创建 client 客户端 76 // 创建 client 客户端
76 client = new WxPayServiceImpl(); 77 client = new WxPayServiceImpl();
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/AbstractSmsClient.java
@@ -41,6 +41,7 @@ public abstract class AbstractSmsClient implements SmsClient { @@ -41,6 +41,7 @@ public abstract class AbstractSmsClient implements SmsClient {
41 return; 41 return;
42 } 42 }
43 log.info("[refresh][配置({})发生变化,重新初始化]", properties); 43 log.info("[refresh][配置({})发生变化,重新初始化]", properties);
  44 + this.properties = properties;
44 // 初始化 45 // 初始化
45 this.init(); 46 this.init();
46 } 47 }
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java
1 package cn.iocoder.yudao.framework.tenant.core.context; 1 package cn.iocoder.yudao.framework.tenant.core.context;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.enums.DocumentEnum; 4 import cn.iocoder.yudao.framework.common.enums.DocumentEnum;
4 import com.alibaba.ttl.TransmittableThreadLocal; 5 import com.alibaba.ttl.TransmittableThreadLocal;
5 6
@@ -21,7 +22,7 @@ public class TenantContextHolder { @@ -21,7 +22,7 @@ public class TenantContextHolder {
21 private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>(); 22 private static final ThreadLocal<Boolean> IGNORE = new TransmittableThreadLocal<>();
22 23
23 /** 24 /**
24 - * 获得租户编号。 25 + * 获得租户编号
25 * 26 *
26 * @return 租户编号 27 * @return 租户编号
27 */ 28 */
@@ -30,6 +31,16 @@ public class TenantContextHolder { @@ -30,6 +31,16 @@ public class TenantContextHolder {
30 } 31 }
31 32
32 /** 33 /**
  34 + * 获得租户编号 String
  35 + *
  36 + * @return 租户编号
  37 + */
  38 + public static String getTenantIdStr() {
  39 + Long tenantId = getTenantId();
  40 + return StrUtil.toStringOrNull(tenantId);
  41 + }
  42 +
  43 + /**
33 * 获得租户编号。如果不存在,则抛出 NullPointerException 异常 44 * 获得租户编号。如果不存在,则抛出 NullPointerException 异常
34 * 45 *
35 * @return 租户编号 46 * @return 租户编号
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
1 package cn.iocoder.yudao.framework.excel.core.util; 1 package cn.iocoder.yudao.framework.excel.core.util;
2 2
3 import com.alibaba.excel.EasyExcel; 3 import com.alibaba.excel.EasyExcel;
  4 +import com.alibaba.excel.converters.longconverter.LongStringConverter;
4 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; 5 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
5 import org.springframework.web.multipart.MultipartFile; 6 import org.springframework.web.multipart.MultipartFile;
6 7
7 import jakarta.servlet.http.HttpServletResponse; 8 import jakarta.servlet.http.HttpServletResponse;
8 import java.io.IOException; 9 import java.io.IOException;
9 import java.net.URLEncoder; 10 import java.net.URLEncoder;
  11 +import java.nio.charset.StandardCharsets;
10 import java.util.List; 12 import java.util.List;
11 13
12 /** 14 /**
@@ -33,9 +35,10 @@ public class ExcelUtils { @@ -33,9 +35,10 @@ public class ExcelUtils {
33 EasyExcel.write(response.getOutputStream(), head) 35 EasyExcel.write(response.getOutputStream(), head)
34 .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 36 .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
35 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 37 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
  38 + .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
36 .sheet(sheetName).doWrite(data); 39 .sheet(sheetName).doWrite(data);
37 // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 40 // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
38 - response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); 41 + response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8));
39 response.setContentType("application/vnd.ms-excel;charset=UTF-8"); 42 response.setContentType("application/vnd.ms-excel;charset=UTF-8");
40 } 43 }
41 44
yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/s3/S3FileClient.java
@@ -81,7 +81,7 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; { @@ -81,7 +81,7 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; {
81 } 81 }
82 // 腾讯云必须有 region,否则会报错 82 // 腾讯云必须有 region,否则会报错
83 if (config.getEndpoint().contains(ENDPOINT_TENCENT)) { 83 if (config.getEndpoint().contains(ENDPOINT_TENCENT)) {
84 - return StrUtil.subAfter(config.getEndpoint(), ".cos.", false) 84 + return StrUtil.subAfter(config.getEndpoint(), "cos.", false)
85 .replaceAll("." + ENDPOINT_TENCENT, ""); // 去除 Endpoint 85 .replaceAll("." + ENDPOINT_TENCENT, ""); // 去除 Endpoint
86 } 86 }
87 return null; 87 return null;
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/config/YudaoFlowableConfiguration.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.flowable.config; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.flowable.config;
3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; 3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
4 import cn.iocoder.yudao.framework.flowable.core.web.FlowableWebFilter; 4 import cn.iocoder.yudao.framework.flowable.core.web.FlowableWebFilter;
5 import org.springframework.boot.autoconfigure.AutoConfiguration; 5 import org.springframework.boot.autoconfigure.AutoConfiguration;
  6 +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
6 import org.springframework.boot.web.servlet.FilterRegistrationBean; 7 import org.springframework.boot.web.servlet.FilterRegistrationBean;
7 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Bean;
8 import org.springframework.core.task.AsyncListenableTaskExecutor; 9 import org.springframework.core.task.AsyncListenableTaskExecutor;
@@ -17,6 +18,7 @@ public class YudaoFlowableConfiguration { @@ -17,6 +18,7 @@ public class YudaoFlowableConfiguration {
17 * 如果不创建,会导致项目启动时,Flowable 报错的问题 18 * 如果不创建,会导致项目启动时,Flowable 报错的问题
18 */ 19 */
19 @Bean 20 @Bean
  21 + @ConditionalOnMissingBean
20 public AsyncListenableTaskExecutor taskExecutor() { 22 public AsyncListenableTaskExecutor taskExecutor() {
21 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 23 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
22 executor.setCorePoolSize(8); 24 executor.setCorePoolSize(8);
@@ -40,4 +42,5 @@ public class YudaoFlowableConfiguration { @@ -40,4 +42,5 @@ public class YudaoFlowableConfiguration {
40 registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER); 42 registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER);
41 return registrationBean; 43 return registrationBean;
42 } 44 }
  45 +
43 } 46 }
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
@@ -65,6 +65,12 @@ @@ -65,6 +65,12 @@
65 <dependency> 65 <dependency>
66 <groupId>com.baomidou</groupId> 66 <groupId>com.baomidou</groupId>
67 <artifactId>dynamic-datasource-spring-boot3-starter</artifactId> <!-- 多数据源 --> 67 <artifactId>dynamic-datasource-spring-boot3-starter</artifactId> <!-- 多数据源 -->
  68 + <exclusions>
  69 + <exclusion>
  70 + <groupId>org.springframework.boot</groupId>
  71 + <artifactId>spring-boot-starter-undertow</artifactId>
  72 + </exclusion>
  73 + </exclusions>
68 </dependency> 74 </dependency>
69 75
70 <dependency> 76 <dependency>
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
@@ -146,8 +146,8 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; { @@ -146,8 +146,8 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
146 * 146 *
147 * @param entities 实体们 147 * @param entities 实体们
148 */ 148 */
149 - default void insertBatch(Collection<T> entities) {  
150 - Db.saveBatch(entities); 149 + default Boolean insertBatch(Collection<T> entities) {
  150 + return Db.saveBatch(entities);
151 } 151 }
152 152
153 /** 153 /**
@@ -156,28 +156,28 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; { @@ -156,28 +156,28 @@ public interface BaseMapperX&lt;T&gt; extends MPJBaseMapper&lt;T&gt; {
156 * @param entities 实体们 156 * @param entities 实体们
157 * @param size 插入数量 Db.saveBatch 默认为 1000 157 * @param size 插入数量 Db.saveBatch 默认为 1000
158 */ 158 */
159 - default void insertBatch(Collection<T> entities, int size) {  
160 - Db.saveBatch(entities, size); 159 + default Boolean insertBatch(Collection<T> entities, int size) {
  160 + return Db.saveBatch(entities, size);
161 } 161 }
162 162
163 - default void updateBatch(T update) {  
164 - update(update, new QueryWrapper<>()); 163 + default int updateBatch(T update) {
  164 + return update(update, new QueryWrapper<>());
165 } 165 }
166 166
167 - default void updateBatch(Collection<T> entities) {  
168 - Db.updateBatchById(entities); 167 + default Boolean updateBatch(Collection<T> entities) {
  168 + return Db.updateBatchById(entities);
169 } 169 }
170 170
171 - default void updateBatch(Collection<T> entities, int size) {  
172 - Db.updateBatchById(entities, size); 171 + default Boolean updateBatch(Collection<T> entities, int size) {
  172 + return Db.updateBatchById(entities, size);
173 } 173 }
174 174
175 - default void insertOrUpdate(T entity) {  
176 - Db.saveOrUpdate(entity); 175 + default Boolean insertOrUpdate(T entity) {
  176 + return Db.saveOrUpdate(entity);
177 } 177 }
178 178
179 - default void insertOrUpdateBatch(Collection<T> collection) {  
180 - Db.saveOrUpdateBatch(collection); 179 + default Boolean insertOrUpdateBatch(Collection<T> collection) {
  180 + return Db.saveOrUpdateBatch(collection);
181 } 181 }
182 182
183 default int delete(String field, String value) { 183 default int delete(String field, String value) {
yudao-framework/yudao-spring-boot-starter-security/pom.xml
@@ -12,7 +12,10 @@ @@ -12,7 +12,10 @@
12 <packaging>jar</packaging> 12 <packaging>jar</packaging>
13 13
14 <name>${project.artifactId}</name> 14 <name>${project.artifactId}</name>
15 - <description>用户的认证、权限的校验</description> 15 + <description>
  16 + 1. security:用户的认证、权限的校验,实现「谁」可以做「什么事」
  17 + 2. operatelog:操作日志,实现「谁」在「什么时间」对「什么」做了「什么事」
  18 + </description>
16 <url>https://github.com/YunaiV/ruoyi-vue-pro</url> 19 <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
17 20
18 <dependencies> 21 <dependencies>
@@ -50,6 +53,13 @@ @@ -50,6 +53,13 @@
50 <artifactId>guava</artifactId> 53 <artifactId>guava</artifactId>
51 </dependency> 54 </dependency>
52 55
  56 + <dependency>
  57 + <!-- Spring Boot 通用操作日志组件,基于注解实现 -->
  58 + <!-- 此组件解决的问题是:「谁」在「什么时间」对「什么」做了「什么事」 -->
  59 + <groupId>io.github.mouzt</groupId>
  60 + <artifactId>bizlog-sdk</artifactId>
  61 + </dependency>
  62 +
53 <!-- 业务组件 --> 63 <!-- 业务组件 -->
54 <dependency> 64 <dependency>
55 <groupId>cn.iocoder.boot</groupId> 65 <groupId>cn.iocoder.boot</groupId>
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/config/YudaoOperateLogV2Configuration.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.operatelog.config;
  2 +
  3 +import cn.iocoder.yudao.framework.operatelog.core.service.ILogRecordServiceImpl;
  4 +import com.mzt.logapi.service.ILogRecordService;
  5 +import com.mzt.logapi.starter.annotation.EnableLogRecord;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.boot.autoconfigure.AutoConfiguration;
  8 +import org.springframework.context.annotation.Bean;
  9 +import org.springframework.context.annotation.Primary;
  10 +
  11 +/**
  12 + * 操作日志配置类
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦
  17 +@AutoConfiguration
  18 +@Slf4j
  19 +public class YudaoOperateLogV2Configuration {
  20 +
  21 + @Bean
  22 + @Primary
  23 + public ILogRecordService iLogRecordServiceImpl() {
  24 + return new ILogRecordServiceImpl();
  25 + }
  26 +
  27 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/package-info.java renamed to yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/package-info.java
1 /** 1 /**
2 - * 商机(销售机会) 2 + * 占位,无特殊作用
3 */ 3 */
4 -package cn.iocoder.yudao.module.crm.dal.dataobject.business; 4 +package cn.iocoder.yudao.framework.operatelog.core;
5 \ No newline at end of file 5 \ 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 renamed to yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/ILogRecordServiceImpl.java
1 -package cn.iocoder.yudao.module.system.framework.bizlog.service; 1 +package cn.iocoder.yudao.framework.operatelog.core.service;
2 2
3 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; 3 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
4 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; 4 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
5 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 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; 6 +import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
  7 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
8 import com.mzt.logapi.beans.LogRecord; 8 import com.mzt.logapi.beans.LogRecord;
9 import com.mzt.logapi.service.ILogRecordService; 9 import com.mzt.logapi.service.ILogRecordService;
10 import jakarta.annotation.Resource; 10 import jakarta.annotation.Resource;
11 import jakarta.servlet.http.HttpServletRequest; 11 import jakarta.servlet.http.HttpServletRequest;
12 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
13 -import org.springframework.stereotype.Service;  
14 13
15 -import java.util.Collections;  
16 import java.util.List; 14 import java.util.List;
17 15
18 -// TODO @puhui999:这个应该搞到 operatelog 组件里哈; 16 +// TODO @puhui999:LogRecordServiceImpl 改成这个名字哈
19 /** 17 /**
20 * 操作日志 ILogRecordService 实现类 18 * 操作日志 ILogRecordService 实现类
21 * 19 *
22 - * 基于 {@link OperateLogService} 实现,记录操作日志 20 + * 基于 {@link OperateLogApi} 实现,记录操作日志
23 * 21 *
24 * @author HUIHUI 22 * @author HUIHUI
25 */ 23 */
26 @Slf4j 24 @Slf4j
27 -@Service  
28 public class ILogRecordServiceImpl implements ILogRecordService { 25 public class ILogRecordServiceImpl implements ILogRecordService {
29 26
30 @Resource 27 @Resource
31 - private OperateLogService operateLogService; 28 + private OperateLogApi operateLogApi;
32 29
33 @Override 30 @Override
34 public void record(LogRecord logRecord) { 31 public void record(LogRecord logRecord) {
35 - OperateLogV2CreateReqBO reqBO = new OperateLogV2CreateReqBO();  
36 - // 补全通用字段  
37 - reqBO.setTraceId(TracerUtils.getTraceId()); 32 + // 1. 补全通用字段
  33 + OperateLogV2CreateReqDTO reqDTO = new OperateLogV2CreateReqDTO();
  34 + reqDTO.setTraceId(TracerUtils.getTraceId());
38 // 补充用户信息 35 // 补充用户信息
39 - fillUserFields(reqBO); 36 + fillUserFields(reqDTO);
40 // 补全模块信息 37 // 补全模块信息
41 - fillModuleFields(reqBO, logRecord); 38 + fillModuleFields(reqDTO, logRecord);
42 // 补全请求信息 39 // 补全请求信息
43 - fillRequestFields(reqBO);  
44 - // 异步记录日志  
45 - operateLogService.createOperateLogV2(reqBO);  
46 - log.info("操作日志 ===> {}", reqBO); 40 + fillRequestFields(reqDTO);
  41 +
  42 + // 2. 异步记录日志
  43 + operateLogApi.createOperateLogV2(reqDTO);
  44 + // TODO 测试结束删除或搞个开关
  45 + log.info("操作日志 ===> {}", reqDTO);
47 } 46 }
48 47
49 - private static void fillUserFields(OperateLogV2CreateReqBO reqBO) {  
50 - reqBO.setUserId(WebFrameworkUtils.getLoginUserId());  
51 - reqBO.setUserType(WebFrameworkUtils.getLoginUserType()); 48 + private static void fillUserFields(OperateLogV2CreateReqDTO reqDTO) {
  49 + // TODO @puhui999:使用 SecurityFrameworkUtils。因为要考虑,rpc、mq、job,它其实不是 web;
  50 + reqDTO.setUserId(WebFrameworkUtils.getLoginUserId());
  51 + reqDTO.setUserType(WebFrameworkUtils.getLoginUserType());
52 } 52 }
53 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"} 54 + public static void fillModuleFields(OperateLogV2CreateReqDTO reqDTO, LogRecord logRecord) {
  55 + reqDTO.setType(logRecord.getType()); // 大模块类型,例如:CRM 客户
  56 + reqDTO.setSubType(logRecord.getSubType());// 操作名称,例如:转移客户
  57 + reqDTO.setBizId(Long.parseLong(logRecord.getBizNo())); // 业务编号,例如:客户编号
  58 + reqDTO.setAction(logRecord.getAction());// 操作内容,例如:修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  59 + reqDTO.setExtra(logRecord.getExtra()); // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"}
60 } 60 }
61 61
62 - private static void fillRequestFields(OperateLogV2CreateReqBO reqBO) { 62 + private static void fillRequestFields(OperateLogV2CreateReqDTO reqDTO) {
63 // 获得 Request 对象 63 // 获得 Request 对象
64 HttpServletRequest request = ServletUtils.getRequest(); 64 HttpServletRequest request = ServletUtils.getRequest();
65 if (request == null) { 65 if (request == null) {
66 return; 66 return;
67 } 67 }
68 // 补全请求信息 68 // 补全请求信息
69 - reqBO.setRequestMethod(request.getMethod());  
70 - reqBO.setRequestUrl(request.getRequestURI());  
71 - reqBO.setUserIp(ServletUtils.getClientIP(request));  
72 - reqBO.setUserAgent(ServletUtils.getUserAgent(request)); 69 + reqDTO.setRequestMethod(request.getMethod());
  70 + reqDTO.setRequestUrl(request.getRequestURI());
  71 + reqDTO.setUserIp(ServletUtils.getClientIP(request));
  72 + reqDTO.setUserAgent(ServletUtils.getUserAgent(request));
73 } 73 }
74 74
75 @Override 75 @Override
76 public List<LogRecord> queryLog(String bizNo, String type) { 76 public List<LogRecord> queryLog(String bizNo, String type) {
77 - return Collections.emptyList(); 77 + throw new UnsupportedOperationException("使用 OperateLogApi 进行操作日志的查询");
78 } 78 }
79 79
80 @Override 80 @Override
81 public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) { 81 public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {
82 - return Collections.emptyList(); 82 + throw new UnsupportedOperationException("使用 OperateLogApi 进行操作日志的查询");
83 } 83 }
84 84
85 -} 85 -}
  86 +}
86 \ No newline at end of file 87 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/operatelog/package-info.java 0 → 100644
  1 +/**
  2 + * 基于 mzt-log 框架
  3 + * 实现操作日志功能
  4 + *
  5 + * @author HUIHUI
  6 + */
  7 +package cn.iocoder.yudao.framework.operatelog;
yudao-framework/yudao-spring-boot-starter-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
1 cn.iocoder.yudao.framework.security.config.YudaoSecurityAutoConfiguration 1 cn.iocoder.yudao.framework.security.config.YudaoSecurityAutoConfiguration
2 -cn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter  
3 \ No newline at end of file 2 \ No newline at end of file
  3 +cn.iocoder.yudao.framework.security.config.YudaoWebSecurityConfigurerAdapter
  4 +cn.iocoder.yudao.framework.operatelog.config.YudaoOperateLogV2Configuration
4 \ No newline at end of file 5 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
1 package cn.iocoder.yudao.framework.web.core.util; 1 package cn.iocoder.yudao.framework.web.core.util;
2 2
3 import cn.hutool.core.util.NumberUtil; 3 import cn.hutool.core.util.NumberUtil;
  4 +import cn.hutool.extra.servlet.ServletUtil;
  5 +import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
4 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 7 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  8 +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
6 import cn.iocoder.yudao.framework.web.config.WebProperties; 9 import cn.iocoder.yudao.framework.web.config.WebProperties;
7 import org.springframework.web.context.request.RequestAttributes; 10 import org.springframework.web.context.request.RequestAttributes;
8 import org.springframework.web.context.request.RequestContextHolder; 11 import org.springframework.web.context.request.RequestContextHolder;
@@ -25,6 +28,13 @@ public class WebFrameworkUtils { @@ -25,6 +28,13 @@ public class WebFrameworkUtils {
25 28
26 public static final String HEADER_TENANT_ID = "tenant-id"; 29 public static final String HEADER_TENANT_ID = "tenant-id";
27 30
  31 + /**
  32 + * 终端的 Header
  33 + *
  34 + * @see cn.iocoder.yudao.framework.common.enums.TerminalEnum
  35 + */
  36 + public static final String HEADER_TERMINAL = "terminal";
  37 +
28 private static WebProperties properties; 38 private static WebProperties properties;
29 39
30 public WebFrameworkUtils(WebProperties webProperties) { 40 public WebFrameworkUtils(WebProperties webProperties) {
@@ -107,6 +117,15 @@ public class WebFrameworkUtils { @@ -107,6 +117,15 @@ public class WebFrameworkUtils {
107 return getLoginUserId(request); 117 return getLoginUserId(request);
108 } 118 }
109 119
  120 + public static Integer getTerminal() {
  121 + HttpServletRequest request = getRequest();
  122 + if (request == null) {
  123 + return TerminalEnum.UNKNOWN.getTerminal();
  124 + }
  125 + String terminalValue = request.getHeader(HEADER_TERMINAL);
  126 + return NumberUtil.parseInt(terminalValue, TerminalEnum.UNKNOWN.getTerminal());
  127 + }
  128 +
110 public static void setCommonResult(ServletRequest request, CommonResult<?> result) { 129 public static void setCommonResult(ServletRequest request, CommonResult<?> result) {
111 request.setAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT, result); 130 request.setAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT, result);
112 } 131 }
yudao-framework/yudao-spring-boot-starter-websocket/pom.xml
@@ -38,17 +38,6 @@ @@ -38,17 +38,6 @@
38 <artifactId>spring-boot-starter-websocket</artifactId> 38 <artifactId>spring-boot-starter-websocket</artifactId>
39 </dependency> 39 </dependency>
40 40
41 - <!-- Web 相关 -->  
42 - <dependency>  
43 - <!-- 为什么是 websocket 依赖 security 呢?而不是 security 拓展 websocket 呢?  
44 - 因为 websocket 和 LoginUser 当前登录的用户有一定的相关性,具体可见 WebSocketSessionManagerImpl 逻辑。  
45 - 如果让 security 拓展 websocket 的话,会导致 websocket 组件的封装很散,进而增大理解成本。  
46 - -->  
47 - <groupId>cn.iocoder.boot</groupId>  
48 - <artifactId>yudao-spring-boot-starter-security</artifactId>  
49 - <scope>provided</scope>  
50 - </dependency>  
51 -  
52 <!-- 消息队列相关 --> 41 <!-- 消息队列相关 -->
53 <dependency> 42 <dependency>
54 <groupId>cn.iocoder.boot</groupId> 43 <groupId>cn.iocoder.boot</groupId>
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/session/WebSocketSessionManagerImpl.java
@@ -66,7 +66,7 @@ public class WebSocketSessionManagerImpl implements WebSocketSessionManager { @@ -66,7 +66,7 @@ public class WebSocketSessionManagerImpl implements WebSocketSessionManager {
66 @Override 66 @Override
67 public void removeSession(WebSocketSession session) { 67 public void removeSession(WebSocketSession session) {
68 // 移除从 idSessions 中 68 // 移除从 idSessions 中
69 - idSessions.remove(session.getId(), session); 69 + idSessions.remove(session.getId());
70 // 移除从 idSessions 中 70 // 移除从 idSessions 中
71 LoginUser user = WebSocketFrameworkUtils.getLoginUser(session); 71 LoginUser user = WebSocketFrameworkUtils.getLoginUser(session);
72 if (user == null) { 72 if (user == null) {
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 8 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
9 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; 9 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
  10 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
10 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*; 11 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
11 import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert; 12 import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
12 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; 13 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
@@ -71,7 +72,8 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -71,7 +72,8 @@ public class BpmModelServiceImpl implements BpmModelService {
71 modelQuery.modelCategory(pageVO.getCategory()); 72 modelQuery.modelCategory(pageVO.getCategory());
72 } 73 }
73 // 执行查询 74 // 执行查询
74 - List<Model> models = modelQuery.orderByCreateTime().desc() 75 + List<Model> models = modelQuery.modelTenantId(TenantContextHolder.getTenantIdStr())
  76 + .orderByCreateTime().desc()
75 .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); 77 .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
76 78
77 // 获得 Form Map 79 // 获得 Form Map
@@ -107,6 +109,7 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -107,6 +109,7 @@ public class BpmModelServiceImpl implements BpmModelService {
107 // 创建流程定义 109 // 创建流程定义
108 Model model = repositoryService.newModel(); 110 Model model = repositoryService.newModel();
109 BpmModelConvert.INSTANCE.copy(model, createReqVO); 111 BpmModelConvert.INSTANCE.copy(model, createReqVO);
  112 + model.setTenantId(TenantContextHolder.getTenantIdStr());
110 // 保存流程定义 113 // 保存流程定义
111 repositoryService.saveModel(model); 114 repositoryService.saveModel(model);
112 // 保存 BPMN XML 115 // 保存 BPMN XML
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
@@ -6,6 +6,8 @@ import cn.hutool.core.util.StrUtil; @@ -6,6 +6,8 @@ import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 7 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
8 import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils; 8 import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils;
  9 +import cn.iocoder.yudao.framework.flowable.core.util.FlowableUtils;
  10 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO; 11 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionListReqVO;
10 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO; 12 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO;
11 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; 13 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
@@ -124,6 +126,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ @@ -124,6 +126,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
124 Deployment deploy = repositoryService.createDeployment() 126 Deployment deploy = repositoryService.createDeployment()
125 .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory()) 127 .key(createReqDTO.getKey()).name(createReqDTO.getName()).category(createReqDTO.getCategory())
126 .addBytes(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes()) 128 .addBytes(createReqDTO.getKey() + BPMN_FILE_SUFFIX, createReqDTO.getBpmnBytes())
  129 + .tenantId(TenantContextHolder.getTenantIdStr())
127 .deploy(); 130 .deploy();
128 131
129 // 设置 ProcessDefinition 的 category 分类 132 // 设置 ProcessDefinition 的 category 分类
@@ -234,6 +237,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ @@ -234,6 +237,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
234 definitionQuery.active(); 237 definitionQuery.active();
235 } 238 }
236 // 执行查询 239 // 执行查询
  240 + definitionQuery.processDefinitionTenantId(TenantContextHolder.getTenantIdStr());
237 List<ProcessDefinition> processDefinitions = definitionQuery.list(); 241 List<ProcessDefinition> processDefinitions = definitionQuery.list();
238 if (CollUtil.isEmpty(processDefinitions)) { 242 if (CollUtil.isEmpty(processDefinitions)) {
239 return Collections.emptyList(); 243 return Collections.emptyList();
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
1 -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.flowable.core.context.FlowableContextHolder; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import jakarta.annotation.Resource; import jakarta.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * <p> * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * <p> * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * <p> * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getAssignee()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getAssignee()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> assignee) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 设置上下文信息 // TODO @hai:要不往 variables 存到一个全局固定 key 里,减少对上下文的依赖 FlowableContextHolder.setAssignee(assignee); // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables).setAssignee(assignee)); return instance.getId(); } @Override public List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey) { // 1. 先从上下文获取,首次提交数据库中查询不到 List<Long> result = FlowableContextHolder.getAssigneeByTaskDefinitionKey(taskDefinitionKey); if (CollUtil.isNotEmpty(result)) { return result; } // 2. 从数据库中获取 BpmProcessInstanceExtDO instance = processInstanceExtMapper.selectByProcessInstanceId(processInstanceId); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } if (CollUtil.isNotEmpty(instance.getAssignee())) { return instance.getAssignee().get(taskDefinitionKey); } return Collections.emptyList(); } }  
2 \ No newline at end of file 1 \ No newline at end of file
  2 +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.flowable.core.context.FlowableContextHolder; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import jakarta.annotation.Resource; import jakarta.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * <p> * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * <p> * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * <p> * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getAssignee()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getAssignee()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String) event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> assignee) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 设置上下文信息 // TODO @hai:要不往 variables 存到一个全局固定 key 里,减少对上下文的依赖 FlowableContextHolder.setAssignee(assignee); // 创建流程实例 ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables).setAssignee(assignee)); return instance.getId(); } @Override public List<Long> getAssigneeByProcessInstanceIdAndTaskDefinitionKey(String processInstanceId, String taskDefinitionKey) { // 1. 先从上下文获取,首次提交数据库中查询不到 List<Long> result = FlowableContextHolder.getAssigneeByTaskDefinitionKey(taskDefinitionKey); if (CollUtil.isNotEmpty(result)) { return result; } // 2. 从数据库中获取 BpmProcessInstanceExtDO instance = processInstanceExtMapper.selectByProcessInstanceId(processInstanceId); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } if (CollUtil.isNotEmpty(instance.getAssignee())) { return instance.getAssignee().get(taskDefinitionKey); } return Collections.emptyList(); } }
3 \ No newline at end of file 3 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/package-info.java deleted
1 -package cn.iocoder.yudao.module.bpm.service.task;  
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -39,6 +39,10 @@ public interface ErrorCodeConstants { @@ -39,6 +39,10 @@ public interface ErrorCodeConstants {
39 ErrorCode CUSTOMER_IN_POOL = new ErrorCode(1_020_006_004, "客户【{}】放入公海失败,原因:已经是公海客户"); 39 ErrorCode CUSTOMER_IN_POOL = new ErrorCode(1_020_006_004, "客户【{}】放入公海失败,原因:已经是公海客户");
40 ErrorCode CUSTOMER_LOCKED_PUT_POOL_FAIL = new ErrorCode(1_020_006_005, "客户【{}】放入公海失败,原因:客户已锁定"); 40 ErrorCode CUSTOMER_LOCKED_PUT_POOL_FAIL = new ErrorCode(1_020_006_005, "客户【{}】放入公海失败,原因:客户已锁定");
41 ErrorCode CUSTOMER_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_006_006, "更新客户【{}】负责人失败, 原因:系统异常"); 41 ErrorCode CUSTOMER_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_006_006, "更新客户【{}】负责人失败, 原因:系统异常");
  42 + ErrorCode CUSTOMER_LOCK_FAIL_IS_LOCK = new ErrorCode(1_020_006_007, "锁定客户失败,它已经处于锁定状态");
  43 + ErrorCode CUSTOMER_UNLOCK_FAIL_IS_UNLOCK = new ErrorCode(1_020_006_008, "锁定客户失败,它已经处于未锁定状态");
  44 + ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "锁定客户失败,超出锁定规则上限");
  45 + ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "操作失败,超出客户数拥有上限");
42 46
43 // ========== 权限管理 1_020_007_000 ========== 47 // ========== 权限管理 1_020_007_000 ==========
44 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在"); 48 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
1 package cn.iocoder.yudao.module.crm.enums; 1 package cn.iocoder.yudao.module.crm.enums;
2 2
3 -// TODO 芋艿:操作日志;看看这个类怎么搞个好点的规范;  
4 /** 3 /**
5 * CRM 操作日志枚举 4 * CRM 操作日志枚举
6 * 5 *
@@ -22,6 +21,13 @@ public interface LogRecordConstants { @@ -22,6 +21,13 @@ public interface LogRecordConstants {
22 //======================= 客户转移操作日志 ======================= 21 //======================= 客户转移操作日志 =======================
23 22
24 String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; 23 String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
25 - String TRANSFER_CUSTOMER_LOG_FAIL = ""; // TODO @puhui999:这个可以删除哈,一般不搞失败的日志 24 +
  25 + // TODO @puhui999:这里格式是不是可以这样;目的是:统一管理,也减少 Service 里各种“复杂”字符串
  26 + // ======================= Customer 客户 =======================
  27 + String CUSTOMER_TYPE = "CRM 客户";
  28 + String CUSTOMER_CREATE_SUB_TYPE = "创建客户";
  29 + String CUSTOMER_CREATE_SUCCESS = "更新了客户{_DIFF{#updateReqVO}}";
  30 +
  31 + String CUSTOMER_UPDATE_SUB_TYPE = "更新客户";
26 32
27 } 33 }
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLimitConfigTypeEnum.java
@@ -18,19 +18,19 @@ public enum CrmCustomerLimitConfigTypeEnum implements IntArrayValuable { @@ -18,19 +18,19 @@ public enum CrmCustomerLimitConfigTypeEnum implements IntArrayValuable {
18 /** 18 /**
19 * 拥有客户数限制 19 * 拥有客户数限制
20 */ 20 */
21 - CUSTOMER_QUANTITY_LIMIT(1, "拥有客户数限制"), 21 + CUSTOMER_OWNER_LIMIT(1, "拥有客户数限制"),
22 /** 22 /**
23 * 锁定客户数限制 23 * 锁定客户数限制
24 */ 24 */
25 CUSTOMER_LOCK_LIMIT(2, "锁定客户数限制"), 25 CUSTOMER_LOCK_LIMIT(2, "锁定客户数限制"),
26 ; 26 ;
27 27
28 - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLimitConfigTypeEnum::getCode).toArray(); 28 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLimitConfigTypeEnum::getType).toArray();
29 29
30 /** 30 /**
31 * 状态 31 * 状态
32 */ 32 */
33 - private final Integer code; 33 + private final Integer type;
34 /** 34 /**
35 * 状态名 35 * 状态名
36 */ 36 */
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/business/CrmBusinessController.java
@@ -7,8 +7,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -7,8 +7,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 7 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
8 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 8 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
9 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*; 9 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*;
10 -import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusQueryVO;  
11 -import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO;  
12 import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert; 10 import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
13 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; 11 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
14 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
@@ -30,12 +28,10 @@ import org.springframework.web.bind.annotation.*; @@ -30,12 +28,10 @@ import org.springframework.web.bind.annotation.*;
30 28
31 import java.io.IOException; 29 import java.io.IOException;
32 import java.util.List; 30 import java.util.List;
33 -import java.util.Objects;  
34 -import java.util.Set;  
35 -import java.util.stream.Collectors;  
36 31
37 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 32 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
38 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; 33 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
  34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
39 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 35 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
40 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 36 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
41 37
@@ -47,16 +43,14 @@ public class CrmBusinessController { @@ -47,16 +43,14 @@ public class CrmBusinessController {
47 43
48 @Resource 44 @Resource
49 private CrmBusinessService businessService; 45 private CrmBusinessService businessService;
50 -  
51 @Resource 46 @Resource
52 private CrmCustomerService customerService; 47 private CrmCustomerService customerService;
53 -  
54 @Resource 48 @Resource
55 private CrmBusinessStatusTypeService businessStatusTypeService; 49 private CrmBusinessStatusTypeService businessStatusTypeService;
56 -  
57 @Resource 50 @Resource
58 private CrmBusinessStatusService businessStatusService; 51 private CrmBusinessStatusService businessStatusService;
59 52
  53 + // TODO @商机待定:CrmBusinessCreateReqVO、CrmBusinessUpdateReqVO、CrmBusinessRespVO 按照新的 VO 规范
60 @PostMapping("/create") 54 @PostMapping("/create")
61 @Operation(summary = "创建商机") 55 @Operation(summary = "创建商机")
62 @PreAuthorize("@ss.hasPermission('crm:business:create')") 56 @PreAuthorize("@ss.hasPermission('crm:business:create')")
@@ -95,27 +89,7 @@ public class CrmBusinessController { @@ -95,27 +89,7 @@ public class CrmBusinessController {
95 @PreAuthorize("@ss.hasPermission('crm:business:query')") 89 @PreAuthorize("@ss.hasPermission('crm:business:query')")
96 public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) { 90 public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPage(@Valid CrmBusinessPageReqVO pageVO) {
97 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId()); 91 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(pageVO, getLoginUserId());
98 - if (CollUtil.isEmpty(pageResult.getList())) {  
99 - return success(PageResult.empty(pageResult.getTotal()));  
100 - }  
101 - // 处理客户名称回显  
102 - // TODO @ljlleo:可以使用 CollectionUtils.convertSet 替代常用的 stream 操作,更简洁一点;下面几个也是哈;  
103 - Set<Long> customerIds = pageResult.getList().stream()  
104 - .map(CrmBusinessDO::getCustomerId).filter(Objects::nonNull).collect(Collectors.toSet());  
105 - List<CrmCustomerDO> customerList = customerService.getCustomerList(customerIds, getLoginUserId());  
106 - // 处理商机状态类型名称回显  
107 - Set<Long> statusTypeIds = pageResult.getList().stream()  
108 - .map(CrmBusinessDO::getStatusTypeId).filter(Objects::nonNull).collect(Collectors.toSet());  
109 - CrmBusinessStatusTypeQueryVO queryStatusTypeVO = new CrmBusinessStatusTypeQueryVO();  
110 - queryStatusTypeVO.setIdList(statusTypeIds);  
111 - List<CrmBusinessStatusTypeDO> statusTypeList = businessStatusTypeService.selectList(queryStatusTypeVO);  
112 - // 处理商机状态名称回显  
113 - Set<Long> statusIds = pageResult.getList().stream()  
114 - .map(CrmBusinessDO::getStatusId).filter(Objects::nonNull).collect(Collectors.toSet());  
115 - CrmBusinessStatusQueryVO queryVO = new CrmBusinessStatusQueryVO();  
116 - queryVO.setIdList(statusIds);  
117 - List<CrmBusinessStatusDO> statusList = businessStatusService.selectList(queryVO);  
118 - return success(CrmBusinessConvert.INSTANCE.convertPage(pageResult, customerList, statusTypeList, statusList)); 92 + return success(buildBusinessDetailPageResult(pageResult));
119 } 93 }
120 94
121 @GetMapping("/page-by-customer") 95 @GetMapping("/page-by-customer")
@@ -123,24 +97,15 @@ public class CrmBusinessController { @@ -123,24 +97,15 @@ public class CrmBusinessController {
123 public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByCustomer(@Valid CrmBusinessPageReqVO pageReqVO) { 97 public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByCustomer(@Valid CrmBusinessPageReqVO pageReqVO) {
124 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); 98 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
125 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomerId(pageReqVO); 99 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomerId(pageReqVO);
126 - // 处理客户名称回显  
127 - // TODO @ljlleo:可以使用 CollectionUtils.convertSet 替代常用的 stream 操作,更简洁一点;下面几个也是哈;  
128 - Set<Long> customerIds = pageResult.getList().stream()  
129 - .map(CrmBusinessDO::getCustomerId).filter(Objects::nonNull).collect(Collectors.toSet());  
130 - List<CrmCustomerDO> customerList = customerService.getCustomerList(customerIds, getLoginUserId());  
131 - // 处理商机状态类型名称回显  
132 - Set<Long> statusTypeIds = pageResult.getList().stream()  
133 - .map(CrmBusinessDO::getStatusTypeId).filter(Objects::nonNull).collect(Collectors.toSet());  
134 - CrmBusinessStatusTypeQueryVO queryStatusTypeVO = new CrmBusinessStatusTypeQueryVO();  
135 - queryStatusTypeVO.setIdList(statusTypeIds);  
136 - List<CrmBusinessStatusTypeDO> statusTypeList = businessStatusTypeService.selectList(queryStatusTypeVO);  
137 - // 处理商机状态名称回显  
138 - Set<Long> statusIds = pageResult.getList().stream()  
139 - .map(CrmBusinessDO::getStatusId).filter(Objects::nonNull).collect(Collectors.toSet());  
140 - CrmBusinessStatusQueryVO queryVO = new CrmBusinessStatusQueryVO();  
141 - queryVO.setIdList(statusIds);  
142 - List<CrmBusinessStatusDO> statusList = businessStatusService.selectList(queryVO);  
143 - return success(CrmBusinessConvert.INSTANCE.convertPage(pageResult, customerList, statusTypeList, statusList)); 100 + return success(buildBusinessDetailPageResult(pageResult));
  101 + }
  102 +
  103 + @GetMapping("/page-by-contact")
  104 + @Operation(summary = "获得联系人的商机分页")
  105 + @PreAuthorize("@ss.hasPermission('crm:business:query')")
  106 + public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessContactPage(@Valid CrmBusinessPageReqVO pageReqVO) {
  107 + PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByContact(pageReqVO);
  108 + return success(buildBusinessDetailPageResult(pageResult));
144 } 109 }
145 110
146 @GetMapping("/export-excel") 111 @GetMapping("/export-excel")
@@ -152,8 +117,27 @@ public class CrmBusinessController { @@ -152,8 +117,27 @@ public class CrmBusinessController {
152 exportReqVO.setPageSize(PAGE_SIZE_NONE); 117 exportReqVO.setPageSize(PAGE_SIZE_NONE);
153 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(exportReqVO, getLoginUserId()); 118 PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPage(exportReqVO, getLoginUserId());
154 // 导出 Excel 119 // 导出 Excel
155 - ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessExcelVO.class,  
156 - CrmBusinessConvert.INSTANCE.convertList02(pageResult.getList())); 120 + ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessRespVO.class,
  121 + buildBusinessDetailPageResult(pageResult).getList());
  122 + }
  123 +
  124 + /**
  125 + * 构建详细的商机分页结果
  126 + *
  127 + * @param pageResult 简单的商机分页结果
  128 + * @return 详细的商机分页结果
  129 + */
  130 + private PageResult<CrmBusinessRespVO> buildBusinessDetailPageResult(PageResult<CrmBusinessDO> pageResult) {
  131 + if (CollUtil.isEmpty(pageResult.getList())) {
  132 + return PageResult.empty(pageResult.getTotal());
  133 + }
  134 + List<CrmBusinessStatusTypeDO> statusTypeList = businessStatusTypeService.getBusinessStatusTypeList(
  135 + convertSet(pageResult.getList(), CrmBusinessDO::getStatusTypeId));
  136 + List<CrmBusinessStatusDO> statusList = businessStatusService.getBusinessStatusList(
  137 + convertSet(pageResult.getList(), CrmBusinessDO::getStatusId));
  138 + List<CrmCustomerDO> customerList = customerService.getCustomerList(
  139 + convertSet(pageResult.getList(), CrmBusinessDO::getCustomerId));
  140 + return CrmBusinessConvert.INSTANCE.convertPage(pageResult, customerList, statusTypeList, statusList);
157 } 141 }
158 142
159 @PutMapping("/transfer") 143 @PutMapping("/transfer")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java
@@ -20,6 +20,9 @@ public class CrmBusinessPageReqVO extends PageParam { @@ -20,6 +20,9 @@ public class CrmBusinessPageReqVO extends PageParam {
20 @Schema(description = "客户编号", example = "10795") 20 @Schema(description = "客户编号", example = "10795")
21 private Long customerId; 21 private Long customerId;
22 22
  23 + @Schema(description = "联系人编号", example = "10795")
  24 + private Long contactId;
  25 +
23 @Schema(description = "场景类型", example = "1") 26 @Schema(description = "场景类型", example = "1")
24 @InEnum(CrmSceneTypeEnum.class) 27 @InEnum(CrmSceneTypeEnum.class)
25 private Integer sceneType; // 场景类型,为 null 时则表示全部 28 private Integer sceneType; // 场景类型,为 null 时则表示全部
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
@@ -6,6 +6,7 @@ import cn.hutool.core.util.NumberUtil; @@ -6,6 +6,7 @@ import cn.hutool.core.util.NumberUtil;
6 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
  9 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 10 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 11 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
11 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*; 12 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
@@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.crm.convert.contact.CrmContactConvert; @@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.crm.convert.contact.CrmContactConvert;
13 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; 14 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 15 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
15 import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants; 16 import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants;
  17 +import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;
16 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; 18 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
17 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 20 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -54,10 +56,13 @@ public class CrmContactController { @@ -54,10 +56,13 @@ public class CrmContactController {
54 private CrmContactService contactService; 56 private CrmContactService contactService;
55 @Resource 57 @Resource
56 private CrmCustomerService customerService; 58 private CrmCustomerService customerService;
57 -  
58 @Resource 59 @Resource
59 private AdminUserApi adminUserApi; 60 private AdminUserApi adminUserApi;
60 61
  62 + @Resource
  63 + private CrmContactBusinessService contactBusinessLinkService;
  64 +
  65 + // TODO @zyna:CrmContactCreateReqVO、CrmContactUpdateReqVO、CrmContactRespVO 按照新的 VO 规范搞哈;可以参考 dept 模块
61 @PostMapping("/create") 66 @PostMapping("/create")
62 @Operation(summary = "创建联系人") 67 @Operation(summary = "创建联系人")
63 @PreAuthorize("@ss.hasPermission('crm:contact:create')") 68 @PreAuthorize("@ss.hasPermission('crm:contact:create')")
@@ -96,7 +101,7 @@ public class CrmContactController { @@ -96,7 +101,7 @@ public class CrmContactController {
96 NumberUtil.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 101 NumberUtil.parseLong(contact.getCreator()), contact.getOwnerUserId())));
97 // 2. 获取客户信息 102 // 2. 获取客户信息
98 List<CrmCustomerDO> customerList = customerService.getCustomerList( 103 List<CrmCustomerDO> customerList = customerService.getCustomerList(
99 - Collections.singletonList(contact.getCustomerId()), getLoginUserId()); 104 + Collections.singletonList(contact.getCustomerId()));
100 // 3. 直属上级 105 // 3. 直属上级
101 List<CrmContactDO> parentContactList = contactService.getContactList( 106 List<CrmContactDO> parentContactList = contactService.getContactList(
102 Collections.singletonList(contact.getParentId()), getLoginUserId()); 107 Collections.singletonList(contact.getParentId()), getLoginUserId());
@@ -107,10 +112,11 @@ public class CrmContactController { @@ -107,10 +112,11 @@ public class CrmContactController {
107 @Operation(summary = "获得联系人列表") 112 @Operation(summary = "获得联系人列表")
108 @PreAuthorize("@ss.hasPermission('crm:contact:query')") 113 @PreAuthorize("@ss.hasPermission('crm:contact:query')")
109 public CommonResult<List<CrmContactSimpleRespVO>> getSimpleContactList() { 114 public CommonResult<List<CrmContactSimpleRespVO>> getSimpleContactList() {
  115 + // TODO @zyna:建议 contactService 单独搞个 list 接口哈
110 CrmContactPageReqVO pageReqVO = new CrmContactPageReqVO(); 116 CrmContactPageReqVO pageReqVO = new CrmContactPageReqVO();
111 pageReqVO.setPageSize(PAGE_SIZE_NONE); 117 pageReqVO.setPageSize(PAGE_SIZE_NONE);
112 List<CrmContactDO> list = contactService.getContactPage(pageReqVO, getLoginUserId()).getList(); 118 List<CrmContactDO> list = contactService.getContactPage(pageReqVO, getLoginUserId()).getList();
113 - return success(CrmContactConvert.INSTANCE.convertAllList(list)); 119 + return success(BeanUtils.toBean(list, CrmContactSimpleRespVO.class));
114 } 120 }
115 121
116 @GetMapping("/page") 122 @GetMapping("/page")
@@ -118,7 +124,7 @@ public class CrmContactController { @@ -118,7 +124,7 @@ public class CrmContactController {
118 @PreAuthorize("@ss.hasPermission('crm:contact:query')") 124 @PreAuthorize("@ss.hasPermission('crm:contact:query')")
119 public CommonResult<PageResult<CrmContactRespVO>> getContactPage(@Valid CrmContactPageReqVO pageVO) { 125 public CommonResult<PageResult<CrmContactRespVO>> getContactPage(@Valid CrmContactPageReqVO pageVO) {
120 PageResult<CrmContactDO> pageResult = contactService.getContactPage(pageVO, getLoginUserId()); 126 PageResult<CrmContactDO> pageResult = contactService.getContactPage(pageVO, getLoginUserId());
121 - return success(convertDetailContactPage(pageResult)); 127 + return success(buildContactDetailPage(pageResult));
122 } 128 }
123 129
124 @GetMapping("/page-by-customer") 130 @GetMapping("/page-by-customer")
@@ -126,7 +132,7 @@ public class CrmContactController { @@ -126,7 +132,7 @@ public class CrmContactController {
126 public CommonResult<PageResult<CrmContactRespVO>> getContactPageByCustomer(@Valid CrmContactPageReqVO pageVO) { 132 public CommonResult<PageResult<CrmContactRespVO>> getContactPageByCustomer(@Valid CrmContactPageReqVO pageVO) {
127 Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空"); 133 Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
128 PageResult<CrmContactDO> pageResult = contactService.getContactPageByCustomerId(pageVO); 134 PageResult<CrmContactDO> pageResult = contactService.getContactPageByCustomerId(pageVO);
129 - return success(convertDetailContactPage(pageResult)); 135 + return success(buildContactDetailPage(pageResult));
130 } 136 }
131 137
132 @GetMapping("/export-excel") 138 @GetMapping("/export-excel")
@@ -138,23 +144,23 @@ public class CrmContactController { @@ -138,23 +144,23 @@ public class CrmContactController {
138 exportReqVO.setPageNo(PAGE_SIZE_NONE); 144 exportReqVO.setPageNo(PAGE_SIZE_NONE);
139 PageResult<CrmContactDO> pageResult = contactService.getContactPage(exportReqVO, getLoginUserId()); 145 PageResult<CrmContactDO> pageResult = contactService.getContactPage(exportReqVO, getLoginUserId());
140 ExcelUtils.write(response, "联系人.xls", "数据", CrmContactRespVO.class, 146 ExcelUtils.write(response, "联系人.xls", "数据", CrmContactRespVO.class,
141 - convertDetailContactPage(pageResult).getList()); 147 + buildContactDetailPage(pageResult).getList());
142 } 148 }
143 149
144 /** 150 /**
145 - * 转换成详细的联系人分页,即读取关联信息 151 + * 构建详细的联系人分页结果
146 * 152 *
147 - * @param pageResult 联系人分页  
148 - * @return 详细的联系人分页 153 + * @param pageResult 简单的联系人分页结果
  154 + * @return 详细的联系人分页结果
149 */ 155 */
150 - private PageResult<CrmContactRespVO> convertDetailContactPage(PageResult<CrmContactDO> pageResult) { 156 + private PageResult<CrmContactRespVO> buildContactDetailPage(PageResult<CrmContactDO> pageResult) {
151 List<CrmContactDO> contactList = pageResult.getList(); 157 List<CrmContactDO> contactList = pageResult.getList();
152 if (CollUtil.isEmpty(contactList)) { 158 if (CollUtil.isEmpty(contactList)) {
153 return PageResult.empty(pageResult.getTotal()); 159 return PageResult.empty(pageResult.getTotal());
154 } 160 }
155 // 1. 获取客户列表 161 // 1. 获取客户列表
156 List<CrmCustomerDO> crmCustomerDOList = customerService.getCustomerList( 162 List<CrmCustomerDO> crmCustomerDOList = customerService.getCustomerList(
157 - convertSet(contactList, CrmContactDO::getCustomerId), getLoginUserId()); 163 + convertSet(contactList, CrmContactDO::getCustomerId));
158 // 2. 获取创建人、负责人列表 164 // 2. 获取创建人、负责人列表
159 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, 165 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList,
160 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 166 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
@@ -172,4 +178,22 @@ public class CrmContactController { @@ -172,4 +178,22 @@ public class CrmContactController {
172 return success(true); 178 return success(true);
173 } 179 }
174 180
  181 + // ================== 关联/取关联系人 ===================
  182 +
  183 + @PostMapping("/create-business-list")
  184 + @Operation(summary = "创建联系人与联系人的关联")
  185 + @PreAuthorize("@ss.hasPermission('crm:contact:create-business')")
  186 + public CommonResult<Boolean> createContactBusinessList(@Valid @RequestBody CrmContactBusinessReqVO createReqVO) {
  187 + contactBusinessLinkService.createContactBusinessList(createReqVO);
  188 + return success(true);
  189 + }
  190 +
  191 + @DeleteMapping("/delete-business-list")
  192 + @Operation(summary = "删除联系人与联系人的关联")
  193 + @PreAuthorize("@ss.hasPermission('crm:contact:delete-business')")
  194 + public CommonResult<Boolean> deleteContactBusinessList(@Valid @RequestBody CrmContactBusinessReqVO deleteReqVO) {
  195 + contactBusinessLinkService.deleteContactBusinessList(deleteReqVO);
  196 + return success(true);
  197 + }
  198 +
175 } 199 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactBaseVO.java
@@ -8,11 +8,11 @@ import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; @@ -8,11 +8,11 @@ import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
8 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; 8 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
9 import com.alibaba.excel.annotation.ExcelProperty; 9 import com.alibaba.excel.annotation.ExcelProperty;
10 import io.swagger.v3.oas.annotations.media.Schema; 10 import io.swagger.v3.oas.annotations.media.Schema;
  11 +import jakarta.validation.constraints.Email;
  12 +import jakarta.validation.constraints.NotNull;
11 import lombok.Data; 13 import lombok.Data;
12 import org.springframework.format.annotation.DateTimeFormat; 14 import org.springframework.format.annotation.DateTimeFormat;
13 15
14 -import jakarta.validation.constraints.Email;  
15 -import jakarta.validation.constraints.NotNull;  
16 import java.time.LocalDateTime; 16 import java.time.LocalDateTime;
17 17
18 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; 18 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@@ -75,29 +75,29 @@ public class CrmContactBaseVO { @@ -75,29 +75,29 @@ public class CrmContactBaseVO {
75 @ExcelProperty(value = "邮箱",order = 4) 75 @ExcelProperty(value = "邮箱",order = 4)
76 private String email; 76 private String email;
77 77
  78 + @Schema(description = "地区编号", example = "20158")
  79 + private Integer areaId;
  80 +
78 @ExcelProperty(value = "地址",order = 5) 81 @ExcelProperty(value = "地址",order = 5)
79 @Schema(description = "地址") 82 @Schema(description = "地址")
80 - private String address;  
81 -  
82 - @Schema(description = "下次联系时间")  
83 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)  
84 - @ExcelProperty(value = "下次联系时间",order = 6)  
85 - private LocalDateTime nextTime; 83 + private String detailAddress;
86 84
87 @Schema(description = "备注", example = "你说的对") 85 @Schema(description = "备注", example = "你说的对")
88 @ExcelProperty(value = "备注",order = 6) 86 @ExcelProperty(value = "备注",order = 6)
89 private String remark; 87 private String remark;
90 88
91 - @Schema(description = "最后跟进时间")  
92 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
93 - @ExcelProperty(value = "最后跟进时间",order = 6)  
94 - private LocalDateTime lastTime;  
95 -  
96 @Schema(description = "负责人用户编号", example = "14334") 89 @Schema(description = "负责人用户编号", example = "14334")
97 @NotNull(message = "负责人不能为空") 90 @NotNull(message = "负责人不能为空")
98 private Long ownerUserId; 91 private Long ownerUserId;
99 92
100 - @Schema(description = "地区编号", example = "20158")  
101 - private Integer areaId; 93 + @Schema(description = "最后跟进时间")
  94 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  95 + @ExcelProperty(value = "最后跟进时间",order = 6)
  96 + private LocalDateTime contactLastTime;
  97 +
  98 + @Schema(description = "下次联系时间")
  99 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
  100 + @ExcelProperty(value = "下次联系时间",order = 6)
  101 + private LocalDateTime contactNextTime;
102 102
103 } 103 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactBusinessReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.util.List;
  9 +
  10 +@Schema(description = "管理后台 - CRM 联系人商机 Request VO") // 用于关联,取消关联的操作
  11 +@Data
  12 +public class CrmContactBusinessReqVO {
  13 +
  14 + @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20878")
  15 + @NotNull(message="联系人不能为空")
  16 + private Long contactId;
  17 +
  18 + @Schema(description = "商机编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "7638")
  19 + @NotEmpty(message="商机不能为空")
  20 + private List<Long> businessIds;
  21 +
  22 +}
0 \ No newline at end of file 23 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contactbusinesslink/CrmContactBusinessLinkController.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
4 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
5 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkPageReqVO;  
8 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkRespVO;  
9 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkSaveReqVO;  
10 -import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;  
11 -import cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink.CrmContactBusinessLinkDO;  
12 -import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;  
13 -import cn.iocoder.yudao.module.crm.service.contactbusinesslink.CrmContactBusinessLinkService;  
14 -import io.swagger.v3.oas.annotations.Operation;  
15 -import io.swagger.v3.oas.annotations.Parameter;  
16 -import io.swagger.v3.oas.annotations.tags.Tag;  
17 -import org.springframework.security.access.prepost.PreAuthorize;  
18 -import org.springframework.transaction.annotation.Transactional;  
19 -import org.springframework.validation.annotation.Validated;  
20 -import org.springframework.web.bind.annotation.*;  
21 -  
22 -import jakarta.annotation.Resource;  
23 -import jakarta.validation.Valid;  
24 -import java.util.List;  
25 -  
26 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
27 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
28 -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;  
29 -  
30 -@Tag(name = "管理后台 - CRM 联系人商机关联")  
31 -@RestController  
32 -@RequestMapping("/crm/contact-business-link")  
33 -@Validated  
34 -public class CrmContactBusinessLinkController {  
35 -  
36 - @Resource  
37 - private CrmContactBusinessLinkService contactBusinessLinkService;  
38 - @Resource  
39 - private CrmBusinessService crmBusinessService;  
40 -  
41 - // TODO @zyna:createContactBusinessLink 和 createContactBusinessLinkBatch 是不是合并成一个接口?contactId、List<businessId>  
42 - @PostMapping("/create")  
43 - @Operation(summary = "创建联系人商机关联")  
44 - @PreAuthorize("@ss.hasPermission('crm:contact-business-link:create')")  
45 - public CommonResult<Long> createContactBusinessLink(@Valid @RequestBody CrmContactBusinessLinkSaveReqVO createReqVO) {  
46 - return success(contactBusinessLinkService.createContactBusinessLink(createReqVO));  
47 - }  
48 -  
49 - @PostMapping("/create-batch")  
50 - @Operation(summary = "创建联系人商机关联")  
51 - @PreAuthorize("@ss.hasPermission('crm:contact-business-link:create')")  
52 - @Transactional(rollbackFor = Exception.class)  
53 - public CommonResult<Boolean> createContactBusinessLinkBatch(  
54 - @Valid @RequestBody List<CrmContactBusinessLinkSaveReqVO> createReqVO) {  
55 - createReqVO.stream().forEach(item -> {  
56 - CrmBusinessDO crmBusinessDO = crmBusinessService.getBusiness(item.getBusinessId());  
57 - if(crmBusinessDO == null){  
58 - throw exception(BUSINESS_NOT_EXISTS);  
59 - }  
60 - });  
61 - contactBusinessLinkService.createContactBusinessLinkBatch(createReqVO);  
62 - return success(true);  
63 - }  
64 -  
65 - // TODO @zyna:这个接口是不是可以删除掉了哈?应该不存在更新。  
66 - @PutMapping("/update")  
67 - @Operation(summary = "更新联系人商机关联")  
68 - @PreAuthorize("@ss.hasPermission('crm:contact-business-link:update')")  
69 - public CommonResult<Boolean> updateContactBusinessLink(@Valid @RequestBody CrmContactBusinessLinkSaveReqVO updateReqVO) {  
70 - contactBusinessLinkService.updateContactBusinessLink(updateReqVO);  
71 - return success(true);  
72 - }  
73 -  
74 - // TODO @zyna:删除,是不是传递 ids?  
75 - @DeleteMapping("/delete-batch")  
76 - @Operation(summary = "批量删除联系人商机关联")  
77 - @PreAuthorize("@ss.hasPermission('crm:contact-business-link:delete')")  
78 - public CommonResult<Boolean> deleteContactBusinessLinkBatch(@Valid @RequestBody List<CrmContactBusinessLinkSaveReqVO> deleteList) {  
79 - contactBusinessLinkService.deleteContactBusinessLink(deleteList);  
80 - return success(true);  
81 - }  
82 -  
83 - // TODO @zyna:这个接口是不是可以删除掉了哈?应该不存在单个读取;  
84 - @GetMapping("/get")  
85 - @Operation(summary = "获得联系人商机关联")  
86 - @Parameter(name = "id", description = "编号", required = true, example = "1024")  
87 - @PreAuthorize("@ss.hasPermission('crm:contact-business-link:query')")  
88 - public CommonResult<CrmContactBusinessLinkRespVO> getContactBusinessLink(@RequestParam("id") Long id) {  
89 - CrmContactBusinessLinkDO contactBusinessLink = contactBusinessLinkService.getContactBusinessLink(id);  
90 - return success(BeanUtils.toBean(contactBusinessLink, CrmContactBusinessLinkRespVO.class));  
91 - }  
92 -  
93 - // TODO @zyna:这个可以转化下,使用客户编号去查询,就是使用 CrmBusinessController 的 getBusinessPageByCustomer 接口;目的是:复用  
94 - @GetMapping("/page-by-contact")  
95 - @Operation(summary = "获得联系人商机关联")  
96 - @PreAuthorize("@ss.hasPermission('crm:contact-business-link:query')")  
97 - public CommonResult<PageResult<CrmBusinessRespVO>> getContactBusinessLinkByContact(  
98 - @Valid CrmContactBusinessLinkPageReqVO pageReqVO) {  
99 - PageResult<CrmBusinessRespVO> contactBusinessLink = contactBusinessLinkService.getContactBusinessLinkPageByContact(pageReqVO);  
100 - return success(contactBusinessLink);  
101 - }  
102 -  
103 - // TODO @zyna:这个优化下,搞到 CrmBusinessController 里去,加一个 CrmBusinessController 的 getBusinessPageByContact 接口;目的是:  
104 - @GetMapping("/page")  
105 - @Operation(summary = "获得联系人商机关联分页")  
106 - @PreAuthorize("@ss.hasPermission('crm:contact-business-link:query')")  
107 - public CommonResult<PageResult<CrmContactBusinessLinkRespVO>> getContactBusinessLinkPage(  
108 - @Valid CrmContactBusinessLinkPageReqVO pageReqVO) {  
109 - PageResult<CrmContactBusinessLinkDO> pageResult = contactBusinessLinkService.getContactBusinessLinkPage(pageReqVO);  
110 - return success(BeanUtils.toBean(pageResult, CrmContactBusinessLinkRespVO.class));  
111 - }  
112 -  
113 - // TODO @zyna:最终梳理完后,应该就 2 个接口,要不直接合并到 CrmContactController 中,不作为独立模块,就关联、接触关联。其实和 user 设置它有哪些岗位、部门是类似的。  
114 -  
115 -}  
116 \ 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/controller/admin/contactbusinesslink/vo/CrmContactBusinessLinkRespVO.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo;  
2 -  
3 -import com.alibaba.excel.annotation.ExcelProperty;  
4 -import io.swagger.v3.oas.annotations.media.Schema;  
5 -import lombok.Data;  
6 -  
7 -import java.time.LocalDateTime;  
8 -  
9 -@Schema(description = "管理后台 - CRM 联系人商机关联 Response VO")  
10 -@Data  
11 -public class CrmContactBusinessLinkRespVO {  
12 -  
13 - @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "17220")  
14 - @ExcelProperty("主键")  
15 - private Long id;  
16 -  
17 - @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20878")  
18 - private Long contactId;  
19 -  
20 - @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7638")  
21 - private Long businessId;  
22 -  
23 - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)  
24 - private LocalDateTime createTime;  
25 -  
26 -}  
27 \ 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/controller/admin/contactbusinesslink/vo/CrmContactBusinessLinkSaveReqVO.java deleted
1 -package cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.Data;  
5 -  
6 -import jakarta.validation.constraints.NotNull;  
7 -  
8 -@Schema(description = "管理后台 - CRM 联系人商机关联新增/修改 Request VO")  
9 -@Data  
10 -public class CrmContactBusinessLinkSaveReqVO {  
11 -  
12 - @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "17220")  
13 - private Long id;  
14 -  
15 - @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20878")  
16 - @NotNull(message="联系人不能为空")  
17 - private Long contactId;  
18 -  
19 - @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7638")  
20 - @NotNull(message="商机不能为空")  
21 - private Long businessId;  
22 -  
23 -}  
24 \ 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/controller/admin/contract/CrmContractController.java
@@ -88,7 +88,7 @@ public class CrmContractController { @@ -88,7 +88,7 @@ public class CrmContractController {
88 @PreAuthorize("@ss.hasPermission('crm:contract:query')") 88 @PreAuthorize("@ss.hasPermission('crm:contract:query')")
89 public CommonResult<PageResult<ContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) { 89 public CommonResult<PageResult<ContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) {
90 PageResult<CrmContractDO> pageResult = contractService.getContractPage(pageVO, getLoginUserId()); 90 PageResult<CrmContractDO> pageResult = contractService.getContractPage(pageVO, getLoginUserId());
91 - return success(convertDetailContractPage(pageResult)); 91 + return success(buildContractDetailPage(pageResult));
92 } 92 }
93 93
94 @GetMapping("/page-by-customer") 94 @GetMapping("/page-by-customer")
@@ -96,7 +96,7 @@ public class CrmContractController { @@ -96,7 +96,7 @@ public class CrmContractController {
96 public CommonResult<PageResult<ContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) { 96 public CommonResult<PageResult<ContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) {
97 Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空"); 97 Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
98 PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO); 98 PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO);
99 - return success(convertDetailContractPage(pageResult)); 99 + return success(buildContractDetailPage(pageResult));
100 } 100 }
101 101
102 @GetMapping("/export-excel") 102 @GetMapping("/export-excel")
@@ -112,19 +112,19 @@ public class CrmContractController { @@ -112,19 +112,19 @@ public class CrmContractController {
112 } 112 }
113 113
114 /** 114 /**
115 - * 转换成详细的合同分页,即读取关联信息 115 + * 构建详细的合同分页结果
116 * 116 *
117 - * @param pageResult 合同分页  
118 - * @return 详细的合同分页 117 + * @param pageResult 简单的合同分页结果
  118 + * @return 详细的合同分页结果
119 */ 119 */
120 - private PageResult<ContractRespVO> convertDetailContractPage(PageResult<CrmContractDO> pageResult) { 120 + private PageResult<ContractRespVO> buildContractDetailPage(PageResult<CrmContractDO> pageResult) {
121 List<CrmContractDO> contactList = pageResult.getList(); 121 List<CrmContractDO> contactList = pageResult.getList();
122 if (CollUtil.isEmpty(contactList)) { 122 if (CollUtil.isEmpty(contactList)) {
123 return PageResult.empty(pageResult.getTotal()); 123 return PageResult.empty(pageResult.getTotal());
124 } 124 }
125 // 1. 获取客户列表 125 // 1. 获取客户列表
126 List<CrmCustomerDO> customerList = customerService.getCustomerList( 126 List<CrmCustomerDO> customerList = customerService.getCustomerList(
127 - convertSet(contactList, CrmContractDO::getCustomerId), getLoginUserId()); 127 + convertSet(contactList, CrmContractDO::getCustomerId));
128 // 2. 获取创建人、负责人列表 128 // 2. 获取创建人、负责人列表
129 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, 129 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList,
130 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 130 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
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,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer; @@ -3,6 +3,7 @@ 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;
8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; 9 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
@@ -12,6 +13,7 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; @@ -12,6 +13,7 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
12 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 13 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
13 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 14 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
14 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; 15 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
  16 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
15 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; 17 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
16 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
17 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 19 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -56,15 +58,18 @@ public class CrmCustomerController { @@ -56,15 +58,18 @@ public class CrmCustomerController {
56 @Resource 58 @Resource
57 private OperateLogApi operateLogApi; 59 private OperateLogApi operateLogApi;
58 60
  61 + // TODO @puhui999:把 CrmCustomerCreateReqVO、CrmCustomerUpdateReqVO、CrmCustomerRespVO 按照新的规范,搞一下哈;
59 @PostMapping("/create") 62 @PostMapping("/create")
60 @Operation(summary = "创建客户") 63 @Operation(summary = "创建客户")
  64 + @OperateLog(enable = false) // TODO 关闭原有日志记录;@puhui999:注解都先删除。先记录,没关系。我们下个迭代,就都删除掉操作日志了;
61 @PreAuthorize("@ss.hasPermission('crm:customer:create')") 65 @PreAuthorize("@ss.hasPermission('crm:customer:create')")
62 public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerCreateReqVO createReqVO) { 66 public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerCreateReqVO createReqVO) {
63 return success(customerService.createCustomer(createReqVO, getLoginUserId())); 67 return success(customerService.createCustomer(createReqVO, getLoginUserId()));
64 } 68 }
65 69
66 @PutMapping("/update") 70 @PutMapping("/update")
67 - //@Operation(summary = "更新客户") 71 + @Operation(summary = "更新客户")
  72 + @OperateLog(enable = false) // TODO 关闭原有日志记录
68 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 73 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
69 public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) { 74 public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
70 customerService.updateCustomer(updateReqVO); 75 customerService.updateCustomer(updateReqVO);
@@ -73,6 +78,7 @@ public class CrmCustomerController { @@ -73,6 +78,7 @@ public class CrmCustomerController {
73 78
74 @DeleteMapping("/delete") 79 @DeleteMapping("/delete")
75 @Operation(summary = "删除客户") 80 @Operation(summary = "删除客户")
  81 + @OperateLog(enable = false) // TODO 关闭原有日志记录
76 @Parameter(name = "id", description = "编号", required = true) 82 @Parameter(name = "id", description = "编号", required = true)
77 @PreAuthorize("@ss.hasPermission('crm:customer:delete')") 83 @PreAuthorize("@ss.hasPermission('crm:customer:delete')")
78 public CommonResult<Boolean> deleteCustomer(@RequestParam("id") Long id) { 84 public CommonResult<Boolean> deleteCustomer(@RequestParam("id") Long id) {
@@ -97,6 +103,7 @@ public class CrmCustomerController { @@ -97,6 +103,7 @@ public class CrmCustomerController {
97 return success(CrmCustomerConvert.INSTANCE.convert(customer, userMap, deptMap)); 103 return success(CrmCustomerConvert.INSTANCE.convert(customer, userMap, deptMap));
98 } 104 }
99 105
  106 + // TODO @puhui999:这个查询会查出多个;微信发你图了
100 @GetMapping("/page") 107 @GetMapping("/page")
101 @Operation(summary = "获得客户分页") 108 @Operation(summary = "获得客户分页")
102 @PreAuthorize("@ss.hasPermission('crm:customer:query')") 109 @PreAuthorize("@ss.hasPermission('crm:customer:query')")
@@ -108,6 +115,7 @@ public class CrmCustomerController { @@ -108,6 +115,7 @@ public class CrmCustomerController {
108 } 115 }
109 116
110 // 2. 拼接数据 117 // 2. 拼接数据
  118 + // TODO @puhui999:距离进入公海的时间
111 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( 119 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
112 convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId()))); 120 convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId())));
113 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); 121 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
@@ -128,38 +136,30 @@ public class CrmCustomerController { @@ -128,38 +136,30 @@ public class CrmCustomerController {
128 } 136 }
129 137
130 @PutMapping("/transfer") 138 @PutMapping("/transfer")
131 - //@Operation(summary = "客户转移") 139 + @Operation(summary = "转移客户")
  140 + @OperateLog(enable = false) // TODO 关闭原有日志记录
132 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 141 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
133 public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { 142 public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
134 customerService.transferCustomer(reqVO, getLoginUserId()); 143 customerService.transferCustomer(reqVO, getLoginUserId());
135 return success(true); 144 return success(true);
136 } 145 }
137 146
138 - // TODO @puhui999:operate-log-list 或者 operate-log-page 如果分页  
139 - @GetMapping("/operate-log") 147 + // TODO @puhui999:是不是接口只要传递 bizId,由 Controller 自己组装出 OperateLogV2PageReqDTO
  148 + @GetMapping("/operate-log-page")
140 @Operation(summary = "获得客户操作日志") 149 @Operation(summary = "获得客户操作日志")
141 - @Parameter(name = "id", description = "编号", required = true, example = "1024")  
142 @PreAuthorize("@ss.hasPermission('crm:customer:query')") 150 @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)); 151 + public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(CrmCustomerOperateLogPageReqVO reqVO) {
  152 + reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
  153 + reqVO.setBizType(CRM_CUSTOMER);
  154 + return success(operateLogApi.getOperateLogPage(BeanUtils.toBean(reqVO, OperateLogV2PageReqDTO.class)));
155 } 155 }
156 156
157 - // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了;  
158 @PutMapping("/lock") 157 @PutMapping("/lock")
159 @Operation(summary = "锁定/解锁客户") 158 @Operation(summary = "锁定/解锁客户")
  159 + @OperateLog(enable = false) // TODO 关闭原有日志记录
160 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 160 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
161 - public CommonResult<Boolean> lockCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {  
162 - customerService.lockCustomer(updateReqVO); 161 + public CommonResult<Boolean> lockCustomer(@Valid @RequestBody CrmCustomerLockReqVO lockReqVO) {
  162 + customerService.lockCustomer(lockReqVO, getLoginUserId());
163 return success(true); 163 return success(true);
164 } 164 }
165 165
@@ -167,6 +167,7 @@ public class CrmCustomerController { @@ -167,6 +167,7 @@ public class CrmCustomerController {
167 167
168 @PutMapping("/put-pool") 168 @PutMapping("/put-pool")
169 @Operation(summary = "数据放入公海") 169 @Operation(summary = "数据放入公海")
  170 + @OperateLog(enable = false) // TODO 关闭原有日志记录
170 @Parameter(name = "id", description = "客户编号", required = true, example = "1024") 171 @Parameter(name = "id", description = "客户编号", required = true, example = "1024")
171 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 172 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
172 public CommonResult<Boolean> putCustomerPool(@RequestParam("id") Long id) { 173 public CommonResult<Boolean> putCustomerPool(@RequestParam("id") Long id) {
@@ -183,6 +184,7 @@ public class CrmCustomerController { @@ -183,6 +184,7 @@ public class CrmCustomerController {
183 return success(true); 184 return success(true);
184 } 185 }
185 186
  187 + // TODO @puhui999:需要搞个 VO 类
186 @PutMapping("/distribute") 188 @PutMapping("/distribute")
187 @Operation(summary = "分配公海给对应负责人") 189 @Operation(summary = "分配公海给对应负责人")
188 @Parameters({ 190 @Parameters({
@@ -192,7 +194,6 @@ public class CrmCustomerController { @@ -192,7 +194,6 @@ public class CrmCustomerController {
192 @PreAuthorize("@ss.hasPermission('crm:customer:distribute')") 194 @PreAuthorize("@ss.hasPermission('crm:customer:distribute')")
193 public CommonResult<Boolean> distributeCustomer(@RequestParam(value = "ids") List<Long> ids, 195 public CommonResult<Boolean> distributeCustomer(@RequestParam(value = "ids") List<Long> ids,
194 @RequestParam(value = "ownerUserId") Long ownerUserId) { 196 @RequestParam(value = "ownerUserId") Long ownerUserId) {
195 - // 领取公海数据  
196 customerService.receiveCustomer(ids, ownerUserId); 197 customerService.receiveCustomer(ids, ownerUserId);
197 return success(true); 198 return success(true);
198 } 199 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerLimitConfigController.java
@@ -43,6 +43,7 @@ public class CrmCustomerLimitConfigController { @@ -43,6 +43,7 @@ public class CrmCustomerLimitConfigController {
43 @Resource 43 @Resource
44 private AdminUserApi adminUserApi; 44 private AdminUserApi adminUserApi;
45 45
  46 + // TODO @puhui999:可以把 vo 改下哈
46 @PostMapping("/create") 47 @PostMapping("/create")
47 @Operation(summary = "创建客户限制配置") 48 @Operation(summary = "创建客户限制配置")
48 @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:create')") 49 @PreAuthorize("@ss.hasPermission('crm:customer-limit-config:create')")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerPoolConfigController.java
@@ -26,6 +26,7 @@ public class CrmCustomerPoolConfigController { @@ -26,6 +26,7 @@ public class CrmCustomerPoolConfigController {
26 @Resource 26 @Resource
27 private CrmCustomerPoolConfigService customerPoolConfigService; 27 private CrmCustomerPoolConfigService customerPoolConfigService;
28 28
  29 + // TODO @puhui999:可以把 vo 改下哈
29 @GetMapping("/get") 30 @GetMapping("/get")
30 @Operation(summary = "获取客户公海规则设置") 31 @Operation(summary = "获取客户公海规则设置")
31 @PreAuthorize("@ss.hasPermission('crm:customer-pool-config:query')") 32 @PreAuthorize("@ss.hasPermission('crm:customer-pool-config:query')")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerLockReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - CRM 客户锁定/解锁 Request VO")
  7 +@Data
  8 +public class CrmCustomerLockReqVO {
  9 +
  10 + @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  11 + private Long id;
  12 +
  13 + @Schema(description = "客户锁定状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
  14 + private Boolean lockStatus;
  15 +
  16 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerOperateLogPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.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 = "管理后台 - crm 客户操作日志分页 Request VO")
  8 +@Data
  9 +public class CrmCustomerOperateLogPageReqVO 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-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java
@@ -28,4 +28,6 @@ public class CrmCustomerTransferReqVO { @@ -28,4 +28,6 @@ public class CrmCustomerTransferReqVO {
28 @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 28 @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
29 private Integer oldOwnerPermissionLevel; 29 private Integer oldOwnerPermissionLevel;
30 30
  31 + // TODO @puhui999:联系人、商机、合同的转移
  32 +
31 } 33 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/poolconfig/CrmCustomerPoolConfigSaveReqVO.java
@@ -16,9 +16,9 @@ import java.util.Objects; @@ -16,9 +16,9 @@ import java.util.Objects;
16 @ToString(callSuper = true) 16 @ToString(callSuper = true)
17 public class CrmCustomerPoolConfigSaveReqVO extends CrmCustomerPoolConfigBaseVO { 17 public class CrmCustomerPoolConfigSaveReqVO extends CrmCustomerPoolConfigBaseVO {
18 18
19 - // TODO @wanwan:AssertTrue 必须 is 开头哈;注意需要 json 忽略下,避免被序列化; 19 + // TODO @puhui999:AssertTrue 必须 is 开头哈;注意需要 json 忽略下,避免被序列化;
20 @AssertTrue(message = "客户公海规则设置不正确") 20 @AssertTrue(message = "客户公海规则设置不正确")
21 - // TODO @wanwan:这个方法,是不是拆成 2 个,一个校验 contactExpireDays、一个校验 dealExpireDays; 21 + // TODO @puhui999:这个方法,是不是拆成 2 个,一个校验 contactExpireDays、一个校验 dealExpireDays;
22 public boolean poolEnableValid() { 22 public boolean poolEnableValid() {
23 if (!BooleanUtil.isTrue(getEnabled())) { 23 if (!BooleanUtil.isTrue(getEnabled())) {
24 return true; 24 return true;
@@ -27,7 +27,7 @@ public class CrmCustomerPoolConfigSaveReqVO extends CrmCustomerPoolConfigBaseVO @@ -27,7 +27,7 @@ public class CrmCustomerPoolConfigSaveReqVO extends CrmCustomerPoolConfigBaseVO
27 } 27 }
28 28
29 @AssertTrue(message = "客户公海规则设置不正确") 29 @AssertTrue(message = "客户公海规则设置不正确")
30 - // TODO @wanwan:这个方法,是不是改成 isNotifyDaysValid() 更好点?本质校验的是 notifyDays 是否为空 30 + // TODO @puhui999:这个方法,是不是改成 isNotifyDaysValid() 更好点?本质校验的是 notifyDays 是否为空
31 public boolean notifyEnableValid() { 31 public boolean notifyEnableValid() {
32 if (!BooleanUtil.isTrue(getNotifyEnabled())) { 32 if (!BooleanUtil.isTrue(getNotifyEnabled())) {
33 return true; 33 return true;
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;
@@ -103,6 +103,7 @@ public class CrmPermissionController { @@ -103,6 +103,7 @@ public class CrmPermissionController {
103 // 拼接数据 103 // 拼接数据
104 List<AdminUserRespDTO> userList = adminUserApi.getUserList(convertSet(permission, CrmPermissionDO::getUserId)); 104 List<AdminUserRespDTO> userList = adminUserApi.getUserList(convertSet(permission, CrmPermissionDO::getUserId));
105 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId)); 105 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userList, AdminUserRespDTO::getDeptId));
  106 + // TODO @puhui999:可能 postIds 为空的时候,会导致报错,看看怎么 fix 下
106 Set<Long> postIds = CollectionUtils.convertSetByFlatMap(userList, AdminUserRespDTO::getPostIds, Collection::stream); 107 Set<Long> postIds = CollectionUtils.convertSetByFlatMap(userList, AdminUserRespDTO::getPostIds, Collection::stream);
107 Map<Long, PostRespDTO> postMap = postApi.getPostMap(postIds); 108 Map<Long, PostRespDTO> postMap = postApi.getPostMap(postIds);
108 return success(CrmPermissionConvert.INSTANCE.convert(permission, userList, deptMap, postMap)); 109 return success(CrmPermissionConvert.INSTANCE.convert(permission, userList, deptMap, postMap));
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
@@ -93,7 +93,7 @@ public class CrmReceivableController { @@ -93,7 +93,7 @@ public class CrmReceivableController {
93 @PreAuthorize("@ss.hasPermission('crm:receivable:query')") 93 @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
94 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) { 94 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) {
95 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId()); 95 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId());
96 - return success(convertDetailReceivablePage(pageResult)); 96 + return success(buildReceivableDetailPage(pageResult));
97 } 97 }
98 98
99 @GetMapping("/page-by-customer") 99 @GetMapping("/page-by-customer")
@@ -101,7 +101,7 @@ public class CrmReceivableController { @@ -101,7 +101,7 @@ public class CrmReceivableController {
101 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) { 101 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) {
102 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); 102 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
103 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO); 103 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO);
104 - return success(convertDetailReceivablePage(pageResult)); 104 + return success(buildReceivableDetailPage(pageResult));
105 } 105 }
106 106
107 // TODO 芋艿:后面在优化导出 107 // TODO 芋艿:后面在优化导出
@@ -115,23 +115,23 @@ public class CrmReceivableController { @@ -115,23 +115,23 @@ public class CrmReceivableController {
115 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(exportReqVO, getLoginUserId()); 115 PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(exportReqVO, getLoginUserId());
116 // 导出 Excel 116 // 导出 Excel
117 ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class, 117 ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class,
118 - convertDetailReceivablePage(pageResult).getList()); 118 + buildReceivableDetailPage(pageResult).getList());
119 } 119 }
120 120
121 /** 121 /**
122 - * 转换成详细的回款分页,即读取关联信息 122 + * 构建详细的回款分页结果
123 * 123 *
124 - * @param pageResult 回款分页  
125 - * @return 详细的回款分页 124 + * @param pageResult 简单的回款分页结果
  125 + * @return 详细的回款分页结果
126 */ 126 */
127 - private PageResult<CrmReceivableRespVO> convertDetailReceivablePage(PageResult<CrmReceivableDO> pageResult) { 127 + private PageResult<CrmReceivableRespVO> buildReceivableDetailPage(PageResult<CrmReceivableDO> pageResult) {
128 List<CrmReceivableDO> receivableList = pageResult.getList(); 128 List<CrmReceivableDO> receivableList = pageResult.getList();
129 if (CollUtil.isEmpty(receivableList)) { 129 if (CollUtil.isEmpty(receivableList)) {
130 return PageResult.empty(pageResult.getTotal()); 130 return PageResult.empty(pageResult.getTotal());
131 } 131 }
132 // 1. 获取客户列表 132 // 1. 获取客户列表
133 List<CrmCustomerDO> customerList = customerService.getCustomerList( 133 List<CrmCustomerDO> customerList = customerService.getCustomerList(
134 - convertSet(receivableList, CrmReceivableDO::getCustomerId), getLoginUserId()); 134 + convertSet(receivableList, CrmReceivableDO::getCustomerId));
135 // 2. 获取创建人、负责人列表 135 // 2. 获取创建人、负责人列表
136 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivableList, 136 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivableList,
137 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 137 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
@@ -123,10 +123,10 @@ public class CrmReceivablePlanController { @@ -123,10 +123,10 @@ public class CrmReceivablePlanController {
123 } 123 }
124 124
125 /** 125 /**
126 - * 转换成详细的回款计划分页,即读取关联信息 126 + * 构建详细的回款计划分页结果
127 * 127 *
128 - * @param pageResult 回款计划分页  
129 - * @return 详细的回款计划分页 128 + * @param pageResult 简单的回款计划分页结果
  129 + * @return 详细的回款计划分页结果
130 */ 130 */
131 private PageResult<CrmReceivablePlanRespVO> convertDetailReceivablePlanPage(PageResult<CrmReceivablePlanDO> pageResult) { 131 private PageResult<CrmReceivablePlanRespVO> convertDetailReceivablePlanPage(PageResult<CrmReceivablePlanDO> pageResult) {
132 List<CrmReceivablePlanDO> receivablePlanList = pageResult.getList(); 132 List<CrmReceivablePlanDO> receivablePlanList = pageResult.getList();
@@ -135,7 +135,7 @@ public class CrmReceivablePlanController { @@ -135,7 +135,7 @@ public class CrmReceivablePlanController {
135 } 135 }
136 // 1. 获取客户列表 136 // 1. 获取客户列表
137 List<CrmCustomerDO> customerList = customerService.getCustomerList( 137 List<CrmCustomerDO> customerList = customerService.getCustomerList(
138 - convertSet(receivablePlanList, CrmReceivablePlanDO::getCustomerId), getLoginUserId()); 138 + convertSet(receivablePlanList, CrmReceivablePlanDO::getCustomerId));
139 // 2. 获取创建人、负责人列表 139 // 2. 获取创建人、负责人列表
140 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivablePlanList, 140 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(receivablePlanList,
141 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 141 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java
1 package cn.iocoder.yudao.module.crm.convert.business; 1 package cn.iocoder.yudao.module.crm.convert.business;
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.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*; 5 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.*;
5 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; 6 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
6 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
@@ -9,7 +10,6 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; @@ -9,7 +10,6 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
9 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; 10 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
10 import org.mapstruct.Mapper; 11 import org.mapstruct.Mapper;
11 import org.mapstruct.Mapping; 12 import org.mapstruct.Mapping;
12 -import org.mapstruct.Mappings;  
13 import org.mapstruct.factory.Mappers; 13 import org.mapstruct.factory.Mappers;
14 14
15 import java.util.List; 15 import java.util.List;
@@ -34,25 +34,23 @@ public interface CrmBusinessConvert { @@ -34,25 +34,23 @@ public interface CrmBusinessConvert {
34 CrmBusinessRespVO convert(CrmBusinessDO bean); 34 CrmBusinessRespVO convert(CrmBusinessDO bean);
35 List<CrmBusinessRespVO> convert(List<CrmBusinessDO> bean); 35 List<CrmBusinessRespVO> convert(List<CrmBusinessDO> bean);
36 36
37 - PageResult<CrmBusinessRespVO> convertPage(PageResult<CrmBusinessDO> page);  
38 -  
39 List<CrmBusinessExcelVO> convertList02(List<CrmBusinessDO> list); 37 List<CrmBusinessExcelVO> convertList02(List<CrmBusinessDO> list);
40 38
41 @Mapping(target = "bizId", source = "reqVO.id") 39 @Mapping(target = "bizId", source = "reqVO.id")
42 CrmPermissionTransferReqBO convert(CrmBusinessTransferReqVO reqVO, Long userId); 40 CrmPermissionTransferReqBO convert(CrmBusinessTransferReqVO reqVO, Long userId);
43 41
44 - default PageResult<CrmBusinessRespVO> convertPage(PageResult<CrmBusinessDO> page, List<CrmCustomerDO> customerList, 42 + default PageResult<CrmBusinessRespVO> convertPage(PageResult<CrmBusinessDO> pageResult, List<CrmCustomerDO> customerList,
45 List<CrmBusinessStatusTypeDO> statusTypeList, List<CrmBusinessStatusDO> statusList) { 43 List<CrmBusinessStatusTypeDO> statusTypeList, List<CrmBusinessStatusDO> statusList) {
46 - PageResult<CrmBusinessRespVO> result = convertPage(page); 44 + PageResult<CrmBusinessRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmBusinessRespVO.class);
47 // 拼接关联字段 45 // 拼接关联字段
48 Map<Long, String> customerMap = convertMap(customerList, CrmCustomerDO::getId, CrmCustomerDO::getName); 46 Map<Long, String> customerMap = convertMap(customerList, CrmCustomerDO::getId, CrmCustomerDO::getName);
49 Map<Long, String> statusTypeMap = convertMap(statusTypeList, CrmBusinessStatusTypeDO::getId, CrmBusinessStatusTypeDO::getName); 47 Map<Long, String> statusTypeMap = convertMap(statusTypeList, CrmBusinessStatusTypeDO::getId, CrmBusinessStatusTypeDO::getName);
50 Map<Long, String> statusMap = convertMap(statusList, CrmBusinessStatusDO::getId, CrmBusinessStatusDO::getName); 48 Map<Long, String> statusMap = convertMap(statusList, CrmBusinessStatusDO::getId, CrmBusinessStatusDO::getName);
51 - result.getList().forEach(type -> type 49 + voPageResult.getList().forEach(type -> type
52 .setCustomerName(customerMap.get(type.getCustomerId())) 50 .setCustomerName(customerMap.get(type.getCustomerId()))
53 .setStatusTypeName(statusTypeMap.get(type.getStatusTypeId())) 51 .setStatusTypeName(statusTypeMap.get(type.getStatusTypeId()))
54 .setStatusName(statusMap.get(type.getStatusId()))); 52 .setStatusName(statusMap.get(type.getStatusId())));
55 - return result; 53 + return voPageResult;
56 } 54 }
57 55
58 } 56 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java
1 package cn.iocoder.yudao.module.crm.convert.contact; 1 package cn.iocoder.yudao.module.crm.convert.contact;
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.framework.common.util.collection.CollectionUtils;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; 6 import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
5 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*; 7 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
6 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
@@ -13,12 +15,10 @@ import org.mapstruct.factory.Mappers; @@ -13,12 +15,10 @@ import org.mapstruct.factory.Mappers;
13 15
14 import java.util.List; 16 import java.util.List;
15 import java.util.Map; 17 import java.util.Map;
16 -import java.util.stream.Collectors;  
17 18
18 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 19 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
19 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; 20 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
20 21
21 -// TODO 芋艿:convert 后面在梳理下,略微有点乱  
22 /** 22 /**
23 * CRM 联系人 Convert 23 * CRM 联系人 Convert
24 * 24 *
@@ -39,64 +39,37 @@ public interface CrmContactConvert { @@ -39,64 +39,37 @@ public interface CrmContactConvert {
39 39
40 PageResult<CrmContactRespVO> convertPage(PageResult<CrmContactDO> page); 40 PageResult<CrmContactRespVO> convertPage(PageResult<CrmContactDO> page);
41 41
  42 + @Mapping(target = "bizId", source = "reqVO.id")
  43 + CrmPermissionTransferReqBO convert(CrmContactTransferReqVO reqVO, Long userId);
  44 +
42 default PageResult<CrmContactRespVO> convertPage(PageResult<CrmContactDO> pageResult, Map<Long, AdminUserRespDTO> userMap, 45 default PageResult<CrmContactRespVO> convertPage(PageResult<CrmContactDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
43 List<CrmCustomerDO> customerList, List<CrmContactDO> parentContactList) { 46 List<CrmCustomerDO> customerList, List<CrmContactDO> parentContactList) {
44 - List<CrmContactRespVO> list = converList(pageResult.getList(), userMap, customerList, parentContactList);  
45 - return convertPage(pageResult).setList(list); 47 + PageResult<CrmContactRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmContactRespVO.class);
  48 + // 拼接关联字段
  49 + Map<Long, CrmContactDO> parentContactMap = convertMap(parentContactList, CrmContactDO::getId);
  50 + Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);
  51 + voPageResult.getList().forEach(item -> {
  52 + setUserInfo(item, userMap);
  53 + findAndThen(customerMap, item.getCustomerId(), customer -> item.setCustomerName(customer.getName()));
  54 + findAndThen(parentContactMap, item.getParentId(), contactDO -> item.setParentName(contactDO.getName()));
  55 + });
  56 + return voPageResult;
46 } 57 }
47 58
48 - List<CrmContactSimpleRespVO> convertAllList(List<CrmContactDO> list);  
49 -  
50 - @Mapping(target = "bizId", source = "reqVO.id")  
51 - CrmPermissionTransferReqBO convert(CrmContactTransferReqVO reqVO, Long userId);  
52 -  
53 - /**  
54 - * 转换详情信息  
55 - *  
56 - * @param contactDO 联系人  
57 - * @param userMap 用户列表  
58 - * @param crmCustomerDOList 客户  
59 - * @return ContactRespVO  
60 - */  
61 - default CrmContactRespVO convert(CrmContactDO contactDO, Map<Long, AdminUserRespDTO> userMap, List<CrmCustomerDO> crmCustomerDOList,  
62 - List<CrmContactDO> contactList) { 59 + default CrmContactRespVO convert(CrmContactDO contactDO, Map<Long, AdminUserRespDTO> userMap,
  60 + List<CrmCustomerDO> customerList, List<CrmContactDO> parentContactList) {
63 CrmContactRespVO contactVO = convert(contactDO); 61 CrmContactRespVO contactVO = convert(contactDO);
64 setUserInfo(contactVO, userMap); 62 setUserInfo(contactVO, userMap);
65 - Map<Long, CrmCustomerDO> ustomerMap = crmCustomerDOList.stream().collect(Collectors.toMap(CrmCustomerDO::getId, v -> v));  
66 - Map<Long, CrmContactDO> contactMap = contactList.stream().collect(Collectors.toMap(CrmContactDO::getId, v -> v));  
67 - findAndThen(ustomerMap, contactDO.getCustomerId(), customer -> contactVO.setCustomerName(customer.getName())); 63 + Map<Long, CrmCustomerDO> customerMap = CollectionUtils.convertMap(customerList, CrmCustomerDO::getId);
  64 + Map<Long, CrmContactDO> contactMap = CollectionUtils.convertMap(parentContactList, CrmContactDO::getId);
  65 + findAndThen(customerMap, contactDO.getCustomerId(), customer -> contactVO.setCustomerName(customer.getName()));
68 findAndThen(contactMap, contactDO.getParentId(), contact -> contactVO.setParentName(contact.getName())); 66 findAndThen(contactMap, contactDO.getParentId(), contact -> contactVO.setParentName(contact.getName()));
69 return contactVO; 67 return contactVO;
70 } 68 }
71 69
72 - default List<CrmContactRespVO> converList(List<CrmContactDO> contactList, Map<Long, AdminUserRespDTO> userMap,  
73 - List<CrmCustomerDO> customerList, List<CrmContactDO> parentContactList) {  
74 - List<CrmContactRespVO> result = convertList(contactList);  
75 - Map<Long, CrmContactDO> parentContactMap = convertMap(parentContactList, CrmContactDO::getId);  
76 - Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);  
77 - result.forEach(item -> {  
78 - setUserInfo(item, userMap);  
79 - findAndThen(customerMap, item.getCustomerId(), customer -> { // TODO @zyna:这里的 { 可以去掉  
80 - item.setCustomerName(customer.getName());  
81 - });  
82 - findAndThen(parentContactMap, item.getParentId(), contactDO -> { // TODO @zyna:这里的 { 可以去掉  
83 - item.setParentName(contactDO.getName());  
84 - });  
85 - });  
86 - return result;  
87 - }  
88 -  
89 - /**  
90 - * 设置用户信息  
91 - *  
92 - * @param contactRespVO 联系人Response VO  
93 - * @param userMap 用户信息 map  
94 - */  
95 static void setUserInfo(CrmContactRespVO contactRespVO, Map<Long, AdminUserRespDTO> userMap) { 70 static void setUserInfo(CrmContactRespVO contactRespVO, Map<Long, AdminUserRespDTO> userMap) {
96 contactRespVO.setAreaName(AreaUtils.format(contactRespVO.getAreaId())); 71 contactRespVO.setAreaName(AreaUtils.format(contactRespVO.getAreaId()));
97 - findAndThen(userMap, contactRespVO.getOwnerUserId(), user -> {  
98 - contactRespVO.setOwnerUserName(user == null ? "" : user.getNickname());  
99 - }); 72 + findAndThen(userMap, contactRespVO.getOwnerUserId(), user -> contactRespVO.setOwnerUserName(user.getNickname()));
100 findAndThen(userMap, Long.parseLong(contactRespVO.getCreator()), user -> contactRespVO.setCreatorName(user.getNickname())); 73 findAndThen(userMap, Long.parseLong(contactRespVO.getCreator()), user -> contactRespVO.setCreatorName(user.getNickname()));
101 } 74 }
102 75
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contactbusinessslink/CrmContactBusinessLinkConvert.java deleted
1 -package cn.iocoder.yudao.module.crm.convert.contactbusinessslink;  
2 -  
3 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkSaveReqVO;  
4 -import cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink.CrmContactBusinessLinkDO;  
5 -import org.mapstruct.Mapper;  
6 -import org.mapstruct.factory.Mappers;  
7 -  
8 -import java.util.List;  
9 -  
10 -// TODO @zyna:使用 BeanUtils 慢慢替代现有的 mapstruct 哈  
11 -@Mapper  
12 -public interface CrmContactBusinessLinkConvert {  
13 - CrmContactBusinessLinkConvert INSTANCE = Mappers.getMapper(CrmContactBusinessLinkConvert.class);  
14 - CrmContactBusinessLinkDO convert(CrmContactBusinessLinkSaveReqVO bean);  
15 - List<CrmContactBusinessLinkDO> convert(List<CrmContactBusinessLinkSaveReqVO> bean);  
16 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java
1 package cn.iocoder.yudao.module.crm.convert.contract; 1 package cn.iocoder.yudao.module.crm.convert.contract;
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.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*; 5 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
5 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 6 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
6 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@@ -43,23 +44,15 @@ public interface CrmContractConvert { @@ -43,23 +44,15 @@ public interface CrmContractConvert {
43 44
44 default PageResult<ContractRespVO> convertPage(PageResult<CrmContractDO> pageResult, Map<Long, AdminUserRespDTO> userMap, 45 default PageResult<ContractRespVO> convertPage(PageResult<CrmContractDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
45 List<CrmCustomerDO> customerList) { 46 List<CrmCustomerDO> customerList) {
46 - return new PageResult<>(converList(pageResult.getList(), userMap, customerList), pageResult.getTotal());  
47 - }  
48 -  
49 - default List<ContractRespVO> converList(List<CrmContractDO> contractList, Map<Long, AdminUserRespDTO> userMap,  
50 - List<CrmCustomerDO> customerList) {  
51 - List<ContractRespVO> result = convertList(contractList); 47 + PageResult<ContractRespVO> voPageResult = BeanUtils.toBean(pageResult, ContractRespVO.class);
  48 + // 拼接关联字段
52 Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId); 49 Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);
53 - result.forEach(item -> {  
54 - setUserInfo(item, userMap);  
55 - findAndThen(customerMap, item.getCustomerId(), customer -> item.setCustomerName(customer.getName())); 50 + voPageResult.getList().forEach(contract -> {
  51 + findAndThen(userMap, contract.getOwnerUserId(), user -> contract.setOwnerUserName(user.getNickname()));
  52 + findAndThen(userMap, Long.parseLong(contract.getCreator()), user -> contract.setCreatorName(user.getNickname()));
  53 + findAndThen(customerMap, contract.getCustomerId(), customer -> contract.setCustomerName(customer.getName()));
56 }); 54 });
57 - return result;  
58 - }  
59 -  
60 - static void setUserInfo(ContractRespVO contract, Map<Long, AdminUserRespDTO> userMap) {  
61 - findAndThen(userMap, contract.getOwnerUserId(), user -> contract.setOwnerUserName(user.getNickname()));  
62 - findAndThen(userMap, Long.parseLong(contract.getCreator()), user -> contract.setCreatorName(user.getNickname())); 55 + return voPageResult;
63 } 56 }
64 57
65 } 58 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerConvert.java
@@ -12,7 +12,6 @@ import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; @@ -12,7 +12,6 @@ import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
12 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 12 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
13 import org.mapstruct.Mapper; 13 import org.mapstruct.Mapper;
14 import org.mapstruct.Mapping; 14 import org.mapstruct.Mapping;
15 -import org.mapstruct.Mappings;  
16 import org.mapstruct.factory.Mappers; 15 import org.mapstruct.factory.Mappers;
17 16
18 import java.util.List; 17 import java.util.List;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java
1 package cn.iocoder.yudao.module.crm.convert.receivable; 1 package cn.iocoder.yudao.module.crm.convert.receivable;
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.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableTransferReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableTransferReqVO;
@@ -36,29 +37,19 @@ public interface CrmReceivableConvert { @@ -36,29 +37,19 @@ public interface CrmReceivableConvert {
36 37
37 CrmReceivableRespVO convert(CrmReceivableDO bean); 38 CrmReceivableRespVO convert(CrmReceivableDO bean);
38 39
39 - List<CrmReceivableRespVO> convertList(List<CrmReceivableDO> list);  
40 -  
41 default PageResult<CrmReceivableRespVO> convertPage(PageResult<CrmReceivableDO> pageResult, Map<Long, AdminUserRespDTO> userMap, 40 default PageResult<CrmReceivableRespVO> convertPage(PageResult<CrmReceivableDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
42 List<CrmCustomerDO> customerList, List<CrmContractDO> contractList) { 41 List<CrmCustomerDO> customerList, List<CrmContractDO> contractList) {
43 - return new PageResult<>(converList(pageResult.getList(), userMap, customerList, contractList), pageResult.getTotal());  
44 - }  
45 -  
46 - default List<CrmReceivableRespVO> converList(List<CrmReceivableDO> receivableList, Map<Long, AdminUserRespDTO> userMap,  
47 - List<CrmCustomerDO> customerList, List<CrmContractDO> contractList) {  
48 - List<CrmReceivableRespVO> result = convertList(receivableList); 42 + PageResult<CrmReceivableRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmReceivableRespVO.class);
  43 + // 拼接关联字段
49 Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId); 44 Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);
50 Map<Long, CrmContractDO> contractMap = convertMap(contractList, CrmContractDO::getId); 45 Map<Long, CrmContractDO> contractMap = convertMap(contractList, CrmContractDO::getId);
51 - result.forEach(item -> {  
52 - setUserInfo(item, userMap);  
53 - findAndThen(customerMap, item.getCustomerId(), customer -> item.setCustomerName(customer.getName()));  
54 - findAndThen(contractMap, item.getContractId(), contract -> item.setContractNo(contract.getNo())); 46 + voPageResult.getList().forEach(receivable -> {
  47 + findAndThen(userMap, receivable.getOwnerUserId(), user -> receivable.setOwnerUserName(user.getNickname()));
  48 + findAndThen(userMap, Long.parseLong(receivable.getCreator()), user -> receivable.setCreatorName(user.getNickname()));
  49 + findAndThen(customerMap, receivable.getCustomerId(), customer -> receivable.setCustomerName(customer.getName()));
  50 + findAndThen(contractMap, receivable.getContractId(), contract -> receivable.setContractNo(contract.getNo()));
55 }); 51 });
56 - return result;  
57 - }  
58 -  
59 - static void setUserInfo(CrmReceivableRespVO receivable, Map<Long, AdminUserRespDTO> userMap) {  
60 - findAndThen(userMap, receivable.getOwnerUserId(), user -> receivable.setOwnerUserName(user.getNickname()));  
61 - findAndThen(userMap, Long.parseLong(receivable.getCreator()), user -> receivable.setCreatorName(user.getNickname())); 52 + return voPageResult;
62 } 53 }
63 54
64 @Mapping(target = "bizId", source = "reqVO.id") 55 @Mapping(target = "bizId", source = "reqVO.id")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java
1 package cn.iocoder.yudao.module.crm.convert.receivable; 1 package cn.iocoder.yudao.module.crm.convert.receivable;
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.framework.common.util.object.BeanUtils;
4 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanTransferReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanTransferReqVO;
@@ -37,33 +38,22 @@ public interface CrmReceivablePlanConvert { @@ -37,33 +38,22 @@ public interface CrmReceivablePlanConvert {
37 38
38 CrmReceivablePlanRespVO convert(CrmReceivablePlanDO bean); 39 CrmReceivablePlanRespVO convert(CrmReceivablePlanDO bean);
39 40
40 - List<CrmReceivablePlanRespVO> convertList(List<CrmReceivablePlanDO> list);  
41 -  
42 default PageResult<CrmReceivablePlanRespVO> convertPage(PageResult<CrmReceivablePlanDO> pageResult, Map<Long, AdminUserRespDTO> userMap, 41 default PageResult<CrmReceivablePlanRespVO> convertPage(PageResult<CrmReceivablePlanDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
43 List<CrmCustomerDO> customerList, List<CrmContractDO> contractList, 42 List<CrmCustomerDO> customerList, List<CrmContractDO> contractList,
44 List<CrmReceivableDO> receivableList) { 43 List<CrmReceivableDO> receivableList) {
45 - return new PageResult<>(converList(pageResult.getList(), userMap, customerList, contractList, receivableList), pageResult.getTotal());  
46 - }  
47 -  
48 - default List<CrmReceivablePlanRespVO> converList(List<CrmReceivablePlanDO> receivablePlanList, Map<Long, AdminUserRespDTO> userMap,  
49 - List<CrmCustomerDO> customerList, List<CrmContractDO> contractList,  
50 - List<CrmReceivableDO> receivableList) {  
51 - List<CrmReceivablePlanRespVO> result = convertList(receivablePlanList); 44 + PageResult<CrmReceivablePlanRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmReceivablePlanRespVO.class);
  45 + // 拼接关联字段
52 Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId); 46 Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);
53 Map<Long, CrmContractDO> contractMap = convertMap(contractList, CrmContractDO::getId); 47 Map<Long, CrmContractDO> contractMap = convertMap(contractList, CrmContractDO::getId);
54 Map<Long, CrmReceivableDO> receivableMap = convertMap(receivableList, CrmReceivableDO::getId); 48 Map<Long, CrmReceivableDO> receivableMap = convertMap(receivableList, CrmReceivableDO::getId);
55 - result.forEach(item -> {  
56 - setUserInfo(item, userMap);  
57 - findAndThen(customerMap, item.getCustomerId(), customer -> item.setCustomerName(customer.getName()));  
58 - findAndThen(contractMap, item.getContractId(), contract -> item.setContractNo(contract.getNo()));  
59 - findAndThen(receivableMap, item.getReceivableId(), receivable -> item.setReturnType(receivable.getReturnType())); 49 + voPageResult.getList().forEach(receivablePlan -> {
  50 + findAndThen(userMap, receivablePlan.getOwnerUserId(), user -> receivablePlan.setOwnerUserName(user.getNickname()));
  51 + findAndThen(userMap, Long.parseLong(receivablePlan.getCreator()), user -> receivablePlan.setCreatorName(user.getNickname()));
  52 + findAndThen(customerMap, receivablePlan.getCustomerId(), customer -> receivablePlan.setCustomerName(customer.getName()));
  53 + findAndThen(contractMap, receivablePlan.getContractId(), contract -> receivablePlan.setContractNo(contract.getNo()));
  54 + findAndThen(receivableMap, receivablePlan.getReceivableId(), receivable -> receivablePlan.setReturnType(receivable.getReturnType()));
60 }); 55 });
61 - return result;  
62 - }  
63 -  
64 - static void setUserInfo(CrmReceivablePlanRespVO receivablePlan, Map<Long, AdminUserRespDTO> userMap) {  
65 - findAndThen(userMap, receivablePlan.getOwnerUserId(), user -> receivablePlan.setOwnerUserName(user.getNickname()));  
66 - findAndThen(userMap, Long.parseLong(receivablePlan.getCreator()), user -> receivablePlan.setCreatorName(user.getNickname())); 56 + return voPageResult;
67 } 57 }
68 58
69 @Mapping(target = "bizId", source = "reqVO.id") 59 @Mapping(target = "bizId", source = "reqVO.id")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contactbusinesslink/CrmContactBusinessLinkDO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactBusinessDO.java
1 -package cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink; 1 +package cn.iocoder.yudao.module.crm.dal.dataobject.contact;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; 4 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
5 -import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;  
6 import com.baomidou.mybatisplus.annotation.KeySequence; 5 import com.baomidou.mybatisplus.annotation.KeySequence;
7 import com.baomidou.mybatisplus.annotation.TableId; 6 import com.baomidou.mybatisplus.annotation.TableId;
8 import com.baomidou.mybatisplus.annotation.TableName; 7 import com.baomidou.mybatisplus.annotation.TableName;
9 import lombok.*; 8 import lombok.*;
10 9
11 -// TODO @zyna:可以放到 contact 包下  
12 /** 10 /**
13 - * CRM 联系人商机关联 DO 11 + * CRM 联系人与商机的关联 DO
14 * 12 *
15 * @author 芋道源码 13 * @author 芋道源码
16 */ 14 */
17 -@TableName("crm_contact_business_link")  
18 -@KeySequence("crm_contact_business_link_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 15 +@TableName("crm_contact_business")
  16 +@KeySequence("crm_contact_business_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
19 @Data 17 @Data
20 @EqualsAndHashCode(callSuper = true) 18 @EqualsAndHashCode(callSuper = true)
21 @ToString(callSuper = true) 19 @ToString(callSuper = true)
22 @Builder 20 @Builder
23 @NoArgsConstructor 21 @NoArgsConstructor
24 @AllArgsConstructor 22 @AllArgsConstructor
25 -public class CrmContactBusinessLinkDO extends BaseDO { 23 +public class CrmContactBusinessDO extends BaseDO {
26 24
27 /** 25 /**
28 * 主键 26 * 主键
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contact/CrmContactDO.java
1 package cn.iocoder.yudao.module.crm.dal.dataobject.contact; 1 package cn.iocoder.yudao.module.crm.dal.dataobject.contact;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
4 import com.baomidou.mybatisplus.annotation.KeySequence; 5 import com.baomidou.mybatisplus.annotation.KeySequence;
5 import com.baomidou.mybatisplus.annotation.TableId; 6 import com.baomidou.mybatisplus.annotation.TableId;
6 import com.baomidou.mybatisplus.annotation.TableName; 7 import com.baomidou.mybatisplus.annotation.TableName;
@@ -29,9 +30,11 @@ public class CrmContactDO extends BaseDO { @@ -29,9 +30,11 @@ public class CrmContactDO extends BaseDO {
29 @TableId 30 @TableId
30 private Long id; 31 private Long id;
31 /** 32 /**
32 - * 下次联系时间 33 + * 客户编号
  34 + *
  35 + * 关联 {@link CrmCustomerDO#getId()}
33 */ 36 */
34 - private LocalDateTime nextTime; 37 + private Long customerId;
35 /** 38 /**
36 * 手机号 39 * 手机号
37 */ 40 */
@@ -45,21 +48,20 @@ public class CrmContactDO extends BaseDO { @@ -45,21 +48,20 @@ public class CrmContactDO extends BaseDO {
45 */ 48 */
46 private String email; 49 private String email;
47 /** 50 /**
48 - * 客户编号 51 + * 所在地
  52 + *
  53 + * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段
49 */ 54 */
50 - private Long customerId; 55 + private Integer areaId;
51 /** 56 /**
52 - * 地址 57 + * 详细地址
53 */ 58 */
54 - private String address; 59 + private String detailAddress;
55 /** 60 /**
56 * 备注 61 * 备注
57 */ 62 */
58 private String remark; 63 private String remark;
59 - /**  
60 - * 最后跟进时间  
61 - */  
62 - private LocalDateTime lastTime; 64 +
63 /** 65 /**
64 * 直属上级 66 * 直属上级
65 * 67 *
@@ -100,10 +102,13 @@ public class CrmContactDO extends BaseDO { @@ -100,10 +102,13 @@ public class CrmContactDO extends BaseDO {
100 private Long ownerUserId; 102 private Long ownerUserId;
101 103
102 /** 104 /**
103 - * 所在地  
104 - *  
105 - * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段 105 + * 最后跟进时间
106 */ 106 */
107 - private Integer areaId; 107 + private LocalDateTime contactLastTime;
  108 + // TODO @puhui999:增加一个字段 contactLastContent;最后跟进内容
  109 + /**
  110 + * 下次联系时间
  111 + */
  112 + private LocalDateTime contactNextTime;
108 113
109 } 114 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java
@@ -12,7 +12,7 @@ import java.time.LocalDateTime; @@ -12,7 +12,7 @@ import java.time.LocalDateTime;
12 // TODO 芋艿:调整下字段 12 // TODO 芋艿:调整下字段
13 13
14 /** 14 /**
15 - * 客户 DO 15 + * CRM 客户 DO
16 * 16 *
17 * @author Wanwan 17 * @author Wanwan
18 */ 18 */
@@ -104,17 +104,21 @@ public class CrmCustomerDO extends BaseDO { @@ -104,17 +104,21 @@ public class CrmCustomerDO extends BaseDO {
104 */ 104 */
105 private Long ownerUserId; 105 private Long ownerUserId;
106 /** 106 /**
107 - * 地区编号 107 + * 所在地
  108 + *
  109 + * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段
108 */ 110 */
109 private Integer areaId; 111 private Integer areaId;
110 /** 112 /**
111 * 详细地址 113 * 详细地址
112 */ 114 */
113 private String detailAddress; 115 private String detailAddress;
  116 +
114 /** 117 /**
115 * 最后跟进时间 118 * 最后跟进时间
116 */ 119 */
117 private LocalDateTime contactLastTime; 120 private LocalDateTime contactLastTime;
  121 + // TODO @puhui999:增加一个字段 contactLastContent;最后跟进内容
118 /** 122 /**
119 * 下次联系时间 123 * 下次联系时间
120 */ 124 */
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.dataobject.followup;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  5 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
  6 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  7 +import com.baomidou.mybatisplus.annotation.KeySequence;
  8 +import com.baomidou.mybatisplus.annotation.TableId;
  9 +import com.baomidou.mybatisplus.annotation.TableName;
  10 +import lombok.*;
  11 +
  12 +import java.time.LocalDateTime;
  13 +import java.util.List;
  14 +
  15 +// TODO @puhui999:界面:做成一个 list 列表,字段是 id、跟进人、跟进方式、跟进时间、跟进内容、下次联系时间、关联联系人、关联商机
  16 +// TODO @puhui999:界面:记录时,弹窗,表单字段是跟进方式、跟进内容、下次联系时间、关联联系人、关联商机;其中关联联系人、关联商机,要做成对应的组件列。
  17 +/**
  18 + * 跟进记录 DO
  19 + *
  20 + * 用于记录客户、联系人的每一次跟进
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@TableName(value = "crm_follow_up_record")
  25 +@KeySequence("crm_follow_up_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  26 +@Data
  27 +@EqualsAndHashCode(callSuper = true)
  28 +@ToString(callSuper = true)
  29 +@Builder
  30 +@NoArgsConstructor
  31 +@AllArgsConstructor
  32 +public class CrmFollowUpRecordDO extends BaseDO {
  33 +
  34 + /**
  35 + * 编号
  36 + */
  37 + @TableId
  38 + private Long id;
  39 +
  40 + /**
  41 + * 数据类型
  42 + *
  43 + * 枚举 {@link CrmBizTypeEnum}
  44 + */
  45 + private Integer bizType;
  46 + /**
  47 + * 数据编号
  48 + *
  49 + * 关联 {@link CrmBizTypeEnum} 对应模块 DO 的 id 字段
  50 + */
  51 + private Long bizId;
  52 +
  53 + /**
  54 + * 跟进类型
  55 + *
  56 + * TODO @puhui999:可以搞个数据字典,打电话、发短信、上门拜访、微信、邮箱、QQ
  57 + */
  58 + private Integer type;
  59 + /**
  60 + * 跟进内容
  61 + */
  62 + private String content;
  63 + /**
  64 + * 下次联系时间
  65 + */
  66 + private LocalDateTime nextTime;
  67 +
  68 + /**
  69 + * 关联的商机编号数组
  70 + *
  71 + * 关联 {@link CrmBusinessDO#getId()}
  72 + */
  73 + private List<Long> businessIds;
  74 + /**
  75 + * 关联的联系人编号数组
  76 + *
  77 + * 关联 {@link CrmContactDO#getId()}
  78 + */
  79 + private List<Long> contactIds;
  80 +
  81 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
@@ -30,7 +30,14 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; { @@ -30,7 +30,14 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; {
30 30
31 default PageResult<CrmBusinessDO> selectPageByCustomerId(CrmBusinessPageReqVO pageReqVO) { 31 default PageResult<CrmBusinessDO> selectPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {
32 return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmBusinessDO>() 32 return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmBusinessDO>()
33 - .eq(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号 33 + .eq(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号
  34 + .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
  35 + .orderByDesc(CrmBusinessDO::getId));
  36 + }
  37 +
  38 + default PageResult<CrmBusinessDO> selectPageByContactId(CrmBusinessPageReqVO pageReqVO, Collection<Long> businessIds) {
  39 + return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmBusinessDO>()
  40 + .in(CrmBusinessDO::getId, businessIds) // 指定商机编号
34 .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName()) 41 .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
35 .orderByDesc(CrmBusinessDO::getId)); 42 .orderByDesc(CrmBusinessDO::getId));
36 } 43 }
@@ -38,11 +45,8 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; { @@ -38,11 +45,8 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; {
38 default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) { 45 default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
39 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>(); 46 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>();
40 // 拼接数据权限的查询条件 47 // 拼接数据权限的查询条件
41 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), 48 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(),
42 CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); 49 CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
43 - if (!condition) {  
44 - return PageResult.empty();  
45 - }  
46 // 拼接自身的查询条件 50 // 拼接自身的查询条件
47 query.selectAll(CrmBusinessDO.class) 51 query.selectAll(CrmBusinessDO.class)
48 .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName()) 52 .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/contactbusinesslink/CrmContactBusinessLinkMapper.java deleted
1 -package cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink;  
2 -  
3 -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.query.LambdaQueryWrapperX;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkPageReqVO;  
7 -import cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink.CrmContactBusinessLinkDO;  
8 -import org.apache.ibatis.annotations.Mapper;  
9 -  
10 -/**  
11 - * CRM 联系人商机关联 Mapper  
12 - *  
13 - * @author 芋道源码  
14 - */  
15 -@Mapper  
16 -public interface CrmContactBusinessLinkMapper extends BaseMapperX<CrmContactBusinessLinkDO> {  
17 -  
18 - default PageResult<CrmContactBusinessLinkDO> selectPage(CrmContactBusinessLinkPageReqVO reqVO) {  
19 - return selectPage(reqVO, new LambdaQueryWrapperX<CrmContactBusinessLinkDO>()  
20 - .eqIfPresent(CrmContactBusinessLinkDO::getContactId, reqVO.getContactId())  
21 - .eqIfPresent(CrmContactBusinessLinkDO::getBusinessId, reqVO.getBusinessId())  
22 - .betweenIfPresent(CrmContactBusinessLinkDO::getCreateTime, reqVO.getCreateTime())  
23 - .orderByDesc(CrmContactBusinessLinkDO::getId));  
24 - } // TODO @zyna:方法和方法之间要有空行  
25 - default PageResult<CrmContactBusinessLinkDO> selectPageByContact(CrmContactBusinessLinkPageReqVO reqVO) {  
26 - return selectPage(reqVO, new LambdaQueryWrapperX<CrmContactBusinessLinkDO>()  
27 - .eqIfPresent(CrmContactBusinessLinkDO::getContactId, reqVO.getContactId())  
28 - .orderByDesc(CrmContactBusinessLinkDO::getId));  
29 - }  
30 -}  
31 \ 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/dal/mysql/contactbusinesslink/CrmContactBusinessMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
  5 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +
  8 +import java.util.Collection;
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * CRM 联系人与商机的关联 Mapper
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Mapper
  17 +public interface CrmContactBusinessMapper extends BaseMapperX<CrmContactBusinessDO> {
  18 +
  19 + default CrmContactBusinessDO selectByContactIdAndBusinessId(Long contactId, Long businessId) {
  20 + return selectOne(CrmContactBusinessDO::getContactId, contactId,
  21 + CrmContactBusinessDO::getBusinessId, businessId);
  22 + }
  23 +
  24 + default void deleteByContactIdAndBusinessId(Long contactId, Collection<Long> businessIds) {
  25 + delete(new LambdaQueryWrapper<CrmContactBusinessDO>()
  26 + .eq(CrmContactBusinessDO::getContactId, contactId)
  27 + .in(CrmContactBusinessDO::getBusinessId, businessIds));
  28 + }
  29 +
  30 + default List<CrmContactBusinessDO> selectListByContactId(Long contactId) {
  31 + return selectList(CrmContactBusinessDO::getContactId, contactId);
  32 + }
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
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/CrmCustomerLimitConfigMapper.java
@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmC @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmC
7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
  10 +import java.util.List;
  11 +
10 /** 12 /**
11 * 客户限制配置 Mapper 13 * 客户限制配置 Mapper
12 * 14 *
@@ -21,4 +23,15 @@ public interface CrmCustomerLimitConfigMapper extends BaseMapperX&lt;CrmCustomerLim @@ -21,4 +23,15 @@ public interface CrmCustomerLimitConfigMapper extends BaseMapperX&lt;CrmCustomerLim
21 .orderByDesc(CrmCustomerLimitConfigDO::getId)); 23 .orderByDesc(CrmCustomerLimitConfigDO::getId));
22 } 24 }
23 25
  26 + default List<CrmCustomerLimitConfigDO> selectListByTypeAndUserIdAndDeptId(
  27 + Integer type, Long userId, Long deptId) {
  28 + LambdaQueryWrapperX<CrmCustomerLimitConfigDO> query = new LambdaQueryWrapperX<CrmCustomerLimitConfigDO>()
  29 + .eq(CrmCustomerLimitConfigDO::getType, type);
  30 + query.apply("FIND_IN_SET({0}, user_ids) > 0", userId);
  31 + if (deptId != null) {
  32 + query.apply("FIND_IN_SET({0}, dept_ids) > 0", deptId);
  33 + }
  34 + return selectList(query);
  35 + }
  36 +
24 } 37 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.customer; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.customer;
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.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.MPJLambdaWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@@ -9,6 +10,7 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; @@ -9,6 +10,7 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
9 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; 10 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
10 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 11 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
11 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
  13 +import org.springframework.lang.Nullable;
12 14
13 import java.util.Collection; 15 import java.util.Collection;
14 import java.util.List; 16 import java.util.List;
@@ -21,6 +23,18 @@ import java.util.List; @@ -21,6 +23,18 @@ import java.util.List;
21 @Mapper 23 @Mapper
22 public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { 24 public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
23 25
  26 + default Long selectCountByLockStatusAndOwnerUserId(Boolean lockStatus, Long ownerUserId) {
  27 + return selectCount(new LambdaUpdateWrapper<CrmCustomerDO>()
  28 + .eq(CrmCustomerDO::getLockStatus, lockStatus)
  29 + .eq(CrmCustomerDO::getOwnerUserId, ownerUserId));
  30 + }
  31 +
  32 + default Long selectCountByDealStatusAndOwnerUserId(@Nullable Boolean dealStatus, Long ownerUserId) {
  33 + return selectCount(new LambdaQueryWrapperX<CrmCustomerDO>()
  34 + .eqIfPresent(CrmCustomerDO::getDealStatus, dealStatus)
  35 + .eq(CrmCustomerDO::getOwnerUserId, ownerUserId));
  36 + }
  37 +
24 default int updateOwnerUserIdById(Long id, Long ownerUserId) { 38 default int updateOwnerUserIdById(Long id, Long ownerUserId) {
25 return update(new LambdaUpdateWrapper<CrmCustomerDO>() 39 return update(new LambdaUpdateWrapper<CrmCustomerDO>()
26 .eq(CrmCustomerDO::getId, id) 40 .eq(CrmCustomerDO::getId, id)
@@ -30,11 +44,8 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -30,11 +44,8 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
30 default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) { 44 default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
31 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); 45 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
32 // 拼接数据权限的查询条件 46 // 拼接数据权限的查询条件
33 - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), 47 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
34 CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool()); 48 CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
35 - if (!condition) {  
36 - return PageResult.empty();  
37 - }  
38 // 拼接自身的查询条件 49 // 拼接自身的查询条件
39 query.selectAll(CrmCustomerDO.class) 50 query.selectAll(CrmCustomerDO.class)
40 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) 51 .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/bizlog/function/CrmIndustryParseFunction.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/operatelog/core/CrmIndustryParseFunction.java
1 -package cn.iocoder.yudao.module.crm.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.crm.framework.operatelog.core;
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,9 +8,8 @@ import org.springframework.stereotype.Component; @@ -8,9 +8,8 @@ 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 + * 行业的 {@link IParseFunction} 实现类
14 * 13 *
15 * @author HUIHUI 14 * @author HUIHUI
16 */ 15 */
@@ -30,18 +29,10 @@ public class CrmIndustryParseFunction implements IParseFunction { @@ -30,18 +29,10 @@ public class CrmIndustryParseFunction implements IParseFunction {
30 29
31 @Override 30 @Override
32 public String apply(Object value) { 31 public String apply(Object value) {
33 - if (value == null) { 32 + if (StrUtil.isEmptyIfStr(value)) {
34 return ""; 33 return "";
35 } 34 }
36 - if (StrUtil.isEmpty(value.toString())) {  
37 - return "";  
38 - }  
39 -  
40 - // 获取行业信息  
41 - try {  
42 - return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, value.toString());  
43 - } catch (Exception ignored) {  
44 - }  
45 - return ""; 35 + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, value.toString());
46 } 36 }
  37 +
47 } 38 }
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/core/CrmLevelParseFunction.java
1 -package cn.iocoder.yudao.module.crm.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.crm.framework.operatelog.core;
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;
@@ -9,7 +9,7 @@ import org.springframework.stereotype.Component; @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component;
9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL; 9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL;
10 10
11 /** 11 /**
12 - * 自定义函数-通过客户等级编号获取客户等级信息 12 + * 客户等级的 {@link IParseFunction} 实现类
13 * 13 *
14 * @author HUIHUI 14 * @author HUIHUI
15 */ 15 */
@@ -29,18 +29,10 @@ public class CrmLevelParseFunction implements IParseFunction { @@ -29,18 +29,10 @@ public class CrmLevelParseFunction implements IParseFunction {
29 29
30 @Override 30 @Override
31 public String apply(Object value) { 31 public String apply(Object value) {
32 - if (value == null) { 32 + if (StrUtil.isEmptyIfStr(value)) {
33 return ""; 33 return "";
34 } 34 }
35 - if (StrUtil.isEmpty(value.toString())) {  
36 - return "";  
37 - }  
38 -  
39 - // 获取客户等级信息  
40 - try {  
41 - return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_LEVEL, value.toString());  
42 - } catch (Exception ignored) {  
43 - }  
44 - return ""; 35 + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_LEVEL, value.toString());
45 } 36 }
  37 +
46 } 38 }
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/core/CrmSourceParseFunction.java
1 -package cn.iocoder.yudao.module.crm.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.crm.framework.operatelog.core;
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;
@@ -9,7 +9,7 @@ import org.springframework.stereotype.Component; @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component;
9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE; 9 import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE;
10 10
11 /** 11 /**
12 - * 自定义函数-通过客户来源编号获取客户来源信息 12 + * 客户来源的 {@link IParseFunction} 实现类
13 * 13 *
14 * @author HUIHUI 14 * @author HUIHUI
15 */ 15 */
@@ -29,18 +29,10 @@ public class CrmSourceParseFunction implements IParseFunction { @@ -29,18 +29,10 @@ public class CrmSourceParseFunction implements IParseFunction {
29 29
30 @Override 30 @Override
31 public String apply(Object value) { 31 public String apply(Object value) {
32 - if (value == null) { 32 + if (StrUtil.isEmptyIfStr(value)) {
33 return ""; 33 return "";
34 } 34 }
35 - if (StrUtil.isEmpty(value.toString())) {  
36 - return "";  
37 - }  
38 -  
39 - // 获取客户来源信息  
40 - try {  
41 - return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_SOURCE, value.toString());  
42 - } catch (Exception ignored) {  
43 - }  
44 - return ""; 35 + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_SOURCE, value.toString());
45 } 36 }
  37 +
46 } 38 }
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/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.isCrmAdmin()) {
  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.module.crm.enums.permission.CrmPermissionRoleCodeEnum;
  5 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
  6 +
  7 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  8 +
  9 +/**
  10 + * 数据权限工具类
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +public class CrmPermissionUtils {
  15 +
  16 + /**
  17 + * 校验用户是否是 CRM 管理员
  18 + *
  19 + * @return 是/否
  20 + */
  21 + public static boolean isCrmAdmin() {
  22 + return SingletonManager.getPermissionApi().hasAnyRoles(getLoginUserId(), CrmPermissionRoleCodeEnum.CRM_ADMIN.getCode());
  23 + }
  24 +
  25 + /**
  26 + * 静态内部类实现单例获取
  27 + *
  28 + * @author HUIHUI
  29 + */
  30 + private static class SingletonManager {
  31 +
  32 + private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class);
  33 +
  34 + public static PermissionApi getPermissionApi() {
  35 + return PERMISSION_API;
  36 + }
  37 +
  38 + }
  39 +
  40 +}
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/CrmBusinessService.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
6 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateReqVO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  9 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
9 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
10 import jakarta.validation.Valid; 11 import jakarta.validation.Valid;
11 12
@@ -75,11 +76,21 @@ public interface CrmBusinessService { @@ -75,11 +76,21 @@ public interface CrmBusinessService {
75 * 数据权限:基于 {@link CrmCustomerDO} 读取 76 * 数据权限:基于 {@link CrmCustomerDO} 读取
76 * 77 *
77 * @param pageReqVO 分页查询 78 * @param pageReqVO 分页查询
78 - * @return 联系人分页 79 + * @return 商机分页
79 */ 80 */
80 PageResult<CrmBusinessDO> getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO); 81 PageResult<CrmBusinessDO> getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO);
81 82
82 /** 83 /**
  84 + * 获得商机分页,基于指定联系人
  85 + *
  86 + * 数据权限:基于 {@link CrmContactDO} 读取
  87 + *
  88 + * @param pageReqVO 分页参数
  89 + * @return 商机分页
  90 + */
  91 + PageResult<CrmBusinessDO> getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO);
  92 +
  93 + /**
83 * 商机转移 94 * 商机转移
84 * 95 *
85 * @param reqVO 请求 96 * @param reqVO 请求
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
@@ -9,11 +9,12 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi @@ -9,11 +9,12 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusi
9 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessUpdateReqVO;
10 import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert; 10 import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
11 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; 11 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  12 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
12 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper; 13 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
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.service.customer.CrmCustomerService; 16 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
  17 +import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;
17 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 18 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
18 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 19 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
19 import jakarta.annotation.Resource; 20 import jakarta.annotation.Resource;
@@ -25,6 +26,7 @@ import java.util.Collection; @@ -25,6 +26,7 @@ import java.util.Collection;
25 import java.util.List; 26 import java.util.List;
26 27
27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 28 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
28 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS; 30 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
29 31
30 /** 32 /**
@@ -38,24 +40,26 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -38,24 +40,26 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
38 40
39 @Resource 41 @Resource
40 private CrmBusinessMapper businessMapper; 42 private CrmBusinessMapper businessMapper;
41 - @Resource  
42 - private CrmCustomerService customerService;  
43 43
44 @Resource 44 @Resource
45 - private CrmPermissionService crmPermissionService; 45 + private CrmPermissionService permissionService;
  46 + @Resource
  47 + private CrmContactBusinessService contactBusinessService;
46 48
47 @Override 49 @Override
48 @Transactional(rollbackFor = Exception.class) 50 @Transactional(rollbackFor = Exception.class)
  51 + // TODO @商机待定:操作日志;
49 public Long createBusiness(CrmBusinessCreateReqVO createReqVO, Long userId) { 52 public Long createBusiness(CrmBusinessCreateReqVO createReqVO, Long userId) {
50 - // 插入 53 + // 1. 插入商机
51 CrmBusinessDO business = CrmBusinessConvert.INSTANCE.convert(createReqVO); 54 CrmBusinessDO business = CrmBusinessConvert.INSTANCE.convert(createReqVO);
52 businessMapper.insert(business); 55 businessMapper.insert(business);
  56 + // TODO 商机待定:插入商机与产品的关联表;校验商品存在
53 57
54 - // 创建数据权限  
55 - crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())  
56 - .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 58 + // TODO 商机待定:在联系人的详情页,如果直接【新建商机】,则需要关联下。这里要搞个 CrmContactBusinessDO 表
57 59
58 - // 返回 60 + // 2. 创建数据权限
  61 + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())
  62 + .setBizId(business.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
59 return business.getId(); 63 return business.getId();
60 } 64 }
61 65
@@ -63,12 +67,17 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -63,12 +67,17 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
63 @Transactional(rollbackFor = Exception.class) 67 @Transactional(rollbackFor = Exception.class)
64 @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", 68 @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id",
65 level = CrmPermissionLevelEnum.WRITE) 69 level = CrmPermissionLevelEnum.WRITE)
  70 + // TODO @商机待定:操作日志;
66 public void updateBusiness(CrmBusinessUpdateReqVO updateReqVO) { 71 public void updateBusiness(CrmBusinessUpdateReqVO updateReqVO) {
67 - // 校验存在 72 + // 1. 校验存在
68 validateBusinessExists(updateReqVO.getId()); 73 validateBusinessExists(updateReqVO.getId());
69 - // 更新 74 +
  75 + // 2. 更新商机
70 CrmBusinessDO updateObj = CrmBusinessConvert.INSTANCE.convert(updateReqVO); 76 CrmBusinessDO updateObj = CrmBusinessConvert.INSTANCE.convert(updateReqVO);
71 businessMapper.updateById(updateObj); 77 businessMapper.updateById(updateObj);
  78 + // TODO 商机待定:插入商机与产品的关联表;校验商品存在
  79 +
  80 + // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表
72 } 81 }
73 82
74 @Override 83 @Override
@@ -77,10 +86,12 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -77,10 +86,12 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
77 public void deleteBusiness(Long id) { 86 public void deleteBusiness(Long id) {
78 // 校验存在 87 // 校验存在
79 validateBusinessExists(id); 88 validateBusinessExists(id);
  89 + // TODO @商机待定:需要校验有没关联合同。CrmContractDO 的 businessId 字段
  90 +
80 // 删除 91 // 删除
81 businessMapper.deleteById(id); 92 businessMapper.deleteById(id);
82 // 删除数据权限 93 // 删除数据权限
83 - crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id); 94 + permissionService.deletePermission(CrmBizTypeEnum.CRM_BUSINESS.getType(), id);
84 } 95 }
85 96
86 private CrmBusinessDO validateBusinessExists(Long id) { 97 private CrmBusinessDO validateBusinessExists(Long id) {
@@ -117,18 +128,31 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -117,18 +128,31 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
117 } 128 }
118 129
119 @Override 130 @Override
  131 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#pageReqVO.contactId", level = CrmPermissionLevelEnum.READ)
  132 + public PageResult<CrmBusinessDO> getBusinessPageByContact(CrmBusinessPageReqVO pageReqVO) {
  133 + // 1. 查询关联的商机编号
  134 + List<CrmContactBusinessDO> contactBusinessList = contactBusinessService.getContactBusinessListByContactId(
  135 + pageReqVO.getContactId());
  136 + if (CollUtil.isEmpty(contactBusinessList)) {
  137 + return PageResult.empty();
  138 + }
  139 + // 2. 查询商机分页
  140 + return businessMapper.selectPageByContactId(pageReqVO,
  141 + convertSet(contactBusinessList, CrmContactBusinessDO::getBusinessId));
  142 + }
  143 +
  144 + @Override
120 @Transactional(rollbackFor = Exception.class) 145 @Transactional(rollbackFor = Exception.class)
  146 + // TODO @puhui999:操作日志
121 public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) { 147 public void transferBusiness(CrmBusinessTransferReqVO reqVO, Long userId) {
122 // 1 校验商机是否存在 148 // 1 校验商机是否存在
123 validateBusinessExists(reqVO.getId()); 149 validateBusinessExists(reqVO.getId());
124 150
125 // 2.1 数据权限转移 151 // 2.1 数据权限转移
126 - crmPermissionService.transferPermission( 152 + permissionService.transferPermission(
127 CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())); 153 CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()));
128 // 2.2 设置新的负责人 154 // 2.2 设置新的负责人
129 businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 155 businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
130 -  
131 - // 3. TODO 记录转移日志  
132 } 156 }
133 157
134 } 158 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusService.java
@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusine @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusine
7 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
8 8
9 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
  10 +
  11 +import java.util.Collection;
10 import java.util.List; 12 import java.util.List;
11 13
12 /** 14 /**
@@ -64,4 +66,12 @@ public interface CrmBusinessStatusService { @@ -64,4 +66,12 @@ public interface CrmBusinessStatusService {
64 */ 66 */
65 List<CrmBusinessStatusDO> selectList(CrmBusinessStatusQueryVO queryVO); 67 List<CrmBusinessStatusDO> selectList(CrmBusinessStatusQueryVO queryVO);
66 68
  69 + /**
  70 + * 获得商机状态列表
  71 + *
  72 + * @param ids 编号数组
  73 + * @return 商机状态列表
  74 + */
  75 + List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids);
  76 +
67 } 77 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusServiceImpl.java
@@ -12,6 +12,7 @@ import org.springframework.validation.annotation.Validated; @@ -12,6 +12,7 @@ import org.springframework.validation.annotation.Validated;
12 12
13 import jakarta.annotation.Resource; 13 import jakarta.annotation.Resource;
14 14
  15 +import java.util.Collection;
15 import java.util.List; 16 import java.util.List;
16 17
17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -77,4 +78,9 @@ public class CrmBusinessStatusServiceImpl implements CrmBusinessStatusService { @@ -77,4 +78,9 @@ public class CrmBusinessStatusServiceImpl implements CrmBusinessStatusService {
77 return businessStatusMapper.selectList(queryVO); 78 return businessStatusMapper.selectList(queryVO);
78 } 79 }
79 80
  81 + @Override
  82 + public List<CrmBusinessStatusDO> getBusinessStatusList(Collection<Long> ids) {
  83 + return businessStatusMapper.selectBatchIds(ids);
  84 + }
  85 +
80 } 86 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeService.java
@@ -5,8 +5,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusiness @@ -5,8 +5,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusiness
5 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
8 -  
9 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
  9 +
  10 +import java.util.Collection;
10 import java.util.List; 11 import java.util.List;
11 12
12 /** 13 /**
@@ -63,4 +64,12 @@ public interface CrmBusinessStatusTypeService { @@ -63,4 +64,12 @@ public interface CrmBusinessStatusTypeService {
63 */ 64 */
64 List<CrmBusinessStatusTypeDO> selectList(CrmBusinessStatusTypeQueryVO queryVO); 65 List<CrmBusinessStatusTypeDO> selectList(CrmBusinessStatusTypeQueryVO queryVO);
65 66
  67 + /**
  68 + * 获得商机状态类型列表
  69 + *
  70 + * @param ids 编号数组
  71 + * @return 商机状态类型列表
  72 + */
  73 + List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids);
  74 +
66 } 75 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessStatusTypeServiceImpl.java
@@ -17,6 +17,8 @@ import org.springframework.transaction.annotation.Transactional; @@ -17,6 +17,8 @@ import org.springframework.transaction.annotation.Transactional;
17 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
18 18
19 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
  20 +
  21 +import java.util.Collection;
20 import java.util.List; 22 import java.util.List;
21 23
22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -118,4 +120,9 @@ public class CrmBusinessStatusTypeServiceImpl implements CrmBusinessStatusTypeSe @@ -118,4 +120,9 @@ public class CrmBusinessStatusTypeServiceImpl implements CrmBusinessStatusTypeSe
118 return businessStatusTypeMapper.selectList(queryVO); 120 return businessStatusTypeMapper.selectList(queryVO);
119 } 121 }
120 122
  123 + @Override
  124 + public List<CrmBusinessStatusTypeDO> getBusinessStatusTypeList(Collection<Long> ids) {
  125 + return businessStatusTypeMapper.selectBatchIds(ids);
  126 + }
  127 +
121 } 128 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/package-info.java deleted
1 -/**  
2 - * 商机(销售机会)  
3 - */  
4 -package cn.iocoder.yudao.module.crm.service.business;  
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/clue/package-info.java deleted
1 -/**  
2 - * 线索  
3 - */  
4 -package cn.iocoder.yudao.module.crm.service.clue;  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.contact;
  2 +
  3 +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;
  4 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
  5 +import jakarta.validation.Valid;
  6 +
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * CRM 联系人与商机的关联 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface CrmContactBusinessService {
  15 +
  16 + /**
  17 + * 创建联系人与商机的关联
  18 + *
  19 + * @param createReqVO 创建信息
  20 + */
  21 + void createContactBusinessList(@Valid CrmContactBusinessReqVO createReqVO);
  22 +
  23 + /**
  24 + * 删除联系人与商机的关联
  25 + *
  26 + * @param deleteReqVO 删除信息
  27 + */
  28 + void deleteContactBusinessList(@Valid CrmContactBusinessReqVO deleteReqVO);
  29 +
  30 + /**
  31 + * 获得联系人与商机的关联列表,基于联系人编号
  32 + *
  33 + * @param contactId 联系人编号
  34 + * @return 联系人商机关联
  35 + */
  36 + List<CrmContactBusinessDO> getContactBusinessListByContactId(Long contactId);
  37 +
  38 +}
0 \ No newline at end of file 39 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.contact;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusinessReqVO;
  5 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  6 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
  7 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
  8 +import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper;
  9 +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
  10 +import jakarta.annotation.Resource;
  11 +import org.springframework.context.annotation.Lazy;
  12 +import org.springframework.stereotype.Service;
  13 +import org.springframework.validation.annotation.Validated;
  14 +
  15 +import java.util.ArrayList;
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  19 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
  20 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_NOT_EXISTS;
  21 +
  22 +// TODO @puhui999:数据权限的校验;每个操作;
  23 +/**
  24 + * 联系人与商机的关联 Service 实现类
  25 + *
  26 + * @author 芋道源码
  27 + */
  28 +@Service
  29 +@Validated
  30 +public class CrmContactBusinessServiceImpl implements CrmContactBusinessService {
  31 +
  32 + @Resource
  33 + private CrmContactBusinessMapper contactBusinessMapper;
  34 +
  35 + @Resource
  36 + @Lazy // 延迟加载,为了解决延迟加载
  37 + private CrmBusinessService businessService;
  38 + @Resource
  39 + @Lazy // 延迟加载,为了解决延迟加载
  40 + private CrmContactService contactService;
  41 +
  42 + @Override
  43 + public void createContactBusinessList(CrmContactBusinessReqVO createReqVO) {
  44 + CrmContactDO contact = contactService.getContact(createReqVO.getContactId());
  45 + if (contact == null) {
  46 + throw exception(CONTACT_NOT_EXISTS);
  47 + }
  48 + // 遍历处理,考虑到一般数量不会太多,代码处理简单
  49 + List<CrmContactBusinessDO> saveDOList = new ArrayList<>();
  50 + createReqVO.getBusinessIds().forEach(businessId -> {
  51 + CrmBusinessDO business = businessService.getBusiness(businessId);
  52 + if (business == null) {
  53 + throw exception(BUSINESS_NOT_EXISTS);
  54 + }
  55 + // 关联判重
  56 + if (contactBusinessMapper.selectByContactIdAndBusinessId(createReqVO.getContactId(), businessId) != null) {
  57 + return;
  58 + }
  59 + saveDOList.add(new CrmContactBusinessDO(null, createReqVO.getContactId(), businessId));
  60 + });
  61 + // 批量插入
  62 + if (CollUtil.isNotEmpty(saveDOList)) {
  63 + contactBusinessMapper.insertBatch(saveDOList);
  64 + }
  65 + }
  66 +
  67 + @Override
  68 + public void deleteContactBusinessList(CrmContactBusinessReqVO deleteReqVO) {
  69 + CrmContactDO contact = contactService.getContact(deleteReqVO.getContactId());
  70 + if (contact == null) {
  71 + throw exception(CONTACT_NOT_EXISTS);
  72 + }
  73 + // 直接删除
  74 + contactBusinessMapper.deleteByContactIdAndBusinessId(
  75 + deleteReqVO.getContactId(), deleteReqVO.getBusinessIds());
  76 + }
  77 +
  78 + @Override
  79 + public List<CrmContactBusinessDO> getContactBusinessListByContactId(Long contactId) {
  80 + return contactBusinessMapper.selectListByContactId(contactId);
  81 + }
  82 +
  83 +}
0 \ No newline at end of file 84 \ No newline at end of file
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;
@@ -43,33 +43,38 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -43,33 +43,38 @@ public class CrmContactServiceImpl implements CrmContactService {
43 private CrmCustomerService customerService; 43 private CrmCustomerService customerService;
44 @Resource 44 @Resource
45 private CrmPermissionService crmPermissionService; 45 private CrmPermissionService crmPermissionService;
46 -  
47 @Resource 46 @Resource
48 private AdminUserApi adminUserApi; 47 private AdminUserApi adminUserApi;
49 48
50 @Override 49 @Override
51 @Transactional(rollbackFor = Exception.class) 50 @Transactional(rollbackFor = Exception.class)
  51 + // TODO @zyna:增加操作日志,可以参考 CustomerService;内容是 新建了联系人【名字】
52 public Long createContact(CrmContactCreateReqVO createReqVO, Long userId) { 52 public Long createContact(CrmContactCreateReqVO createReqVO, Long userId) {
53 - // 1.1 校验 53 + // 1. 校验
54 validateRelationDataExists(createReqVO); 54 validateRelationDataExists(createReqVO);
55 - // 1.2 插入 55 +
  56 + // 2. 插入联系人
56 CrmContactDO contact = CrmContactConvert.INSTANCE.convert(createReqVO); 57 CrmContactDO contact = CrmContactConvert.INSTANCE.convert(createReqVO);
57 contactMapper.insert(contact); 58 contactMapper.insert(contact);
58 59
59 - // 2. 创建数据权限 60 + // 3. 创建数据权限
60 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) 61 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
61 .setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()).setBizId(contact.getId()) 62 .setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()).setBizId(contact.getId())
62 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); 63 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
  64 +
  65 + // TODO @zyna:特殊逻辑:如果在【商机】详情那,点击【新增联系人】时,可以自动绑定商机
63 return contact.getId(); 66 return contact.getId();
64 } 67 }
65 68
66 @Override 69 @Override
67 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 70 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
  71 + // TODO @zyna:增加操作日志,可以参考 CustomerService;需要 diff 出字段
68 public void updateContact(CrmContactUpdateReqVO updateReqVO) { 72 public void updateContact(CrmContactUpdateReqVO updateReqVO) {
69 // 1. 校验存在 73 // 1. 校验存在
70 validateContactExists(updateReqVO.getId()); 74 validateContactExists(updateReqVO.getId());
71 validateRelationDataExists(updateReqVO); 75 validateRelationDataExists(updateReqVO);
72 - // 2. 更新 76 +
  77 + // 2. 更新联系人
73 CrmContactDO updateObj = CrmContactConvert.INSTANCE.convert(updateReqVO); 78 CrmContactDO updateObj = CrmContactConvert.INSTANCE.convert(updateReqVO);
74 contactMapper.updateById(updateObj); 79 contactMapper.updateById(updateObj);
75 } 80 }
@@ -99,10 +104,15 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -99,10 +104,15 @@ public class CrmContactServiceImpl implements CrmContactService {
99 public void deleteContact(Long id) { 104 public void deleteContact(Long id) {
100 // 校验存在 105 // 校验存在
101 validateContactExists(id); 106 validateContactExists(id);
  107 + // TODO @zyna:如果有关联的合同,不允许删除;Contract.contactId
  108 +
102 // 删除 109 // 删除
103 contactMapper.deleteById(id); 110 contactMapper.deleteById(id);
104 // 删除数据权限 111 // 删除数据权限
105 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTACT.getType(), id); 112 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CONTACT.getType(), id);
  113 + // TODO @zyna:删除商机联系人关联
  114 +
  115 + // TODO @puhui999:删除跟进记录
106 } 116 }
107 117
108 private void validateContactExists(Long id) { 118 private void validateContactExists(Long id) {
@@ -137,6 +147,8 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -137,6 +147,8 @@ public class CrmContactServiceImpl implements CrmContactService {
137 } 147 }
138 148
139 @Override 149 @Override
  150 + // TODO @puhui999:权限校验
  151 + // TODO @puhui999:记录操作日志;将联系人【名字】转移给【新负责人】
140 public void transferContact(CrmContactTransferReqVO reqVO, Long userId) { 152 public void transferContact(CrmContactTransferReqVO reqVO, Long userId) {
141 // 1 校验联系人是否存在 153 // 1 校验联系人是否存在
142 validateContactExists(reqVO.getId()); 154 validateContactExists(reqVO.getId());
@@ -150,4 +162,4 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -150,4 +162,4 @@ public class CrmContactServiceImpl implements CrmContactService {
150 // 3. TODO 记录转移日志 162 // 3. TODO 记录转移日志
151 } 163 }
152 164
153 -} 165 -}
  166 +}
154 \ No newline at end of file 167 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contactbusinesslink/CrmContactBusinessLinkService.java deleted
1 -package cn.iocoder.yudao.module.crm.service.contactbusinesslink;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;  
5 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkPageReqVO;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkSaveReqVO;  
7 -import cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink.CrmContactBusinessLinkDO;  
8 -  
9 -import jakarta.validation.Valid;  
10 -import java.util.List;  
11 -  
12 -/**  
13 - * CRM 联系人商机关联 Service 接口  
14 - *  
15 - * @author 芋道源码  
16 - */  
17 -public interface CrmContactBusinessLinkService {  
18 -  
19 - /**  
20 - * 创建联系人商机关联  
21 - *  
22 - * @param createReqVO 创建信息  
23 - * @return 编号  
24 - */  
25 - Long createContactBusinessLink(@Valid CrmContactBusinessLinkSaveReqVO createReqVO);  
26 -  
27 - /**  
28 - * 创建联系人商机关联  
29 - *  
30 - * @param createReqVO 创建信息  
31 - */  
32 - void createContactBusinessLinkBatch(@Valid List<CrmContactBusinessLinkSaveReqVO> createReqVO);  
33 -  
34 - /**  
35 - * 更新联系人商机关联  
36 - *  
37 - * @param updateReqVO 更新信息  
38 - */  
39 - void updateContactBusinessLink(@Valid CrmContactBusinessLinkSaveReqVO updateReqVO);  
40 -  
41 - /**  
42 - * 删除联系人商机关联  
43 - *  
44 - * @param createReqVO 删除列表  
45 - */  
46 - void deleteContactBusinessLink(@Valid List<CrmContactBusinessLinkSaveReqVO> createReqVO);  
47 -  
48 - /**  
49 - * 获得联系人商机关联  
50 - *  
51 - * @param id 编号  
52 - * @return 联系人商机关联  
53 - */  
54 - CrmContactBusinessLinkDO getContactBusinessLink(Long id);  
55 -  
56 - /**  
57 - * 获得联系人商机关联分页  
58 - *  
59 - * @param pageReqVO 编号  
60 - * @return 联系人商机关联  
61 - */  
62 - PageResult<CrmBusinessRespVO> getContactBusinessLinkPageByContact(CrmContactBusinessLinkPageReqVO pageReqVO);  
63 -  
64 - /**  
65 - * 获得联系人商机关联分页  
66 - *  
67 - * @param pageReqVO 分页查询  
68 - * @return 联系人商机关联分页  
69 - */  
70 - PageResult<CrmContactBusinessLinkDO> getContactBusinessLinkPage(CrmContactBusinessLinkPageReqVO pageReqVO);  
71 -  
72 -}  
73 \ 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/service/contactbusinesslink/CrmContactBusinessLinkServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.crm.service.contactbusinesslink;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
5 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
6 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;  
8 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkPageReqVO;  
9 -import cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo.CrmContactBusinessLinkSaveReqVO;  
10 -import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;  
11 -import cn.iocoder.yudao.module.crm.convert.contactbusinessslink.CrmContactBusinessLinkConvert;  
12 -import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;  
13 -import cn.iocoder.yudao.module.crm.dal.dataobject.contactbusinesslink.CrmContactBusinessLinkDO;  
14 -import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessLinkMapper;  
15 -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;  
16 -import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;  
17 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;  
18 -import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;  
19 -import jakarta.annotation.Resource;  
20 -import org.springframework.stereotype.Service;  
21 -import org.springframework.validation.annotation.Validated;  
22 -  
23 -import java.util.List;  
24 -  
25 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
26 -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;  
27 -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_BUSINESS_LINK_NOT_EXISTS;  
28 -  
29 -// TODO @puhui999:数据权限的校验;每个操作;  
30 -/**  
31 - * 联系人商机关联 Service 实现类  
32 - *  
33 - * @author 芋道源码  
34 - */  
35 -@Service  
36 -@Validated  
37 -public class CrmContactBusinessLinkServiceImpl implements CrmContactBusinessLinkService {  
38 -  
39 - @Resource  
40 - private CrmContactBusinessLinkMapper contactBusinessLinkMapper;  
41 - @Resource  
42 - private CrmBusinessService crmBusinessService;  
43 -  
44 - @Override  
45 - public Long createContactBusinessLink(CrmContactBusinessLinkSaveReqVO createReqVO) {  
46 - CrmContactBusinessLinkDO contactBusinessLink = BeanUtils.toBean(createReqVO, CrmContactBusinessLinkDO.class);  
47 - contactBusinessLinkMapper.insert(contactBusinessLink);  
48 - return contactBusinessLink.getId();  
49 - }  
50 -  
51 - @Override  
52 - public void createContactBusinessLinkBatch(List<CrmContactBusinessLinkSaveReqVO> createReqVOList) {  
53 - // 插入  
54 - // TODO @zyna:如果已经关联过,不用重复插入;  
55 - // TODO @zyna:contact 和 business 存在校验,挪到这里,Controller 不用 @Transactional 注解,添加到这里哈。尽量业务都在 Service;  
56 - List<CrmContactBusinessLinkDO> saveDoList = CrmContactBusinessLinkConvert.INSTANCE.convert(createReqVOList);  
57 - contactBusinessLinkMapper.insertBatch(saveDoList);  
58 - }  
59 -  
60 - @Override  
61 - @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)  
62 - public void updateContactBusinessLink(CrmContactBusinessLinkSaveReqVO updateReqVO) {  
63 - // 校验存在  
64 - validateContactBusinessLinkExists(updateReqVO.getId());  
65 - // 更新  
66 - CrmContactBusinessLinkDO updateObj = BeanUtils.toBean(updateReqVO, CrmContactBusinessLinkDO.class);  
67 - contactBusinessLinkMapper.updateById(updateObj);  
68 - }  
69 -  
70 - @Override  
71 - public void deleteContactBusinessLink(List<CrmContactBusinessLinkSaveReqVO> createReqVO) {  
72 - // 删除  
73 - createReqVO.forEach(item -> {  
74 - contactBusinessLinkMapper.delete(new LambdaQueryWrapperX<CrmContactBusinessLinkDO>()  
75 - .eq(CrmContactBusinessLinkDO::getBusinessId,item.getBusinessId())  
76 - .eq(CrmContactBusinessLinkDO::getContactId,item.getContactId())  
77 - .eq(CrmContactBusinessLinkDO::getDeleted,0));  
78 - });  
79 - }  
80 -  
81 - private void validateContactBusinessLinkExists(Long id) {  
82 - if (contactBusinessLinkMapper.selectById(id) == null) {  
83 - throw exception(CONTACT_BUSINESS_LINK_NOT_EXISTS);  
84 - }  
85 - }  
86 -  
87 - @Override  
88 - @CrmPermission(bizType = CrmBizTypeEnum.CRM_BUSINESS, bizId = "#id", level = CrmPermissionLevelEnum.READ)  
89 - public CrmContactBusinessLinkDO getContactBusinessLink(Long id) {  
90 - return contactBusinessLinkMapper.selectById(id);  
91 - }  
92 -  
93 - @Override  
94 - public PageResult<CrmBusinessRespVO> getContactBusinessLinkPageByContact(CrmContactBusinessLinkPageReqVO pageReqVO) {  
95 - CrmContactBusinessLinkPageReqVO crmContactBusinessLinkPageReqVO = new CrmContactBusinessLinkPageReqVO();  
96 - crmContactBusinessLinkPageReqVO.setContactId(pageReqVO.getContactId());  
97 - PageResult<CrmContactBusinessLinkDO> businessLinkDOS = contactBusinessLinkMapper.selectPageByContact(crmContactBusinessLinkPageReqVO);  
98 - List<CrmBusinessDO> businessDOS = crmBusinessService.getBusinessList(CollectionUtils.convertList(businessLinkDOS.getList(),  
99 - CrmContactBusinessLinkDO::getBusinessId), getLoginUserId());  
100 - PageResult<CrmBusinessRespVO> pageResult = new PageResult<CrmBusinessRespVO>();  
101 - pageResult.setList(CrmBusinessConvert.INSTANCE.convert(businessDOS));  
102 - pageResult.setTotal(businessLinkDOS.getTotal());  
103 - return pageResult;  
104 -  
105 - }  
106 -  
107 - @Override  
108 - public PageResult<CrmContactBusinessLinkDO> getContactBusinessLinkPage(CrmContactBusinessLinkPageReqVO pageReqVO) {  
109 - return contactBusinessLinkMapper.selectPage(pageReqVO);  
110 - }  
111 -  
112 -}  
113 \ 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/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;
@@ -42,8 +42,10 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -42,8 +42,10 @@ public class CrmContractServiceImpl implements CrmContractService {
42 private CrmPermissionService crmPermissionService; 42 private CrmPermissionService crmPermissionService;
43 43
44 @Override 44 @Override
  45 + // TODO @puhui999:添加操作日志
45 public Long createContract(CrmContractCreateReqVO createReqVO, Long userId) { 46 public Long createContract(CrmContractCreateReqVO createReqVO, Long userId) {
46 - // 插入 47 + // TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO
  48 + // 插入合同
47 CrmContractDO contract = CrmContractConvert.INSTANCE.convert(createReqVO); 49 CrmContractDO contract = CrmContractConvert.INSTANCE.convert(createReqVO);
48 contractMapper.insert(contract); 50 contractMapper.insert(contract);
49 51
@@ -57,18 +59,26 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -57,18 +59,26 @@ public class CrmContractServiceImpl implements CrmContractService {
57 @Override 59 @Override
58 @Transactional(rollbackFor = Exception.class) 60 @Transactional(rollbackFor = Exception.class)
59 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 61 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
  62 + // TODO @puhui999:添加操作日志
60 public void updateContract(CrmContractUpdateReqVO updateReqVO) { 63 public void updateContract(CrmContractUpdateReqVO updateReqVO) {
  64 + // TODO @合同待定:只有草稿、审批中,可以编辑;
61 // 校验存在 65 // 校验存在
62 validateContractExists(updateReqVO.getId()); 66 validateContractExists(updateReqVO.getId());
63 - // 更新 67 + // 更新合同
64 CrmContractDO updateObj = CrmContractConvert.INSTANCE.convert(updateReqVO); 68 CrmContractDO updateObj = CrmContractConvert.INSTANCE.convert(updateReqVO);
65 contractMapper.updateById(updateObj); 69 contractMapper.updateById(updateObj);
  70 + // TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO
66 } 71 }
67 72
  73 + // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
  74 +
  75 + // TODO @合同待定:缺一个发起审批的接口;只有草稿可以发起审批;CrmAuditStatusEnum
  76 +
68 @Override 77 @Override
69 @Transactional(rollbackFor = Exception.class) 78 @Transactional(rollbackFor = Exception.class)
70 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 79 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
71 public void deleteContract(Long id) { 80 public void deleteContract(Long id) {
  81 + // TODO @合同待定:如果被 CrmReceivableDO 所使用,则不允许删除
72 // 校验存在 82 // 校验存在
73 validateContractExists(id); 83 validateContractExists(id);
74 // 删除 84 // 删除
@@ -112,6 +122,8 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -112,6 +122,8 @@ public class CrmContractServiceImpl implements CrmContractService {
112 122
113 @Override 123 @Override
114 @Transactional(rollbackFor = Exception.class) 124 @Transactional(rollbackFor = Exception.class)
  125 + // 3. TODO @puhui999:记录转移日志
  126 + // TODO @puhui999:权限校验,这里要搞哇?
115 public void transferContract(CrmContractTransferReqVO reqVO, Long userId) { 127 public void transferContract(CrmContractTransferReqVO reqVO, Long userId) {
116 // 1. 校验合同是否存在 128 // 1. 校验合同是否存在
117 validateContractExists(reqVO.getId()); 129 validateContractExists(reqVO.getId());
@@ -121,9 +133,7 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -121,9 +133,7 @@ public class CrmContractServiceImpl implements CrmContractService {
121 CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType())); 133 CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
122 // 2.2 设置负责人 134 // 2.2 设置负责人
123 contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 135 contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
124 -  
125 - // 3. TODO 记录转移日志  
126 -  
127 } 136 }
128 137
  138 + // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒;
129 } 139 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigService.java
@@ -5,9 +5,10 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmC @@ -5,9 +5,10 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmC
5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigUpdateReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigUpdateReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
8 -  
9 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
10 9
  10 +import java.util.List;
  11 +
11 /** 12 /**
12 * 客户限制配置 Service 接口 13 * 客户限制配置 Service 接口
13 * 14 *
@@ -53,4 +54,12 @@ public interface CrmCustomerLimitConfigService { @@ -53,4 +54,12 @@ public interface CrmCustomerLimitConfigService {
53 */ 54 */
54 PageResult<CrmCustomerLimitConfigDO> getCustomerLimitConfigPage(CrmCustomerLimitConfigPageReqVO pageReqVO); 55 PageResult<CrmCustomerLimitConfigDO> getCustomerLimitConfigPage(CrmCustomerLimitConfigPageReqVO pageReqVO);
55 56
  57 + /**
  58 + * 查询用户对应的配置列表
  59 + *
  60 + * @param type 类型
  61 + * @param userId 用户类型
  62 + */
  63 + List<CrmCustomerLimitConfigDO> getCustomerLimitConfigListByUserId(Integer type, Long userId);
  64 +
56 } 65 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerLimitConfigServiceImpl.java
1 package cn.iocoder.yudao.module.crm.service.customer; 1 package cn.iocoder.yudao.module.crm.service.customer;
2 2
  3 +import cn.hutool.core.lang.Assert;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigCreateReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigCreateReqVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;
@@ -9,12 +10,14 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfi @@ -9,12 +10,14 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfi
9 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerLimitConfigMapper; 10 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerLimitConfigMapper;
10 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 11 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
11 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 12 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  13 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
12 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
13 import org.springframework.validation.annotation.Validated; 15 import org.springframework.validation.annotation.Validated;
14 16
15 import jakarta.annotation.Resource; 17 import jakarta.annotation.Resource;
16 18
17 import java.util.Collection; 19 import java.util.Collection;
  20 +import java.util.List;
18 21
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_LIMIT_CONFIG_NOT_EXISTS; 23 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_LIMIT_CONFIG_NOT_EXISTS;
@@ -30,12 +33,14 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig @@ -30,12 +33,14 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig
30 33
31 @Resource 34 @Resource
32 private CrmCustomerLimitConfigMapper customerLimitConfigMapper; 35 private CrmCustomerLimitConfigMapper customerLimitConfigMapper;
  36 +
33 @Resource 37 @Resource
34 private DeptApi deptApi; 38 private DeptApi deptApi;
35 @Resource 39 @Resource
36 private AdminUserApi adminUserApi; 40 private AdminUserApi adminUserApi;
37 41
38 @Override 42 @Override
  43 + // TODO @puhui999:操作日志
39 public Long createCustomerLimitConfig(CrmCustomerLimitConfigCreateReqVO createReqVO) { 44 public Long createCustomerLimitConfig(CrmCustomerLimitConfigCreateReqVO createReqVO) {
40 validateUserAndDept(createReqVO.getUserIds(), createReqVO.getDeptIds()); 45 validateUserAndDept(createReqVO.getUserIds(), createReqVO.getDeptIds());
41 // 插入 46 // 插入
@@ -46,6 +51,7 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig @@ -46,6 +51,7 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig
46 } 51 }
47 52
48 @Override 53 @Override
  54 + // TODO @puhui999:操作日志
49 public void updateCustomerLimitConfig(CrmCustomerLimitConfigUpdateReqVO updateReqVO) { 55 public void updateCustomerLimitConfig(CrmCustomerLimitConfigUpdateReqVO updateReqVO) {
50 // 校验存在 56 // 校验存在
51 validateCustomerLimitConfigExists(updateReqVO.getId()); 57 validateCustomerLimitConfigExists(updateReqVO.getId());
@@ -56,6 +62,7 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig @@ -56,6 +62,7 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig
56 } 62 }
57 63
58 @Override 64 @Override
  65 + // TODO @puhui999:操作日志
59 public void deleteCustomerLimitConfig(Long id) { 66 public void deleteCustomerLimitConfig(Long id) {
60 // 校验存在 67 // 校验存在
61 validateCustomerLimitConfigExists(id); 68 validateCustomerLimitConfigExists(id);
@@ -90,4 +97,11 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig @@ -90,4 +97,11 @@ public class CrmCustomerLimitConfigServiceImpl implements CrmCustomerLimitConfig
90 adminUserApi.validateUserList(userIds); 97 adminUserApi.validateUserList(userIds);
91 } 98 }
92 99
  100 + @Override
  101 + public List<CrmCustomerLimitConfigDO> getCustomerLimitConfigListByUserId(Integer type, Long userId) {
  102 + AdminUserRespDTO user = adminUserApi.getUser(userId);
  103 + Assert.notNull(user, "用户({})不存在", userId);
  104 + return customerLimitConfigMapper.selectListByTypeAndUserIdAndDeptId(type, userId, user.getDeptId());
  105 + }
  106 +
93 } 107 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerPoolConfigServiceImpl.java
@@ -38,6 +38,7 @@ public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigSe @@ -38,6 +38,7 @@ public class CrmCustomerPoolConfigServiceImpl implements CrmCustomerPoolConfigSe
38 * @param saveReqVO 更新信息 38 * @param saveReqVO 更新信息
39 */ 39 */
40 @Override 40 @Override
  41 + // TODO @puhui999:操作日志
41 public void saveCustomerPoolConfig(CrmCustomerPoolConfigSaveReqVO saveReqVO) { 42 public void saveCustomerPoolConfig(CrmCustomerPoolConfigSaveReqVO saveReqVO) {
42 // 存在,则进行更新 43 // 存在,则进行更新
43 CrmCustomerPoolConfigDO dbConfig = getCustomerPoolConfig(); 44 CrmCustomerPoolConfigDO dbConfig = getCustomerPoolConfig();
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
1 package cn.iocoder.yudao.module.crm.service.customer; 1 package cn.iocoder.yudao.module.crm.service.customer;
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.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO;  
5 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerUpdateReqVO; 4 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
9 import jakarta.validation.Valid; 6 import jakarta.validation.Valid;
10 7
@@ -56,7 +53,7 @@ public interface CrmCustomerService { @@ -56,7 +53,7 @@ public interface CrmCustomerService {
56 * @return 客户列表 53 * @return 客户列表
57 * @author ljlleo 54 * @author ljlleo
58 */ 55 */
59 - List<CrmCustomerDO> getCustomerList(Collection<Long> ids, Long userId); 56 + List<CrmCustomerDO> getCustomerList(Collection<Long> ids);
60 57
61 /** 58 /**
62 * 获得客户分页 59 * 获得客户分页
@@ -85,9 +82,10 @@ public interface CrmCustomerService { @@ -85,9 +82,10 @@ public interface CrmCustomerService {
85 /** 82 /**
86 * 锁定/解锁客户 83 * 锁定/解锁客户
87 * 84 *
88 - * @param updateReqVO 更新信息 85 + * @param lockReqVO 更新信息
  86 + * @param userId 用户编号
89 */ 87 */
90 - void lockCustomer(@Valid CrmCustomerUpdateReqVO updateReqVO); 88 + void lockCustomer(@Valid CrmCustomerLockReqVO lockReqVO, Long userId);
91 89
92 // ==================== 公海相关操作 ==================== 90 // ==================== 公海相关操作 ====================
93 91
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -2,17 +2,16 @@ package cn.iocoder.yudao.module.crm.service.customer; @@ -2,17 +2,16 @@ package cn.iocoder.yudao.module.crm.service.customer;
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.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 -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.CrmCustomerTransferReqVO;  
9 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerUpdateReqVO; 7 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
10 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; 8 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
11 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  10 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
12 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; 11 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 12 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 13 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
15 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 14 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
16 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 15 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
17 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 16 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 17 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -24,12 +23,15 @@ import org.springframework.stereotype.Service; @@ -24,12 +23,15 @@ import org.springframework.stereotype.Service;
24 import org.springframework.transaction.annotation.Transactional; 23 import org.springframework.transaction.annotation.Transactional;
25 import org.springframework.validation.annotation.Validated; 24 import org.springframework.validation.annotation.Validated;
26 25
  26 +import java.time.LocalDateTime;
27 import java.util.*; 27 import java.util.*;
28 28
29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
30 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 30 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
31 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER; 31 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER;
32 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS; 32 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS;
  33 +import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
  34 +import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT;
33 import static java.util.Collections.singletonList; 35 import static java.util.Collections.singletonList;
34 36
35 /** 37 /**
@@ -45,50 +47,71 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -45,50 +47,71 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
45 private CrmCustomerMapper customerMapper; 47 private CrmCustomerMapper customerMapper;
46 48
47 @Resource 49 @Resource
48 - private CrmPermissionService crmPermissionService; 50 + private CrmPermissionService permissionService;
  51 + @Resource
  52 + private CrmCustomerLimitConfigService customerLimitConfigService;
49 53
50 @Resource 54 @Resource
51 private AdminUserApi adminUserApi; 55 private AdminUserApi adminUserApi;
52 56
53 @Override 57 @Override
54 @Transactional(rollbackFor = Exception.class) 58 @Transactional(rollbackFor = Exception.class)
  59 + @LogRecord(type = CRM_CUSTOMER, subType = "创建客户", bizNo = "{{#customerId}}", success = "创建了客户") // TODO @puhui999:创建了客户【客户名】,要记录进去;不然在展示操作日志的全列表,看不清楚是哪个客户哈;
55 public Long createCustomer(CrmCustomerCreateReqVO createReqVO, Long userId) { 60 public Long createCustomer(CrmCustomerCreateReqVO createReqVO, Long userId) {
56 - // 插入  
57 - CrmCustomerDO customer = CrmCustomerConvert.INSTANCE.convert(createReqVO); 61 + // 1. 校验拥有客户是否到达上限
  62 + validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);
  63 +
  64 + // 2. 插入客户
  65 + CrmCustomerDO customer = CrmCustomerConvert.INSTANCE.convert(createReqVO)
  66 + .setLockStatus(false).setDealStatus(false)
  67 + .setContactLastTime(LocalDateTime.now());
  68 + // TODO @puhui999:可能要加个 receiveTime 字段,记录最后接收时间
58 customerMapper.insert(customer); 69 customerMapper.insert(customer);
59 70
60 - // 创建数据权限  
61 - crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) 71 + // 3. 创建数据权限
  72 + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
62 .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 73 .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
  74 +
  75 + // 4. 记录操作日志
  76 + LogRecordContext.putVariable("customerId", customer.getId());
63 return customer.getId(); 77 return customer.getId();
64 } 78 }
65 79
66 @Override 80 @Override
67 @Transactional(rollbackFor = Exception.class) 81 @Transactional(rollbackFor = Exception.class)
68 - @LogRecord(success = "更新了客户{_DIFF{#updateReqVO}}", type = CRM_CUSTOMER, subType = "更新客户", bizNo = "{{#updateReqVO.id}}") 82 + @LogRecord(type = CRM_CUSTOMER, subType = "更新客户", bizNo = "{{#updateReqVO.id}}", success = "更新了客户{_DIFF{#updateReqVO}}", extra = "{{#extra}}")
69 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 83 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
70 public void updateCustomer(CrmCustomerUpdateReqVO updateReqVO) { 84 public void updateCustomer(CrmCustomerUpdateReqVO updateReqVO) {
71 - // 校验存在  
72 - CrmCustomerDO oldCustomerDO = validateCustomerExists(updateReqVO.getId()); 85 + // TODO @puhui999:更新的时候,要把 updateReqVO 负责人设置为空,避免修改。
  86 + // 1. 校验存在
  87 + CrmCustomerDO oldCustomer = validateCustomerExists(updateReqVO.getId());
73 88
74 - // __DIFF 函数传递了一个参数,传递的参数是修改之后的对象,这种方式需要在方法内部向 LogRecordContext 中 put 一个变量,代表是之前的对象,这个对象可以是null  
75 - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomerDO, CrmCustomerUpdateReqVO.class));  
76 - // 更新 89 + // 2. 更新客户
77 CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO); 90 CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO);
78 customerMapper.updateById(updateObj); 91 customerMapper.updateById(updateObj);
  92 +
  93 + // 3. 记录操作日志
  94 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CrmCustomerUpdateReqVO.class));
  95 + // TODO 扩展信息测试 @puhui999:看着没啥问题,可以删除啦;
  96 + HashMap<String, Object> extra = new HashMap<>();
  97 + extra.put("tips", "随便记录一点啦");
  98 + LogRecordContext.putVariable("extra", extra);
79 } 99 }
80 100
81 @Override 101 @Override
82 @Transactional(rollbackFor = Exception.class) 102 @Transactional(rollbackFor = Exception.class)
  103 + @LogRecord(type = CRM_CUSTOMER, subType = "删除客户", bizNo = "{{#id}}", success = "删除了客户")
83 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 104 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
84 public void deleteCustomer(Long id) { 105 public void deleteCustomer(Long id) {
85 // 校验存在 106 // 校验存在
86 validateCustomerExists(id); 107 validateCustomerExists(id);
  108 + // TODO @puhui999:如果有联系人、商机,则不允许删除;
87 109
88 // 删除 110 // 删除
89 customerMapper.deleteById(id); 111 customerMapper.deleteById(id);
90 // 删除数据权限 112 // 删除数据权限
91 - crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); 113 + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
  114 + // TODO @puhui999:删除跟进记录
92 } 115 }
93 116
94 private CrmCustomerDO validateCustomerExists(Long id) { 117 private CrmCustomerDO validateCustomerExists(Long id) {
@@ -106,11 +129,11 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -106,11 +129,11 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
106 } 129 }
107 130
108 @Override 131 @Override
109 - public List<CrmCustomerDO> getCustomerList(Collection<Long> ids, Long userId) { 132 + public List<CrmCustomerDO> getCustomerList(Collection<Long> ids) {
110 if (CollUtil.isEmpty(ids)) { 133 if (CollUtil.isEmpty(ids)) {
111 return Collections.emptyList(); 134 return Collections.emptyList();
112 } 135 }
113 - return customerMapper.selectBatchIds(ids, userId); 136 + return customerMapper.selectBatchIds(ids);
114 } 137 }
115 138
116 @Override 139 @Override
@@ -125,50 +148,96 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -125,50 +148,96 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
125 */ 148 */
126 @Override 149 @Override
127 public void validateCustomer(Long customerId) { 150 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 - } 151 + validateCustomerExists(customerId);
136 } 152 }
137 153
138 @Override 154 @Override
139 @Transactional(rollbackFor = Exception.class) 155 @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}}") 156 + @LogRecord(type = CRM_CUSTOMER, subType = "转移客户", bizNo = "{{#reqVO.id}}", success = TRANSFER_CUSTOMER_LOG_SUCCESS)
142 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) 157 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
143 public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) { 158 public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {
144 - // 1. 校验客户是否存在  
145 - validateCustomer(reqVO.getId());  
146 - // 添加 crmCustomer 到日志上下文 TODO 日志记录放在 service 里是因为已经过了权限校验查询时不用走两次校验  
147 - // TODO @puhui999:customer 不用查询,从 1. 拿到哈;然后 put这个动作,可以放到 3.;这样逻辑结构就是,校验、逻辑、日志,更加清晰  
148 - LogRecordContext.putVariable("crmCustomer", customerMapper.selectById(reqVO.getId())); 159 + // 1.1 校验客户是否存在
  160 + CrmCustomerDO customer = validateCustomerExists(reqVO.getId());
  161 + // 1.2 校验拥有客户是否到达上限
  162 + validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1);
  163 +
149 // 2.1 数据权限转移 164 // 2.1 数据权限转移
150 - crmPermissionService.transferPermission( 165 + permissionService.transferPermission(
151 CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())); 166 CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()));
152 // 2.2 转移后重新设置负责人 167 // 2.2 转移后重新设置负责人
153 customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 168 customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
154 169
155 // 3. TODO 记录转移日志 170 // 3. TODO 记录转移日志
  171 + LogRecordContext.putVariable("crmCustomer", customer);
156 } 172 }
157 173
158 @Override 174 @Override
159 - public void lockCustomer(CrmCustomerUpdateReqVO updateReqVO) {  
160 - // 校验存在  
161 - validateCustomerExists(updateReqVO.getId());  
162 - // TODO @Joey:可以校验下,如果已经对应的锁定状态,报个业务异常;原因是:后续这个业务会记录操作日志,会记录多了;  
163 - // TODO @芋艿:业务完善,增加锁定上限; 175 + // TODO @puhui999:看看这个能不能根据条件,写操作日志;
  176 + // TODO 如果是 锁定,则 subType 为 锁定客户;success 为 将客户【】锁定
  177 + // TODO 如果是 解锁,则 subType 为 解锁客户;success 为 将客户【】解锁
  178 + @LogRecord(type = CRM_CUSTOMER, subType = "锁定/解锁客户", bizNo = "{{#updateReqVO.id}}", success = "锁定了客户")
  179 + // TODO @puhui999:数据权限
  180 + public void lockCustomer(CrmCustomerLockReqVO lockReqVO, Long userId) {
  181 + // 1.1 校验当前客户是否存在
  182 + validateCustomerExists(lockReqVO.getId());
  183 + // 1.2 校验当前是否重复操作锁定/解锁状态
  184 + CrmCustomerDO customer = customerMapper.selectById(lockReqVO.getId());
  185 + if (customer.getLockStatus().equals(lockReqVO.getLockStatus())) {
  186 + throw exception(customer.getLockStatus() ? CUSTOMER_LOCK_FAIL_IS_LOCK : CUSTOMER_UNLOCK_FAIL_IS_UNLOCK);
  187 + }
  188 + // 1.3 校验锁定上限。
  189 + if (lockReqVO.getLockStatus()) {
  190 + validateCustomerExceedLockLimit(userId);
  191 + }
164 192
165 - // 更新  
166 - CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO);  
167 - customerMapper.updateById(updateObj); 193 + // 2. 更新锁定状态
  194 + customerMapper.updateById(BeanUtils.toBean(lockReqVO, CrmCustomerDO.class));
  195 + }
  196 +
  197 + /**
  198 + * 校验用户拥有的客户数量,是否到达上限
  199 + *
  200 + * @param userId 用户编号
  201 + * @param newCount 附加数量
  202 + */
  203 + private void validateCustomerExceedOwnerLimit(Long userId, int newCount) {
  204 + List<CrmCustomerLimitConfigDO> limitConfigs = customerLimitConfigService.getCustomerLimitConfigListByUserId(
  205 + CUSTOMER_OWNER_LIMIT.getType(), userId);
  206 + if (CollUtil.isEmpty(limitConfigs)) {
  207 + return;
  208 + }
  209 + Long ownerCount = customerMapper.selectCountByDealStatusAndOwnerUserId(null, userId);
  210 + Long dealOwnerCount = customerMapper.selectCountByDealStatusAndOwnerUserId(true, userId);
  211 + limitConfigs.forEach(limitConfig -> {
  212 + long nowCount = limitConfig.getDealCountEnabled() ? ownerCount : ownerCount - dealOwnerCount;
  213 + if (nowCount + newCount > limitConfig.getMaxCount()) {
  214 + throw exception(CUSTOMER_OWNER_EXCEED_LIMIT);
  215 + }
  216 + });
  217 + }
  218 +
  219 + /**
  220 + * 校验用户锁定的客户数量,是否到达上限
  221 + *
  222 + * @param userId 用户编号
  223 + */
  224 + private void validateCustomerExceedLockLimit(Long userId) {
  225 + List<CrmCustomerLimitConfigDO> limitConfigs = customerLimitConfigService.getCustomerLimitConfigListByUserId(
  226 + CUSTOMER_LOCK_LIMIT.getType(), userId);
  227 + if (CollUtil.isEmpty(limitConfigs)) {
  228 + return;
  229 + }
  230 + Long lockCount = customerMapper.selectCountByLockStatusAndOwnerUserId(true, userId);
  231 + Integer maxCount = CollectionUtils.getMaxValue(limitConfigs, CrmCustomerLimitConfigDO::getMaxCount);
  232 + assert maxCount != null;
  233 + if (lockCount >= maxCount) {
  234 + throw exception(CUSTOMER_LOCK_EXCEED_LIMIT);
  235 + }
168 } 236 }
169 237
170 @Override 238 @Override
171 @Transactional(rollbackFor = Exception.class) 239 @Transactional(rollbackFor = Exception.class)
  240 + @LogRecord(type = CRM_CUSTOMER, subType = "客户放入公海", bizNo = "{{#id}}", success = "将客户放入了公海") // TODO @puhui999:将客户【】放入了公海
172 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 241 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
173 public void putCustomerPool(Long id) { 242 public void putCustomerPool(Long id) {
174 // 1. 校验存在 243 // 1. 校验存在
@@ -187,12 +256,18 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -187,12 +256,18 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
187 throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL); 256 throw exception(CUSTOMER_UPDATE_OWNER_USER_FAIL);
188 } 257 }
189 // 3. 删除负责人数据权限 258 // 3. 删除负责人数据权限
190 - crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(), 259 + permissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customer.getId(),
191 CrmPermissionLevelEnum.OWNER.getLevel()); 260 CrmPermissionLevelEnum.OWNER.getLevel());
  261 + // TODO @puhui999:联系人的负责人,也要设置为 null;这块和领取是对应的;因为领取后,负责人也要关联过来;
192 } 262 }
193 263
194 @Override 264 @Override
195 @Transactional(rollbackFor = Exception.class) 265 @Transactional(rollbackFor = Exception.class)
  266 + // TODO @puhui999:权限校验
  267 +
  268 + // TODO @puhui999:如果是分配,操作日志是 “将客户【】分配给【】”
  269 + // TODO @puhui999:如果是领取,操作日志是“领取客户【】”;
  270 + // TODO @puhui999:如果是多条,则需要记录多条操作日志;不然 bizId 不好关联
196 public void receiveCustomer(List<Long> ids, Long ownerUserId) { 271 public void receiveCustomer(List<Long> ids, Long ownerUserId) {
197 // 1.1 校验存在 272 // 1.1 校验存在
198 List<CrmCustomerDO> customers = customerMapper.selectBatchIds(ids); 273 List<CrmCustomerDO> customers = customerMapper.selectBatchIds(ids);
@@ -210,8 +285,10 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -210,8 +285,10 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
210 // 校验成交状态 285 // 校验成交状态
211 validateCustomerDeal(customer); 286 validateCustomerDeal(customer);
212 }); 287 });
  288 + // 1.4 校验负责人是否到达上限
  289 + validateCustomerExceedOwnerLimit(ownerUserId, customers.size());
213 290
214 - // 2. 领取公海数据 291 + // 2.1 领取公海数据
215 List<CrmCustomerDO> updateCustomers = new ArrayList<>(); 292 List<CrmCustomerDO> updateCustomers = new ArrayList<>();
216 List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>(); 293 List<CrmPermissionCreateReqBO> createPermissions = new ArrayList<>();
217 customers.forEach(customer -> { 294 customers.forEach(customer -> {
@@ -221,11 +298,11 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -221,11 +298,11 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
221 createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) 298 createPermissions.add(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
222 .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); 299 .setBizId(customer.getId()).setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
223 }); 300 });
224 -  
225 - // 3.1 更新客户负责人 301 + // 2.2 更新客户负责人
226 customerMapper.updateBatch(updateCustomers); 302 customerMapper.updateBatch(updateCustomers);
227 - // 3.2 创建负责人数据权限  
228 - crmPermissionService.createPermissionBatch(createPermissions); 303 + // 2.3 创建负责人数据权限
  304 + permissionService.createPermissionBatch(createPermissions);
  305 + // TODO @芋艿:要不要处理关联的联系人???
229 } 306 }
230 307
231 private void validateCustomerOwnerExists(CrmCustomerDO customer, Boolean pool) { 308 private void validateCustomerOwnerExists(CrmCustomerDO customer, Boolean pool) {
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,8 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { @@ -89,8 +90,8 @@ 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 // 不是拥有者,并且不是超管
  94 + || (!isOwner(oldPermission.getLevel()) && !CrmPermissionUtils.isCrmAdmin())) {
94 throw exception(CRM_PERMISSION_DENIED, bizTypeName); 95 throw exception(CRM_PERMISSION_DENIED, bizTypeName);
95 } 96 }
96 // 1.1 校验转移对象是否已经是该负责人 97 // 1.1 校验转移对象是否已经是该负责人
@@ -138,7 +139,6 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { @@ -138,7 +139,6 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
138 } 139 }
139 140
140 @Override 141 @Override
141 - @Transactional(rollbackFor = Exception.class) // TODO @puhui999:这里不用加的,就一个操作哈;  
142 public void deletePermission(Integer bizType, Long bizId) { 142 public void deletePermission(Integer bizType, Long bizId) {
143 int deletedCount = crmPermissionMapper.deletePermission(bizType, bizId); 143 int deletedCount = crmPermissionMapper.deletePermission(bizType, bizId);
144 if (deletedCount == 0) { 144 if (deletedCount == 0) {
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImpl.java
@@ -35,18 +35,20 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService @@ -35,18 +35,20 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService
35 private CrmProductService crmProductService; 35 private CrmProductService crmProductService;
36 36
37 @Override 37 @Override
  38 + // TODO @puhui999:操作日志
38 public Long createProductCategory(CrmProductCategoryCreateReqVO createReqVO) { 39 public Long createProductCategory(CrmProductCategoryCreateReqVO createReqVO) {
39 // 1.1 校验父分类存在 40 // 1.1 校验父分类存在
40 validateParentProductCategory(createReqVO.getParentId()); 41 validateParentProductCategory(createReqVO.getParentId());
41 // 1.2 分类名称是否存在 42 // 1.2 分类名称是否存在
42 validateProductNameExists(null, createReqVO.getParentId(), createReqVO.getName()); 43 validateProductNameExists(null, createReqVO.getParentId(), createReqVO.getName());
43 - // 2. 插入 44 + // 2. 插入分类
44 CrmProductCategoryDO category = BeanUtils.toBean(createReqVO, CrmProductCategoryDO.class); 45 CrmProductCategoryDO category = BeanUtils.toBean(createReqVO, CrmProductCategoryDO.class);
45 productCategoryMapper.insert(category); 46 productCategoryMapper.insert(category);
46 return category.getId(); 47 return category.getId();
47 } 48 }
48 49
49 @Override 50 @Override
  51 + // TODO @puhui999:操作日志
50 public void updateProductCategory(CrmProductCategoryCreateReqVO updateReqVO) { 52 public void updateProductCategory(CrmProductCategoryCreateReqVO updateReqVO) {
51 // 1.1 校验存在 53 // 1.1 校验存在
52 validateProductCategoryExists(updateReqVO.getId()); 54 validateProductCategoryExists(updateReqVO.getId());
@@ -54,7 +56,7 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService @@ -54,7 +56,7 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService
54 validateParentProductCategory(updateReqVO.getParentId()); 56 validateParentProductCategory(updateReqVO.getParentId());
55 // 1.3 分类名称是否存在 57 // 1.3 分类名称是否存在
56 validateProductNameExists(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); 58 validateProductNameExists(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
57 - // 2. 更新 59 + // 2. 更新分类
58 CrmProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, CrmProductCategoryDO.class); 60 CrmProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, CrmProductCategoryDO.class);
59 productCategoryMapper.updateById(updateObj); 61 productCategoryMapper.updateById(updateObj);
60 } 62 }
@@ -91,19 +93,19 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService @@ -91,19 +93,19 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService
91 } 93 }
92 94
93 @Override 95 @Override
  96 + // TODO @puhui999:操作日志
94 public void deleteProductCategory(Long id) { 97 public void deleteProductCategory(Long id) {
95 - // TODO zange:参考 mall: ProductCategoryServiceImpl 补充下必要的参数校验;  
96 - // 校验存在 98 + // 1.1 校验存在
97 validateProductCategoryExists(id); 99 validateProductCategoryExists(id);
98 - // 校验是否还有子分类 100 + // 1.2 校验是否还有子分类
99 if (productCategoryMapper.selectCountByParentId(id) > 0) { 101 if (productCategoryMapper.selectCountByParentId(id) > 0) {
100 throw exception(product_CATEGORY_EXISTS_CHILDREN); 102 throw exception(product_CATEGORY_EXISTS_CHILDREN);
101 } 103 }
102 - // 校验是否被产品使用 104 + // 1.3 校验是否被产品使用
103 if (crmProductService.getProductByCategoryId(id) !=null) { 105 if (crmProductService.getProductByCategoryId(id) !=null) {
104 throw exception(PRODUCT_CATEGORY_USED); 106 throw exception(PRODUCT_CATEGORY_USED);
105 } 107 }
106 - // 删除 108 + // 2. 删除
107 productCategoryMapper.deleteById(id); 109 productCategoryMapper.deleteById(id);
108 } 110 }
109 111
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java
@@ -48,6 +48,7 @@ public class CrmProductServiceImpl implements CrmProductService { @@ -48,6 +48,7 @@ public class CrmProductServiceImpl implements CrmProductService {
48 private AdminUserApi adminUserApi; 48 private AdminUserApi adminUserApi;
49 49
50 @Override 50 @Override
  51 + // TODO @puhui999:操作日志
51 public Long createProduct(CrmProductSaveReqVO createReqVO) { 52 public Long createProduct(CrmProductSaveReqVO createReqVO) {
52 // 校验产品 53 // 校验产品
53 adminUserApi.validateUserList(Collections.singleton(createReqVO.getOwnerUserId())); 54 adminUserApi.validateUserList(Collections.singleton(createReqVO.getOwnerUserId()));
@@ -66,6 +67,7 @@ public class CrmProductServiceImpl implements CrmProductService { @@ -66,6 +67,7 @@ public class CrmProductServiceImpl implements CrmProductService {
66 } 67 }
67 68
68 @Override 69 @Override
  70 + // TODO @puhui999:操作日志
69 public void updateProduct(CrmProductSaveReqVO updateReqVO) { 71 public void updateProduct(CrmProductSaveReqVO updateReqVO) {
70 // 校验产品 72 // 校验产品
71 updateReqVO.setOwnerUserId(null); // 不修改负责人 73 updateReqVO.setOwnerUserId(null); // 不修改负责人
@@ -102,6 +104,7 @@ public class CrmProductServiceImpl implements CrmProductService { @@ -102,6 +104,7 @@ public class CrmProductServiceImpl implements CrmProductService {
102 } 104 }
103 105
104 @Override 106 @Override
  107 + // TODO @puhui999:操作日志
105 public void deleteProduct(Long id) { 108 public void deleteProduct(Long id) {
106 // 校验存在 109 // 校验存在
107 validateProductExists(id); 110 validateProductExists(id);
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/package-info.java deleted
1 -/**  
2 - * 产品表  
3 - */  
4 -package cn.iocoder.yudao.module.crm.service.product;  
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;
@@ -52,7 +52,10 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -52,7 +52,10 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
52 private CrmPermissionService crmPermissionService; 52 private CrmPermissionService crmPermissionService;
53 53
54 @Override 54 @Override
  55 + // TODO @puhui999:操作日志
55 public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO, Long userId) { 56 public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO, Long userId) {
  57 + // TODO @liuhongfeng:第几期的计算;基于是 contractId + contractDO 的第几个还款
  58 + // TODO @liuhongfeng contractId:校验合同是否存在
56 // 插入 59 // 插入
57 CrmReceivablePlanDO receivablePlan = CrmReceivablePlanConvert.INSTANCE.convert(createReqVO); 60 CrmReceivablePlanDO receivablePlan = CrmReceivablePlanConvert.INSTANCE.convert(createReqVO);
58 receivablePlan.setFinishStatus(false); 61 receivablePlan.setFinishStatus(false);
@@ -87,7 +90,9 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -87,7 +90,9 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
87 90
88 @Override 91 @Override
89 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 92 @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
  93 + // TODO @puhui999:操作日志
90 public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) { 94 public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) {
  95 + // TODO @liuhongfeng:如果已经有对应的还款,则不允许编辑;
91 // 校验存在 96 // 校验存在
92 validateReceivablePlanExists(updateReqVO.getId()); 97 validateReceivablePlanExists(updateReqVO.getId());
93 98
@@ -136,6 +141,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -136,6 +141,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
136 return receivablePlanMapper.selectPageByCustomerId(pageReqVO); 141 return receivablePlanMapper.selectPageByCustomerId(pageReqVO);
137 } 142 }
138 143
  144 + // TODO @puhui999:这个没有 transfer 接口;可能是的哈
139 @Override 145 @Override
140 public void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId) { 146 public void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId) {
141 // 1 校验回款计划是否存在 147 // 1 校验回款计划是否存在
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;
@@ -53,10 +53,10 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -53,10 +53,10 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
53 @Resource 53 @Resource
54 private CrmPermissionService crmPermissionService; 54 private CrmPermissionService crmPermissionService;
55 55
56 - // TODO @liuhongfeng:创建还款后,是不是什么时候,要更新 plan?  
57 @Override 56 @Override
  57 + // TODO @puhui999:操作日志
58 public Long createReceivable(CrmReceivableCreateReqVO createReqVO) { 58 public Long createReceivable(CrmReceivableCreateReqVO createReqVO) {
59 - // 插入 59 + // 插入还款
60 CrmReceivableDO receivable = CrmReceivableConvert.INSTANCE.convert(createReqVO); 60 CrmReceivableDO receivable = CrmReceivableConvert.INSTANCE.convert(createReqVO);
61 if (ObjectUtil.isNull(receivable.getAuditStatus())) { 61 if (ObjectUtil.isNull(receivable.getAuditStatus())) {
62 receivable.setAuditStatus(CommonStatusEnum.ENABLE.getStatus()); 62 receivable.setAuditStatus(CommonStatusEnum.ENABLE.getStatus());
@@ -64,15 +64,17 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -64,15 +64,17 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
64 receivable.setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus()); 64 receivable.setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
65 65
66 // TODO @liuhongfeng:一般来说,逻辑的写法,是要先检查,后操作 db;所以,你这个 check 应该放到 CrmReceivableDO receivable 之前; 66 // TODO @liuhongfeng:一般来说,逻辑的写法,是要先检查,后操作 db;所以,你这个 check 应该放到 CrmReceivableDO receivable 之前;
67 - // 校验  
68 checkReceivable(receivable); 67 checkReceivable(receivable);
69 68
70 receivableMapper.insert(receivable); 69 receivableMapper.insert(receivable);
  70 +
  71 + // TODO @liuhongfeng:需要更新关联的 plan
71 return receivable.getId(); 72 return receivable.getId();
72 } 73 }
73 74
74 // TODO @liuhongfeng:这里的括号要注意排版; 75 // TODO @liuhongfeng:这里的括号要注意排版;
75 private void checkReceivable(CrmReceivableDO receivable) { 76 private void checkReceivable(CrmReceivableDO receivable) {
  77 + // TODO @liuhongfeng:校验 no 的唯一性
76 // TODO @liuhongfeng:这个放在参数校验合适 78 // TODO @liuhongfeng:这个放在参数校验合适
77 if (ObjectUtil.isNull(receivable.getContractId())) { 79 if (ObjectUtil.isNull(receivable.getContractId())) {
78 throw exception(CONTRACT_NOT_EXISTS); 80 throw exception(CONTRACT_NOT_EXISTS);
@@ -96,17 +98,29 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -96,17 +98,29 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
96 } 98 }
97 99
98 @Override 100 @Override
  101 + // TODO @puhui999:操作日志
  102 + // TODO @puhui999:权限校验
99 public void updateReceivable(CrmReceivableUpdateReqVO updateReqVO) { 103 public void updateReceivable(CrmReceivableUpdateReqVO updateReqVO) {
100 // 校验存在 104 // 校验存在
101 validateReceivableExists(updateReqVO.getId()); 105 validateReceivableExists(updateReqVO.getId());
  106 + // TODO @liuhongfeng:只有在草稿、审核中,可以提交修改
102 107
103 - // 更新 108 + // 更新还款
104 CrmReceivableDO updateObj = CrmReceivableConvert.INSTANCE.convert(updateReqVO); 109 CrmReceivableDO updateObj = CrmReceivableConvert.INSTANCE.convert(updateReqVO);
105 receivableMapper.updateById(updateObj); 110 receivableMapper.updateById(updateObj);
  111 +
  112 + // TODO @liuhongfeng:需要更新关联的 plan
106 } 113 }
107 114
  115 + // TODO @liuhongfeng:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
  116 +
  117 + // TODO @liuhongfeng:缺一个发起审批的接口;只有草稿可以发起审批;CrmAuditStatusEnum
  118 +
108 @Override 119 @Override
  120 + // TODO @puhui999:操作日志
  121 + // TODO @puhui999:权限校验
109 public void deleteReceivable(Long id) { 122 public void deleteReceivable(Long id) {
  123 + // TODO @liuhongfeng:如果被 CrmReceivablePlanDO 所使用,则不允许删除
110 // 校验存在 124 // 校验存在
111 validateReceivableExists(id); 125 validateReceivableExists(id);
112 // 删除 126 // 删除
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/package-info.java deleted
1 -/**  
2 - * 回款  
3 - */  
4 -package cn.iocoder.yudao.module.crm.service.receivable;  
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 (!CrmPermissionUtils.isCrmAdmin() && 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 (CrmPermissionUtils.isCrmAdmin()) {// 管理员不需要数据权限
88 return; 85 return;
89 } 86 }
90 87
@@ -93,39 +90,25 @@ public class CrmQueryWrapperUtils { @@ -93,39 +90,25 @@ 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 - /**  
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 /** 93 /**
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);
128 101
  102 + private static final MybatisPlusJoinProperties MYBATIS_PLUS_JOIN_PROPERTIES = SpringUtil.getBean(MybatisPlusJoinProperties.class);
  103 +
  104 + public static AdminUserApi getAdminUserApi() {
  105 + return ADMIN_USER_API;
  106 + }
  107 +
  108 + public static MybatisPlusJoinProperties getMybatisPlusJoinProperties() {
  109 + return MYBATIS_PLUS_JOIN_PROPERTIES;
  110 + }
  111 +
129 } 112 }
130 113
131 } 114 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.infra.controller.admin.file; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.infra.controller.admin.file;
2 2
3 import cn.hutool.core.io.IoUtil; 3 import cn.hutool.core.io.IoUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
  5 +import cn.hutool.core.util.URLUtil;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -70,6 +71,8 @@ public class FileController { @@ -70,6 +71,8 @@ public class FileController {
70 if (StrUtil.isEmpty(path)) { 71 if (StrUtil.isEmpty(path)) {
71 throw new IllegalArgumentException("结尾的 path 路径必须传递"); 72 throw new IllegalArgumentException("结尾的 path 路径必须传递");
72 } 73 }
  74 + // 解码,解决中文路径的问题 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/
  75 + path = URLUtil.decode(path);
73 76
74 // 读取内容 77 // 读取内容
75 byte[] content = fileService.getFileContent(configId, path); 78 byte[] content = fileService.getFileContent(configId, path);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/DiyTemplateController.java
@@ -82,6 +82,7 @@ public class DiyTemplateController { @@ -82,6 +82,7 @@ public class DiyTemplateController {
82 return success(DiyTemplateConvert.INSTANCE.convertPage(pageResult)); 82 return success(DiyTemplateConvert.INSTANCE.convertPage(pageResult));
83 } 83 }
84 84
  85 + // TODO @疯狂:这个要不和 getDiyTemplate 合并,然后 DiyTemplateRespVO 里面直接把 DiyPagePropertyRespVO 也加上。减少 VO 好了,管理后台 get 多返回点数据,也问题不大的。目的,还是想尽可能降低大家的理解成本哈;
85 @GetMapping("/get-property") 86 @GetMapping("/get-property")
86 @Operation(summary = "获得装修模板属性") 87 @Operation(summary = "获得装修模板属性")
87 @Parameter(name = "id", description = "编号", required = true, example = "1024") 88 @Parameter(name = "id", description = "编号", required = true, example = "1024")
@@ -92,6 +93,7 @@ public class DiyTemplateController { @@ -92,6 +93,7 @@ public class DiyTemplateController {
92 return success(DiyTemplateConvert.INSTANCE.convertPropertyVo(diyTemplate, pages)); 93 return success(DiyTemplateConvert.INSTANCE.convertPropertyVo(diyTemplate, pages));
93 } 94 }
94 95
  96 + // TODO @疯狂:这个接口,要不和 useDiyTemplate 合并成一个,然后 VO 改成我们新的 VO 规范。不改的字段,就不传递。
95 @PutMapping("/update-property") 97 @PutMapping("/update-property")
96 @Operation(summary = "更新装修模板属性") 98 @Operation(summary = "更新装修模板属性")
97 @PreAuthorize("@ss.hasPermission('promotion:diy-template:update')") 99 @PreAuthorize("@ss.hasPermission('promotion:diy-template:update')")
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,97 @@ public class AppActivityController { @@ -64,45 +75,97 @@ public class AppActivityController {
64 if (CollUtil.isEmpty(spuIds)) { 75 if (CollUtil.isEmpty(spuIds)) {
65 return new ArrayList<>(); 76 return new ArrayList<>();
66 } 77 }
67 - LocalDateTime now = LocalDateTime.now(); 78 + // 获取开启的且开始的且没有结束的活动
68 List<AppActivityRespVO> activityList = new ArrayList<>(); 79 List<AppActivityRespVO> activityList = new ArrayList<>();
  80 + LocalDateTime now = LocalDateTime.now();
  81 + // 1. 拼团活动
  82 + getCombinationActivities(spuIds, now, activityList);
  83 + // 2. 秒杀活动
  84 + getSeckillActivities(spuIds, now, activityList);
  85 + // 3. 砍价活动
  86 + getBargainActivities(spuIds, now, activityList);
  87 + // 4. 限时折扣活动
  88 + getDiscountActivities(spuIds, now, activityList);
  89 + // 5. 满减送活动
  90 + getRewardActivities(spuIds, now, activityList);
  91 + return activityList;
  92 + }
69 93
70 - // 1. 拼团活动 - 获取开启的且开始的且没有结束的活动 94 + private void getCombinationActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
71 List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatusAndDateTimeLt( 95 List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(
72 spuIds, CommonStatusEnum.ENABLE.getStatus(), now); 96 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 - }); 97 + if (CollUtil.isEmpty(combinationActivities)) {
  98 + return;
79 } 99 }
80 100
81 - // 2. 秒杀活动 - 获取开启的且开始的且没有结束的活动 101 + combinationActivities.forEach(item -> {
  102 + activityList.add(new AppActivityRespVO(item.getId(), PromotionTypeEnum.COMBINATION_ACTIVITY.getType(),
  103 + item.getName(), item.getSpuId(), item.getStartTime(), item.getEndTime()));
  104 + });
  105 + }
  106 +
  107 + private void getSeckillActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
82 List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatusAndDateTimeLt( 108 List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(
83 spuIds, CommonStatusEnum.ENABLE.getStatus(), now); 109 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 - }); 110 + if (CollUtil.isEmpty(seckillActivities)) {
  111 + return;
90 } 112 }
91 113
92 - // 3. 砍价活动 - 获取开启的且开始的且没有结束的活动 114 + seckillActivities.forEach(item -> {
  115 + activityList.add(new AppActivityRespVO(item.getId(), PromotionTypeEnum.SECKILL_ACTIVITY.getType(),
  116 + item.getName(), item.getSpuId(), item.getStartTime(), item.getEndTime()));
  117 + });
  118 + }
  119 +
  120 + private void getBargainActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
93 List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatusAndDateTimeLt( 121 List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatusAndDateTimeLt(
94 spuIds, CommonStatusEnum.ENABLE.getStatus(), now); 122 spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
95 if (CollUtil.isNotEmpty(bargainActivities)) { 123 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 - }); 124 + return;
101 } 125 }
102 126
103 - // TODO 芋艿:满减送活动  
104 - // TODO 芋艿:限时折扣活动  
105 - return activityList; 127 + bargainActivities.forEach(item -> {
  128 + activityList.add(new AppActivityRespVO(item.getId(), PromotionTypeEnum.BARGAIN_ACTIVITY.getType(),
  129 + item.getName(), item.getSpuId(), item.getStartTime(), item.getEndTime()));
  130 + });
  131 + }
  132 +
  133 + private void getDiscountActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
  134 + List<DiscountActivityDO> discountActivities = discountActivityService.getDiscountActivityBySpuIdsAndStatusAndDateTimeLt(
  135 + spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
  136 + if (CollUtil.isEmpty(discountActivities)) {
  137 + return;
  138 + }
  139 +
  140 + List<DiscountProductDO> products = discountActivityService.getDiscountProductsByActivityId(
  141 + convertSet(discountActivities, DiscountActivityDO::getId));
  142 + Map<Long, Long> productMap = convertMap(products, DiscountProductDO::getActivityId, DiscountProductDO::getSpuId);
  143 + discountActivities.forEach(item -> activityList.add(new AppActivityRespVO(item.getId(), PromotionTypeEnum.DISCOUNT_ACTIVITY.getType(),
  144 + item.getName(), productMap.get(item.getId()), item.getStartTime(), item.getEndTime())));
  145 + }
  146 +
  147 + private void getRewardActivities(Collection<Long> spuIds, LocalDateTime now, List<AppActivityRespVO> activityList) {
  148 + List<RewardActivityDO> rewardActivityList = rewardActivityService.getRewardActivityBySpuIdsAndStatusAndDateTimeLt(
  149 + spuIds, PromotionActivityStatusEnum.RUN.getStatus(), now);
  150 + if (CollUtil.isEmpty(rewardActivityList)) {
  151 + return;
  152 + }
  153 +
  154 + Map<Long, Optional<RewardActivityDO>> spuIdAndActivityMap = spuIds.stream()
  155 + .collect(Collectors.toMap(
  156 + spuId -> spuId,
  157 + spuId -> rewardActivityList.stream()
  158 + .filter(activity -> activity.getProductSpuIds().contains(spuId))
  159 + .max(Comparator.comparing(RewardActivityDO::getCreateTime))));
  160 + for (Long supId : spuIdAndActivityMap.keySet()) {
  161 + if (spuIdAndActivityMap.get(supId).isEmpty()) {
  162 + continue;
  163 + }
  164 +
  165 + RewardActivityDO rewardActivityDO = spuIdAndActivityMap.get(supId).get();
  166 + activityList.add(new AppActivityRespVO(rewardActivityDO.getId(), PromotionTypeEnum.REWARD_ACTIVITY.getType(),
  167 + rewardActivityDO.getName(), supId, rewardActivityDO.getStartTime(), rewardActivityDO.getEndTime()));
  168 + }
106 } 169 }
107 170
108 } 171 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/vo/AppActivityRespVO.java
1 package cn.iocoder.yudao.module.promotion.controller.app.activity.vo; 1 package cn.iocoder.yudao.module.promotion.controller.app.activity.vo;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.AllArgsConstructor;
4 import lombok.Data; 5 import lombok.Data;
  6 +import lombok.NoArgsConstructor;
5 7
6 import java.time.LocalDateTime; 8 import java.time.LocalDateTime;
7 9
8 @Schema(description = "用户 App - 营销活动 Response VO") 10 @Schema(description = "用户 App - 营销活动 Response VO")
  11 +@AllArgsConstructor
  12 +@NoArgsConstructor
9 @Data 13 @Data
10 public class AppActivityRespVO { 14 public class AppActivityRespVO {
11 15
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleController.java
@@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.promotion.controller.app.article; @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.promotion.controller.app.article;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO;
6 import cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticleRespVO; 7 import cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticleRespVO;
7 import cn.iocoder.yudao.module.promotion.convert.article.ArticleConvert; 8 import cn.iocoder.yudao.module.promotion.convert.article.ArticleConvert;
  9 +import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
8 import cn.iocoder.yudao.module.promotion.service.article.ArticleService; 10 import cn.iocoder.yudao.module.promotion.service.article.ArticleService;
9 import io.swagger.v3.oas.annotations.Operation; 11 import io.swagger.v3.oas.annotations.Operation;
10 import io.swagger.v3.oas.annotations.Parameter; 12 import io.swagger.v3.oas.annotations.Parameter;
@@ -51,9 +53,15 @@ public class AppArticleController { @@ -51,9 +53,15 @@ public class AppArticleController {
51 53
52 @RequestMapping("/get") 54 @RequestMapping("/get")
53 @Operation(summary = "获得文章详情") 55 @Operation(summary = "获得文章详情")
54 - @Parameter(name = "id", description = "文章编号", example = "1024")  
55 - public CommonResult<AppArticleRespVO> getArticle(@RequestParam("id") Long id) {  
56 - return success(ArticleConvert.INSTANCE.convert01(articleService.getArticle(id))); 56 + @Parameters({
  57 + @Parameter(name = "id", description = "文章编号", example = "1024"),
  58 + @Parameter(name = "title", description = "文章标题", example = "1024"),
  59 + })
  60 + public CommonResult<AppArticleRespVO> getArticle(@RequestParam(value = "id", required = false) Long id,
  61 + @RequestParam(value = "title", required = false) String title) {
  62 + ArticleDO article = id != null ? articleService.getArticle(id)
  63 + : articleService.getLastArticleByTitle(title);
  64 + return success(BeanUtils.toBean(article, AppArticleRespVO.class));
57 } 65 }
58 66
59 @PutMapping("/add-browse-count") 67 @PutMapping("/add-browse-count")
@@ -64,4 +72,4 @@ public class AppArticleController { @@ -64,4 +72,4 @@ public class AppArticleController {
64 return success(true); 72 return success(true);
65 } 73 }
66 74
67 -} 75 -}
  76 +}
68 \ No newline at end of file 77 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/decorate/AppDecorateController.java
@@ -25,6 +25,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -25,6 +25,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
25 @RestController 25 @RestController
26 @RequestMapping("/promotion/decorate") 26 @RequestMapping("/promotion/decorate")
27 @Validated 27 @Validated
  28 +@Deprecated // 废弃
28 public class AppDecorateController { 29 public class AppDecorateController {
29 30
30 @Resource 31 @Resource
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/diy/AppDiyTemplateController.java
@@ -33,6 +33,7 @@ public class AppDiyTemplateController { @@ -33,6 +33,7 @@ public class AppDiyTemplateController {
33 @Resource 33 @Resource
34 private DiyPageService diyPageService; 34 private DiyPageService diyPageService;
35 35
  36 + // TODO @疯狂:要不要把 used 和 get 接口合并哈;不传递 id,直接拿默认;
36 @GetMapping("/used") 37 @GetMapping("/used")
37 @Operation(summary = "使用中的装修模板") 38 @Operation(summary = "使用中的装修模板")
38 public CommonResult<AppDiyTemplatePropertyRespVO> getUsedDiyTemplate() { 39 public CommonResult<AppDiyTemplatePropertyRespVO> getUsedDiyTemplate() {
@@ -54,6 +55,7 @@ public class AppDiyTemplateController { @@ -54,6 +55,7 @@ public class AppDiyTemplateController {
54 } 55 }
55 // 查询模板下的页面 56 // 查询模板下的页面
56 List<DiyPageDO> pages = diyPageService.getDiyPageByTemplateId(diyTemplate.getId()); 57 List<DiyPageDO> pages = diyPageService.getDiyPageByTemplateId(diyTemplate.getId());
  58 + // TODO @疯狂:首页、我的,要不枚举到 DiyPageDO 例如说 NAME_USER,NAME_HOME 类似这种哈;
57 String home = findFirst(pages, page -> "首页".equals(page.getName()), DiyPageDO::getProperty); 59 String home = findFirst(pages, page -> "首页".equals(page.getName()), DiyPageDO::getProperty);
58 String user = findFirst(pages, page -> "我的".equals(page.getName()), DiyPageDO::getProperty); 60 String user = findFirst(pages, page -> "我的".equals(page.getName()), DiyPageDO::getProperty);
59 // 拼接返回 61 // 拼接返回
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyPageDO.java
@@ -44,6 +44,7 @@ public class DiyPageDO extends BaseDO { @@ -44,6 +44,7 @@ public class DiyPageDO extends BaseDO {
44 * 备注 44 * 备注
45 */ 45 */
46 private String remark; 46 private String remark;
  47 + // TODO @疯狂:这个字段要不改成 previewPicUrls,和别的模块一样用 pic 作为图片哇?
47 /** 48 /**
48 * 预览图,多个逗号分隔 49 * 预览图,多个逗号分隔
49 */ 50 */
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyTemplateDO.java
@@ -50,13 +50,14 @@ public class DiyTemplateDO extends BaseDO { @@ -50,13 +50,14 @@ public class DiyTemplateDO extends BaseDO {
50 * 备注 50 * 备注
51 */ 51 */
52 private String remark; 52 private String remark;
  53 + // TODO @疯狂:这个字段要不改成 previewPicUrls,和别的模块一样用 pic 作为图片哇?
53 /** 54 /**
54 * 预览图 55 * 预览图
55 */ 56 */
56 @TableField(typeHandler = StringListTypeHandler.class) 57 @TableField(typeHandler = StringListTypeHandler.class)
57 private List<String> previewImageUrls; 58 private List<String> previewImageUrls;
58 /** 59 /**
59 - * 底部导航属性,JSON 格式 60 + * uni-app 底部导航属性,JSON 格式
60 */ 61 */
61 private String property; 62 private String property;
62 63
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/article/ArticleMapper.java
@@ -38,6 +38,10 @@ public interface ArticleMapper extends BaseMapperX&lt;ArticleDO&gt; { @@ -38,6 +38,10 @@ public interface ArticleMapper extends BaseMapperX&lt;ArticleDO&gt; {
38 .eqIfPresent(ArticleDO::getRecommendBanner, recommendBanner)); 38 .eqIfPresent(ArticleDO::getRecommendBanner, recommendBanner));
39 } 39 }
40 40
  41 + default List<ArticleDO> selectListByTitle(String title) {
  42 + return selectList(ArticleDO::getTitle, title);
  43 + }
  44 +
41 default PageResult<ArticleDO> selectPage(AppArticlePageReqVO pageReqVO) { 45 default PageResult<ArticleDO> selectPage(AppArticlePageReqVO pageReqVO) {
42 return selectPage(pageReqVO, new LambdaQueryWrapperX<ArticleDO>() 46 return selectPage(pageReqVO, new LambdaQueryWrapperX<ArticleDO>()
43 .eqIfPresent(ArticleDO::getCategoryId, pageReqVO.getCategoryId())); 47 .eqIfPresent(ArticleDO::getCategoryId, pageReqVO.getCategoryId()));
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/article/ArticleService.java
@@ -11,14 +11,14 @@ import jakarta.validation.Valid; @@ -11,14 +11,14 @@ import jakarta.validation.Valid;
11 import java.util.List; 11 import java.util.List;
12 12
13 /** 13 /**
14 - * 文章详情 Service 接口 14 + * 文章 Service 接口
15 * 15 *
16 * @author HUIHUI 16 * @author HUIHUI
17 */ 17 */
18 public interface ArticleService { 18 public interface ArticleService {
19 19
20 /** 20 /**
21 - * 创建文章详情 21 + * 创建文章
22 * 22 *
23 * @param createReqVO 创建信息 23 * @param createReqVO 创建信息
24 * @return 编号 24 * @return 编号
@@ -26,61 +26,63 @@ public interface ArticleService { @@ -26,61 +26,63 @@ public interface ArticleService {
26 Long createArticle(@Valid ArticleCreateReqVO createReqVO); 26 Long createArticle(@Valid ArticleCreateReqVO createReqVO);
27 27
28 /** 28 /**
29 - * 更新文章详情 29 + * 更新文章
30 * 30 *
31 * @param updateReqVO 更新信息 31 * @param updateReqVO 更新信息
32 */ 32 */
33 void updateArticle(@Valid ArticleUpdateReqVO updateReqVO); 33 void updateArticle(@Valid ArticleUpdateReqVO updateReqVO);
34 34
35 /** 35 /**
36 - * 删除文章详情 36 + * 删除文章
37 * 37 *
38 * @param id 编号 38 * @param id 编号
39 */ 39 */
40 void deleteArticle(Long id); 40 void deleteArticle(Long id);
41 41
42 /** 42 /**
43 - * 获得文章详情 43 + * 获得文章
44 * 44 *
45 * @param id 编号 45 * @param id 编号
46 - * @return 文章详情 46 + * @return 文章
47 */ 47 */
48 ArticleDO getArticle(Long id); 48 ArticleDO getArticle(Long id);
49 49
50 /** 50 /**
51 - * 获得文章详情分页 51 + * 基于标题,获得文章
  52 + *
  53 + * 如果有重名的文章,获取最后发布的
  54 + *
  55 + * @param title 标题
  56 + * @return 文章
  57 + */
  58 + ArticleDO getLastArticleByTitle(String title);
  59 +
  60 + /**
  61 + * 获得文章分页
52 * 62 *
53 * @param pageReqVO 分页查询 63 * @param pageReqVO 分页查询
54 - * @return 文章详情分页 64 + * @return 文章分页
55 */ 65 */
56 PageResult<ArticleDO> getArticlePage(ArticlePageReqVO pageReqVO); 66 PageResult<ArticleDO> getArticlePage(ArticlePageReqVO pageReqVO);
57 67
58 /** 68 /**
59 - * 获得文章详情列表 69 + * 获得文章列表
60 * 70 *
61 * @param recommendHot 是否热门 71 * @param recommendHot 是否热门
62 * @param recommendBanner 是否轮播图 72 * @param recommendBanner 是否轮播图
63 - * @return 文章详情列表 73 + * @return 文章列表
64 */ 74 */
65 List<ArticleDO> getArticleCategoryListByRecommend(Boolean recommendHot, Boolean recommendBanner); 75 List<ArticleDO> getArticleCategoryListByRecommend(Boolean recommendHot, Boolean recommendBanner);
66 76
67 /** 77 /**
68 - * 获得文章详情分页 78 + * 获得文章分页
69 * 79 *
70 * @param pageReqVO 分页查询 80 * @param pageReqVO 分页查询
71 - * @return 文章详情分页 81 + * @return 文章分页
72 */ 82 */
73 PageResult<ArticleDO> getArticlePage(AppArticlePageReqVO pageReqVO); 83 PageResult<ArticleDO> getArticlePage(AppArticlePageReqVO pageReqVO);
74 84
75 /** 85 /**
76 - * 获得指定分类的文章列表  
77 - *  
78 - * @param categoryId 文章分类编号  
79 - * @return 文章列表  
80 - */  
81 - List<ArticleDO> getArticleByCategoryId(Long categoryId);  
82 -  
83 - /**  
84 * 获得指定分类的文章数量 86 * 获得指定分类的文章数量
85 * 87 *
86 * @param categoryId 文章分类编号 88 * @param categoryId 文章分类编号
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleServiceImpl.java
1 package cn.iocoder.yudao.module.promotion.service.article; 1 package cn.iocoder.yudao.module.promotion.service.article;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;
@@ -86,6 +87,12 @@ public class ArticleServiceImpl implements ArticleService { @@ -86,6 +87,12 @@ public class ArticleServiceImpl implements ArticleService {
86 } 87 }
87 88
88 @Override 89 @Override
  90 + public ArticleDO getLastArticleByTitle(String title) {
  91 + List<ArticleDO> articles = articleMapper.selectListByTitle(title);
  92 + return CollUtil.getLast(articles);
  93 + }
  94 +
  95 + @Override
89 public PageResult<ArticleDO> getArticlePage(ArticlePageReqVO pageReqVO) { 96 public PageResult<ArticleDO> getArticlePage(ArticlePageReqVO pageReqVO) {
90 return articleMapper.selectPage(pageReqVO); 97 return articleMapper.selectPage(pageReqVO);
91 } 98 }
@@ -101,11 +108,6 @@ public class ArticleServiceImpl implements ArticleService { @@ -101,11 +108,6 @@ public class ArticleServiceImpl implements ArticleService {
101 } 108 }
102 109
103 @Override 110 @Override
104 - public List<ArticleDO> getArticleByCategoryId(Long categoryId) {  
105 - return articleMapper.selectList(ArticleDO::getCategoryId, categoryId);  
106 - }  
107 -  
108 - @Override  
109 public Long getArticleCountByCategoryId(Long categoryId) { 111 public Long getArticleCountByCategoryId(Long categoryId) {
110 return articleMapper.selectCount(ArticleDO::getCategoryId, categoryId); 112 return articleMapper.selectCount(ArticleDO::getCategoryId, categoryId);
111 } 113 }
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,15 @@ public interface DiscountActivityService { @@ -89,4 +90,15 @@ 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(
  102 + Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
  103 +
92 } 104 }
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-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
10 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 10 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
12 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO; 12 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO;
  13 +import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
13 import cn.iocoder.yudao.module.trade.service.order.TradeOrderLogService; 14 import cn.iocoder.yudao.module.trade.service.order.TradeOrderLogService;
14 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 15 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
15 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; 16 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java
@@ -47,12 +47,6 @@ public class AppAfterSaleController { @@ -47,12 +47,6 @@ public class AppAfterSaleController {
47 return success(AfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id))); 47 return success(AfterSaleConvert.INSTANCE.convert(afterSaleService.getAfterSale(getLoginUserId(), id)));
48 } 48 }
49 49
50 - @GetMapping(value = "/get-applying-count")  
51 - @Operation(summary = "获得进行中的售后订单数量")  
52 - public CommonResult<Long> getApplyingAfterSaleCount() {  
53 - return success(afterSaleService.getApplyingAfterSaleCount(getLoginUserId()));  
54 - }  
55 -  
56 @PostMapping(value = "/create") 50 @PostMapping(value = "/create")
57 @Operation(summary = "申请售后") 51 @Operation(summary = "申请售后")
58 public CommonResult<Long> createAfterSale(@RequestBody AppAfterSaleCreateReqVO createReqVO) { 52 public CommonResult<Long> createAfterSale(@RequestBody AppAfterSaleCreateReqVO createReqVO) {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageRecordController.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.controller.app.brokerage; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.controller.app.brokerage;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.security.core.annotations.PreAuthenticated; 6 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
6 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO; 7 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;
7 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO; 8 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;
@@ -39,7 +40,7 @@ public class AppBrokerageRecordController { @@ -39,7 +40,7 @@ public class AppBrokerageRecordController {
39 public CommonResult<PageResult<AppBrokerageRecordRespVO>> getBrokerageRecordPage(@Valid AppBrokerageRecordPageReqVO pageReqVO) { 40 public CommonResult<PageResult<AppBrokerageRecordRespVO>> getBrokerageRecordPage(@Valid AppBrokerageRecordPageReqVO pageReqVO) {
40 PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage( 41 PageResult<BrokerageRecordDO> pageResult = brokerageRecordService.getBrokerageRecordPage(
41 BrokerageRecordConvert.INSTANCE.convert(pageReqVO, getLoginUserId())); 42 BrokerageRecordConvert.INSTANCE.convert(pageReqVO, getLoginUserId()));
42 - return success(BrokerageRecordConvert.INSTANCE.convertPage02(pageResult)); 43 + return success(BeanUtils.toBean(pageResult, AppBrokerageRecordRespVO.class));
43 } 44 }
44 45
45 @GetMapping("/get-product-brokerage-price") 46 @GetMapping("/get-product-brokerage-price")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordPageReqVO.java
@@ -6,11 +6,20 @@ import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; @@ -6,11 +6,20 @@ import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
6 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum; 6 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
7 import io.swagger.v3.oas.annotations.media.Schema; 7 import io.swagger.v3.oas.annotations.media.Schema;
8 import lombok.Data; 8 import lombok.Data;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import java.time.LocalDateTime;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
9 14
10 @Schema(description = "应用 App - 分销记录分页 Request VO") 15 @Schema(description = "应用 App - 分销记录分页 Request VO")
11 @Data 16 @Data
12 public class AppBrokerageRecordPageReqVO extends PageParam { 17 public class AppBrokerageRecordPageReqVO extends PageParam {
13 18
  19 + @Schema(description = "创建时间")
  20 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  21 + private LocalDateTime[] createTime;
  22 +
14 @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 23 @Schema(description = "业务类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
15 @InEnum(value = BrokerageRecordBizTypeEnum.class, message = "业务类型必须是 {value}") 24 @InEnum(value = BrokerageRecordBizTypeEnum.class, message = "业务类型必须是 {value}")
16 private Integer bizType; 25 private Integer bizType;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageRecordRespVO.java
@@ -12,12 +12,18 @@ public class AppBrokerageRecordRespVO { @@ -12,12 +12,18 @@ public class AppBrokerageRecordRespVO {
12 @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") 12 @Schema(description = "记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
13 private Long id; 13 private Long id;
14 14
  15 + @Schema(description = "业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  16 + private String bizId;
  17 +
15 @Schema(description = "分销标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户下单") 18 @Schema(description = "分销标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户下单")
16 private String title; 19 private String title;
17 20
18 @Schema(description = "分销金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") 21 @Schema(description = "分销金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
19 private Integer price; 22 private Integer price;
20 23
  24 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  25 + private Integer status;
  26 +
21 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 27 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
22 private LocalDateTime createTime; 28 private LocalDateTime createTime;
23 29
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
13 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 13 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
14 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; 14 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
15 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; 15 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
  16 +import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
16 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; 17 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
17 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 18 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
18 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; 19 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
@@ -49,6 +50,9 @@ public class AppTradeOrderController { @@ -49,6 +50,9 @@ public class AppTradeOrderController {
49 private DeliveryExpressService deliveryExpressService; 50 private DeliveryExpressService deliveryExpressService;
50 51
51 @Resource 52 @Resource
  53 + private AfterSaleService afterSaleService;
  54 +
  55 + @Resource
52 private TradeOrderProperties tradeOrderProperties; 56 private TradeOrderProperties tradeOrderProperties;
53 57
54 @GetMapping("/settlement") 58 @GetMapping("/settlement")
@@ -61,9 +65,8 @@ public class AppTradeOrderController { @@ -61,9 +65,8 @@ public class AppTradeOrderController {
61 @PostMapping("/create") 65 @PostMapping("/create")
62 @Operation(summary = "创建订单") 66 @Operation(summary = "创建订单")
63 @PreAuthenticated 67 @PreAuthenticated
64 - public CommonResult<AppTradeOrderCreateRespVO> createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO,  
65 - @RequestHeader Integer terminal) {  
66 - TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), getClientIP(), createReqVO, terminal); 68 + public CommonResult<AppTradeOrderCreateRespVO> createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO) {
  69 + TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), createReqVO);
67 return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId())); 70 return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId()));
68 } 71 }
69 72
@@ -132,6 +135,8 @@ public class AppTradeOrderController { @@ -132,6 +135,8 @@ public class AppTradeOrderController {
132 // 待评价 135 // 待评价
133 orderCount.put("uncommentedCount", tradeOrderQueryService.getOrderCount(getLoginUserId(), 136 orderCount.put("uncommentedCount", tradeOrderQueryService.getOrderCount(getLoginUserId(),
134 TradeOrderStatusEnum.COMPLETED.getStatus(), false)); 137 TradeOrderStatusEnum.COMPLETED.getStatus(), false));
  138 + // 售后数量
  139 + orderCount.put("afterSaleCount", afterSaleService.getApplyingAfterSaleCount(getLoginUserId()));
135 return success(orderCount); 140 return success(orderCount);
136 } 141 }
137 142
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/brokerage/BrokerageRecordConvert.java
@@ -70,8 +70,6 @@ public interface BrokerageRecordConvert { @@ -70,8 +70,6 @@ public interface BrokerageRecordConvert {
70 70
71 BrokerageRecordPageReqVO convert(AppBrokerageRecordPageReqVO pageReqVO, Long userId); 71 BrokerageRecordPageReqVO convert(AppBrokerageRecordPageReqVO pageReqVO, Long userId);
72 72
73 - PageResult<AppBrokerageRecordRespVO> convertPage02(PageResult<BrokerageRecordDO> pageResult);  
74 -  
75 default PageResult<AppBrokerageUserRankByPriceRespVO> convertPage03(PageResult<AppBrokerageUserRankByPriceRespVO> pageResult, Map<Long, MemberUserRespDTO> userMap) { 73 default PageResult<AppBrokerageUserRankByPriceRespVO> convertPage03(PageResult<AppBrokerageUserRankByPriceRespVO> pageResult, Map<Long, MemberUserRespDTO> userMap) {
76 for (AppBrokerageUserRankByPriceRespVO vo : pageResult.getList()) { 74 for (AppBrokerageUserRankByPriceRespVO vo : pageResult.getList()) {
77 copyTo(userMap.get(vo.getId()), vo); 75 copyTo(userMap.get(vo.getId()), vo);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
@@ -69,8 +69,7 @@ public interface TradeOrderConvert { @@ -69,8 +69,7 @@ public interface TradeOrderConvert {
69 @Mapping(source = "calculateRespBO.price.vipPrice", target = "vipPrice"), 69 @Mapping(source = "calculateRespBO.price.vipPrice", target = "vipPrice"),
70 @Mapping(source = "calculateRespBO.price.payPrice", target = "payPrice") 70 @Mapping(source = "calculateRespBO.price.payPrice", target = "payPrice")
71 }) 71 })
72 - TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO,  
73 - TradePriceCalculateRespBO calculateRespBO); 72 + TradeOrderDO convert(Long userId, AppTradeOrderCreateReqVO createReqVO, TradePriceCalculateRespBO calculateRespBO);
74 73
75 TradeOrderRespDTO convert(TradeOrderDO orderDO); 74 TradeOrderRespDTO convert(TradeOrderDO orderDO);
76 75
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
@@ -36,12 +36,10 @@ public interface TradeOrderUpdateService { @@ -36,12 +36,10 @@ public interface TradeOrderUpdateService {
36 * 【会员】创建交易订单 36 * 【会员】创建交易订单
37 * 37 *
38 * @param userId 登录用户 38 * @param userId 登录用户
39 - * @param userIp 用户 IP 地址  
40 * @param createReqVO 创建交易订单请求模型 39 * @param createReqVO 创建交易订单请求模型
41 - * @param terminal 终端 {@link TerminalEnum}  
42 * @return 交易订单的 40 * @return 交易订单的
43 */ 41 */
44 - TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, Integer terminal); 42 + TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO);
45 43
46 /** 44 /**
47 * 更新交易订单已支付 45 * 更新交易订单已支付
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -64,6 +64,8 @@ import java.util.Set; @@ -64,6 +64,8 @@ import java.util.Set;
64 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 64 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
65 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 65 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
66 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.minusTime; 66 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.minusTime;
  67 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
  68 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getTerminal;
67 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; 69 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
68 70
69 /** 71 /**
@@ -158,11 +160,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -158,11 +160,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
158 @Override 160 @Override
159 @Transactional(rollbackFor = Exception.class) 161 @Transactional(rollbackFor = Exception.class)
160 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE) 162 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
161 - public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, Integer terminal) { 163 + public TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO) {
162 // 1.1 价格计算 164 // 1.1 价格计算
163 TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO); 165 TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
164 // 1.2 构建订单 166 // 1.2 构建订单
165 - TradeOrderDO order = buildTradeOrder(userId, userIp, createReqVO, calculateRespBO, terminal); 167 + TradeOrderDO order = buildTradeOrder(userId, createReqVO, calculateRespBO);
166 List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO); 168 List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO);
167 169
168 // 2. 订单创建前的逻辑 170 // 2. 订单创建前的逻辑
@@ -178,15 +180,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -178,15 +180,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
178 return order; 180 return order;
179 } 181 }
180 182
181 - private TradeOrderDO buildTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,  
182 - TradePriceCalculateRespBO calculateRespBO, Integer terminal) {  
183 - TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO); 183 + private TradeOrderDO buildTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
  184 + TradePriceCalculateRespBO calculateRespBO) {
  185 + TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, createReqVO, calculateRespBO);
184 order.setType(calculateRespBO.getType()); 186 order.setType(calculateRespBO.getType());
185 order.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX)); 187 order.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX));
186 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); 188 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
187 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); 189 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
188 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum)); 190 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
189 - order.setTerminal(terminal); 191 + order.setUserIp(getClientIP()).setTerminal(getTerminal());
190 // 支付 + 退款信息 192 // 支付 + 退款信息
191 order.setAdjustPrice(0).setPayStatus(false); 193 order.setAdjustPrice(0).setPayStatus(false);
192 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0); 194 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java
@@ -18,7 +18,7 @@ public interface ErrorCodeConstants { @@ -18,7 +18,7 @@ public interface ErrorCodeConstants {
18 // ========== AUTH 模块 1-004-003-000 ========== 18 // ========== AUTH 模块 1-004-003-000 ==========
19 ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确"); 19 ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_004_003_000, "登录失败,账号密码不正确");
20 ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_004_003_001, "登录失败,账号被禁用"); 20 ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_004_003_001, "登录失败,账号被禁用");
21 - ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_004_003_005, "未绑定账号,需要进行绑定"); 21 + ErrorCode AUTH_SOCIAL_USER_NOT_FOUND = new ErrorCode(1_004_003_005, "登录失败,解析不到三方登录信息");
22 ErrorCode AUTH_MOBILE_USED = new ErrorCode(1_004_003_007, "手机号已经被使用"); 22 ErrorCode AUTH_MOBILE_USED = new ErrorCode(1_004_003_007, "手机号已经被使用");
23 23
24 // ========== 用户收件地址 1-004-004-000 ========== 24 // ========== 用户收件地址 1-004-004-000 ==========
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
@@ -73,9 +73,8 @@ public class AppAuthController { @@ -73,9 +73,8 @@ public class AppAuthController {
73 73
74 @PostMapping("/sms-login") 74 @PostMapping("/sms-login")
75 @Operation(summary = "使用手机 + 验证码登录") 75 @Operation(summary = "使用手机 + 验证码登录")
76 - public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO,  
77 - @RequestHeader Integer terminal) {  
78 - return success(authService.smsLogin(reqVO, terminal)); 76 + public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
  77 + return success(authService.smsLogin(reqVO));
79 } 78 }
80 79
81 @PostMapping("/send-sms-code") 80 @PostMapping("/send-sms-code")
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java
@@ -15,11 +15,11 @@ import jakarta.validation.constraints.NotEmpty; @@ -15,11 +15,11 @@ import jakarta.validation.constraints.NotEmpty;
15 @Builder 15 @Builder
16 public class AppAuthWeixinMiniAppLoginReqVO { 16 public class AppAuthWeixinMiniAppLoginReqVO {
17 17
18 - @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello") 18 + @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
19 @NotEmpty(message = "手机 code 不能为空") 19 @NotEmpty(message = "手机 code 不能为空")
20 private String phoneCode; 20 private String phoneCode;
21 21
22 - @Schema(description = "登录 code,小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word") 22 + @Schema(description = "登录 code,小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word")
23 @NotEmpty(message = "登录 code 不能为空") 23 @NotEmpty(message = "登录 code 不能为空")
24 private String loginCode; 24 private String loginCode;
25 25
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/point/AppMemberPointRecordController.java
@@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.controller.app.point; @@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.controller.app.point;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageParam; 4 import cn.iocoder.yudao.framework.common.pojo.PageParam;
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.security.core.annotations.PreAuthenticated; 7 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
  8 +import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordPageReqVO;
7 import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordRespVO; 9 import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordRespVO;
8 import cn.iocoder.yudao.module.member.convert.point.MemberPointRecordConvert; 10 import cn.iocoder.yudao.module.member.convert.point.MemberPointRecordConvert;
9 import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; 11 import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO;
@@ -33,9 +35,10 @@ public class AppMemberPointRecordController { @@ -33,9 +35,10 @@ public class AppMemberPointRecordController {
33 @GetMapping("/page") 35 @GetMapping("/page")
34 @Operation(summary = "获得用户积分记录分页") 36 @Operation(summary = "获得用户积分记录分页")
35 @PreAuthenticated 37 @PreAuthenticated
36 - public CommonResult<PageResult<AppMemberPointRecordRespVO>> getPointRecordPage(@Valid PageParam pageVO) {  
37 - PageResult<MemberPointRecordDO> pageResult = pointRecordService.getPointRecordPage(getLoginUserId(), pageVO);  
38 - return success(MemberPointRecordConvert.INSTANCE.convertPage02(pageResult)); 38 + public CommonResult<PageResult<AppMemberPointRecordRespVO>> getPointRecordPage(
  39 + @Valid AppMemberPointRecordPageReqVO pageReqVO) {
  40 + PageResult<MemberPointRecordDO> pageResult = pointRecordService.getPointRecordPage(getLoginUserId(), pageReqVO);
  41 + return success(BeanUtils.toBean(pageResult, AppMemberPointRecordRespVO.class));
39 } 42 }
40 43
41 } 44 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contactbusinesslink/vo/CrmContactBusinessLinkPageReqVO.java renamed to yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/point/vo/AppMemberPointRecordPageReqVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.contactbusinesslink.vo; 1 +package cn.iocoder.yudao.module.member.controller.app.point.vo;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 5 import lombok.Data;
6 -import lombok.EqualsAndHashCode;  
7 -import lombok.ToString;  
8 import org.springframework.format.annotation.DateTimeFormat; 6 import org.springframework.format.annotation.DateTimeFormat;
9 7
10 import java.time.LocalDateTime; 8 import java.time.LocalDateTime;
11 9
12 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.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
13 11
14 -@Schema(description = "管理后台 - CRM 联系人商机关联分页 Request VO") 12 +@Schema(description = "用户 App - 用户积分记录分页 Request VO")
15 @Data 13 @Data
16 -@EqualsAndHashCode(callSuper = true)  
17 -@ToString(callSuper = true)  
18 -public class CrmContactBusinessLinkPageReqVO extends PageParam { 14 +public class AppMemberPointRecordPageReqVO extends PageParam {
19 15
20 - @Schema(description = "联系人编号", example = "20878")  
21 - private Long contactId; 16 + @Schema(description = "是否增加积分", example = "true")
  17 + private Boolean addStatus; // true - 增加;false - 减少;null - 不筛选
22 18
23 - @Schema(description = "商机编号", example = "7638")  
24 - private Long businessId;  
25 -  
26 - @Schema(description = "创建时间")  
27 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 19 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  20 + @Schema(description = "创建时间")
28 private LocalDateTime[] createTime; 21 private LocalDateTime[] createTime;
29 22
30 -}  
31 \ No newline at end of file 23 \ No newline at end of file
  24 +}
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java
@@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.member.controller.app.social; @@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.member.controller.app.social;
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.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 6 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
6 import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO; 7 import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO;
  8 +import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserRespVO;
7 import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; 9 import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
8 -import cn.iocoder.yudao.module.member.convert.social.SocialUserConvert;  
9 import cn.iocoder.yudao.module.system.api.social.SocialUserApi; 10 import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
  11 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  12 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
  13 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
  14 +import io.swagger.v3.oas.annotations.Parameter;
10 import io.swagger.v3.oas.annotations.tags.Tag; 15 import io.swagger.v3.oas.annotations.tags.Tag;
11 import io.swagger.v3.oas.annotations.Operation; 16 import io.swagger.v3.oas.annotations.Operation;
12 import org.springframework.validation.annotation.Validated; 17 import org.springframework.validation.annotation.Validated;
@@ -15,11 +20,12 @@ import org.springframework.web.bind.annotation.*; @@ -15,11 +20,12 @@ import org.springframework.web.bind.annotation.*;
15 import jakarta.annotation.Resource; 20 import jakarta.annotation.Resource;
16 import jakarta.validation.Valid; 21 import jakarta.validation.Valid;
17 22
  23 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
18 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 24 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
19 25
20 @Tag(name = "用户 App - 社交用户") 26 @Tag(name = "用户 App - 社交用户")
21 @RestController 27 @RestController
22 -@RequestMapping("/system/social-user") 28 +@RequestMapping("/member/social-user")
23 @Validated 29 @Validated
24 public class AppSocialUserController { 30 public class AppSocialUserController {
25 31
@@ -28,17 +34,30 @@ public class AppSocialUserController { @@ -28,17 +34,30 @@ public class AppSocialUserController {
28 34
29 @PostMapping("/bind") 35 @PostMapping("/bind")
30 @Operation(summary = "社交绑定,使用 code 授权码") 36 @Operation(summary = "社交绑定,使用 code 授权码")
31 - public CommonResult<Boolean> socialBind(@RequestBody @Valid AppSocialUserBindReqVO reqVO) {  
32 - socialUserApi.bindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.MEMBER.getValue(), reqVO));  
33 - return CommonResult.success(true); 37 + public CommonResult<String> socialBind(@RequestBody @Valid AppSocialUserBindReqVO reqVO) {
  38 + SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO(getLoginUserId(), UserTypeEnum.MEMBER.getValue(),
  39 + reqVO.getType(), reqVO.getCode(), reqVO.getState());
  40 + String openid = socialUserApi.bindSocialUser(reqDTO);
  41 + return success(openid);
34 } 42 }
35 43
36 @DeleteMapping("/unbind") 44 @DeleteMapping("/unbind")
37 @Operation(summary = "取消社交绑定") 45 @Operation(summary = "取消社交绑定")
38 @PreAuthenticated 46 @PreAuthenticated
39 public CommonResult<Boolean> socialUnbind(@RequestBody AppSocialUserUnbindReqVO reqVO) { 47 public CommonResult<Boolean> socialUnbind(@RequestBody AppSocialUserUnbindReqVO reqVO) {
40 - socialUserApi.unbindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.MEMBER.getValue(), reqVO));  
41 - return CommonResult.success(true); 48 + SocialUserUnbindReqDTO reqDTO = new SocialUserUnbindReqDTO(getLoginUserId(), UserTypeEnum.MEMBER.getValue(),
  49 + reqVO.getType(), reqVO.getOpenid());
  50 + socialUserApi.unbindSocialUser(reqDTO);
  51 + return success(true);
  52 + }
  53 +
  54 + @GetMapping("/get")
  55 + @Operation(summary = "获得社交用户")
  56 + @Parameter(name = "type", description = "社交平台的类型,参见 SocialTypeEnum 枚举值", required = true, example = "10")
  57 + @PreAuthenticated
  58 + public CommonResult<AppSocialUserRespVO> getSocialUser(@RequestParam("type") Integer type) {
  59 + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(UserTypeEnum.MEMBER.getValue(), getLoginUserId(), type);
  60 + return success(BeanUtils.toBean(socialUser, AppSocialUserRespVO.class));
42 } 61 }
43 62
44 } 63 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserBindReqVO.java
@@ -13,9 +13,6 @@ import jakarta.validation.constraints.NotNull; @@ -13,9 +13,6 @@ import jakarta.validation.constraints.NotNull;
13 13
14 @Schema(description = "用户 APP - 社交绑定 Request VO,使用 code 授权码") 14 @Schema(description = "用户 APP - 社交绑定 Request VO,使用 code 授权码")
15 @Data 15 @Data
16 -@NoArgsConstructor  
17 -@AllArgsConstructor  
18 -@Builder  
19 public class AppSocialUserBindReqVO { 16 public class AppSocialUserBindReqVO {
20 17
21 @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") 18 @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.member.controller.app.social.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "用户 APP - 社交用户 Response VO")
  7 +@Data
  8 +public class AppSocialUserRespVO {
  9 +
  10 + @Schema(description = "社交用户的 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
  11 + private String openid;
  12 +
  13 + @Schema(description = "社交用户的昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
  14 + private String nickname;
  15 +
  16 + @Schema(description = "社交用户的头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
  17 + private String avatar;
  18 +
  19 +}
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/vo/AppSocialUserUnbindReqVO.java
@@ -13,9 +13,6 @@ import jakarta.validation.constraints.NotNull; @@ -13,9 +13,6 @@ import jakarta.validation.constraints.NotNull;
13 13
14 @Schema(description = "用户 APP - 取消社交绑定 Request VO") 14 @Schema(description = "用户 APP - 取消社交绑定 Request VO")
15 @Data 15 @Data
16 -@NoArgsConstructor  
17 -@AllArgsConstructor  
18 -@Builder  
19 public class AppSocialUserUnbindReqVO { 16 public class AppSocialUserUnbindReqVO {
20 17
21 @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") 18 @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.java
@@ -57,6 +57,14 @@ public class AppMemberUserController { @@ -57,6 +57,14 @@ public class AppMemberUserController {
57 return success(true); 57 return success(true);
58 } 58 }
59 59
  60 + @PutMapping("/update-mobile-by-weixin")
  61 + @Operation(summary = "基于微信小程序的授权码,修改用户手机")
  62 + @PreAuthenticated
  63 + public CommonResult<Boolean> updateUserMobileByWeixin(@RequestBody @Valid AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
  64 + userService.updateUserMobileByWeixin(getLoginUserId(), reqVO);
  65 + return success(true);
  66 + }
  67 +
60 @PutMapping("/update-password") 68 @PutMapping("/update-password")
61 @Operation(summary = "修改用户密码", description = "用户修改密码时使用") 69 @Operation(summary = "修改用户密码", description = "用户修改密码时使用")
62 @PreAuthenticated 70 @PreAuthenticated
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileByWeixinReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.member.controller.app.user.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "用户 APP - 基于微信小程序的授权码,修改手机 Request VO")
  8 +@Data
  9 +public class AppMemberUserUpdateMobileByWeixinReqVO {
  10 +
  11 + @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得",
  12 + requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
  13 + @NotEmpty(message = "手机 code 不能为空")
  14 + private String code;
  15 +
  16 +}
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileReqVO.java
@@ -14,9 +14,6 @@ import jakarta.validation.constraints.Pattern; @@ -14,9 +14,6 @@ import jakarta.validation.constraints.Pattern;
14 14
15 @Schema(description = "用户 APP - 修改手机 Request VO") 15 @Schema(description = "用户 APP - 修改手机 Request VO")
16 @Data 16 @Data
17 -@NoArgsConstructor  
18 -@AllArgsConstructor  
19 -@Builder  
20 public class AppMemberUserUpdateMobileReqVO { 17 public class AppMemberUserUpdateMobileReqVO {
21 18
22 @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 19 @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@@ -25,14 +22,13 @@ public class AppMemberUserUpdateMobileReqVO { @@ -25,14 +22,13 @@ public class AppMemberUserUpdateMobileReqVO {
25 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") 22 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
26 private String code; 23 private String code;
27 24
28 - @Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED,example = "15823654487") 25 + @Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED, example = "15823654487")
29 @NotBlank(message = "手机号不能为空") 26 @NotBlank(message = "手机号不能为空")
30 @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") 27 @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
31 @Mobile 28 @Mobile
32 private String mobile; 29 private String mobile;
33 30
34 - @Schema(description = "原手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
35 - @NotEmpty(message = "原手机验证码不能为空") 31 + @Schema(description = "原手机验证码", example = "1024")
36 @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") 32 @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
37 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") 33 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
38 private String oldCode; 34 private String oldCode;
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/point/MemberPointRecordConvert.java
@@ -34,6 +34,4 @@ public interface MemberPointRecordConvert { @@ -34,6 +34,4 @@ public interface MemberPointRecordConvert {
34 } 34 }
35 PageResult<MemberPointRecordRespVO> convertPage(PageResult<MemberPointRecordDO> pageResult); 35 PageResult<MemberPointRecordRespVO> convertPage(PageResult<MemberPointRecordDO> pageResult);
36 36
37 - PageResult<AppMemberPointRecordRespVO> convertPage02(PageResult<MemberPointRecordDO> pageResult);  
38 -  
39 } 37 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/social/SocialUserConvert.java deleted
1 -package cn.iocoder.yudao.module.member.convert.social;  
2 -  
3 -import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO;  
4 -import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;  
5 -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;  
6 -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;  
7 -import org.mapstruct.Mapper;  
8 -import org.mapstruct.factory.Mappers;  
9 -  
10 -@Mapper  
11 -public interface SocialUserConvert {  
12 -  
13 - SocialUserConvert INSTANCE = Mappers.getMapper(SocialUserConvert.class);  
14 -  
15 - SocialUserBindReqDTO convert(Long userId, Integer userType, AppSocialUserBindReqVO reqVO);  
16 -  
17 - SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO);  
18 -  
19 -}  
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/point/MemberPointRecordMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO; 7 import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO;
  8 +import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordPageReqVO;
8 import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; 9 import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO;
9 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
10 11
@@ -27,9 +28,14 @@ public interface MemberPointRecordMapper extends BaseMapperX&lt;MemberPointRecordDO @@ -27,9 +28,14 @@ public interface MemberPointRecordMapper extends BaseMapperX&lt;MemberPointRecordDO
27 .orderByDesc(MemberPointRecordDO::getId)); 28 .orderByDesc(MemberPointRecordDO::getId));
28 } 29 }
29 30
30 - default PageResult<MemberPointRecordDO> selectPage(Long userId, PageParam pageVO) {  
31 - return selectPage(pageVO, new LambdaQueryWrapperX<MemberPointRecordDO>() 31 + default PageResult<MemberPointRecordDO> selectPage(Long userId, AppMemberPointRecordPageReqVO pageReqVO) {
  32 + return selectPage(pageReqVO, new LambdaQueryWrapperX<MemberPointRecordDO>()
32 .eq(MemberPointRecordDO::getUserId, userId) 33 .eq(MemberPointRecordDO::getUserId, userId)
  34 + .betweenIfPresent(MemberPointRecordDO::getCreateTime, pageReqVO.getCreateTime())
  35 + .gt(Boolean.TRUE.equals(pageReqVO.getAddStatus()),
  36 + MemberPointRecordDO::getPoint, 0)
  37 + .lt(Boolean.FALSE.equals(pageReqVO.getAddStatus()),
  38 + MemberPointRecordDO::getPoint, 0)
33 .orderByDesc(MemberPointRecordDO::getId)); 39 .orderByDesc(MemberPointRecordDO::getId));
34 } 40 }
35 41
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java
1 package cn.iocoder.yudao.module.member.service.auth; 1 package cn.iocoder.yudao.module.member.service.auth;
2 2
3 -import cn.iocoder.yudao.framework.common.enums.TerminalEnum;  
4 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; 3 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
5 4
6 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
@@ -33,10 +32,9 @@ public interface MemberAuthService { @@ -33,10 +32,9 @@ public interface MemberAuthService {
33 * 手机 + 验证码登陆 32 * 手机 + 验证码登陆
34 * 33 *
35 * @param reqVO 登陆信息 34 * @param reqVO 登陆信息
36 - * @param terminal 终端 {@link TerminalEnum}  
37 * @return 登录结果 35 * @return 登录结果
38 */ 36 */
39 - AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, Integer terminal); 37 + AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO);
40 38
41 /** 39 /**
42 * 社交登录,使用 code 授权码 40 * 社交登录,使用 code 授权码
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
@@ -36,6 +36,7 @@ import java.util.Objects; @@ -36,6 +36,7 @@ import java.util.Objects;
36 36
37 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 37 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
38 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; 38 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
  39 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getTerminal;
39 import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; 40 import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
40 41
41 /** 42 /**
@@ -78,13 +79,13 @@ public class MemberAuthServiceImpl implements MemberAuthService { @@ -78,13 +79,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
78 79
79 @Override 80 @Override
80 @Transactional 81 @Transactional
81 - public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO, Integer terminal) { 82 + public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) {
82 // 校验验证码 83 // 校验验证码
83 String userIp = getClientIP(); 84 String userIp = getClientIP();
84 smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp)); 85 smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp));
85 86
86 // 获得获得注册用户 87 // 获得获得注册用户
87 - MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, terminal); 88 + MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, getTerminal());
88 Assert.notNull(user, "获取用户失败,结果为空"); 89 Assert.notNull(user, "获取用户失败,结果为空");
89 90
90 // 如果 socialType 非空,说明需要绑定社交用户 91 // 如果 socialType 非空,说明需要绑定社交用户
@@ -99,16 +100,25 @@ public class MemberAuthServiceImpl implements MemberAuthService { @@ -99,16 +100,25 @@ public class MemberAuthServiceImpl implements MemberAuthService {
99 } 100 }
100 101
101 @Override 102 @Override
  103 + @Transactional
102 public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) { 104 public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) {
103 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 105 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
104 - SocialUserRespDTO socialUser = socialUserApi.getSocialUser(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), 106 + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByCode(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
105 reqVO.getCode(), reqVO.getState()); 107 reqVO.getCode(), reqVO.getState());
106 if (socialUser == null) { 108 if (socialUser == null) {
107 - throw exception(AUTH_THIRD_LOGIN_NOT_BIND); 109 + throw exception(AUTH_SOCIAL_USER_NOT_FOUND);
108 } 110 }
109 111
110 - // 自动登录  
111 - MemberUserDO user = userService.getUser(socialUser.getUserId()); 112 + // 情况一:已绑定,直接读取用户信息
  113 + MemberUserDO user;
  114 + if (socialUser.getUserId() != null) {
  115 + user = userService.getUser(socialUser.getUserId());
  116 + // 情况二:未绑定,注册用户 + 绑定用户
  117 + } else {
  118 + user = userService.createUser(socialUser.getNickname(), socialUser.getAvatar(), getClientIP(), getTerminal());
  119 + socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
  120 + reqVO.getType(), reqVO.getCode(), reqVO.getState()));
  121 + }
112 if (user == null) { 122 if (user == null) {
113 throw exception(USER_NOT_EXISTS); 123 throw exception(USER_NOT_EXISTS);
114 } 124 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordService.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.member.service.point; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.member.service.point;
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO; 5 import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO;
  6 +import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordPageReqVO;
6 import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; 7 import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO;
7 import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; 8 import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
8 9
@@ -25,10 +26,10 @@ public interface MemberPointRecordService { @@ -25,10 +26,10 @@ public interface MemberPointRecordService {
25 * 【会员】获得积分记录分页 26 * 【会员】获得积分记录分页
26 * 27 *
27 * @param userId 用户编号 28 * @param userId 用户编号
28 - * @param pageVO 分页查询 29 + * @param pageReqVO 分页查询
29 * @return 签到记录分页 30 * @return 签到记录分页
30 */ 31 */
31 - PageResult<MemberPointRecordDO> getPointRecordPage(Long userId, PageParam pageVO); 32 + PageResult<MemberPointRecordDO> getPointRecordPage(Long userId, AppMemberPointRecordPageReqVO pageReqVO);
32 33
33 /** 34 /**
34 * 创建用户积分记录 35 * 创建用户积分记录
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/point/MemberPointRecordServiceImpl.java
@@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil; @@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageParam; 5 import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO; 7 import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO;
  8 +import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordPageReqVO;
8 import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO; 9 import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO;
9 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 10 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
10 import cn.iocoder.yudao.module.member.dal.mysql.point.MemberPointRecordMapper; 11 import cn.iocoder.yudao.module.member.dal.mysql.point.MemberPointRecordMapper;
@@ -59,8 +60,8 @@ public class MemberPointRecordServiceImpl implements MemberPointRecordService { @@ -59,8 +60,8 @@ public class MemberPointRecordServiceImpl implements MemberPointRecordService {
59 } 60 }
60 61
61 @Override 62 @Override
62 - public PageResult<MemberPointRecordDO> getPointRecordPage(Long userId, PageParam pageVO) {  
63 - return memberPointRecordMapper.selectPage(userId, pageVO); 63 + public PageResult<MemberPointRecordDO> getPointRecordPage(Long userId, AppMemberPointRecordPageReqVO pageReqVO) {
  64 + return memberPointRecordMapper.selectPage(userId, pageReqVO);
64 } 65 }
65 66
66 @Override 67 @Override
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java
@@ -5,10 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -5,10 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.validation.Mobile; 5 import cn.iocoder.yudao.framework.common.validation.Mobile;
6 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; 6 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
7 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; 7 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
8 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;  
9 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;  
10 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;  
11 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO; 8 +import cn.iocoder.yudao.module.member.controller.app.user.vo.*;
12 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 9 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
13 10
14 import jakarta.validation.Valid; 11 import jakarta.validation.Valid;
@@ -50,6 +47,18 @@ public interface MemberUserService { @@ -50,6 +47,18 @@ public interface MemberUserService {
50 MemberUserDO createUserIfAbsent(@Mobile String mobile, String registerIp, Integer terminal); 47 MemberUserDO createUserIfAbsent(@Mobile String mobile, String registerIp, Integer terminal);
51 48
52 /** 49 /**
  50 + * 创建用户
  51 + * 目的:三方登录时,如果未绑定用户时,自动创建对应用户
  52 + *
  53 + * @param nickname 昵称
  54 + * @param avtar 头像
  55 + * @param registerIp 注册 IP
  56 + * @param terminal 终端 {@link TerminalEnum}
  57 + * @return 用户对象
  58 + */
  59 + MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal);
  60 +
  61 + /**
53 * 更新用户的最后登陆信息 62 * 更新用户的最后登陆信息
54 * 63 *
55 * @param id 用户编号 64 * @param id 用户编号
@@ -82,7 +91,7 @@ public interface MemberUserService { @@ -82,7 +91,7 @@ public interface MemberUserService {
82 void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO); 91 void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO);
83 92
84 /** 93 /**
85 - * 【会员】修改手机 94 + * 【会员】修改手机,基于手机验证码
86 * 95 *
87 * @param userId 用户编号 96 * @param userId 用户编号
88 * @param reqVO 请求信息 97 * @param reqVO 请求信息
@@ -90,6 +99,14 @@ public interface MemberUserService { @@ -90,6 +99,14 @@ public interface MemberUserService {
90 void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO); 99 void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO);
91 100
92 /** 101 /**
  102 + * 【会员】修改手机,基于微信小程序的授权码
  103 + *
  104 + * @param userId 用户编号
  105 + * @param reqVO 请求信息
  106 + */
  107 + void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO);
  108 +
  109 + /**
93 * 【会员】修改密码 110 * 【会员】修改密码
94 * 111 *
95 * @param userId 用户编号 112 * @param userId 用户编号
@@ -169,4 +186,5 @@ public interface MemberUserService { @@ -169,4 +186,5 @@ public interface MemberUserService {
169 * @return 更新结果 186 * @return 更新结果
170 */ 187 */
171 boolean updateUserPoint(Long userId, Integer point); 188 boolean updateUserPoint(Long userId, Integer point);
  189 +
172 } 190 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
@@ -2,18 +2,15 @@ package cn.iocoder.yudao.module.member.service.user; @@ -2,18 +2,15 @@ package cn.iocoder.yudao.module.member.service.user;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.ListUtil; 4 import cn.hutool.core.collection.ListUtil;
5 -import cn.hutool.core.util.IdUtil;  
6 -import cn.hutool.core.util.ObjectUtil;  
7 -import cn.hutool.core.util.StrUtil; 5 +import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.util.*;
8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  8 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
9 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; 11 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
12 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; 12 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
13 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;  
14 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;  
15 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;  
16 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO; 13 +import cn.iocoder.yudao.module.member.controller.app.user.vo.*;
17 import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; 14 import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
18 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert; 15 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
19 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 16 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
@@ -21,6 +18,8 @@ import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; @@ -21,6 +18,8 @@ import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
21 import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer; 18 import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer;
22 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; 19 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
23 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; 20 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
  21 +import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
  22 +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
24 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; 23 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
25 import com.google.common.annotations.VisibleForTesting; 24 import com.google.common.annotations.VisibleForTesting;
26 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
@@ -57,6 +56,9 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -57,6 +56,9 @@ public class MemberUserServiceImpl implements MemberUserService {
57 private SmsCodeApi smsCodeApi; 56 private SmsCodeApi smsCodeApi;
58 57
59 @Resource 58 @Resource
  59 + private SocialClientApi socialClientApi;
  60 +
  61 + @Resource
60 private PasswordEncoder passwordEncoder; 62 private PasswordEncoder passwordEncoder;
61 63
62 @Resource 64 @Resource
@@ -81,10 +83,17 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -81,10 +83,17 @@ public class MemberUserServiceImpl implements MemberUserService {
81 return user; 83 return user;
82 } 84 }
83 // 用户不存在,则进行创建 85 // 用户不存在,则进行创建
84 - return createUser(mobile, registerIp, terminal); 86 + return createUser(mobile, null, null, registerIp, terminal);
85 } 87 }
86 88
87 - private MemberUserDO createUser(String mobile, String registerIp, Integer terminal) { 89 + @Override
  90 + @Transactional(rollbackFor = Exception.class)
  91 + public MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal) {
  92 + return createUser(null, nickname, avtar, registerIp, terminal);
  93 + }
  94 +
  95 + private MemberUserDO createUser(String mobile, String nickname, String avtar,
  96 + String registerIp, Integer terminal) {
88 // 生成密码 97 // 生成密码
89 String password = IdUtil.fastSimpleUUID(); 98 String password = IdUtil.fastSimpleUUID();
90 // 插入用户 99 // 插入用户
@@ -92,8 +101,12 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -92,8 +101,12 @@ public class MemberUserServiceImpl implements MemberUserService {
92 user.setMobile(mobile); 101 user.setMobile(mobile);
93 user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 102 user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
94 user.setPassword(encodePassword(password)); // 加密密码 103 user.setPassword(encodePassword(password)); // 加密密码
95 - user.setRegisterIp(registerIp);  
96 - user.setRegisterTerminal(terminal); 104 + user.setRegisterIp(registerIp).setRegisterTerminal(terminal);
  105 + user.setNickname(nickname).setAvatar(avtar); // 基础信息
  106 + if (StrUtil.isEmpty(nickname)) {
  107 + // 昵称为空时,随机一个名字,避免一些依赖 nickname 的逻辑报错,或者有点丑。例如说,短信发送有昵称时~
  108 + user.setNickname("用户" + RandomUtil.randomNumbers(6));
  109 + }
97 memberUserMapper.insert(user); 110 memberUserMapper.insert(user);
98 111
99 // 发送 MQ 消息:用户创建 112 // 发送 MQ 消息:用户创建
@@ -136,23 +149,39 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -136,23 +149,39 @@ public class MemberUserServiceImpl implements MemberUserService {
136 @Override 149 @Override
137 @Transactional(rollbackFor = Exception.class) 150 @Transactional(rollbackFor = Exception.class)
138 public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) { 151 public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) {
139 - // 检测用户是否存在 152 + // 1.1 检测用户是否存在
140 MemberUserDO user = validateUserExists(userId); 153 MemberUserDO user = validateUserExists(userId);
141 - // 校验新手机是否已经被绑定 154 + // 1.2 校验新手机是否已经被绑定
142 validateMobileUnique(null, reqVO.getMobile()); 155 validateMobileUnique(null, reqVO.getMobile());
143 156
144 - // 校验旧手机和旧验证码  
145 - smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())  
146 - .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));  
147 - // 使用新验证码 157 + // 2.1 校验旧手机和旧验证码
  158 + // 补充说明:从安全性来说,老手机也校验 oldCode 验证码会更安全。但是由于 uni-app 商城界面暂时没做,所以这里不强制校验
  159 + if (StrUtil.isNotEmpty(reqVO.getOldCode())) {
  160 + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())
  161 + .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
  162 + }
  163 + // 2.2 使用新验证码
148 smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode()) 164 smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode())
149 .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())); 165 .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
150 166
151 - // 更新用户手机 167 + // 3. 更新用户手机
152 memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build()); 168 memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build());
153 } 169 }
154 170
155 @Override 171 @Override
  172 + public void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
  173 + // 1.1 获得对应的手机号信息
  174 + SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
  175 + UserTypeEnum.MEMBER.getValue(), reqVO.getCode());
  176 + Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
  177 + // 1.2 校验新手机是否已经被绑定
  178 + validateMobileUnique(userId, phoneNumberInfo.getPhoneNumber());
  179 +
  180 + // 2. 更新用户手机
  181 + memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(phoneNumberInfo.getPhoneNumber()).build());
  182 + }
  183 +
  184 + @Override
156 public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) { 185 public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) {
157 // 检测用户是否存在 186 // 检测用户是否存在
158 MemberUserDO user = validateUserExists(userId); 187 MemberUserDO user = validateUserExists(userId);
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/order/PayOrderExtensionMapper.java
@@ -24,10 +24,10 @@ public interface PayOrderExtensionMapper extends BaseMapperX&lt;PayOrderExtensionDO @@ -24,10 +24,10 @@ public interface PayOrderExtensionMapper extends BaseMapperX&lt;PayOrderExtensionDO
24 return selectList(PayOrderExtensionDO::getOrderId, orderId); 24 return selectList(PayOrderExtensionDO::getOrderId, orderId);
25 } 25 }
26 26
27 - default List<PayOrderExtensionDO> selectListByStatusAndCreateTimeGe(Integer status, LocalDateTime minCreateTime) { 27 + default List<PayOrderExtensionDO> selectListByStatusAndCreateTimeLe(Integer status, LocalDateTime minCreateTime) {
28 return selectList(new LambdaQueryWrapper<PayOrderExtensionDO>() 28 return selectList(new LambdaQueryWrapper<PayOrderExtensionDO>()
29 .eq(PayOrderExtensionDO::getStatus, status) 29 .eq(PayOrderExtensionDO::getStatus, status)
30 - .ge(PayOrderExtensionDO::getCreateTime, minCreateTime)); 30 + .le(PayOrderExtensionDO::getCreateTime, minCreateTime));
31 } 31 }
32 32
33 } 33 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletRechargeMapper.java
@@ -24,7 +24,8 @@ public interface PayWalletRechargeMapper extends BaseMapperX&lt;PayWalletRechargeDO @@ -24,7 +24,8 @@ public interface PayWalletRechargeMapper extends BaseMapperX&lt;PayWalletRechargeDO
24 default PageResult<PayWalletRechargeDO> selectPage(PageParam pageReqVO, Long walletId, Boolean payStatus) { 24 default PageResult<PayWalletRechargeDO> selectPage(PageParam pageReqVO, Long walletId, Boolean payStatus) {
25 return selectPage(pageReqVO, new LambdaQueryWrapperX<PayWalletRechargeDO>() 25 return selectPage(pageReqVO, new LambdaQueryWrapperX<PayWalletRechargeDO>()
26 .eq(PayWalletRechargeDO::getWalletId, walletId) 26 .eq(PayWalletRechargeDO::getWalletId, walletId)
27 - .eq(PayWalletRechargeDO::getPayStatus, payStatus)); 27 + .eq(PayWalletRechargeDO::getPayStatus, payStatus)
  28 + .orderByDesc(PayWalletRechargeDO::getId));
28 } 29 }
29 30
30 } 31 }
31 \ No newline at end of file 32 \ No newline at end of file
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
@@ -232,7 +232,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { @@ -232,7 +232,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
232 throw exception(DEMO_ORDER_NOT_FOUND); 232 throw exception(DEMO_ORDER_NOT_FOUND);
233 } 233 }
234 // 1.2 校验退款订单匹配 234 // 1.2 校验退款订单匹配
235 - if (Objects.equals(order.getPayOrderId(), payRefundId)) { 235 + if (Objects.equals(order.getPayRefundId(), payRefundId)) {
236 log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!order 数据是:{}]", 236 log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!order 数据是:{}]",
237 id, payRefundId, toJsonString(order)); 237 id, payRefundId, toJsonString(order));
238 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR); 238 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
@@ -448,8 +448,8 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -448,8 +448,8 @@ public class PayOrderServiceImpl implements PayOrderService {
448 448
449 @Override 449 @Override
450 public int syncOrder(LocalDateTime minCreateTime) { 450 public int syncOrder(LocalDateTime minCreateTime) {
451 - // 1. 查询指定创建时间内的待支付订单  
452 - List<PayOrderExtensionDO> orderExtensions = orderExtensionMapper.selectListByStatusAndCreateTimeGe( 451 + // 1. 查询指定创建时间前的待支付订单
  452 + List<PayOrderExtensionDO> orderExtensions = orderExtensionMapper.selectListByStatusAndCreateTimeLe(
453 PayOrderStatusEnum.WAITING.getStatus(), minCreateTime); 453 PayOrderStatusEnum.WAITING.getStatus(), minCreateTime);
454 if (CollUtil.isEmpty(orderExtensions)) { 454 if (CollUtil.isEmpty(orderExtensions)) {
455 return 0; 455 return 0;
yudao-module-report/yudao-module-report-biz/pom.xml
@@ -66,7 +66,7 @@ @@ -66,7 +66,7 @@
66 <!-- 积木报表--> 66 <!-- 积木报表-->
67 <dependency> 67 <dependency>
68 <groupId>org.jeecgframework.jimureport</groupId> 68 <groupId>org.jeecgframework.jimureport</groupId>
69 - <artifactId>jimureport-spring-boot-starter</artifactId> 69 + <artifactId>jimureport-spring-boot3-starter</artifactId>
70 </dependency> 70 </dependency>
71 <!-- 单独依赖升级版本,解决低版本validator失败问题 --> 71 <!-- 单独依赖升级版本,解决低版本validator失败问题 -->
72 <dependency> 72 <dependency>
yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/jmreport/core/service/JmReportTokenServiceImpl.java
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; @@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
11 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 11 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
12 import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi; 12 import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
13 import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO; 13 import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
  14 +import cn.iocoder.yudao.module.system.api.permission.RoleApi;
14 import lombok.RequiredArgsConstructor; 15 import lombok.RequiredArgsConstructor;
15 import org.jeecg.modules.jmreport.api.JmReportTokenServiceI; 16 import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
16 import org.springframework.http.HttpHeaders; 17 import org.springframework.http.HttpHeaders;
@@ -128,4 +129,20 @@ public class JmReportTokenServiceImpl implements JmReportTokenServiceI { @@ -128,4 +129,20 @@ public class JmReportTokenServiceImpl implements JmReportTokenServiceI {
128 return user; 129 return user;
129 } 130 }
130 131
  132 + @Override
  133 + public String[] getRoles(String s) {
  134 + // 暂时不用实现,因为不用 JmReport 的角色
  135 + return null;
  136 + }
  137 +
  138 + @Override
  139 + public String getTenantId() {
  140 + // 补充说明:不能直接通过 TenantContext 获取,因为 jimu 报表前端请求时,没有带上 tenant-id Header
  141 + LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
  142 + if (loginUser == null) {
  143 + return null;
  144 + }
  145 + return StrUtil.toStringOrNull(loginUser.getTenantId());
  146 + }
  147 +
131 } 148 }
yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/ureport/config/UReportConfiguration.java
1 package cn.iocoder.yudao.module.report.framework.ureport.config; 1 package cn.iocoder.yudao.module.report.framework.ureport.config;
2 2
3 -import com.bstek.ureport.console.UReportServlet;  
4 import org.springframework.boot.context.properties.EnableConfigurationProperties; 3 import org.springframework.boot.context.properties.EnableConfigurationProperties;
5 -import org.springframework.boot.web.servlet.ServletRegistrationBean;  
6 -import org.springframework.context.annotation.Bean;  
7 import org.springframework.context.annotation.Configuration; 4 import org.springframework.context.annotation.Configuration;
8 import org.springframework.context.annotation.ImportResource; 5 import org.springframework.context.annotation.ImportResource;
9 import org.springframework.context.annotation.PropertySource; 6 import org.springframework.context.annotation.PropertySource;
10 7
11 -import jakarta.servlet.Servlet;  
12 -  
13 /** 8 /**
14 * UReport2 配置类 9 * UReport2 配置类
15 * 10 *
16 * @author 赤焰 11 * @author 赤焰
17 */ 12 */
18 -@Configuration 13 +// @Configuration TODO 芋艿:JDK21 暂时不支持 UReport2,原因是 Spring Boot 3 的 javax 替换成 jakarta 了
19 @ImportResource({"classpath:ureport-console-context.xml"}) 14 @ImportResource({"classpath:ureport-console-context.xml"})
20 @PropertySource(value = {"classpath:ureport.properties"}) // TODO @赤焰:这个可以搞到 application.yaml 里么? 15 @PropertySource(value = {"classpath:ureport.properties"}) // TODO @赤焰:这个可以搞到 application.yaml 里么?
21 @EnableConfigurationProperties({UReportProperties.class}) 16 @EnableConfigurationProperties({UReportProperties.class})
22 public class UReportConfiguration { 17 public class UReportConfiguration {
23 18
24 - @Bean  
25 - public ServletRegistrationBean<Servlet> uReportRegistrationBean() {  
26 - return new ServletRegistrationBean<>(new UReportServlet(), "/ureport/*");  
27 - } 19 +// TODO 芋艿:JDK21 暂时不支持 UReport2,原因是 Spring Boot 3 的 javax 替换成 jakarta 了
  20 +// @Bean
  21 +// public ServletRegistrationBean<Servlet> uReportRegistrationBean() {
  22 +// return new ServletRegistrationBean<>(new UReportServlet(), "/ureport/*");
  23 +// }
28 24
29 } 25 }
yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/ureport/core/UReportDataSource.java
@@ -18,7 +18,7 @@ import static cn.iocoder.yudao.module.report.enums.ErrorCodeConstants.UREPORT_DA @@ -18,7 +18,7 @@ import static cn.iocoder.yudao.module.report.enums.ErrorCodeConstants.UREPORT_DA
18 * @author 赤焰 18 * @author 赤焰
19 */ 19 */
20 @Slf4j 20 @Slf4j
21 -@Component 21 +//@Component
22 public class UReportDataSource implements BuildinDatasource { 22 public class UReportDataSource implements BuildinDatasource {
23 23
24 private static final String NAME = "UReportDataSource"; 24 private static final String NAME = "UReportDataSource";
yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/ureport/core/UReportDatabaseProvider.java
@@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
29 * @author 赤焰 29 * @author 赤焰
30 */ 30 */
31 // TODO @赤焰:这个 bean 的注解,交给 UReportConfiguration 搞 31 // TODO @赤焰:这个 bean 的注解,交给 UReportConfiguration 搞
32 -@Component 32 +//@Component
33 @Slf4j 33 @Slf4j
34 @Setter 34 @Setter
35 public class UReportDatabaseProvider implements ReportProvider { 35 public class UReportDatabaseProvider implements ReportProvider {
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-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java
@@ -36,9 +36,9 @@ public class LoginLogCreateReqDTO { @@ -36,9 +36,9 @@ public class LoginLogCreateReqDTO {
36 private Integer userType; 36 private Integer userType;
37 /** 37 /**
38 * 用户账号 38 * 用户账号
  39 + *
  40 + * 不再强制校验 username 非空,因为 Member 社交登录时,此时暂时没有 username(mobile)!
39 */ 41 */
40 - @NotBlank(message = "用户账号不能为空")  
41 - @Size(max = 30, message = "用户账号长度不能超过30个字符")  
42 private String username; 42 private String username;
43 43
44 /** 44 /**
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
7 /** 8 /**
@@ -10,7 +11,7 @@ import lombok.Data; @@ -10,7 +11,7 @@ import lombok.Data;
10 * @author HUIHUI 11 * @author HUIHUI
11 */ 12 */
12 @Data 13 @Data
13 -public class OperateLogV2CreateReqBO { 14 +public class OperateLogV2CreateReqDTO {
14 15
15 /** 16 /**
16 * 链路追踪编号 17 * 链路追踪编号
@@ -23,36 +24,36 @@ public class OperateLogV2CreateReqBO { @@ -23,36 +24,36 @@ public class OperateLogV2CreateReqBO {
23 * 24 *
24 * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 25 * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
25 */ 26 */
26 - @NotEmpty(message = "用户编号不能为空") 27 + @NotNull(message = "用户编号不能为空")
27 private Long userId; 28 private Long userId;
28 /** 29 /**
29 * 用户类型 30 * 用户类型
30 * 31 *
31 * 关联 {@link UserTypeEnum} 32 * 关联 {@link UserTypeEnum}
32 */ 33 */
33 - @NotEmpty(message = "用户类型不能为空") 34 + @NotNull(message = "用户类型不能为空")
34 private Integer userType; 35 private Integer userType;
35 /** 36 /**
36 - * 操作模块 37 + * 操作模块类型
37 */ 38 */
38 - @NotEmpty(message = "操作模块不能为空")  
39 - private String module; 39 + @NotEmpty(message = "操作模块类型不能为空")
  40 + private String type;
40 /** 41 /**
41 * 操作名 42 * 操作名
42 */ 43 */
43 @NotEmpty(message = "操作名不能为空") 44 @NotEmpty(message = "操作名不能为空")
44 - private String name; 45 + private String subType;
45 /** 46 /**
46 * 操作模块业务编号 47 * 操作模块业务编号
47 */ 48 */
48 - @NotEmpty(message = "操作模块业务编号不能为空") 49 + @NotNull(message = "操作模块业务编号不能为空")
49 private Long bizId; 50 private Long bizId;
50 /** 51 /**
51 * 操作内容,记录整个操作的明细 52 * 操作内容,记录整个操作的明细
52 * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 53 * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
53 */ 54 */
54 @NotEmpty(message = "操作内容不能为空") 55 @NotEmpty(message = "操作内容不能为空")
55 - private String content; 56 + private String action;
56 /** 57 /**
57 * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) 58 * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
58 * 例如说,记录订单编号,{ orderId: "1"} 59 * 例如说,记录订单编号,{ orderId: "1"}
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;  
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;  
11 -  
12 /** 5 /**
13 * 系统操作日志 Resp DTO 6 * 系统操作日志 Resp DTO
14 * 7 *
@@ -26,17 +19,21 @@ public class OperateLogV2RespDTO { @@ -26,17 +19,21 @@ public class OperateLogV2RespDTO {
26 */ 19 */
27 private Long userId; 20 private Long userId;
28 /** 21 /**
  22 + * 用户名称
  23 + */
  24 + private String userName;
  25 + /**
29 * 用户类型 26 * 用户类型
30 */ 27 */
31 private Integer userType; 28 private Integer userType;
32 /** 29 /**
33 - * 操作模块 30 + * 操作模块类型
34 */ 31 */
35 - private String module; 32 + private String type;
36 /** 33 /**
37 * 操作名 34 * 操作名
38 */ 35 */
39 - private String name; 36 + private String subType;
40 /** 37 /**
41 * 操作模块业务编号 38 * 操作模块业务编号
42 */ 39 */
@@ -44,7 +41,7 @@ public class OperateLogV2RespDTO { @@ -44,7 +41,7 @@ public class OperateLogV2RespDTO {
44 /** 41 /**
45 * 操作内容 42 * 操作内容
46 */ 43 */
47 - private String content; 44 + private String action;
48 /** 45 /**
49 * 拓展字段 46 * 拓展字段
50 */ 47 */
@@ -67,21 +64,4 @@ public class OperateLogV2RespDTO { @@ -67,21 +64,4 @@ public class OperateLogV2RespDTO {
67 */ 64 */
68 private String userAgent; 65 private String userAgent;
69 66
70 - /**  
71 - * 创建时间  
72 - */  
73 - // TODO puhui999: 木得效果怎么肥事  
74 - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)  
75 - private LocalDateTime createTime;  
76 -  
77 - // TODO @puhui999:下面 2 个字段不用返回;用 userId 哈;返回一个 userName  
78 - /**  
79 - * 创建者  
80 - */  
81 - private String creator;  
82 - /**  
83 - * 创建者名称  
84 - */  
85 - private String creatorName;  
86 -  
87 } 67 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java
@@ -30,6 +30,16 @@ public interface SocialUserApi { @@ -30,6 +30,16 @@ public interface SocialUserApi {
30 void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO); 30 void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO);
31 31
32 /** 32 /**
  33 + * 获得社交用户,基于 userId
  34 + *
  35 + * @param userType 用户类型
  36 + * @param userId 用户编号
  37 + * @param socialType 社交平台的类型
  38 + * @return 社交用户
  39 + */
  40 + SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType);
  41 +
  42 + /**
33 * 获得社交用户 43 * 获得社交用户
34 * 44 *
35 * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 45 * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
@@ -40,7 +50,6 @@ public interface SocialUserApi { @@ -40,7 +50,6 @@ public interface SocialUserApi {
40 * @param state state 50 * @param state state
41 * @return 社交用户 51 * @return 社交用户
42 */ 52 */
43 - SocialUserRespDTO getSocialUser(Integer userType, Integer socialType,  
44 - String code, String state); 53 + SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state);
45 54
46 } 55 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java
@@ -15,9 +15,17 @@ import lombok.NoArgsConstructor; @@ -15,9 +15,17 @@ import lombok.NoArgsConstructor;
15 public class SocialUserRespDTO { 15 public class SocialUserRespDTO {
16 16
17 /** 17 /**
18 - * 社交用户 openid 18 + * 社交用户的 openid
19 */ 19 */
20 private String openid; 20 private String openid;
  21 + /**
  22 + * 社交用户的昵称
  23 + */
  24 + private String nickname;
  25 + /**
  26 + * 社交用户的头像
  27 + */
  28 + private String avatar;
21 29
22 /** 30 /**
23 * 关联的用户编号 31 * 关联的用户编号
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java
@@ -3,10 +3,12 @@ package cn.iocoder.yudao.module.system.api.social.dto; @@ -3,10 +3,12 @@ package cn.iocoder.yudao.module.system.api.social.dto;
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.validation.InEnum; 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
5 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; 5 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
  6 +import lombok.AllArgsConstructor;
6 import lombok.Data; 7 import lombok.Data;
7 8
8 import jakarta.validation.constraints.NotEmpty; 9 import jakarta.validation.constraints.NotEmpty;
9 import jakarta.validation.constraints.NotNull; 10 import jakarta.validation.constraints.NotNull;
  11 +import lombok.NoArgsConstructor;
10 12
11 /** 13 /**
12 * 社交绑定 Request DTO,使用 code 授权码 14 * 社交绑定 Request DTO,使用 code 授权码
@@ -14,6 +16,8 @@ import jakarta.validation.constraints.NotNull; @@ -14,6 +16,8 @@ import jakarta.validation.constraints.NotNull;
14 * @author 芋道源码 16 * @author 芋道源码
15 */ 17 */
16 @Data 18 @Data
  19 +@AllArgsConstructor
  20 +@NoArgsConstructor
17 public class SocialUserUnbindReqDTO { 21 public class SocialUserUnbindReqDTO {
18 22
19 /** 23 /**
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.util.object.BeanUtils; 4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO; 5 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
  6 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2CreateReqDTO;
  7 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
6 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO; 8 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
  9 +import cn.iocoder.yudao.module.system.convert.logger.OperateLogConvert;
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;  
18 20
19 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;  
20 -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; 21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
21 22
22 /** 23 /**
23 * 操作日志 API 实现类 24 * 操作日志 API 实现类
@@ -39,22 +40,22 @@ public class OperateLogApiImpl implements OperateLogApi { @@ -39,22 +40,22 @@ public class OperateLogApiImpl implements OperateLogApi {
39 } 40 }
40 41
41 @Override 42 @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(); 43 + @Async
  44 + public void createOperateLogV2(OperateLogV2CreateReqDTO createReqDTO) {
  45 + operateLogService.createOperateLogV2(createReqDTO);
  46 + }
  47 +
  48 + @Override
  49 + public PageResult<OperateLogV2RespDTO> getOperateLogPage(OperateLogV2PageReqDTO pageReqVO) {
  50 + PageResult<OperateLogV2DO> operateLogPage = operateLogService.getOperateLogPage(pageReqVO);
  51 + if (CollUtil.isEmpty(operateLogPage.getList())) {
  52 + return PageResult.empty();
46 } 53 }
47 54
48 // 获取用户 55 // 获取用户
49 - List<AdminUserDO> userList = adminUserService.getUserList(convertSet(logList, item -> Long.parseLong(item.getCreator())));  
50 - Map<Long, AdminUserDO> userMap = convertMap(userList, AdminUserDO::getId);  
51 - 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());  
55 - });  
56 - return bean;  
57 - }); 56 + List<AdminUserDO> userList = adminUserService.getUserList(
  57 + convertSet(operateLogPage.getList(), OperateLogV2DO::getUserId));
  58 + return OperateLogConvert.INSTANCE.convertPage(operateLogPage, userList);
58 } 59 }
59 60
60 } 61 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java
@@ -33,8 +33,13 @@ public class SocialUserApiImpl implements SocialUserApi { @@ -33,8 +33,13 @@ public class SocialUserApiImpl implements SocialUserApi {
33 } 33 }
34 34
35 @Override 35 @Override
36 - public SocialUserRespDTO getSocialUser(Integer userType, Integer socialType, String code, String state) {  
37 - return socialUserService.getSocialUser(userType, socialType, code, state); 36 + public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) {
  37 + return socialUserService.getSocialUserByUserId(userType, userId, socialType);
  38 + }
  39 +
  40 + @Override
  41 + public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) {
  42 + return socialUserService.getSocialUserByCode(userType, socialType, code, state);
38 } 43 }
39 44
40 } 45 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/logger/OperateLogConvert.java
1 package cn.iocoder.yudao.module.system.convert.logger; 1 package cn.iocoder.yudao.module.system.convert.logger;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
6 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO; 8 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
  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 org.mapstruct.Mapper; 12 import org.mapstruct.Mapper;
10 import org.mapstruct.factory.Mappers; 13 import org.mapstruct.factory.Mappers;
@@ -12,6 +15,9 @@ import org.mapstruct.factory.Mappers; @@ -12,6 +15,9 @@ import org.mapstruct.factory.Mappers;
12 import java.util.List; 15 import java.util.List;
13 import java.util.Map; 16 import java.util.Map;
14 17
  18 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  19 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  20 +
15 @Mapper 21 @Mapper
16 public interface OperateLogConvert { 22 public interface OperateLogConvert {
17 23
@@ -25,4 +31,17 @@ public interface OperateLogConvert { @@ -25,4 +31,17 @@ public interface OperateLogConvert {
25 }); 31 });
26 } 32 }
27 33
  34 + default PageResult<OperateLogV2RespDTO> convertPage(PageResult<OperateLogV2DO> operateLogPage, List<AdminUserDO> userList) {
  35 + return BeanUtils.toBean(operateLogPage, OperateLogV2RespDTO.class).setList(setUserInfo(operateLogPage.getList(), userList));
  36 + }
  37 +
  38 + private static List<OperateLogV2RespDTO> setUserInfo(List<OperateLogV2DO> logList, List<AdminUserDO> userList) {
  39 + Map<Long, AdminUserDO> userMap = convertMap(userList, AdminUserDO::getId);
  40 + return CollectionUtils.convertList(logList, item -> {
  41 + OperateLogV2RespDTO respDTO = BeanUtils.toBean(item, OperateLogV2RespDTO.class);
  42 + findAndThen(userMap, item.getUserId(), user -> respDTO.setUserName(user.getNickname()));
  43 + return respDTO;
  44 + });
  45 + }
  46 +
28 } 47 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java
@@ -42,32 +42,31 @@ public class OperateLogV2DO extends BaseDO { @@ -42,32 +42,31 @@ public class OperateLogV2DO extends BaseDO {
42 * 关联 {@link UserTypeEnum} 42 * 关联 {@link UserTypeEnum}
43 */ 43 */
44 private Integer userType; 44 private Integer userType;
45 - // TODO @puhui999:module 改成 type,name 改成 subType;  
46 /** 45 /**
47 - * 操作模块 46 + * 操作模块类型
48 */ 47 */
49 - private String module; 48 + private String type;
50 /** 49 /**
51 * 操作名 50 * 操作名
52 */ 51 */
53 - private String name; 52 + private String subType;
54 /** 53 /**
55 * 操作模块业务编号 54 * 操作模块业务编号
56 */ 55 */
57 private Long bizId; 56 private Long bizId;
58 /** 57 /**
59 - * 操作内容,记录整个操作的明细 58 + * 日志内容,记录整个操作的明细
60 * 59 *
61 * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 60 * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
62 */ 61 */
63 - private String content; 62 + private String action;
64 /** 63 /**
65 * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) 64 * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
66 * 65 *
67 * 例如说,记录订单编号,{ orderId: "1"} 66 * 例如说,记录订单编号,{ orderId: "1"}
68 */ 67 */
69 - // TODO @puhui999:看看能不能类似 exts 搞 json 格式;  
70 private String extra; 68 private String extra;
  69 +
71 /** 70 /**
72 * 请求方法名 71 * 请求方法名
73 */ 72 */
@@ -85,9 +84,4 @@ public class OperateLogV2DO extends BaseDO { @@ -85,9 +84,4 @@ public class OperateLogV2DO extends BaseDO {
85 */ 84 */
86 private String userAgent; 85 private String userAgent;
87 86
88 - // TODO @芋艿:requestUrl、requestMethod  
89 - // TODO @芋艿:javaMethod、javaMethodArgs  
90 - // TODO @芋艿:startTime、duration  
91 - // TODO @芋艿:resultMsg、resultData  
92 -  
93 } 87 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java
@@ -3,29 +3,19 @@ package cn.iocoder.yudao.module.system.dal.mysql.logger; @@ -3,29 +3,19 @@ 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)  
28 - .orderByDesc(OperateLogV2DO::getCreateTime)); 13 + default PageResult<OperateLogV2DO> selectPage(OperateLogV2PageReqDTO pageReqDTO) {
  14 + return selectPage(pageReqDTO, new LambdaQueryWrapperX<OperateLogV2DO>()
  15 + .eqIfPresent(OperateLogV2DO::getType, pageReqDTO.getBizType())
  16 + .eqIfPresent(OperateLogV2DO::getBizId, pageReqDTO.getBizId())
  17 + .eqIfPresent(OperateLogV2DO::getUserId, pageReqDTO.getUserId())
  18 + .orderByDesc(OperateLogV2DO::getId));
29 } 19 }
30 20
31 } 21 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java
@@ -34,4 +34,11 @@ public interface SocialUserBindMapper extends BaseMapperX&lt;SocialUserBindDO&gt; { @@ -34,4 +34,11 @@ public interface SocialUserBindMapper extends BaseMapperX&lt;SocialUserBindDO&gt; {
34 .eq(SocialUserBindDO::getUserType, userType)); 34 .eq(SocialUserBindDO::getUserType, userType));
35 } 35 }
36 36
  37 + default SocialUserBindDO selectByUserIdAndUserTypeAndSocialType(Long userId, Integer userType, Integer socialType) {
  38 + return selectOne(new LambdaQueryWrapperX<SocialUserBindDO>()
  39 + .eq(SocialUserBindDO::getUserId, userId)
  40 + .eq(SocialUserBindDO::getUserType, userType)
  41 + .eq(SocialUserBindDO::getSocialType, socialType));
  42 + }
  43 +
37 } 44 }
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/function/AdminUserParseFunction.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java
1 -package cn.iocoder.yudao.module.system.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.system.framework.operatelog.core;
2 2
3 -import cn.hutool.core.util.ObjUtil;  
4 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 4 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
6 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 5 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -9,9 +8,8 @@ import jakarta.annotation.Resource; @@ -9,9 +8,8 @@ import jakarta.annotation.Resource;
9 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
10 import org.springframework.stereotype.Component; 9 import org.springframework.stereotype.Component;
11 10
12 -// TODO @puhui999:这个微信讨论下,function 叫啥好哈;  
13 /** 11 /**
14 - * 自定义函数-通过用户编号获取用户信息 12 + * 管理员名字的 {@link IParseFunction} 实现类
15 * 13 *
16 * @author HUIHUI 14 * @author HUIHUI
17 */ 15 */
@@ -29,26 +27,22 @@ public class AdminUserParseFunction implements IParseFunction { @@ -29,26 +27,22 @@ public class AdminUserParseFunction implements IParseFunction {
29 27
30 @Override 28 @Override
31 public String apply(Object value) { 29 public String apply(Object value) {
32 - if (value == null) {  
33 - //log.warn("(getAdminUserById) 解析异常参数为 null");  
34 - return "";  
35 - }  
36 - if (StrUtil.isEmpty(value.toString())) {  
37 - //log.warn("(getAdminUserById) 解析异常参数为空"); 30 + if (StrUtil.isEmptyIfStr(value)) {
38 return ""; 31 return "";
39 } 32 }
40 33
41 // 获取用户信息 34 // 获取用户信息
42 AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString())); 35 AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString()));
43 if (user == null) { 36 if (user == null) {
44 - log.warn("(getAdminUserById) 获取用户信息失败,参数为:{}", value); 37 + log.warn("[apply][获取用户{{}}为空", value);
45 return ""; 38 return "";
46 } 39 }
47 // 返回格式 芋道源码(13888888888) 40 // 返回格式 芋道源码(13888888888)
48 String nickname = user.getNickname(); 41 String nickname = user.getNickname();
49 - if (ObjUtil.isNotEmpty(user.getMobile())) {  
50 - return nickname.concat("(").concat(user.getMobile()).concat(")"); 42 + if (StrUtil.isEmpty(user.getMobile())) {
  43 + return nickname;
51 } 44 }
52 - return nickname; 45 + return StrUtil.format("{}({})", nickname, user.getMobile());
53 } 46 }
  47 +
54 } 48 }
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/core/AreaParseFunction.java
1 -package cn.iocoder.yudao.module.system.framework.bizlog.function; 1 +package cn.iocoder.yudao.module.system.framework.operatelog.core;
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;
@@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j; @@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j;
7 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
8 8
9 /** 9 /**
10 - * 自定义函数-通过区域编号获取区域信息 10 + * 地名的 {@link IParseFunction} 实现类
11 * 11 *
12 * @author HUIHUI 12 * @author HUIHUI
13 */ 13 */
@@ -27,13 +27,10 @@ public class AreaParseFunction implements IParseFunction { @@ -27,13 +27,10 @@ public class AreaParseFunction implements IParseFunction {
27 27
28 @Override 28 @Override
29 public String apply(Object value) { 29 public String apply(Object value) {
30 - if (value == null) { 30 + if (StrUtil.isEmptyIfStr(value)) {
31 return ""; 31 return "";
32 } 32 }
33 - if (StrUtil.isEmpty(value.toString())) {  
34 - return "";  
35 - }  
36 -  
37 return AreaUtils.format(Integer.parseInt(value.toString())); 33 return AreaUtils.format(Integer.parseInt(value.toString()));
38 } 34 }
  35 +
39 } 36 }
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/service/auth/AdminAuthServiceImpl.java
@@ -156,9 +156,9 @@ public class AdminAuthServiceImpl implements AdminAuthService { @@ -156,9 +156,9 @@ public class AdminAuthServiceImpl implements AdminAuthService {
156 @Override 156 @Override
157 public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) { 157 public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) {
158 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 158 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
159 - SocialUserRespDTO socialUser = socialUserService.getSocialUser(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), 159 + SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
160 reqVO.getCode(), reqVO.getState()); 160 reqVO.getCode(), reqVO.getState());
161 - if (socialUser == null) { 161 + if (socialUser == null || socialUser.getUserId() == null) {
162 throw exception(AUTH_THIRD_LOGIN_NOT_BIND); 162 throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
163 } 163 }
164 164
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 接口
@@ -36,18 +35,16 @@ public interface OperateLogService { @@ -36,18 +35,16 @@ public interface OperateLogService {
36 /** 35 /**
37 * 记录操作日志 V2 36 * 记录操作日志 V2
38 * 37 *
39 - * @param createReqBO 创建请求 38 + * @param createReqDTO 创建请求
40 */ 39 */
41 - void createOperateLogV2(OperateLogV2CreateReqBO createReqBO); 40 + void createOperateLogV2(OperateLogV2CreateReqDTO createReqDTO);
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,14 @@ public class OperateLogServiceImpl implements OperateLogService { @@ -69,14 +69,14 @@ public class OperateLogServiceImpl implements OperateLogService {
69 // ======================= LOG V2 ======================= 69 // ======================= LOG V2 =======================
70 70
71 @Override 71 @Override
72 - public void createOperateLogV2(OperateLogV2CreateReqBO createReqBO) {  
73 - OperateLogV2DO log = BeanUtils.toBean(createReqBO, OperateLogV2DO.class); 72 + public void createOperateLogV2(OperateLogV2CreateReqDTO createReqDTO) {
  73 + OperateLogV2DO log = BeanUtils.toBean(createReqDTO, OperateLogV2DO.class);
74 operateLogV2Mapper.insert(log); 74 operateLogV2Mapper.insert(log);
75 } 75 }
76 76
77 @Override 77 @Override
78 - public List<OperateLogV2DO> getOperateLogByModuleAndBizId(String module, Long bizId) {  
79 - return operateLogV2Mapper.selectListByModuleAndBizId(module, bizId); 78 + public PageResult<OperateLogV2DO> getOperateLogPage(OperateLogV2PageReqDTO pageReqDTO) {
  79 + return operateLogV2Mapper.selectPage(pageReqDTO);
80 } 80 }
81 81
82 } 82 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java
@@ -30,6 +30,7 @@ import com.xingyuv.jushauth.model.AuthUser; @@ -30,6 +30,7 @@ import com.xingyuv.jushauth.model.AuthUser;
30 import com.xingyuv.jushauth.request.AuthRequest; 30 import com.xingyuv.jushauth.request.AuthRequest;
31 import com.xingyuv.jushauth.utils.AuthStateUtils; 31 import com.xingyuv.jushauth.utils.AuthStateUtils;
32 import com.xingyuv.justauth.AuthRequestFactory; 32 import com.xingyuv.justauth.AuthRequestFactory;
  33 +import jakarta.annotation.Resource;
33 import lombok.SneakyThrows; 34 import lombok.SneakyThrows;
34 import lombok.extern.slf4j.Slf4j; 35 import lombok.extern.slf4j.Slf4j;
35 import me.chanjar.weixin.common.bean.WxJsapiSignature; 36 import me.chanjar.weixin.common.bean.WxJsapiSignature;
@@ -41,7 +42,6 @@ import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; @@ -41,7 +42,6 @@ import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
41 import org.springframework.data.redis.core.StringRedisTemplate; 42 import org.springframework.data.redis.core.StringRedisTemplate;
42 import org.springframework.stereotype.Service; 43 import org.springframework.stereotype.Service;
43 44
44 -import jakarta.annotation.Resource;  
45 import java.time.Duration; 45 import java.time.Duration;
46 import java.util.Objects; 46 import java.util.Objects;
47 47
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java
@@ -46,6 +46,16 @@ public interface SocialUserService { @@ -46,6 +46,16 @@ public interface SocialUserService {
46 void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid); 46 void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid);
47 47
48 /** 48 /**
  49 + * 获得社交用户,基于 userId
  50 + *
  51 + * @param userType 用户类型
  52 + * @param userId 用户编号
  53 + * @param socialType 社交平台的类型
  54 + * @return 社交用户
  55 + */
  56 + SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType);
  57 +
  58 + /**
49 * 获得社交用户 59 * 获得社交用户
50 * 60 *
51 * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 61 * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
@@ -56,7 +66,7 @@ public interface SocialUserService { @@ -56,7 +66,7 @@ public interface SocialUserService {
56 * @param state state 66 * @param state state
57 * @return 社交用户 67 * @return 社交用户
58 */ 68 */
59 - SocialUserRespDTO getSocialUser(Integer userType, Integer socialType, String code, String state); 69 + SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state);
60 70
61 // ==================== 社交用户 CRUD ==================== 71 // ==================== 社交用户 CRUD ====================
62 72
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
@@ -26,7 +26,6 @@ import java.util.List; @@ -26,7 +26,6 @@ import java.util.List;
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
28 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 28 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
29 -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_THIRD_LOGIN_NOT_BIND;  
30 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND; 29 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
31 30
32 /** 31 /**
@@ -94,18 +93,30 @@ public class SocialUserServiceImpl implements SocialUserService { @@ -94,18 +93,30 @@ public class SocialUserServiceImpl implements SocialUserService {
94 } 93 }
95 94
96 @Override 95 @Override
97 - public SocialUserRespDTO getSocialUser(Integer userType, Integer socialType, String code, String state) { 96 + public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) {
  97 + // 获得绑定用户
  98 + SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserIdAndUserTypeAndSocialType(userId, userType, socialType);
  99 + if (socialUserBind == null) {
  100 + return null;
  101 + }
  102 + // 获得社交用户
  103 + SocialUserDO socialUser = socialUserMapper.selectById(socialUserBind.getSocialUserId());
  104 + Assert.notNull(socialUser, "社交用户不能为空");
  105 + return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(),
  106 + socialUserBind.getUserId());
  107 + }
  108 +
  109 + @Override
  110 + public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) {
98 // 获得社交用户 111 // 获得社交用户
99 SocialUserDO socialUser = authSocialUser(socialType, userType, code, state); 112 SocialUserDO socialUser = authSocialUser(socialType, userType, code, state);
100 Assert.notNull(socialUser, "社交用户不能为空"); 113 Assert.notNull(socialUser, "社交用户不能为空");
101 114
102 - // 如果未绑定的社交用户,则无法自动登录,进行报错 115 + // 获得绑定用户
103 SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType, 116 SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType,
104 socialUser.getId()); 117 socialUser.getId());
105 - if (socialUserBind == null) {  
106 - throw exception(AUTH_THIRD_LOGIN_NOT_BIND);  
107 - }  
108 - return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId()); 118 + return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(),
  119 + socialUserBind != null ? socialUserBind.getUserId() : null);
109 } 120 }
110 121
111 /** 122 /**
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java
@@ -21,15 +21,14 @@ import cn.iocoder.yudao.module.system.service.social.SocialUserService; @@ -21,15 +21,14 @@ import cn.iocoder.yudao.module.system.service.social.SocialUserService;
21 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 21 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
22 import com.xingyuv.captcha.model.common.ResponseModel; 22 import com.xingyuv.captcha.model.common.ResponseModel;
23 import com.xingyuv.captcha.service.CaptchaService; 23 import com.xingyuv.captcha.service.CaptchaService;
24 -import org.junit.jupiter.api.BeforeEach;  
25 -import org.junit.jupiter.api.Test;  
26 -import org.springframework.boot.test.mock.mockito.MockBean;  
27 -import org.springframework.context.annotation.Import;  
28 -  
29 import jakarta.annotation.Resource; 24 import jakarta.annotation.Resource;
30 import jakarta.validation.ConstraintViolationException; 25 import jakarta.validation.ConstraintViolationException;
31 import jakarta.validation.Validation; 26 import jakarta.validation.Validation;
32 import jakarta.validation.Validator; 27 import jakarta.validation.Validator;
  28 +import org.junit.jupiter.api.BeforeEach;
  29 +import org.junit.jupiter.api.Test;
  30 +import org.springframework.boot.test.mock.mockito.MockBean;
  31 +import org.springframework.context.annotation.Import;
33 32
34 import static cn.hutool.core.util.RandomUtil.randomEle; 33 import static cn.hutool.core.util.RandomUtil.randomEle;
35 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 34 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
@@ -236,8 +235,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { @@ -236,8 +235,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
236 AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class); 235 AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class);
237 // mock 方法(绑定的用户编号) 236 // mock 方法(绑定的用户编号)
238 Long userId = 1L; 237 Long userId = 1L;
239 - when(socialUserService.getSocialUser(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),  
240 - eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), userId)); 238 + when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
  239 + eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId));
241 // mock(用户) 240 // mock(用户)
242 AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)); 241 AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
243 when(userService.getUser(eq(userId))).thenReturn(user); 242 when(userService.getUser(eq(userId))).thenReturn(user);
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java
@@ -147,7 +147,7 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest { @@ -147,7 +147,7 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
147 socialUserBindMapper.insert(socialUserBind); 147 socialUserBindMapper.insert(socialUserBind);
148 148
149 // 调用 149 // 调用
150 - SocialUserRespDTO socialUser = socialUserService.getSocialUser(userType, type, code, state); 150 + SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(userType, type, code, state);
151 // 断言 151 // 断言
152 assertEquals(userId, socialUser.getUserId()); 152 assertEquals(userId, socialUser.getUserId());
153 assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid()); 153 assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid());
yudao-server/src/main/resources/application-dev.yaml
@@ -5,9 +5,6 @@ server: @@ -5,9 +5,6 @@ server:
5 5
6 spring: 6 spring:
7 # 数据源配置项 7 # 数据源配置项
8 - autoconfigure:  
9 - exclude:  
10 - - org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration # 排除积木报表带来的 MongoDB 的自动配置  
11 datasource: 8 datasource:
12 druid: # Druid 【监控】相关的全局配置 9 druid: # Druid 【监控】相关的全局配置
13 web-stat-filter: 10 web-stat-filter:
yudao-server/src/main/resources/application-local.yaml
@@ -7,7 +7,6 @@ spring: @@ -7,7 +7,6 @@ spring:
7 # 数据源配置项 7 # 数据源配置项
8 autoconfigure: 8 autoconfigure:
9 exclude: 9 exclude:
10 - - org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration # 排除积木报表带来的 MongoDB 的自动配置  
11 - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置 10 - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置
12 - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 11 - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置
13 - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 12 - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置
@@ -188,26 +187,32 @@ logging: @@ -188,26 +187,32 @@ logging:
188 cn.iocoder.yudao.module.trade.dal.mysql: debug 187 cn.iocoder.yudao.module.trade.dal.mysql: debug
189 cn.iocoder.yudao.module.promotion.dal.mysql: debug 188 cn.iocoder.yudao.module.promotion.dal.mysql: debug
190 cn.iocoder.yudao.module.statistics.dal.mysql: debug 189 cn.iocoder.yudao.module.statistics.dal.mysql: debug
  190 + cn.iocoder.yudao.module.crm.dal.mysql: debug
  191 + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示
191 192
192 debug: false 193 debug: false
193 194
194 --- #################### 微信公众号、小程序相关配置 #################### 195 --- #################### 微信公众号、小程序相关配置 ####################
195 wx: 196 wx:
196 mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 197 mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
197 - # app-id: wx041349c6f39b268b  
198 - # secret: 5abee519483bc9f8cb37ce280e814bd0  
199 - app-id: wx5b23ba7a5589ecbb # 测试号 198 +# app-id: wx041349c6f39b268b # 测试号(牛希尧提供的)
  199 +# secret: 5abee519483bc9f8cb37ce280e814bd0
  200 + app-id: wx5b23ba7a5589ecbb # 测试号(自己的)
200 secret: 2a7b3b20c537e52e74afd395eb85f61f 201 secret: 2a7b3b20c537e52e74afd395eb85f61f
  202 +# app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的)
  203 +# secret: bd4f9fab889591b62aeac0d7b8d8b4a0
201 # 存储配置,解决 AccessToken 的跨节点的共享 204 # 存储配置,解决 AccessToken 的跨节点的共享
202 config-storage: 205 config-storage:
203 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 206 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
204 key-prefix: wx # Redis Key 的前缀 207 key-prefix: wx # Redis Key 的前缀
205 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 208 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
206 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 209 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
207 - # appid: wx62056c0d5e8db250 210 + # appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的)
208 # secret: 333ae72f41552af1e998fe1f54e1584a 211 # secret: 333ae72f41552af1e998fe1f54e1584a
209 appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 212 appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
210 secret: 6f270509224a7ae1296bbf1c8cb97aed 213 secret: 6f270509224a7ae1296bbf1c8cb97aed
  214 +# appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的)
  215 +# secret: 4a1a04e07f6a4a0751b39c3064a92c8b
211 config-storage: 216 config-storage:
212 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 217 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
213 key-prefix: wa # Redis Key 的前缀 218 key-prefix: wa # Redis Key 的前缀
@@ -257,7 +262,6 @@ justauth: @@ -257,7 +262,6 @@ justauth:
257 client-id: ${wx.mp.app-id} 262 client-id: ${wx.mp.app-id}
258 client-secret: ${wx.mp.secret} 263 client-secret: ${wx.mp.secret}
259 ignore-check-redirect-uri: true 264 ignore-check-redirect-uri: true
260 - ignore-check-state: true # 微信公众号,未调用后端的 getSocialAuthorizeUrl 方法,所以无法进行 state 校验 TODO 芋艿:后续考虑支持  
261 265
262 cache: 266 cache:
263 type: REDIS 267 type: REDIS
yudao-server/src/main/resources/application.yaml
@@ -79,7 +79,11 @@ mybatis-plus: @@ -79,7 +79,11 @@ 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 + banner: false # 是否打印 mybatis plus join banner,默认true
  83 + sub-table-logic: true # 全局启用副表逻辑删除,默认true。关闭后关联查询不会加副表逻辑删除
  84 + ms-cache: true # 拦截器MappedStatement缓存,默认 true
  85 + table-alias: t # 表别名(默认 t)
  86 + logic-del-type: on # 副表逻辑删除条件的位置,支持 WHERE、ON,默认 ON
83 87
84 # Spring Data Redis 配置 88 # Spring Data Redis 配置
85 spring: 89 spring:
@@ -260,9 +264,9 @@ yudao: @@ -260,9 +264,9 @@ yudao:
260 debug: false 264 debug: false
261 265
262 # 积木报表配置 266 # 积木报表配置
263 -minidao :  
264 - base-package: org.jeecg.modules.jmreport.desreport.dao*  
265 - db-type: mysql 267 +jeecg:
  268 + jmreport:
  269 + saas-mode: tenant
266 270
267 # UReport 配置 271 # UReport 配置
268 ureport: 272 ureport: