Commit bada32750c67282ff7f8c8b1acbad736c3448c28

Authored by 芋道源码
Committed by Gitee
2 parents b0b65448 daf4651a

!795 CRM: 完善线索、商机、联系人、客户、合同、回款、回款计划数据权限

Merge pull request !795 from puhui999/develop
Showing 76 changed files with 1455 additions and 310 deletions
yudao-dependencies/pom.xml
@@ -62,6 +62,7 @@ @@ -62,6 +62,7 @@
62 <jsch.version>0.1.55</jsch.version> 62 <jsch.version>0.1.55</jsch.version>
63 <tika-core.version>2.9.1</tika-core.version> 63 <tika-core.version>2.9.1</tika-core.version>
64 <ip2region.version>2.7.0</ip2region.version> 64 <ip2region.version>2.7.0</ip2region.version>
  65 + <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
65 <!-- 三方云服务相关 --> 66 <!-- 三方云服务相关 -->
66 <okio.version>3.5.0</okio.version> 67 <okio.version>3.5.0</okio.version>
67 <okhttp3.version>4.11.0</okhttp3.version> 68 <okhttp3.version>4.11.0</okhttp3.version>
@@ -100,6 +101,17 @@ @@ -100,6 +101,17 @@
100 <version>${revision}</version> 101 <version>${revision}</version>
101 </dependency> 102 </dependency>
102 <dependency> 103 <dependency>
  104 + <groupId>io.github.mouzt</groupId>
  105 + <artifactId>bizlog-sdk</artifactId>
  106 + <version>${bizlog-sdk.version}</version>
  107 + <exclusions>
  108 + <exclusion> <!-- 排除掉springboot依赖使用项目的 -->
  109 + <groupId>org.springframework.boot</groupId>
  110 + <artifactId>spring-boot-starter</artifactId>
  111 + </exclusion>
  112 + </exclusions>
  113 + </dependency>
  114 + <dependency>
103 <groupId>cn.iocoder.boot</groupId> 115 <groupId>cn.iocoder.boot</groupId>
104 <artifactId>yudao-spring-boot-starter-biz-dict</artifactId> 116 <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
105 <version>${revision}</version> 117 <version>${revision}</version>
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
@@ -177,4 +177,14 @@ public class DateUtils { @@ -177,4 +177,14 @@ public class DateUtils {
177 return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now()); 177 return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
178 } 178 }
179 179
  180 + /**
  181 + * 是否昨天
  182 + *
  183 + * @param date 日期
  184 + * @return 是否
  185 + */
  186 + public static boolean isYesterday(LocalDateTime date) {
  187 + return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1));
  188 + }
  189 +
180 } 190 }
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.enums;
  2 +
  3 +/**
  4 + * CRM 操作日志枚举
  5 + *
  6 + * @author HUIHUI
  7 + */
  8 +public interface LogRecordConstants {
  9 +
  10 + //======================= 客户模块类型 =======================
  11 + // TODO puhui999: 确保模块命名方式为 module + 子模块名称的方式。统一定义模块名称是为了方便查询各自记录的操作日志,列如说:查询客户【张三的操作日志】就可以 module + bizId
  12 + String CRM_LEADS = "CRM-线索";
  13 + String CRM_CUSTOMER = "CRM-客户";
  14 + String CRM_CONTACT = "CRM-联系人";
  15 + String CRM_BUSINESS = "CRM-商机";
  16 + String CRM_CONTRACT = "CRM-合同";
  17 + String CRM_PRODUCT = "CRM-产品";
  18 + String CRM_RECEIVABLE = "CRM-回款";
  19 + String CRM_RECEIVABLE_PLAN = "CRM-回款计划";
  20 +
  21 + //======================= 客户转移操作日志 =======================
  22 +
  23 + String TRANSFER_CUSTOMER_LOG_SUCCESS = "把客户【{{#crmCustomer.name}}】的负责人从【{getAdminUserById{#crmCustomer.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
  24 + String TRANSFER_CUSTOMER_LOG_FAIL = "";
  25 +
  26 +}
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java
@@ -22,7 +22,9 @@ public enum CrmBizTypeEnum implements IntArrayValuable { @@ -22,7 +22,9 @@ public enum CrmBizTypeEnum implements IntArrayValuable {
22 CRM_CONTACT(3, "联系人"), 22 CRM_CONTACT(3, "联系人"),
23 CRM_BUSINESS(4, "商机"), 23 CRM_BUSINESS(4, "商机"),
24 CRM_CONTRACT(5, "合同"), 24 CRM_CONTRACT(5, "合同"),
25 - CRM_PRODUCT(6, "产品") 25 + CRM_PRODUCT(6, "产品"),
  26 + CRM_RECEIVABLE(7, "回款"),
  27 + CRM_RECEIVABLE_PLAN(8, "回款计划")
26 ; 28 ;
27 29
28 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizTypeEnum::getType).toArray(); 30 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmBizTypeEnum::getType).toArray();
yudao-module-crm/yudao-module-crm-biz/pom.xml
@@ -60,6 +60,10 @@ @@ -60,6 +60,10 @@
60 <groupId>cn.iocoder.boot</groupId> 60 <groupId>cn.iocoder.boot</groupId>
61 <artifactId>yudao-spring-boot-starter-excel</artifactId> 61 <artifactId>yudao-spring-boot-starter-excel</artifactId>
62 </dependency> 62 </dependency>
  63 + <dependency>
  64 + <groupId>cn.iocoder.boot</groupId>
  65 + <artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
  66 + </dependency>
63 67
64 <!-- Test 测试相关 --> 68 <!-- Test 测试相关 -->
65 <dependency> 69 <dependency>
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
@@ -122,7 +122,7 @@ public class CrmBusinessController { @@ -122,7 +122,7 @@ public class CrmBusinessController {
122 @Operation(summary = "获得商机分页,基于指定客户") 122 @Operation(summary = "获得商机分页,基于指定客户")
123 public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByCustomer(@Valid CrmBusinessPageReqVO pageReqVO) { 123 public CommonResult<PageResult<CrmBusinessRespVO>> getBusinessPageByCustomer(@Valid CrmBusinessPageReqVO pageReqVO) {
124 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); 124 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
125 - PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomer(pageReqVO, getLoginUserId()); 125 + PageResult<CrmBusinessDO> pageResult = businessService.getBusinessPageByCustomerId(pageReqVO);
126 // 处理客户名称回显 126 // 处理客户名称回显
127 // TODO @ljlleo:可以使用 CollectionUtils.convertSet 替代常用的 stream 操作,更简洁一点;下面几个也是哈; 127 // TODO @ljlleo:可以使用 CollectionUtils.convertSet 替代常用的 stream 操作,更简洁一点;下面几个也是哈;
128 Set<Long> customerIds = pageResult.getList().stream() 128 Set<Long> customerIds = pageResult.getList().stream()
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessPageReqVO.java
@@ -24,7 +24,4 @@ public class CrmBusinessPageReqVO extends PageParam { @@ -24,7 +24,4 @@ public class CrmBusinessPageReqVO extends PageParam {
24 @InEnum(CrmSceneTypeEnum.class) 24 @InEnum(CrmSceneTypeEnum.class)
25 private Integer sceneType; // 场景类型,为 null 时则表示全部 25 private Integer sceneType; // 场景类型,为 null 时则表示全部
26 26
27 - @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")  
28 - private Boolean pool; // null 则表示为不是公海数据  
29 -  
30 } 27 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessTransferReqVO.java
@@ -2,16 +2,15 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; @@ -2,16 +2,15 @@ package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
2 2
3 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 3 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 -import lombok.Data;  
6 -  
7 import jakarta.validation.constraints.NotNull; 5 import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
8 7
9 @Schema(description = "管理后台 - 商机转移 Request VO") 8 @Schema(description = "管理后台 - 商机转移 Request VO")
10 @Data 9 @Data
11 public class CrmBusinessTransferReqVO { 10 public class CrmBusinessTransferReqVO {
12 11
13 @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") 12 @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
14 - @NotNull(message = "联系人编号不能为空") 13 + @NotNull(message = "商机编号不能为空")
15 private Long id; 14 private Long id;
16 15
17 /** 16 /**
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
@@ -88,4 +88,12 @@ public class CrmClueController { @@ -88,4 +88,12 @@ public class CrmClueController {
88 ExcelUtils.write(response, "线索.xls", "数据", CrmClueExcelVO.class, datas); 88 ExcelUtils.write(response, "线索.xls", "数据", CrmClueExcelVO.class, datas);
89 } 89 }
90 90
  91 + @PutMapping("/transfer")
  92 + @Operation(summary = "线索转移")
  93 + @PreAuthorize("@ss.hasPermission('crm:clue:update')")
  94 + public CommonResult<Boolean> transfer(@Valid @RequestBody CrmClueTransferReqVO reqVO) {
  95 + clueService.transferClue(reqVO, getLoginUserId());
  96 + return success(true);
  97 + }
  98 +
91 } 99 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueTransferReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
  2 +
  3 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - 线索转移 Request VO")
  9 +@Data
  10 +public class CrmClueTransferReqVO {
  11 +
  12 + @Schema(description = "线索编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
  13 + @NotNull(message = "线索编号不能为空")
  14 + private Long id;
  15 +
  16 + /**
  17 + * 新负责人的用户编号
  18 + */
  19 + @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
  20 + @NotNull(message = "新负责人的用户编号不能为空")
  21 + private Long newOwnerUserId;
  22 +
  23 + /**
  24 + * 老负责人加入团队后的权限级别。如果 null 说明移除
  25 + *
  26 + * 关联 {@link CrmPermissionLevelEnum}
  27 + */
  28 + @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  29 + private Integer oldOwnerPermissionLevel;
  30 +
  31 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
@@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; @@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
11 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*; 11 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
12 -import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert; 12 +import cn.iocoder.yudao.module.crm.convert.contact.CrmContactConvert;
13 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; 13 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
15 import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants; 15 import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants;
@@ -100,7 +100,7 @@ public class CrmContactController { @@ -100,7 +100,7 @@ public class CrmContactController {
100 // 3. 直属上级 100 // 3. 直属上级
101 List<CrmContactDO> parentContactList = contactService.getContactList( 101 List<CrmContactDO> parentContactList = contactService.getContactList(
102 Collections.singletonList(contact.getParentId()), getLoginUserId()); 102 Collections.singletonList(contact.getParentId()), getLoginUserId());
103 - return success(ContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList)); 103 + return success(CrmContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList));
104 } 104 }
105 105
106 @GetMapping("/simple-all-list") 106 @GetMapping("/simple-all-list")
@@ -110,7 +110,7 @@ public class CrmContactController { @@ -110,7 +110,7 @@ public class CrmContactController {
110 CrmContactPageReqVO pageReqVO = new CrmContactPageReqVO(); 110 CrmContactPageReqVO pageReqVO = new CrmContactPageReqVO();
111 pageReqVO.setPageSize(PAGE_SIZE_NONE); 111 pageReqVO.setPageSize(PAGE_SIZE_NONE);
112 List<CrmContactDO> list = contactService.getContactPage(pageReqVO, getLoginUserId()).getList(); 112 List<CrmContactDO> list = contactService.getContactPage(pageReqVO, getLoginUserId()).getList();
113 - return success(ContactConvert.INSTANCE.convertAllList(list)); 113 + return success(CrmContactConvert.INSTANCE.convertAllList(list));
114 } 114 }
115 115
116 @GetMapping("/page") 116 @GetMapping("/page")
@@ -125,7 +125,7 @@ public class CrmContactController { @@ -125,7 +125,7 @@ public class CrmContactController {
125 @Operation(summary = "获得联系人分页,基于指定客户") 125 @Operation(summary = "获得联系人分页,基于指定客户")
126 public CommonResult<PageResult<CrmContactRespVO>> getContactPageByCustomer(@Valid CrmContactPageReqVO pageVO) { 126 public CommonResult<PageResult<CrmContactRespVO>> getContactPageByCustomer(@Valid CrmContactPageReqVO pageVO) {
127 Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空"); 127 Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
128 - PageResult<CrmContactDO> pageResult = contactService.getContactPageByCustomerId(pageVO, getLoginUserId()); 128 + PageResult<CrmContactDO> pageResult = contactService.getContactPageByCustomerId(pageVO);
129 return success(convertDetailContactPage(pageResult)); 129 return success(convertDetailContactPage(pageResult));
130 } 130 }
131 131
@@ -161,7 +161,15 @@ public class CrmContactController { @@ -161,7 +161,15 @@ public class CrmContactController {
161 // 3. 直属上级 161 // 3. 直属上级
162 List<CrmContactDO> parentContactList = contactService.getContactList( 162 List<CrmContactDO> parentContactList = contactService.getContactList(
163 convertSet(contactList, CrmContactDO::getParentId), getLoginUserId()); 163 convertSet(contactList, CrmContactDO::getParentId), getLoginUserId());
164 - return ContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList); 164 + return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList);
  165 + }
  166 +
  167 + @PutMapping("/transfer")
  168 + @Operation(summary = "联系人转移")
  169 + @PreAuthorize("@ss.hasPermission('crm:contact:update')")
  170 + public CommonResult<Boolean> transfer(@Valid @RequestBody CrmContactTransferReqVO reqVO) {
  171 + contactService.transferContact(reqVO, getLoginUserId());
  172 + return success(true);
165 } 173 }
166 174
167 } 175 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactPageReqVO.java
@@ -39,7 +39,4 @@ public class CrmContactPageReqVO extends PageParam { @@ -39,7 +39,4 @@ public class CrmContactPageReqVO extends PageParam {
39 @InEnum(CrmSceneTypeEnum.class) 39 @InEnum(CrmSceneTypeEnum.class)
40 private Integer sceneType; // 场景类型,为 null 时则表示全部 40 private Integer sceneType; // 场景类型,为 null 时则表示全部
41 41
42 - @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")  
43 - private Boolean pool; // null 则表示为不是公海数据  
44 -  
45 } 42 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
@@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*; 10 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
11 -import cn.iocoder.yudao.module.crm.convert.contract.ContractConvert; 11 +import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
12 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
13 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 13 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
14 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 14 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
@@ -80,7 +80,7 @@ public class CrmContractController { @@ -80,7 +80,7 @@ public class CrmContractController {
80 @PreAuthorize("@ss.hasPermission('crm:contract:query')") 80 @PreAuthorize("@ss.hasPermission('crm:contract:query')")
81 public CommonResult<ContractRespVO> getContract(@RequestParam("id") Long id) { 81 public CommonResult<ContractRespVO> getContract(@RequestParam("id") Long id) {
82 CrmContractDO contract = contractService.getContract(id); 82 CrmContractDO contract = contractService.getContract(id);
83 - return success(ContractConvert.INSTANCE.convert(contract)); 83 + return success(CrmContractConvert.INSTANCE.convert(contract));
84 } 84 }
85 85
86 @GetMapping("/page") 86 @GetMapping("/page")
@@ -95,7 +95,7 @@ public class CrmContractController { @@ -95,7 +95,7 @@ public class CrmContractController {
95 @Operation(summary = "获得联系人分页,基于指定客户") 95 @Operation(summary = "获得联系人分页,基于指定客户")
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.getContractPageByCustomer(pageVO, getLoginUserId()); 98 + PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO);
99 return success(convertDetailContractPage(pageResult)); 99 return success(convertDetailContractPage(pageResult));
100 } 100 }
101 101
@@ -108,7 +108,7 @@ public class CrmContractController { @@ -108,7 +108,7 @@ public class CrmContractController {
108 PageResult<CrmContractDO> pageResult = contractService.getContractPage(exportReqVO, getLoginUserId()); 108 PageResult<CrmContractDO> pageResult = contractService.getContractPage(exportReqVO, getLoginUserId());
109 // 导出 Excel 109 // 导出 Excel
110 ExcelUtils.write(response, "合同.xls", "数据", CrmContractExcelVO.class, 110 ExcelUtils.write(response, "合同.xls", "数据", CrmContractExcelVO.class,
111 - ContractConvert.INSTANCE.convertList02(pageResult.getList())); 111 + CrmContractConvert.INSTANCE.convertList02(pageResult.getList()));
112 } 112 }
113 113
114 /** 114 /**
@@ -128,7 +128,7 @@ public class CrmContractController { @@ -128,7 +128,7 @@ public class CrmContractController {
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())));
131 - return ContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList); 131 + return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList);
132 } 132 }
133 133
134 @PutMapping("/transfer") 134 @PutMapping("/transfer")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractPageReqVO.java
@@ -30,7 +30,4 @@ public class CrmContractPageReqVO extends PageParam { @@ -30,7 +30,4 @@ public class CrmContractPageReqVO extends PageParam {
30 @InEnum(CrmSceneTypeEnum.class) 30 @InEnum(CrmSceneTypeEnum.class)
31 private Integer sceneType; // 场景类型,为 null 时则表示全部 31 private Integer sceneType; // 场景类型,为 null 时则表示全部
32 32
33 - @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")  
34 - private Boolean pool; // null 则表示为不是公海数据 TODO @puhui999:合同没有公海。目前只有【客户】【线索】有公海,其它都没  
35 -  
36 } 33 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
1 -### 请求 /update  
2 -GET {{baseUrl}}/crm/customer/page?pageNo=1&pageSize=10&name="张三" 1 +### 请求 /transfer
  2 +PUT {{baseUrl}}/crm/customer/transfer
  3 +Content-Type: application/json
3 Authorization: Bearer {{token}} 4 Authorization: Bearer {{token}}
4 tenant-id: {{adminTenentId}} 5 tenant-id: {{adminTenentId}}
5 6
  7 +{
  8 + "id": 10,
  9 + "newOwnerUserId": 127
  10 +}
6 11
  12 +### 自定义日志记录结果
  13 +### 操作日志 ===> OperateLogV2CreateReqBO(traceId=, userId=1, userType=2, module=CRM-客户, name=客户转移, bizId=10, content=把客户【张三】的负责人从【芋道源码(15612345678)】变更为了【tttt】, requestMethod=PUT, requestUrl=/admin-api/crm/customer/transfer, userIp=127.0.0.1, userAgent=Apache-HttpClient/4.5.14 (Java/17.0.9))
  14 +
  15 +### diff 日志
  16 +### | 操作日志 ===> OperateLogV2CreateReqBO(traceId=, userId=1, userType=2, module=CRM-客户, name=更新客户, bizId=11, content=更新了客户【所属行业】从【H 住宿和餐饮业】修改为【D 电力、热力、燃气及水生产和供应业】;【客户等级】从【C (非优先客户)】修改为【A (重点客户)】;【客户来源】从【线上咨询】修改为【预约上门】, requestMethod=PUT, requestUrl=/admin-api/crm/customer/update, userIp=0:0:0:0:0:0:0:1, userAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36)
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -11,20 +11,22 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; @@ -11,20 +11,22 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
11 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 11 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
12 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 12 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
13 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 13 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.dto.OperateLogV2RespDTO;
14 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 16 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
15 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 17 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
16 import io.swagger.v3.oas.annotations.Operation; 18 import io.swagger.v3.oas.annotations.Operation;
17 import io.swagger.v3.oas.annotations.Parameter; 19 import io.swagger.v3.oas.annotations.Parameter;
18 import io.swagger.v3.oas.annotations.Parameters; 20 import io.swagger.v3.oas.annotations.Parameters;
19 import io.swagger.v3.oas.annotations.tags.Tag; 21 import io.swagger.v3.oas.annotations.tags.Tag;
  22 +import jakarta.annotation.Resource;
  23 +import jakarta.servlet.http.HttpServletResponse;
  24 +import jakarta.validation.Valid;
20 import org.mapstruct.ap.internal.util.Collections; 25 import org.mapstruct.ap.internal.util.Collections;
21 import org.springframework.security.access.prepost.PreAuthorize; 26 import org.springframework.security.access.prepost.PreAuthorize;
22 import org.springframework.validation.annotation.Validated; 27 import org.springframework.validation.annotation.Validated;
23 import org.springframework.web.bind.annotation.*; 28 import org.springframework.web.bind.annotation.*;
24 29
25 -import jakarta.annotation.Resource;  
26 -import jakarta.servlet.http.HttpServletResponse;  
27 -import jakarta.validation.Valid;  
28 import java.io.IOException; 30 import java.io.IOException;
29 import java.util.List; 31 import java.util.List;
30 import java.util.Map; 32 import java.util.Map;
@@ -36,6 +38,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -36,6 +38,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; 38 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
37 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 39 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
38 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 40 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  41 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER;
39 42
40 @Tag(name = "管理后台 - CRM 客户") 43 @Tag(name = "管理后台 - CRM 客户")
41 @RestController 44 @RestController
@@ -50,6 +53,8 @@ public class CrmCustomerController { @@ -50,6 +53,8 @@ public class CrmCustomerController {
50 private DeptApi deptApi; 53 private DeptApi deptApi;
51 @Resource 54 @Resource
52 private AdminUserApi adminUserApi; 55 private AdminUserApi adminUserApi;
  56 + @Resource
  57 + private OperateLogApi operateLogApi;
53 58
54 @PostMapping("/create") 59 @PostMapping("/create")
55 @Operation(summary = "创建客户") 60 @Operation(summary = "创建客户")
@@ -59,7 +64,7 @@ public class CrmCustomerController { @@ -59,7 +64,7 @@ public class CrmCustomerController {
59 } 64 }
60 65
61 @PutMapping("/update") 66 @PutMapping("/update")
62 - @Operation(summary = "更新客户") 67 + //@Operation(summary = "更新客户")
63 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 68 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
64 public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) { 69 public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerUpdateReqVO updateReqVO) {
65 customerService.updateCustomer(updateReqVO); 70 customerService.updateCustomer(updateReqVO);
@@ -123,13 +128,28 @@ public class CrmCustomerController { @@ -123,13 +128,28 @@ public class CrmCustomerController {
123 } 128 }
124 129
125 @PutMapping("/transfer") 130 @PutMapping("/transfer")
126 - @Operation(summary = "客户转移") 131 + //@Operation(summary = "客户转移")
127 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 132 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
128 public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { 133 public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
129 customerService.transferCustomer(reqVO, getLoginUserId()); 134 customerService.transferCustomer(reqVO, getLoginUserId());
130 return success(true); 135 return success(true);
131 } 136 }
132 137
  138 + @GetMapping("/operate-log")
  139 + @Operation(summary = "获得客户操作日志")
  140 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  141 + @PreAuthorize("@ss.hasPermission('crm:customer:query')")
  142 + public CommonResult<List<OperateLogV2RespDTO>> getOperateLog(@RequestParam("id") Long id) {
  143 + // 1. 获取客户
  144 + CrmCustomerDO customer = customerService.getCustomer(id);
  145 + if (customer == null) {
  146 + return success(null);
  147 + }
  148 +
  149 + // 2. 获取操作日志
  150 + return success(operateLogApi.getOperateLogByModuleAndBizId(CRM_CUSTOMER, id));
  151 + }
  152 +
133 // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了; 153 // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了;
134 @PutMapping("/lock") 154 @PutMapping("/lock")
135 @Operation(summary = "锁定/解锁客户") 155 @Operation(summary = "锁定/解锁客户")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
@@ -3,17 +3,20 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; @@ -3,17 +3,20 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 import cn.iocoder.yudao.framework.common.validation.Mobile; 4 import cn.iocoder.yudao.framework.common.validation.Mobile;
5 import cn.iocoder.yudao.framework.common.validation.Telephone; 5 import cn.iocoder.yudao.framework.common.validation.Telephone;
  6 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
6 import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum; 7 import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum;
  8 +import com.mzt.logapi.starter.annotation.DiffLogField;
7 import io.swagger.v3.oas.annotations.media.Schema; 9 import io.swagger.v3.oas.annotations.media.Schema;
8 -import lombok.Data;  
9 -import org.springframework.format.annotation.DateTimeFormat;  
10 -  
11 import jakarta.validation.constraints.Email; 10 import jakarta.validation.constraints.Email;
12 import jakarta.validation.constraints.NotEmpty; 11 import jakarta.validation.constraints.NotEmpty;
13 import jakarta.validation.constraints.Size; 12 import jakarta.validation.constraints.Size;
  13 +import lombok.Data;
  14 +import org.springframework.format.annotation.DateTimeFormat;
  15 +
14 import java.time.LocalDateTime; 16 import java.time.LocalDateTime;
15 17
16 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 18 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  19 +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
17 20
18 /** 21 /**
19 * 客户 Base VO,提供给添加、修改、详细的子 VO 使用 22 * 客户 Base VO,提供给添加、修改、详细的子 VO 使用
@@ -23,57 +26,73 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @@ -23,57 +26,73 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
23 public class CrmCustomerBaseVO { 26 public class CrmCustomerBaseVO {
24 27
25 @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") 28 @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  29 + @DiffLogField(name = "客户名称")
26 @NotEmpty(message = "客户名称不能为空") 30 @NotEmpty(message = "客户名称不能为空")
27 private String name; 31 private String name;
28 32
29 @Schema(description = "所属行业", example = "1") 33 @Schema(description = "所属行业", example = "1")
  34 + @DiffLogField(name = "所属行业", function = "getIndustryById")
  35 + @DictFormat(CRM_CUSTOMER_INDUSTRY)
30 private Integer industryId; 36 private Integer industryId;
31 37
32 @Schema(description = "客户等级", example = "2") 38 @Schema(description = "客户等级", example = "2")
  39 + @DiffLogField(name = "客户等级", function = "getLevel")
33 @InEnum(CrmCustomerLevelEnum.class) 40 @InEnum(CrmCustomerLevelEnum.class)
34 private Integer level; 41 private Integer level;
35 42
36 @Schema(description = "客户来源", example = "3") 43 @Schema(description = "客户来源", example = "3")
  44 + @DiffLogField(name = "客户来源", function = "getSource")
37 private Integer source; 45 private Integer source;
38 46
39 @Schema(description = "手机", example = "18000000000") 47 @Schema(description = "手机", example = "18000000000")
  48 + @DiffLogField(name = "手机")
40 @Mobile 49 @Mobile
41 private String mobile; 50 private String mobile;
42 51
43 @Schema(description = "电话", example = "18000000000") 52 @Schema(description = "电话", example = "18000000000")
  53 + @DiffLogField(name = "电话")
44 @Telephone 54 @Telephone
45 private String telephone; 55 private String telephone;
46 56
47 @Schema(description = "网址", example = "https://www.baidu.com") 57 @Schema(description = "网址", example = "https://www.baidu.com")
  58 + @DiffLogField(name = "网址")
48 private String website; 59 private String website;
49 60
50 @Schema(description = "QQ", example = "123456789") 61 @Schema(description = "QQ", example = "123456789")
  62 + @DiffLogField(name = "QQ")
51 @Size(max = 20, message = "QQ长度不能超过 20 个字符") 63 @Size(max = 20, message = "QQ长度不能超过 20 个字符")
52 private String qq; 64 private String qq;
53 65
54 - @Schema(description = "wechat", example = "123456789") 66 + @Schema(description = "微信", example = "123456789")
  67 + @DiffLogField(name = "微信")
55 @Size(max = 255, message = "微信长度不能超过 255 个字符") 68 @Size(max = 255, message = "微信长度不能超过 255 个字符")
56 private String wechat; 69 private String wechat;
57 70
58 - @Schema(description = "email", example = "123456789@qq.com") 71 + @Schema(description = "邮箱", example = "123456789@qq.com")
  72 + @DiffLogField(name = "邮箱")
59 @Email(message = "邮箱格式不正确") 73 @Email(message = "邮箱格式不正确")
60 @Size(max = 255, message = "邮箱长度不能超过 255 个字符") 74 @Size(max = 255, message = "邮箱长度不能超过 255 个字符")
61 private String email; 75 private String email;
62 76
63 @Schema(description = "客户描述", example = "任意文字") 77 @Schema(description = "客户描述", example = "任意文字")
  78 + @DiffLogField(name = "客户描述")
64 @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符") 79 @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
65 private String description; 80 private String description;
66 81
67 @Schema(description = "备注", example = "随便") 82 @Schema(description = "备注", example = "随便")
  83 + @DiffLogField(name = "备注")
68 private String remark; 84 private String remark;
69 85
70 @Schema(description = "地区编号", example = "20158") 86 @Schema(description = "地区编号", example = "20158")
  87 + @DiffLogField(name = "地区编号", function = "getAreaById")
71 private Integer areaId; 88 private Integer areaId;
72 89
73 @Schema(description = "详细地址", example = "北京市海淀区") 90 @Schema(description = "详细地址", example = "北京市海淀区")
  91 + @DiffLogField(name = "详细地址")
74 private String detailAddress; 92 private String detailAddress;
75 93
76 @Schema(description = "下次联系时间") 94 @Schema(description = "下次联系时间")
  95 + @DiffLogField(name = "下次联系时间")
77 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 96 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
78 private LocalDateTime contactNextTime; 97 private LocalDateTime contactNextTime;
79 98
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java
@@ -2,16 +2,15 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; @@ -2,16 +2,15 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
2 2
3 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 3 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 -import lombok.Data;  
6 -  
7 import jakarta.validation.constraints.NotNull; 5 import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
8 7
9 @Schema(description = "管理后台 - CRM 客户转移 Request VO") 8 @Schema(description = "管理后台 - CRM 客户转移 Request VO")
10 @Data 9 @Data
11 public class CrmCustomerTransferReqVO { 10 public class CrmCustomerTransferReqVO {
12 11
13 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430") 12 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
14 - @NotNull(message = "联系人编号不能为空") 13 + @NotNull(message = "客户编号不能为空")
15 private Long id; 14 private Long id;
16 15
17 /** 16 /**
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivableController.java
@@ -7,10 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -7,10 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;  
11 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;  
12 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO;  
13 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; 10 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.*;
14 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert; 11 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;
15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 13 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@@ -36,6 +33,7 @@ import java.util.Map; @@ -36,6 +33,7 @@ import java.util.Map;
36 import java.util.stream.Stream; 33 import java.util.stream.Stream;
37 34
38 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 35 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  36 +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
39 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; 37 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
40 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 38 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
41 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 39 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@@ -94,7 +92,7 @@ public class CrmReceivableController { @@ -94,7 +92,7 @@ public class CrmReceivableController {
94 @Operation(summary = "获得回款分页") 92 @Operation(summary = "获得回款分页")
95 @PreAuthorize("@ss.hasPermission('crm:receivable:query')") 93 @PreAuthorize("@ss.hasPermission('crm:receivable:query')")
96 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) { 94 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePage(@Valid CrmReceivablePageReqVO pageReqVO) {
97 - PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(pageReqVO); 95 + PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(pageReqVO, getLoginUserId());
98 return success(convertDetailReceivablePage(pageResult)); 96 return success(convertDetailReceivablePage(pageResult));
99 } 97 }
100 98
@@ -102,7 +100,7 @@ public class CrmReceivableController { @@ -102,7 +100,7 @@ public class CrmReceivableController {
102 @Operation(summary = "获得回款分页,基于指定客户") 100 @Operation(summary = "获得回款分页,基于指定客户")
103 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) { 101 public CommonResult<PageResult<CrmReceivableRespVO>> getReceivablePageByCustomer(@Valid CrmReceivablePageReqVO pageReqVO) {
104 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); 102 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
105 - PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePageByCustomer(pageReqVO); 103 + PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePageByCustomerId(pageReqVO);
106 return success(convertDetailReceivablePage(pageResult)); 104 return success(convertDetailReceivablePage(pageResult));
107 } 105 }
108 106
@@ -113,7 +111,8 @@ public class CrmReceivableController { @@ -113,7 +111,8 @@ public class CrmReceivableController {
113 @OperateLog(type = EXPORT) 111 @OperateLog(type = EXPORT)
114 public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO, 112 public void exportReceivableExcel(@Valid CrmReceivablePageReqVO exportReqVO,
115 HttpServletResponse response) throws IOException { 113 HttpServletResponse response) throws IOException {
116 - PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(exportReqVO); 114 + exportReqVO.setPageSize(PAGE_SIZE_NONE);
  115 + PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(exportReqVO, getLoginUserId());
117 // 导出 Excel 116 // 导出 Excel
118 ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class, 117 ExcelUtils.write(response, "回款.xls", "数据", CrmReceivableRespVO.class,
119 convertDetailReceivablePage(pageResult).getList()); 118 convertDetailReceivablePage(pageResult).getList());
@@ -142,4 +141,12 @@ public class CrmReceivableController { @@ -142,4 +141,12 @@ public class CrmReceivableController {
142 return CrmReceivableConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList); 141 return CrmReceivableConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList);
143 } 142 }
144 143
  144 + @PutMapping("/transfer")
  145 + @Operation(summary = "回款转移")
  146 + @PreAuthorize("@ss.hasPermission('crm:receivable:update')")
  147 + public CommonResult<Boolean> transfer(@Valid @RequestBody CrmReceivableTransferReqVO reqVO) {
  148 + receivableService.transferReceivable(reqVO, getLoginUserId());
  149 + return success(true);
  150 + }
  151 +
145 } 152 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/CrmReceivablePlanController.java
@@ -7,10 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -7,10 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
10 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;  
11 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;  
12 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanRespVO;  
13 -import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; 10 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.*;
14 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert; 11 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert;
15 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 13 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
@@ -38,6 +35,7 @@ import java.util.Map; @@ -38,6 +35,7 @@ import java.util.Map;
38 import java.util.stream.Stream; 35 import java.util.stream.Stream;
39 36
40 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 37 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  38 +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; 39 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
42 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 40 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
43 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 41 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@@ -65,7 +63,7 @@ public class CrmReceivablePlanController { @@ -65,7 +63,7 @@ public class CrmReceivablePlanController {
65 @Operation(summary = "创建回款计划") 63 @Operation(summary = "创建回款计划")
66 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:create')") 64 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:create')")
67 public CommonResult<Long> createReceivablePlan(@Valid @RequestBody CrmReceivablePlanCreateReqVO createReqVO) { 65 public CommonResult<Long> createReceivablePlan(@Valid @RequestBody CrmReceivablePlanCreateReqVO createReqVO) {
68 - return success(receivablePlanService.createReceivablePlan(createReqVO)); 66 + return success(receivablePlanService.createReceivablePlan(createReqVO, getLoginUserId()));
69 } 67 }
70 68
71 @PutMapping("/update") 69 @PutMapping("/update")
@@ -98,7 +96,7 @@ public class CrmReceivablePlanController { @@ -98,7 +96,7 @@ public class CrmReceivablePlanController {
98 @Operation(summary = "获得回款计划分页") 96 @Operation(summary = "获得回款计划分页")
99 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')") 97 @PreAuthorize("@ss.hasPermission('crm:receivable-plan:query')")
100 public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) { 98 public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPage(@Valid CrmReceivablePlanPageReqVO pageReqVO) {
101 - PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO); 99 + PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(pageReqVO, getLoginUserId());
102 return success(convertDetailReceivablePlanPage(pageResult)); 100 return success(convertDetailReceivablePlanPage(pageResult));
103 } 101 }
104 102
@@ -106,7 +104,7 @@ public class CrmReceivablePlanController { @@ -106,7 +104,7 @@ public class CrmReceivablePlanController {
106 @Operation(summary = "获得回款计划分页,基于指定客户") 104 @Operation(summary = "获得回款计划分页,基于指定客户")
107 public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) { 105 public CommonResult<PageResult<CrmReceivablePlanRespVO>> getReceivablePlanPageByCustomer(@Valid CrmReceivablePlanPageReqVO pageReqVO) {
108 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空"); 106 Assert.notNull(pageReqVO.getCustomerId(), "客户编号不能为空");
109 - PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomer(pageReqVO); 107 + PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPageByCustomerId(pageReqVO);
110 return success(convertDetailReceivablePlanPage(pageResult)); 108 return success(convertDetailReceivablePlanPage(pageResult));
111 } 109 }
112 110
@@ -117,7 +115,8 @@ public class CrmReceivablePlanController { @@ -117,7 +115,8 @@ public class CrmReceivablePlanController {
117 @OperateLog(type = EXPORT) 115 @OperateLog(type = EXPORT)
118 public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO, 116 public void exportReceivablePlanExcel(@Valid CrmReceivablePlanPageReqVO exportReqVO,
119 HttpServletResponse response) throws IOException { 117 HttpServletResponse response) throws IOException {
120 - PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(exportReqVO); 118 + exportReqVO.setPageSize(PAGE_SIZE_NONE);
  119 + PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(exportReqVO, getLoginUserId());
121 // 导出 Excel 120 // 导出 Excel
122 ExcelUtils.write(response, "回款计划.xls", "数据", CrmReceivablePlanRespVO.class, 121 ExcelUtils.write(response, "回款计划.xls", "数据", CrmReceivablePlanRespVO.class,
123 convertDetailReceivablePlanPage(pageResult).getList()); 122 convertDetailReceivablePlanPage(pageResult).getList());
@@ -149,4 +148,12 @@ public class CrmReceivablePlanController { @@ -149,4 +148,12 @@ public class CrmReceivablePlanController {
149 return CrmReceivablePlanConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList, receivableList); 148 return CrmReceivablePlanConvert.INSTANCE.convertPage(pageResult, userMap, customerList, contractList, receivableList);
150 } 149 }
151 150
  151 + @PutMapping("/transfer")
  152 + @Operation(summary = "回款计划转移")
  153 + @PreAuthorize("@ss.hasPermission('crm:receivable-plan:update')")
  154 + public CommonResult<Boolean> transfer(@Valid @RequestBody CrmReceivablePlanTransferReqVO reqVO) {
  155 + receivablePlanService.transferReceivablePlan(reqVO, getLoginUserId());
  156 + return success(true);
  157 + }
  158 +
152 } 159 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanPageReqVO.java
@@ -25,7 +25,4 @@ public class CrmReceivablePlanPageReqVO extends PageParam { @@ -25,7 +25,4 @@ public class CrmReceivablePlanPageReqVO extends PageParam {
25 @InEnum(CrmSceneTypeEnum.class) 25 @InEnum(CrmSceneTypeEnum.class)
26 private Integer sceneType; // 场景类型,为 null 时则表示全部 26 private Integer sceneType; // 场景类型,为 null 时则表示全部
27 27
28 - @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")  
29 - private Boolean pool; // null 则表示为不是公海数据  
30 -  
31 } 28 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanTransferReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan;
  2 +
  3 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - CRM 回款计划转移 Request VO")
  9 +@Data
  10 +public class CrmReceivablePlanTransferReqVO {
  11 +
  12 + @Schema(description = "回款计划编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
  13 + @NotNull(message = "回款计划编号不能为空")
  14 + private Long id;
  15 +
  16 + /**
  17 + * 新负责人的用户编号
  18 + */
  19 + @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
  20 + @NotNull(message = "新负责人的用户编号不能为空")
  21 + private Long newOwnerUserId;
  22 +
  23 + /**
  24 + * 老负责人加入团队后的权限级别。如果 null 说明移除
  25 + *
  26 + * 关联 {@link CrmPermissionLevelEnum}
  27 + */
  28 + @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  29 + private Integer oldOwnerPermissionLevel;
  30 +
  31 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivablePageReqVO.java
@@ -27,7 +27,4 @@ public class CrmReceivablePageReqVO extends PageParam { @@ -27,7 +27,4 @@ public class CrmReceivablePageReqVO extends PageParam {
27 @InEnum(CrmSceneTypeEnum.class) 27 @InEnum(CrmSceneTypeEnum.class)
28 private Integer sceneType; // 场景类型,为 null 时则表示全部 28 private Integer sceneType; // 场景类型,为 null 时则表示全部
29 29
30 - @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")  
31 - private Boolean pool; // null 则表示为不是公海数据  
32 -  
33 } 30 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableTransferReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable;
  2 +
  3 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +@Schema(description = "管理后台 - CRM 回款转移 Request VO")
  9 +@Data
  10 +public class CrmReceivableTransferReqVO {
  11 +
  12 + @Schema(description = "回款编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
  13 + @NotNull(message = "回款编号不能为空")
  14 + private Long id;
  15 +
  16 + /**
  17 + * 新负责人的用户编号
  18 + */
  19 + @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
  20 + @NotNull(message = "新负责人的用户编号不能为空")
  21 + private Long newOwnerUserId;
  22 +
  23 + /**
  24 + * 老负责人加入团队后的权限级别。如果 null 说明移除
  25 + *
  26 + * 关联 {@link CrmPermissionLevelEnum}
  27 + */
  28 + @Schema(description = "老负责人加入团队后的权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  29 + private Integer oldOwnerPermissionLevel;
  30 +
  31 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java
1 package cn.iocoder.yudao.module.crm.convert.clue; 1 package cn.iocoder.yudao.module.crm.convert.clue;
2 2
3 -import java.util.*;  
4 -  
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 -  
7 -import org.mapstruct.Mapper;  
8 -import org.mapstruct.factory.Mappers;  
9 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*; 4 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; 5 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
  6 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
  7 +import org.mapstruct.Mapper;
  8 +import org.mapstruct.Mapping;
  9 +import org.mapstruct.factory.Mappers;
  10 +
  11 +import java.util.List;
11 12
12 /** 13 /**
13 * 线索 Convert 14 * 线索 Convert
@@ -29,4 +30,7 @@ public interface CrmClueConvert { @@ -29,4 +30,7 @@ public interface CrmClueConvert {
29 30
30 List<CrmClueExcelVO> convertList02(List<CrmClueDO> list); 31 List<CrmClueExcelVO> convertList02(List<CrmClueDO> list);
31 32
  33 + @Mapping(target = "bizId", source = "reqVO.id")
  34 + CrmPermissionTransferReqBO convert(CrmClueTransferReqVO reqVO, Long userId);
  35 +
32 } 36 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/ContactConvert.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/CrmContactConvert.java
@@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferRe @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferRe
9 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 9 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
10 import org.mapstruct.Mapper; 10 import org.mapstruct.Mapper;
11 import org.mapstruct.Mapping; 11 import org.mapstruct.Mapping;
12 -import org.mapstruct.Mappings;  
13 import org.mapstruct.factory.Mappers; 12 import org.mapstruct.factory.Mappers;
14 13
15 import java.util.List; 14 import java.util.List;
@@ -26,9 +25,9 @@ import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAnd @@ -26,9 +25,9 @@ import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAnd
26 * @author 芋道源码 25 * @author 芋道源码
27 */ 26 */
28 @Mapper 27 @Mapper
29 -public interface ContactConvert { 28 +public interface CrmContactConvert {
30 29
31 - ContactConvert INSTANCE = Mappers.getMapper(ContactConvert.class); 30 + CrmContactConvert INSTANCE = Mappers.getMapper(CrmContactConvert.class);
32 31
33 CrmContactDO convert(CrmContactCreateReqVO bean); 32 CrmContactDO convert(CrmContactCreateReqVO bean);
34 33
@@ -48,10 +47,7 @@ public interface ContactConvert { @@ -48,10 +47,7 @@ public interface ContactConvert {
48 47
49 List<CrmContactSimpleRespVO> convertAllList(List<CrmContactDO> list); 48 List<CrmContactSimpleRespVO> convertAllList(List<CrmContactDO> list);
50 49
51 - @Mappings({  
52 - @Mapping(target = "bizId", source = "reqVO.id"),  
53 - @Mapping(target = "newOwnerUserId", source = "reqVO.id")  
54 - }) 50 + @Mapping(target = "bizId", source = "reqVO.id")
55 CrmPermissionTransferReqBO convert(CrmContactTransferReqVO reqVO, Long userId); 51 CrmPermissionTransferReqBO convert(CrmContactTransferReqVO reqVO, Long userId);
56 52
57 /** 53 /**
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/ContractConvert.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java
@@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferRe @@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferRe
8 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 8 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
9 import org.mapstruct.Mapper; 9 import org.mapstruct.Mapper;
10 import org.mapstruct.Mapping; 10 import org.mapstruct.Mapping;
11 -import org.mapstruct.Mappings;  
12 import org.mapstruct.factory.Mappers; 11 import org.mapstruct.factory.Mappers;
13 12
14 import java.util.List; 13 import java.util.List;
@@ -23,9 +22,9 @@ import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAnd @@ -23,9 +22,9 @@ import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAnd
23 * @author dhb52 22 * @author dhb52
24 */ 23 */
25 @Mapper 24 @Mapper
26 -public interface ContractConvert { 25 +public interface CrmContractConvert {
27 26
28 - ContractConvert INSTANCE = Mappers.getMapper(ContractConvert.class); 27 + CrmContractConvert INSTANCE = Mappers.getMapper(CrmContractConvert.class);
29 28
30 CrmContractDO convert(CrmContractCreateReqVO bean); 29 CrmContractDO convert(CrmContractCreateReqVO bean);
31 30
@@ -39,10 +38,7 @@ public interface ContractConvert { @@ -39,10 +38,7 @@ public interface ContractConvert {
39 38
40 List<CrmContractExcelVO> convertList02(List<CrmContractDO> list); 39 List<CrmContractExcelVO> convertList02(List<CrmContractDO> list);
41 40
42 - @Mappings({  
43 - @Mapping(target = "bizId", source = "reqVO.id"),  
44 - @Mapping(target = "newOwnerUserId", source = "reqVO.id")  
45 - }) 41 + @Mapping(target = "bizId", source = "reqVO.id")
46 CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId); 42 CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId);
47 43
48 default PageResult<ContractRespVO> convertPage(PageResult<CrmContractDO> pageResult, Map<Long, AdminUserRespDTO> userMap, 44 default PageResult<ContractRespVO> convertPage(PageResult<CrmContractDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivableConvert.java
@@ -3,12 +3,15 @@ package cn.iocoder.yudao.module.crm.convert.receivable; @@ -3,12 +3,15 @@ package cn.iocoder.yudao.module.crm.convert.receivable;
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.receivable.vo.receivable.CrmReceivableCreateReqVO; 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.CrmReceivableRespVO; 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.CrmReceivableTransferReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
9 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 10 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
  11 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
10 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 12 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
11 import org.mapstruct.Mapper; 13 import org.mapstruct.Mapper;
  14 +import org.mapstruct.Mapping;
12 import org.mapstruct.factory.Mappers; 15 import org.mapstruct.factory.Mappers;
13 16
14 import java.util.List; 17 import java.util.List;
@@ -58,4 +61,7 @@ public interface CrmReceivableConvert { @@ -58,4 +61,7 @@ public interface CrmReceivableConvert {
58 findAndThen(userMap, Long.parseLong(receivable.getCreator()), user -> receivable.setCreatorName(user.getNickname())); 61 findAndThen(userMap, Long.parseLong(receivable.getCreator()), user -> receivable.setCreatorName(user.getNickname()));
59 } 62 }
60 63
  64 + @Mapping(target = "bizId", source = "reqVO.id")
  65 + CrmPermissionTransferReqBO convert(CrmReceivableTransferReqVO reqVO, Long userId);
  66 +
61 } 67 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/receivable/CrmReceivablePlanConvert.java
@@ -3,13 +3,16 @@ package cn.iocoder.yudao.module.crm.convert.receivable; @@ -3,13 +3,16 @@ package cn.iocoder.yudao.module.crm.convert.receivable;
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.receivable.vo.plan.CrmReceivablePlanCreateReqVO; 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.CrmReceivablePlanRespVO; 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.CrmReceivablePlanTransferReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
9 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 10 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; 11 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
  12 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
11 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 13 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
12 import org.mapstruct.Mapper; 14 import org.mapstruct.Mapper;
  15 +import org.mapstruct.Mapping;
13 import org.mapstruct.factory.Mappers; 16 import org.mapstruct.factory.Mappers;
14 17
15 import java.util.List; 18 import java.util.List;
@@ -63,4 +66,7 @@ public interface CrmReceivablePlanConvert { @@ -63,4 +66,7 @@ public interface CrmReceivablePlanConvert {
63 findAndThen(userMap, Long.parseLong(receivablePlan.getCreator()), user -> receivablePlan.setCreatorName(user.getNickname())); 66 findAndThen(userMap, Long.parseLong(receivablePlan.getCreator()), user -> receivablePlan.setCreatorName(user.getNickname()));
64 } 67 }
65 68
  69 + @Mapping(target = "bizId", source = "reqVO.id")
  70 + CrmPermissionTransferReqBO convert(CrmReceivablePlanTransferReqVO reqVO, Long userId);
  71 +
66 } 72 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.business; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.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.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.business.vo.business.CrmBusinessPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
@@ -27,14 +28,23 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; { @@ -27,14 +28,23 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; {
27 .set(CrmBusinessDO::getOwnerUserId, ownerUserId)); 28 .set(CrmBusinessDO::getOwnerUserId, ownerUserId));
28 } 29 }
29 30
  31 + default PageResult<CrmBusinessDO> selectPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {
  32 + return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmBusinessDO>()
  33 + .eq(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号
  34 + .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
  35 + .orderByDesc(CrmBusinessDO::getId));
  36 + }
  37 +
30 default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) { 38 default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
31 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>(); 39 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>();
32 // 拼接数据权限的查询条件 40 // 拼接数据权限的查询条件
33 - CrmQueryWrapperUtils.builderPageQuery(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), CrmBusinessDO::getId,  
34 - userId, pageReqVO.getSceneType(), pageReqVO.getPool()); 41 + boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(),
  42 + CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
  43 + if (!condition) {
  44 + return PageResult.empty();
  45 + }
35 // 拼接自身的查询条件 46 // 拼接自身的查询条件
36 query.selectAll(CrmBusinessDO.class) 47 query.selectAll(CrmBusinessDO.class)
37 - .eqIfPresent(CrmBusinessDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号  
38 .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName()) 48 .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
39 .orderByDesc(CrmBusinessDO::getId); 49 .orderByDesc(CrmBusinessDO::getId);
40 return selectJoinPage(pageReqVO, CrmBusinessDO.class, query); 50 return selectJoinPage(pageReqVO, CrmBusinessDO.class, query);
@@ -43,7 +53,7 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; { @@ -43,7 +53,7 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; {
43 default List<CrmBusinessDO> selectBatchIds(Collection<Long> ids, Long userId) { 53 default List<CrmBusinessDO> selectBatchIds(Collection<Long> ids, Long userId) {
44 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>(); 54 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>();
45 // 拼接数据权限的查询条件 55 // 拼接数据权限的查询条件
46 - CrmQueryWrapperUtils.builderListQueryBatch(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), ids, userId); 56 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(), ids, userId);
47 return selectJoinList(CrmBusinessDO.class, query); 57 return selectJoinList(CrmBusinessDO.class, query);
48 } 58 }
49 59
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
@@ -30,8 +30,11 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; { @@ -30,8 +30,11 @@ 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 - CrmQueryWrapperUtils.builderPageQuery(query, CrmBizTypeEnum.CRM_LEADS.getType(), CrmClueDO::getId,  
34 - userId, pageReqVO.getSceneType(), pageReqVO.getPool()); 33 + boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(),
  34 + CrmClueDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
  35 + if (!condition) {
  36 + return PageResult.empty();
  37 + }
35 // 拼接自身的查询条件 38 // 拼接自身的查询条件
36 query.selectAll(CrmClueDO.class) 39 query.selectAll(CrmClueDO.class)
37 .likeIfPresent(CrmClueDO::getName, pageReqVO.getName()) 40 .likeIfPresent(CrmClueDO::getName, pageReqVO.getName())
@@ -44,7 +47,7 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; { @@ -44,7 +47,7 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; {
44 default List<CrmClueDO> selectBatchIds(Collection<Long> ids, Long userId) { 47 default List<CrmClueDO> selectBatchIds(Collection<Long> ids, Long userId) {
45 MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>(); 48 MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>();
46 // 拼接数据权限的查询条件 49 // 拼接数据权限的查询条件
47 - CrmQueryWrapperUtils.builderListQueryBatch(query, CrmBizTypeEnum.CRM_LEADS.getType(), ids, userId); 50 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), ids, userId);
48 return selectJoinList(CrmClueDO.class, query); 51 return selectJoinList(CrmClueDO.class, query);
49 } 52 }
50 53
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.contact; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.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.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.contact.vo.CrmContactPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
@@ -27,14 +28,28 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; { @@ -27,14 +28,28 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; {
27 .set(CrmContactDO::getOwnerUserId, ownerUserId)); 28 .set(CrmContactDO::getOwnerUserId, ownerUserId));
28 } 29 }
29 30
  31 + default PageResult<CrmContactDO> selectPageByCustomerId(CrmContactPageReqVO pageVO) {
  32 + return selectPage(pageVO, new LambdaQueryWrapperX<CrmContactDO>()
  33 + .eq(CrmContactDO::getCustomerId, pageVO.getCustomerId()) // 指定客户编号
  34 + .likeIfPresent(CrmContactDO::getName, pageVO.getName())
  35 + .eqIfPresent(CrmContactDO::getMobile, pageVO.getMobile())
  36 + .eqIfPresent(CrmContactDO::getTelephone, pageVO.getTelephone())
  37 + .eqIfPresent(CrmContactDO::getEmail, pageVO.getEmail())
  38 + .eqIfPresent(CrmContactDO::getQq, pageVO.getQq())
  39 + .eqIfPresent(CrmContactDO::getWechat, pageVO.getWechat())
  40 + .orderByDesc(CrmContactDO::getId));
  41 + }
  42 +
30 default PageResult<CrmContactDO> selectPage(CrmContactPageReqVO pageReqVO, Long userId) { 43 default PageResult<CrmContactDO> selectPage(CrmContactPageReqVO pageReqVO, Long userId) {
31 MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>(); 44 MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>();
32 // 拼接数据权限的查询条件 45 // 拼接数据权限的查询条件
33 - CrmQueryWrapperUtils.builderPageQuery(query, CrmBizTypeEnum.CRM_CONTACT.getType(), CrmContactDO::getId,  
34 - userId, pageReqVO.getSceneType(), pageReqVO.getPool()); 46 + boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(),
  47 + CrmContactDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
  48 + if (!condition) {
  49 + return PageResult.empty();
  50 + }
35 // 拼接自身的查询条件 51 // 拼接自身的查询条件
36 query.selectAll(CrmContactDO.class) 52 query.selectAll(CrmContactDO.class)
37 - .eqIfPresent(CrmContactDO::getCustomerId, pageReqVO.getCustomerId()) // 指定客户编号  
38 .likeIfPresent(CrmContactDO::getName, pageReqVO.getName()) 53 .likeIfPresent(CrmContactDO::getName, pageReqVO.getName())
39 .eqIfPresent(CrmContactDO::getMobile, pageReqVO.getMobile()) 54 .eqIfPresent(CrmContactDO::getMobile, pageReqVO.getMobile())
40 .eqIfPresent(CrmContactDO::getTelephone, pageReqVO.getTelephone()) 55 .eqIfPresent(CrmContactDO::getTelephone, pageReqVO.getTelephone())
@@ -48,7 +63,7 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; { @@ -48,7 +63,7 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; {
48 default List<CrmContactDO> selectBatchIds(Collection<Long> ids, Long userId) { 63 default List<CrmContactDO> selectBatchIds(Collection<Long> ids, Long userId) {
49 MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>(); 64 MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>();
50 // 拼接数据权限的查询条件 65 // 拼接数据权限的查询条件
51 - CrmQueryWrapperUtils.builderListQueryBatch(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId); 66 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId);
52 return selectJoinList(CrmContactDO.class, query); 67 return selectJoinList(CrmContactDO.class, query);
53 } 68 }
54 69
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.contract; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.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.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.contract.vo.CrmContractPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -27,13 +28,26 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; { @@ -27,13 +28,26 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; {
27 .set(CrmContractDO::getOwnerUserId, ownerUserId)); 28 .set(CrmContractDO::getOwnerUserId, ownerUserId));
28 } 29 }
29 30
  31 + default PageResult<CrmContractDO> selectPageByCustomerId(CrmContractPageReqVO pageReqVO) {
  32 + return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmContractDO>()
  33 + .eq(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
  34 + .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
  35 + .likeIfPresent(CrmContractDO::getName, pageReqVO.getName())
  36 + .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
  37 + .eqIfPresent(CrmContractDO::getBusinessId, pageReqVO.getBusinessId())
  38 + .orderByDesc(CrmContractDO::getId));
  39 + }
  40 +
30 default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) { 41 default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) {
31 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); 42 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
32 - // 构建数据权限连表条件  
33 - CrmQueryWrapperUtils.builderPageQuery(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), CrmContractDO::getId,  
34 - userId, pageReqVO.getSceneType(), pageReqVO.getPool()); 43 + // 拼接数据权限的查询条件
  44 + boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(),
  45 + CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
  46 + if (!condition) {
  47 + return PageResult.empty();
  48 + }
  49 + // 拼接自身的查询条件
35 mpjLambdaWrapperX.selectAll(CrmContractDO.class) 50 mpjLambdaWrapperX.selectAll(CrmContractDO.class)
36 - .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())  
37 .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo()) 51 .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
38 .likeIfPresent(CrmContractDO::getName, pageReqVO.getName()) 52 .likeIfPresent(CrmContractDO::getName, pageReqVO.getName())
39 .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId()) 53 .eqIfPresent(CrmContractDO::getCustomerId, pageReqVO.getCustomerId())
@@ -45,7 +59,7 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; { @@ -45,7 +59,7 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; {
45 default List<CrmContractDO> selectBatchIds(Collection<Long> ids, Long userId) { 59 default List<CrmContractDO> selectBatchIds(Collection<Long> ids, Long userId) {
46 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); 60 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
47 // 构建数据权限连表条件 61 // 构建数据权限连表条件
48 - CrmQueryWrapperUtils.builderListQueryBatch(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId); 62 + CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId);
49 return selectJoinList(CrmContractDO.class, mpjLambdaWrapperX); 63 return selectJoinList(CrmContractDO.class, mpjLambdaWrapperX);
50 } 64 }
51 65
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -30,8 +30,11 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -30,8 +30,11 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
30 default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) { 30 default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
31 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); 31 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
32 // 拼接数据权限的查询条件 32 // 拼接数据权限的查询条件
33 - CrmQueryWrapperUtils.builderPageQuery(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), CrmCustomerDO::getId,  
34 - userId, pageReqVO.getSceneType(), pageReqVO.getPool()); 33 + boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
  34 + CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
  35 + if (!condition) {
  36 + return PageResult.empty();
  37 + }
35 // 拼接自身的查询条件 38 // 拼接自身的查询条件
36 query.selectAll(CrmCustomerDO.class) 39 query.selectAll(CrmCustomerDO.class)
37 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) 40 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())
@@ -45,7 +48,7 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -45,7 +48,7 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
45 default List<CrmCustomerDO> selectBatchIds(Collection<Long> ids, Long userId) { 48 default List<CrmCustomerDO> selectBatchIds(Collection<Long> ids, Long userId) {
46 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); 49 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
47 // 拼接数据权限的查询条件 50 // 拼接数据权限的查询条件
48 - CrmQueryWrapperUtils.builderListQueryBatch(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, userId); 51 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, userId);
49 return selectJoinList(CrmCustomerDO.class, query); 52 return selectJoinList(CrmCustomerDO.class, query);
50 } 53 }
51 54
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/permission/CrmPermissionMapper.java
@@ -46,4 +46,10 @@ public interface CrmPermissionMapper extends BaseMapperX&lt;CrmPermissionDO&gt; { @@ -46,4 +46,10 @@ public interface CrmPermissionMapper extends BaseMapperX&lt;CrmPermissionDO&gt; {
46 .eq(CrmPermissionDO::getId, id).eq(CrmPermissionDO::getUserId, userId)); 46 .eq(CrmPermissionDO::getId, id).eq(CrmPermissionDO::getUserId, userId));
47 } 47 }
48 48
  49 + default int deletePermission(Integer bizType, Long bizId) {
  50 + return delete(new LambdaQueryWrapperX<CrmPermissionDO>()
  51 + .eq(CrmPermissionDO::getBizType, bizType)
  52 + .eq(CrmPermissionDO::getBizId, bizId));
  53 + }
  54 +
49 } 55 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
@@ -3,10 +3,17 @@ package cn.iocoder.yudao.module.crm.dal.mysql.receivable; @@ -3,10 +3,17 @@ package cn.iocoder.yudao.module.crm.dal.mysql.receivable;
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.framework.mybatis.core.query.MPJLambdaWrapperX;
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
  9 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  10 +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
  11 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
8 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
9 13
  14 +import java.util.Collection;
  15 +import java.util.List;
  16 +
10 /** 17 /**
11 * 回款 Mapper 18 * 回款 Mapper
12 * 19 *
@@ -15,15 +22,13 @@ import org.apache.ibatis.annotations.Mapper; @@ -15,15 +22,13 @@ import org.apache.ibatis.annotations.Mapper;
15 @Mapper 22 @Mapper
16 public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> { 23 public interface CrmReceivableMapper extends BaseMapperX<CrmReceivableDO> {
17 24
18 - default PageResult<CrmReceivableDO> selectPage(CrmReceivablePageReqVO reqVO) {  
19 - return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>()  
20 - .eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo())  
21 - .eqIfPresent(CrmReceivableDO::getPlanId, reqVO.getPlanId())  
22 - .eqIfPresent(CrmReceivableDO::getCustomerId, reqVO.getCustomerId())  
23 - .orderByDesc(CrmReceivableDO::getId)); 25 + default int updateOwnerUserIdById(Long id, Long ownerUserId) {
  26 + return update(new LambdaUpdateWrapper<CrmReceivableDO>()
  27 + .eq(CrmReceivableDO::getId, id)
  28 + .set(CrmReceivableDO::getOwnerUserId, ownerUserId));
24 } 29 }
25 30
26 - default PageResult<CrmReceivableDO> selectPageByCustomer(CrmReceivablePageReqVO reqVO) { 31 + default PageResult<CrmReceivableDO> selectPageByCustomerId(CrmReceivablePageReqVO reqVO) {
27 return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>() 32 return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivableDO>()
28 .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递 33 .eq(CrmReceivableDO::getCustomerId, reqVO.getCustomerId()) // 必须传递
29 .eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo()) 34 .eqIfPresent(CrmReceivableDO::getNo, reqVO.getNo())
@@ -31,4 +36,27 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; { @@ -31,4 +36,27 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; {
31 .orderByDesc(CrmReceivableDO::getId)); 36 .orderByDesc(CrmReceivableDO::getId));
32 } 37 }
33 38
  39 + default PageResult<CrmReceivableDO> selectPage(CrmReceivablePageReqVO pageReqVO, Long userId) {
  40 + MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
  41 + // 拼接数据权限的查询条件
  42 + boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
  43 + CrmReceivableDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
  44 + if (!condition) {
  45 + return PageResult.empty();
  46 + }
  47 + // 拼接自身的查询条件
  48 + query.selectAll(CrmReceivableDO.class)
  49 + .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo())
  50 + .eqIfPresent(CrmReceivableDO::getPlanId, pageReqVO.getPlanId())
  51 + .orderByDesc(CrmReceivableDO::getId);
  52 + return selectJoinPage(pageReqVO, CrmReceivableDO.class, query);
  53 + }
  54 +
  55 + default List<CrmReceivableDO> selectBatchIds(Collection<Long> ids, Long userId) {
  56 + MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
  57 + // 拼接数据权限的查询条件
  58 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(), ids, userId);
  59 + return selectJoinList(CrmReceivableDO.class, query);
  60 + }
  61 +
34 } 62 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
@@ -3,10 +3,17 @@ package cn.iocoder.yudao.module.crm.dal.mysql.receivable; @@ -3,10 +3,17 @@ package cn.iocoder.yudao.module.crm.dal.mysql.receivable;
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.framework.mybatis.core.query.MPJLambdaWrapperX;
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
  9 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  10 +import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
  11 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
8 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
9 13
  14 +import java.util.Collection;
  15 +import java.util.List;
  16 +
10 /** 17 /**
11 * 回款计划 Mapper 18 * 回款计划 Mapper
12 * 19 *
@@ -15,18 +22,40 @@ import org.apache.ibatis.annotations.Mapper; @@ -15,18 +22,40 @@ import org.apache.ibatis.annotations.Mapper;
15 @Mapper 22 @Mapper
16 public interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO> { 23 public interface CrmReceivablePlanMapper extends BaseMapperX<CrmReceivablePlanDO> {
17 24
18 - default PageResult<CrmReceivablePlanDO> selectPage(CrmReceivablePlanPageReqVO reqVO) {  
19 - return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivablePlanDO>()  
20 - .eqIfPresent(CrmReceivablePlanDO::getCustomerId, reqVO.getCustomerId())  
21 - .eqIfPresent(CrmReceivablePlanDO::getContractId, reqVO.getContractId())  
22 - .orderByDesc(CrmReceivablePlanDO::getId)); 25 + default int updateOwnerUserIdById(Long id, Long ownerUserId) {
  26 + return update(new LambdaUpdateWrapper<CrmReceivablePlanDO>()
  27 + .eq(CrmReceivablePlanDO::getId, id)
  28 + .set(CrmReceivablePlanDO::getOwnerUserId, ownerUserId));
23 } 29 }
24 30
25 - default PageResult<CrmReceivablePlanDO> selectPageByCustomer(CrmReceivablePlanPageReqVO reqVO) { 31 + default PageResult<CrmReceivablePlanDO> selectPageByCustomerId(CrmReceivablePlanPageReqVO reqVO) {
26 return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivablePlanDO>() 32 return selectPage(reqVO, new LambdaQueryWrapperX<CrmReceivablePlanDO>()
27 .eq(CrmReceivablePlanDO::getCustomerId, reqVO.getCustomerId()) // 必须传递 33 .eq(CrmReceivablePlanDO::getCustomerId, reqVO.getCustomerId()) // 必须传递
28 .eqIfPresent(CrmReceivablePlanDO::getContractId, reqVO.getContractId()) 34 .eqIfPresent(CrmReceivablePlanDO::getContractId, reqVO.getContractId())
29 .orderByDesc(CrmReceivablePlanDO::getId)); 35 .orderByDesc(CrmReceivablePlanDO::getId));
30 } 36 }
31 37
  38 + default PageResult<CrmReceivablePlanDO> selectPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {
  39 + MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();
  40 + // 拼接数据权限的查询条件
  41 + boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),
  42 + CrmReceivablePlanDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
  43 + if (!condition) {
  44 + return PageResult.empty();
  45 + }
  46 + // 拼接自身的查询条件
  47 + query.selectAll(CrmReceivablePlanDO.class)
  48 + .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId())
  49 + .eqIfPresent(CrmReceivablePlanDO::getContractId, pageReqVO.getContractId())
  50 + .orderByDesc(CrmReceivablePlanDO::getId);
  51 + return selectJoinPage(pageReqVO, CrmReceivablePlanDO.class, query);
  52 + }
  53 +
  54 + default List<CrmReceivablePlanDO> selectBatchIds(Collection<Long> ids, Long userId) {
  55 + MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();
  56 + // 拼接数据权限的查询条件
  57 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), ids, userId);
  58 + return selectJoinList(CrmReceivablePlanDO.class, query);
  59 + }
  60 +
32 } 61 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmIndustryParseFunction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.bizlog.function;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
  5 +import com.mzt.logapi.service.IParseFunction;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY;
  10 +
  11 +/**
  12 + * 自定义函数-通过行业编号获取行业信息
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Slf4j
  17 +@Component
  18 +public class CrmIndustryParseFunction implements IParseFunction {
  19 +
  20 + @Override
  21 + public boolean executeBefore() {
  22 + return true; // 先转换值后对比
  23 + }
  24 +
  25 + @Override
  26 + public String functionName() {
  27 + return "getIndustryById";
  28 + }
  29 +
  30 + @Override
  31 + public String apply(Object value) {
  32 + if (value == null) {
  33 + return "";
  34 + }
  35 + if (StrUtil.isEmpty(value.toString())) {
  36 + return "";
  37 + }
  38 +
  39 + // 获取行业信息
  40 + try {
  41 + return DictFrameworkUtils.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, value.toString());
  42 + } catch (Exception ignored) {
  43 + }
  44 + return "";
  45 + }
  46 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmLevelParseFunction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.bizlog.function;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
  5 +import com.mzt.logapi.service.IParseFunction;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL;
  10 +
  11 +/**
  12 + * 自定义函数-通过客户等级编号获取客户等级信息
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Slf4j
  17 +@Component
  18 +public class CrmLevelParseFunction implements IParseFunction {
  19 +
  20 + @Override
  21 + public boolean executeBefore() {
  22 + return true; // 先转换值后对比
  23 + }
  24 +
  25 + @Override
  26 + public String functionName() {
  27 + return "getLevel";
  28 + }
  29 +
  30 + @Override
  31 + public String apply(Object value) {
  32 + if (value == null) {
  33 + return "";
  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 "";
  45 + }
  46 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/function/CrmSourceParseFunction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.bizlog.function;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
  5 +import com.mzt.logapi.service.IParseFunction;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE;
  10 +
  11 +/**
  12 + * 自定义函数-通过客户来源编号获取客户来源信息
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +@Slf4j
  17 +@Component
  18 +public class CrmSourceParseFunction implements IParseFunction {
  19 +
  20 + @Override
  21 + public boolean executeBefore() {
  22 + return true; // 先转换值后对比
  23 + }
  24 +
  25 + @Override
  26 + public String functionName() {
  27 + return "getSource";
  28 + }
  29 +
  30 + @Override
  31 + public String apply(Object value) {
  32 + if (value == null) {
  33 + return "";
  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 "";
  45 + }
  46 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/bizlog/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.bizlog;
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
@@ -75,10 +75,9 @@ public interface CrmBusinessService { @@ -75,10 +75,9 @@ public interface CrmBusinessService {
75 * 数据权限:基于 {@link CrmCustomerDO} 读取 75 * 数据权限:基于 {@link CrmCustomerDO} 读取
76 * 76 *
77 * @param pageReqVO 分页查询 77 * @param pageReqVO 分页查询
78 - * @param userId 用户编号  
79 * @return 联系人分页 78 * @return 联系人分页
80 */ 79 */
81 - PageResult<CrmBusinessDO> getBusinessPageByCustomer(CrmBusinessPageReqVO pageReqVO, Long userId); 80 + PageResult<CrmBusinessDO> getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO);
82 81
83 /** 82 /**
84 * 商机转移 83 * 商机转移
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
@@ -111,11 +111,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -111,11 +111,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
111 } 111 }
112 112
113 @Override 113 @Override
114 - public PageResult<CrmBusinessDO> getBusinessPageByCustomer(CrmBusinessPageReqVO pageReqVO, Long userId) {  
115 - // 校验客户存在 TODO @puhui999:这里不校验  
116 - customerService.validateCustomer(pageReqVO.getCustomerId());  
117 - // TODO @puhui999:感觉这里貌似不太复用用 selectPage,因为他可能没商机权限,只是因为能看 customer,所以可以看到列表  
118 - return businessMapper.selectPage(pageReqVO, userId); 114 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
  115 + public PageResult<CrmBusinessDO> getBusinessPageByCustomerId(CrmBusinessPageReqVO pageReqVO) {
  116 + return businessMapper.selectPageByCustomerId(pageReqVO);
119 } 117 }
120 118
121 @Override 119 @Override
@@ -127,7 +125,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -127,7 +125,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
127 // 2.1 数据权限转移 125 // 2.1 数据权限转移
128 crmPermissionService.transferPermission( 126 crmPermissionService.transferPermission(
129 CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())); 127 CrmBusinessConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()));
130 -  
131 // 2.2 设置新的负责人 128 // 2.2 设置新的负责人
132 businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 129 businessMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
133 130
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.service.clue; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.service.clue;
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.clue.vo.CrmClueCreateReqVO; 4 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueCreateReqVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
8 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
@@ -64,4 +65,12 @@ public interface CrmClueService { @@ -64,4 +65,12 @@ public interface CrmClueService {
64 */ 65 */
65 PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId); 66 PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId);
66 67
  68 + /**
  69 + * 线索转移
  70 + *
  71 + * @param reqVO 请求
  72 + * @param userId 用户编号
  73 + */
  74 + void transferClue(CrmClueTransferReqVO reqVO, Long userId);
  75 +
67 } 76 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
@@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil; @@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueCreateReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueCreateReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
  8 +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueUpdateReqVO;
9 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert; 10 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; 11 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
@@ -101,4 +102,18 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -101,4 +102,18 @@ public class CrmClueServiceImpl implements CrmClueService {
101 return clueMapper.selectPage(pageReqVO, userId); 102 return clueMapper.selectPage(pageReqVO, userId);
102 } 103 }
103 104
  105 + @Override
  106 + public void transferClue(CrmClueTransferReqVO reqVO, Long userId) {
  107 + // 1 校验线索是否存在
  108 + validateClueExists(reqVO.getId());
  109 +
  110 + // 2.1 数据权限转移
  111 + crmPermissionService.transferPermission(
  112 + CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType()));
  113 + // 2.2 设置新的负责人
  114 + clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  115 +
  116 + // 3. TODO 记录转移日志
  117 + }
  118 +
104 } 119 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
@@ -3,8 +3,10 @@ package cn.iocoder.yudao.module.crm.service.contact; @@ -3,8 +3,10 @@ package cn.iocoder.yudao.module.crm.service.contact;
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.contact.vo.CrmContactCreateReqVO; 4 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactCreateReqVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactTransferReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactUpdateReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
  9 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
8 import jakarta.validation.Valid; 10 import jakarta.validation.Valid;
9 11
10 import java.util.Collection; 12 import java.util.Collection;
@@ -71,12 +73,19 @@ public interface CrmContactService { @@ -71,12 +73,19 @@ public interface CrmContactService {
71 /** 73 /**
72 * 获得联系人分页 74 * 获得联系人分页
73 * 75 *
74 - * 数据权限:基于 {@link CrmContactDO} 76 + * 数据权限:基于 {@link CrmCustomerDO}
75 * 77 *
76 * @param pageVO 分页查询 78 * @param pageVO 分页查询
77 - * @param userId 用户编号  
78 * @return 联系人分页 79 * @return 联系人分页
79 */ 80 */
80 - PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO, Long userId); 81 + PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO);
  82 +
  83 + /**
  84 + * 联系人转移
  85 + *
  86 + * @param reqVO 请求
  87 + * @param userId 用户编号
  88 + */
  89 + void transferContact(CrmContactTransferReqVO reqVO, Long userId);
81 90
82 } 91 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
@@ -3,11 +3,8 @@ package cn.iocoder.yudao.module.crm.service.contact; @@ -3,11 +3,8 @@ package cn.iocoder.yudao.module.crm.service.contact;
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.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 -import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBaseVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactCreateReqVO;  
8 -import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactPageReqVO;  
9 -import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactUpdateReqVO;  
10 -import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert; 6 +import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
  7 +import cn.iocoder.yudao.module.crm.convert.contact.CrmContactConvert;
11 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
12 import cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactMapper; 9 import cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactMapper;
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 10 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@@ -56,7 +53,7 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -56,7 +53,7 @@ public class CrmContactServiceImpl implements CrmContactService {
56 // 1.1 校验 53 // 1.1 校验
57 validateRelationDataExists(createReqVO); 54 validateRelationDataExists(createReqVO);
58 // 1.2 插入 55 // 1.2 插入
59 - CrmContactDO contact = ContactConvert.INSTANCE.convert(createReqVO); 56 + CrmContactDO contact = CrmContactConvert.INSTANCE.convert(createReqVO);
60 contactMapper.insert(contact); 57 contactMapper.insert(contact);
61 58
62 // 2. 创建数据权限 59 // 2. 创建数据权限
@@ -73,7 +70,7 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -73,7 +70,7 @@ public class CrmContactServiceImpl implements CrmContactService {
73 validateContactExists(updateReqVO.getId()); 70 validateContactExists(updateReqVO.getId());
74 validateRelationDataExists(updateReqVO); 71 validateRelationDataExists(updateReqVO);
75 // 2. 更新 72 // 2. 更新
76 - CrmContactDO updateObj = ContactConvert.INSTANCE.convert(updateReqVO); 73 + CrmContactDO updateObj = CrmContactConvert.INSTANCE.convert(updateReqVO);
77 contactMapper.updateById(updateObj); 74 contactMapper.updateById(updateObj);
78 } 75 }
79 76
@@ -134,11 +131,23 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -134,11 +131,23 @@ public class CrmContactServiceImpl implements CrmContactService {
134 } 131 }
135 132
136 @Override 133 @Override
137 - public PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO, Long userId) {  
138 - // 校验用户存在  
139 - customerService.validateCustomer(pageVO.getCustomerId());  
140 - // TODO @puhui999:getBusinessPageByCustomer 同理  
141 - return contactMapper.selectPage(pageVO, userId); 134 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageVO.customerId", level = CrmPermissionLevelEnum.READ)
  135 + public PageResult<CrmContactDO> getContactPageByCustomerId(CrmContactPageReqVO pageVO) {
  136 + return contactMapper.selectPageByCustomerId(pageVO);
  137 + }
  138 +
  139 + @Override
  140 + public void transferContact(CrmContactTransferReqVO reqVO, Long userId) {
  141 + // 1 校验联系人是否存在
  142 + validateContactExists(reqVO.getId());
  143 +
  144 + // 2.1 数据权限转移
  145 + crmPermissionService.transferPermission(
  146 + CrmContactConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTACT.getType()));
  147 + // 2.2 设置新的负责人
  148 + contactMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  149 +
  150 + // 3. TODO 记录转移日志
142 } 151 }
143 152
144 } 153 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
@@ -75,12 +75,9 @@ public interface CrmContractService { @@ -75,12 +75,9 @@ public interface CrmContractService {
75 * 数据权限:基于 {@link CrmCustomerDO} 读取 75 * 数据权限:基于 {@link CrmCustomerDO} 读取
76 * 76 *
77 * @param pageReqVO 分页查询 77 * @param pageReqVO 分页查询
78 - * @param userId 用户编号  
79 * @return 联系人分页 78 * @return 联系人分页
80 */ 79 */
81 - default PageResult<CrmContractDO> getContractPageByCustomer(CrmContractPageReqVO pageReqVO, Long userId) {  
82 - return getContractPage(pageReqVO, userId);  
83 - } 80 + PageResult<CrmContractDO> getContractPageByCustomerId(CrmContractPageReqVO pageReqVO);
84 81
85 /** 82 /**
86 * 合同转移 83 * 合同转移
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractCreat @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractCreat
7 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
9 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractUpdateReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractUpdateReqVO;
10 -import cn.iocoder.yudao.module.crm.convert.contract.ContractConvert; 10 +import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
11 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 11 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;
@@ -44,7 +44,7 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -44,7 +44,7 @@ public class CrmContractServiceImpl implements CrmContractService {
44 @Override 44 @Override
45 public Long createContract(CrmContractCreateReqVO createReqVO, Long userId) { 45 public Long createContract(CrmContractCreateReqVO createReqVO, Long userId) {
46 // 插入 46 // 插入
47 - CrmContractDO contract = ContractConvert.INSTANCE.convert(createReqVO); 47 + CrmContractDO contract = CrmContractConvert.INSTANCE.convert(createReqVO);
48 contractMapper.insert(contract); 48 contractMapper.insert(contract);
49 49
50 // 创建数据权限 50 // 创建数据权限
@@ -61,7 +61,7 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -61,7 +61,7 @@ public class CrmContractServiceImpl implements CrmContractService {
61 // 校验存在 61 // 校验存在
62 validateContractExists(updateReqVO.getId()); 62 validateContractExists(updateReqVO.getId());
63 // 更新 63 // 更新
64 - CrmContractDO updateObj = ContractConvert.INSTANCE.convert(updateReqVO); 64 + CrmContractDO updateObj = CrmContractConvert.INSTANCE.convert(updateReqVO);
65 contractMapper.updateById(updateObj); 65 contractMapper.updateById(updateObj);
66 } 66 }
67 67
@@ -105,6 +105,12 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -105,6 +105,12 @@ public class CrmContractServiceImpl implements CrmContractService {
105 } 105 }
106 106
107 @Override 107 @Override
  108 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
  109 + public PageResult<CrmContractDO> getContractPageByCustomerId(CrmContractPageReqVO pageReqVO) {
  110 + return contractMapper.selectPageByCustomerId(pageReqVO);
  111 + }
  112 +
  113 + @Override
108 @Transactional(rollbackFor = Exception.class) 114 @Transactional(rollbackFor = Exception.class)
109 public void transferContract(CrmContractTransferReqVO reqVO, Long userId) { 115 public void transferContract(CrmContractTransferReqVO reqVO, Long userId) {
110 // 1. 校验合同是否存在 116 // 1. 校验合同是否存在
@@ -112,7 +118,7 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -112,7 +118,7 @@ public class CrmContractServiceImpl implements CrmContractService {
112 118
113 // 2.1 数据权限转移 119 // 2.1 数据权限转移
114 crmPermissionService.transferPermission( 120 crmPermissionService.transferPermission(
115 - ContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType())); 121 + CrmContractConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()));
116 // 2.2 设置负责人 122 // 2.2 设置负责人
117 contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 123 contractMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
118 124
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.customer; @@ -2,6 +2,7 @@ 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.object.BeanUtils;
5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO;
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.controller.admin.customer.vo.CrmCustomerTransferReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
@@ -15,6 +16,9 @@ import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; @@ -15,6 +16,9 @@ import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
15 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 16 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
16 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 17 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
17 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  19 +import com.mzt.logapi.context.LogRecordContext;
  20 +import com.mzt.logapi.service.impl.DiffParseFunction;
  21 +import com.mzt.logapi.starter.annotation.LogRecord;
18 import jakarta.annotation.Resource; 22 import jakarta.annotation.Resource;
19 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
20 import org.springframework.transaction.annotation.Transactional; 24 import org.springframework.transaction.annotation.Transactional;
@@ -24,6 +28,8 @@ import java.util.*; @@ -24,6 +28,8 @@ import java.util.*;
24 28
25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 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;
  32 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.TRANSFER_CUSTOMER_LOG_SUCCESS;
27 import static java.util.Collections.singletonList; 33 import static java.util.Collections.singletonList;
28 34
29 /** 35 /**
@@ -59,11 +65,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -59,11 +65,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
59 65
60 @Override 66 @Override
61 @Transactional(rollbackFor = Exception.class) 67 @Transactional(rollbackFor = Exception.class)
  68 + @LogRecord(success = "更新了客户{_DIFF{#updateReqVO}}", type = CRM_CUSTOMER, subType = "更新客户", bizNo = "{{#updateReqVO.id}}")
62 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 69 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
63 public void updateCustomer(CrmCustomerUpdateReqVO updateReqVO) { 70 public void updateCustomer(CrmCustomerUpdateReqVO updateReqVO) {
64 // 校验存在 71 // 校验存在
65 - validateCustomerExists(updateReqVO.getId()); 72 + CrmCustomerDO oldCustomerDO = validateCustomerExists(updateReqVO.getId());
66 73
  74 + // __DIFF 函数传递了一个参数,传递的参数是修改之后的对象,这种方式需要在方法内部向 LogRecordContext 中 put 一个变量,代表是之前的对象,这个对象可以是null
  75 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomerDO, CrmCustomerUpdateReqVO.class));
67 // 更新 76 // 更新
68 CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO); 77 CrmCustomerDO updateObj = CrmCustomerConvert.INSTANCE.convert(updateReqVO);
69 customerMapper.updateById(updateObj); 78 customerMapper.updateById(updateObj);
@@ -82,10 +91,12 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -82,10 +91,12 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
82 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id); 91 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), id);
83 } 92 }
84 93
85 - private void validateCustomerExists(Long id) {  
86 - if (customerMapper.selectById(id) == null) { 94 + private CrmCustomerDO validateCustomerExists(Long id) {
  95 + CrmCustomerDO customerDO = customerMapper.selectById(id);
  96 + if (customerDO == null) {
87 throw exception(CUSTOMER_NOT_EXISTS); 97 throw exception(CUSTOMER_NOT_EXISTS);
88 } 98 }
  99 + return customerDO;
89 } 100 }
90 101
91 @Override 102 @Override
@@ -114,7 +125,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -114,7 +125,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
114 */ 125 */
115 @Override 126 @Override
116 public void validateCustomer(Long customerId) { 127 public void validateCustomer(Long customerId) {
117 - // TODO puhui999: 不返回客户不走校验应该可行  
118 // 校验客户是否存在 128 // 校验客户是否存在
119 if (customerId == null) { 129 if (customerId == null) {
120 throw exception(CUSTOMER_NOT_EXISTS); 130 throw exception(CUSTOMER_NOT_EXISTS);
@@ -127,11 +137,13 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -127,11 +137,13 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
127 137
128 @Override 138 @Override
129 @Transactional(rollbackFor = Exception.class) 139 @Transactional(rollbackFor = Exception.class)
  140 + @LogRecord(success = TRANSFER_CUSTOMER_LOG_SUCCESS, type = CRM_CUSTOMER, subType = "客户转移", bizNo = "{{#reqVO.id}}")
130 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) 141 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER)
131 public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) { 142 public void transferCustomer(CrmCustomerTransferReqVO reqVO, Long userId) {
132 // 1. 校验客户是否存在 143 // 1. 校验客户是否存在
133 validateCustomer(reqVO.getId()); 144 validateCustomer(reqVO.getId());
134 - 145 + // 添加 crmCustomer 到日志上下文 TODO 日志记录放在 service 里是因为已经过了权限校验查询时不用走两次校验
  146 + LogRecordContext.putVariable("crmCustomer", customerMapper.selectById(reqVO.getId()));
135 // 2.1 数据权限转移 147 // 2.1 数据权限转移
136 crmPermissionService.transferPermission( 148 crmPermissionService.transferPermission(
137 CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())); 149 CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()));
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
@@ -138,15 +138,13 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { @@ -138,15 +138,13 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
138 } 138 }
139 139
140 @Override 140 @Override
  141 + @Transactional(rollbackFor = Exception.class)
141 public void deletePermission(Integer bizType, Long bizId) { 142 public void deletePermission(Integer bizType, Long bizId) {
142 - // TODO @puhui999:这种直接写条件删除;不需要先查询,再删除  
143 - List<CrmPermissionDO> permissionList = crmPermissionMapper.selectByBizTypeAndBizId(bizType, bizId);  
144 - if (CollUtil.isEmpty(permissionList)) {  
145 - return;  
146 - }  
147 -  
148 // 删除数据权限 143 // 删除数据权限
149 - crmPermissionMapper.deleteBatchIds(convertSet(permissionList, CrmPermissionDO::getId)); 144 + int deletedCol = crmPermissionMapper.deletePermission(bizType, bizId);
  145 + if (deletedCol == 0) {
  146 + throw exception(CRM_PERMISSION_NOT_EXISTS);
  147 + }
150 } 148 }
151 149
152 @Override 150 @Override
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanService.java
@@ -3,11 +3,12 @@ package cn.iocoder.yudao.module.crm.service.receivable; @@ -3,11 +3,12 @@ package cn.iocoder.yudao.module.crm.service.receivable;
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.receivable.vo.plan.CrmReceivablePlanCreateReqVO; 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.CrmReceivablePlanPageReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanTransferReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
9 -  
10 import jakarta.validation.Valid; 10 import jakarta.validation.Valid;
  11 +
11 import java.util.Collection; 12 import java.util.Collection;
12 import java.util.List; 13 import java.util.List;
13 14
@@ -24,7 +25,7 @@ public interface CrmReceivablePlanService { @@ -24,7 +25,7 @@ public interface CrmReceivablePlanService {
24 * @param createReqVO 创建信息 25 * @param createReqVO 创建信息
25 * @return 编号 26 * @return 编号
26 */ 27 */
27 - Long createReceivablePlan(@Valid CrmReceivablePlanCreateReqVO createReqVO); 28 + Long createReceivablePlan(@Valid CrmReceivablePlanCreateReqVO createReqVO, Long userId);
28 29
29 /** 30 /**
30 * 更新回款计划 31 * 更新回款计划
@@ -62,9 +63,10 @@ public interface CrmReceivablePlanService { @@ -62,9 +63,10 @@ public interface CrmReceivablePlanService {
62 * 数据权限:基于 {@link CrmReceivablePlanDO} 读取 63 * 数据权限:基于 {@link CrmReceivablePlanDO} 读取
63 * 64 *
64 * @param pageReqVO 分页查询 65 * @param pageReqVO 分页查询
  66 + * @param userId 用户编号
65 * @return 回款计划分页 67 * @return 回款计划分页
66 */ 68 */
67 - PageResult<CrmReceivablePlanDO> getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO); 69 + PageResult<CrmReceivablePlanDO> getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId);
68 70
69 /** 71 /**
70 * 获得回款计划分页,基于指定客户 72 * 获得回款计划分页,基于指定客户
@@ -74,6 +76,14 @@ public interface CrmReceivablePlanService { @@ -74,6 +76,14 @@ public interface CrmReceivablePlanService {
74 * @param pageReqVO 分页查询 76 * @param pageReqVO 分页查询
75 * @return 回款计划分页 77 * @return 回款计划分页
76 */ 78 */
77 - PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomer(CrmReceivablePlanPageReqVO pageReqVO); 79 + PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO);
  80 +
  81 + /**
  82 + * 回款计划转移
  83 + *
  84 + * @param reqVO 请求
  85 + * @param userId 用户编号
  86 + */
  87 + void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId);
78 88
79 } 89 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
@@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil; @@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanCreateReqVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanPageReqVO;
  9 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanTransferReqVO;
9 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; 10 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
10 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert; 11 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivablePlanConvert;
11 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -17,10 +18,12 @@ import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; @@ -17,10 +18,12 @@ 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.framework.core.annotations.CrmPermission;
18 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 19 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
19 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;
  22 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  23 +import jakarta.annotation.Resource;
20 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
21 import org.springframework.validation.annotation.Validated; 25 import org.springframework.validation.annotation.Validated;
22 26
23 -import jakarta.annotation.Resource;  
24 import java.util.Collection; 27 import java.util.Collection;
25 import java.util.List; 28 import java.util.List;
26 29
@@ -28,7 +31,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -28,7 +31,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
28 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 31 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
29 32
30 // TODO @liuhongfeng:参考 CrmReceivableServiceImpl 写的 todo 哈; 33 // TODO @liuhongfeng:参考 CrmReceivableServiceImpl 写的 todo 哈;
31 -// TODO @puhui999:数据权限 34 +
32 /** 35 /**
33 * 回款计划 Service 实现类 36 * 回款计划 Service 实现类
34 * 37 *
@@ -45,9 +48,11 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -45,9 +48,11 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
45 private CrmContractService contractService; 48 private CrmContractService contractService;
46 @Resource 49 @Resource
47 private CrmCustomerService customerService; 50 private CrmCustomerService customerService;
  51 + @Resource
  52 + private CrmPermissionService crmPermissionService;
48 53
49 @Override 54 @Override
50 - public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO) { 55 + public Long createReceivablePlan(CrmReceivablePlanCreateReqVO createReqVO, Long userId) {
51 // 插入 56 // 插入
52 CrmReceivablePlanDO receivablePlan = CrmReceivablePlanConvert.INSTANCE.convert(createReqVO); 57 CrmReceivablePlanDO receivablePlan = CrmReceivablePlanConvert.INSTANCE.convert(createReqVO);
53 receivablePlan.setFinishStatus(false); 58 receivablePlan.setFinishStatus(false);
@@ -55,29 +60,33 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -55,29 +60,33 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
55 checkReceivablePlan(receivablePlan); 60 checkReceivablePlan(receivablePlan);
56 61
57 receivablePlanMapper.insert(receivablePlan); 62 receivablePlanMapper.insert(receivablePlan);
  63 + // 创建数据权限
  64 + crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType())
  65 + .setBizId(receivablePlan.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
58 // 返回 66 // 返回
59 return receivablePlan.getId(); 67 return receivablePlan.getId();
60 } 68 }
61 69
62 private void checkReceivablePlan(CrmReceivablePlanDO receivablePlan) { 70 private void checkReceivablePlan(CrmReceivablePlanDO receivablePlan) {
63 71
64 - if(ObjectUtil.isNull(receivablePlan.getContractId())){ 72 + if (ObjectUtil.isNull(receivablePlan.getContractId())) {
65 throw exception(CONTRACT_NOT_EXISTS); 73 throw exception(CONTRACT_NOT_EXISTS);
66 } 74 }
67 75
68 CrmContractDO contract = contractService.getContract(receivablePlan.getContractId()); 76 CrmContractDO contract = contractService.getContract(receivablePlan.getContractId());
69 - if(ObjectUtil.isNull(contract)){ 77 + if (ObjectUtil.isNull(contract)) {
70 throw exception(CONTRACT_NOT_EXISTS); 78 throw exception(CONTRACT_NOT_EXISTS);
71 } 79 }
72 80
73 CrmCustomerDO customer = customerService.getCustomer(receivablePlan.getCustomerId()); 81 CrmCustomerDO customer = customerService.getCustomer(receivablePlan.getCustomerId());
74 - if(ObjectUtil.isNull(customer)){ 82 + if (ObjectUtil.isNull(customer)) {
75 throw exception(CUSTOMER_NOT_EXISTS); 83 throw exception(CUSTOMER_NOT_EXISTS);
76 } 84 }
77 85
78 } 86 }
79 87
80 @Override 88 @Override
  89 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
81 public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) { 90 public void updateReceivablePlan(CrmReceivablePlanUpdateReqVO updateReqVO) {
82 // 校验存在 91 // 校验存在
83 validateReceivablePlanExists(updateReqVO.getId()); 92 validateReceivablePlanExists(updateReqVO.getId());
@@ -88,6 +97,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -88,6 +97,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
88 } 97 }
89 98
90 @Override 99 @Override
  100 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
91 public void deleteReceivablePlan(Long id) { 101 public void deleteReceivablePlan(Long id) {
92 // 校验存在 102 // 校验存在
93 validateReceivablePlanExists(id); 103 validateReceivablePlanExists(id);
@@ -102,6 +112,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -102,6 +112,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
102 } 112 }
103 113
104 @Override 114 @Override
  115 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE_PLAN, bizId = "#id", level = CrmPermissionLevelEnum.READ)
105 public CrmReceivablePlanDO getReceivablePlan(Long id) { 116 public CrmReceivablePlanDO getReceivablePlan(Long id) {
106 return receivablePlanMapper.selectById(id); 117 return receivablePlanMapper.selectById(id);
107 } 118 }
@@ -115,14 +126,28 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { @@ -115,14 +126,28 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService {
115 } 126 }
116 127
117 @Override 128 @Override
118 - public PageResult<CrmReceivablePlanDO> getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO) {  
119 - return receivablePlanMapper.selectPage(pageReqVO); 129 + public PageResult<CrmReceivablePlanDO> getReceivablePlanPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {
  130 + return receivablePlanMapper.selectPage(pageReqVO, userId);
120 } 131 }
121 132
122 @Override 133 @Override
123 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ) 134 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
124 - public PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomer(CrmReceivablePlanPageReqVO pageReqVO) {  
125 - return receivablePlanMapper.selectPageByCustomer(pageReqVO); 135 + public PageResult<CrmReceivablePlanDO> getReceivablePlanPageByCustomerId(CrmReceivablePlanPageReqVO pageReqVO) {
  136 + return receivablePlanMapper.selectPageByCustomerId(pageReqVO);
  137 + }
  138 +
  139 + @Override
  140 + public void transferReceivablePlan(CrmReceivablePlanTransferReqVO reqVO, Long userId) {
  141 + // 1 校验回款计划是否存在
  142 + validateReceivablePlanExists(reqVO.getId());
  143 +
  144 + // 2.1 数据权限转移
  145 + crmPermissionService.transferPermission(
  146 + CrmReceivablePlanConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType()));
  147 + // 2.2 设置新的负责人
  148 + receivablePlanMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  149 +
  150 + // 3. TODO 记录转移日志
126 } 151 }
127 152
128 } 153 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableService.java
@@ -3,11 +3,12 @@ package cn.iocoder.yudao.module.crm.service.receivable; @@ -3,11 +3,12 @@ package cn.iocoder.yudao.module.crm.service.receivable;
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.receivable.vo.receivable.CrmReceivableCreateReqVO; 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.CrmReceivablePageReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableTransferReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
9 -  
10 import jakarta.validation.Valid; 10 import jakarta.validation.Valid;
  11 +
11 import java.util.Collection; 12 import java.util.Collection;
12 import java.util.List; 13 import java.util.List;
13 14
@@ -62,9 +63,10 @@ public interface CrmReceivableService { @@ -62,9 +63,10 @@ public interface CrmReceivableService {
62 * 数据权限:基于 {@link CrmReceivableDO} 读取 63 * 数据权限:基于 {@link CrmReceivableDO} 读取
63 * 64 *
64 * @param pageReqVO 分页查询 65 * @param pageReqVO 分页查询
  66 + * @param userId 用户编号
65 * @return 回款分页 67 * @return 回款分页
66 */ 68 */
67 - PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO); 69 + PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId);
68 70
69 /** 71 /**
70 * 获得回款分页,基于指定客户 72 * 获得回款分页,基于指定客户
@@ -74,6 +76,14 @@ public interface CrmReceivableService { @@ -74,6 +76,14 @@ public interface CrmReceivableService {
74 * @param pageReqVO 分页查询 76 * @param pageReqVO 分页查询
75 * @return 回款分页 77 * @return 回款分页
76 */ 78 */
77 - PageResult<CrmReceivableDO> getReceivablePageByCustomer(CrmReceivablePageReqVO pageReqVO); 79 + PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO);
  80 +
  81 + /**
  82 + * 回款转移
  83 + *
  84 + * @param reqVO 请求
  85 + * @param userId 用户编号
  86 + */
  87 + void transferReceivable(CrmReceivableTransferReqVO reqVO, Long userId);
78 88
79 } 89 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableCreateReqVO;
9 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO;
  10 +import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableTransferReqVO;
10 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; 11 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
11 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert; 12 import cn.iocoder.yudao.module.crm.convert.receivable.CrmReceivableConvert;
12 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 13 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -20,10 +21,11 @@ import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; @@ -20,10 +21,11 @@ import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
20 import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission; 21 import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
21 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 22 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
22 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;
  25 +import jakarta.annotation.Resource;
23 import org.springframework.stereotype.Service; 26 import org.springframework.stereotype.Service;
24 import org.springframework.validation.annotation.Validated; 27 import org.springframework.validation.annotation.Validated;
25 28
26 -import jakarta.annotation.Resource;  
27 import java.util.Collection; 29 import java.util.Collection;
28 import java.util.List; 30 import java.util.List;
29 31
@@ -48,6 +50,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -48,6 +50,8 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
48 private CrmCustomerService customerService; 50 private CrmCustomerService customerService;
49 @Resource 51 @Resource
50 private CrmReceivablePlanService receivablePlanService; 52 private CrmReceivablePlanService receivablePlanService;
  53 + @Resource
  54 + private CrmPermissionService crmPermissionService;
51 55
52 // TODO @liuhongfeng:创建还款后,是不是什么时候,要更新 plan? 56 // TODO @liuhongfeng:创建还款后,是不是什么时候,要更新 plan?
53 @Override 57 @Override
@@ -70,22 +74,22 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -70,22 +74,22 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
70 // TODO @liuhongfeng:这里的括号要注意排版; 74 // TODO @liuhongfeng:这里的括号要注意排版;
71 private void checkReceivable(CrmReceivableDO receivable) { 75 private void checkReceivable(CrmReceivableDO receivable) {
72 // TODO @liuhongfeng:这个放在参数校验合适 76 // TODO @liuhongfeng:这个放在参数校验合适
73 - if(ObjectUtil.isNull(receivable.getContractId())){ 77 + if (ObjectUtil.isNull(receivable.getContractId())) {
74 throw exception(CONTRACT_NOT_EXISTS); 78 throw exception(CONTRACT_NOT_EXISTS);
75 } 79 }
76 80
77 CrmContractDO contract = contractService.getContract(receivable.getContractId()); 81 CrmContractDO contract = contractService.getContract(receivable.getContractId());
78 - if(ObjectUtil.isNull(contract)){ 82 + if (ObjectUtil.isNull(contract)) {
79 throw exception(CONTRACT_NOT_EXISTS); 83 throw exception(CONTRACT_NOT_EXISTS);
80 } 84 }
81 85
82 CrmCustomerDO customer = customerService.getCustomer(receivable.getCustomerId()); 86 CrmCustomerDO customer = customerService.getCustomer(receivable.getCustomerId());
83 - if(ObjectUtil.isNull(customer)){ 87 + if (ObjectUtil.isNull(customer)) {
84 throw exception(CUSTOMER_NOT_EXISTS); 88 throw exception(CUSTOMER_NOT_EXISTS);
85 } 89 }
86 90
87 CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(receivable.getPlanId()); 91 CrmReceivablePlanDO receivablePlan = receivablePlanService.getReceivablePlan(receivable.getPlanId());
88 - if(ObjectUtil.isNull(receivablePlan)){ 92 + if (ObjectUtil.isNull(receivablePlan)) {
89 throw exception(RECEIVABLE_PLAN_NOT_EXISTS); 93 throw exception(RECEIVABLE_PLAN_NOT_EXISTS);
90 } 94 }
91 95
@@ -129,16 +133,29 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { @@ -129,16 +133,29 @@ public class CrmReceivableServiceImpl implements CrmReceivableService {
129 return receivableMapper.selectBatchIds(ids); 133 return receivableMapper.selectBatchIds(ids);
130 } 134 }
131 135
132 - // TODO @芋艿:数据权限  
133 @Override 136 @Override
134 - public PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO) {  
135 - return receivableMapper.selectPage(pageReqVO); 137 + public PageResult<CrmReceivableDO> getReceivablePage(CrmReceivablePageReqVO pageReqVO, Long userId) {
  138 + return receivableMapper.selectPage(pageReqVO, userId);
136 } 139 }
137 140
138 @Override 141 @Override
139 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ) 142 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#pageReqVO.customerId", level = CrmPermissionLevelEnum.READ)
140 - public PageResult<CrmReceivableDO> getReceivablePageByCustomer(CrmReceivablePageReqVO pageReqVO) {  
141 - return receivableMapper.selectPageByCustomer(pageReqVO); 143 + public PageResult<CrmReceivableDO> getReceivablePageByCustomerId(CrmReceivablePageReqVO pageReqVO) {
  144 + return receivableMapper.selectPageByCustomerId(pageReqVO);
  145 + }
  146 +
  147 + @Override
  148 + public void transferReceivable(CrmReceivableTransferReqVO reqVO, Long userId) {
  149 + // 1 校验回款是否存在
  150 + validateReceivableExists(reqVO.getId());
  151 +
  152 + // 2.1 数据权限转移
  153 + crmPermissionService.transferPermission(
  154 + CrmReceivableConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_RECEIVABLE.getType()));
  155 + // 2.2 设置新的负责人
  156 + receivableMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
  157 +
  158 + // 3. TODO 记录转移日志
142 } 159 }
143 160
144 } 161 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java
@@ -6,6 +6,7 @@ import cn.hutool.extra.spring.SpringUtil; @@ -6,6 +6,7 @@ import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; 6 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.system.api.user.AdminUserApi; 10 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
10 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 11 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
11 import com.baomidou.mybatisplus.core.toolkit.support.SFunction; 12 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
@@ -17,7 +18,7 @@ import java.util.List; @@ -17,7 +18,7 @@ import java.util.List;
17 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 18 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
18 19
19 /** 20 /**
20 - * CRM 分页查询工具类 21 + * CRM 查询工具类
21 * 22 *
22 * @author HUIHUI 23 * @author HUIHUI
23 */ 24 */
@@ -26,17 +27,17 @@ public class CrmQueryWrapperUtils { @@ -26,17 +27,17 @@ public class CrmQueryWrapperUtils {
26 /** 27 /**
27 * 构造 CRM 数据类型数据分页查询条件 28 * 构造 CRM 数据类型数据分页查询条件
28 * 29 *
29 - * @param query 连表查询对象  
30 - * @param bizType 数据类型 {@link CrmBizTypeEnum}  
31 - * @param bizId 数据编号  
32 - * @param userId 用户编号  
33 - * @param sceneType 场景类型  
34 - * @param pool 公海 30 + * @param query 连表查询对象
  31 + * @param bizType 数据类型 {@link CrmBizTypeEnum}
  32 + * @param bizId 数据编号
  33 + * @param userId 用户编号
  34 + * @param sceneType 场景类型
  35 + * @param pool 公海
  36 + * @return 是否 (是:需要执行查询,否:不需要查询调用方法直接返回空)
35 */ 37 */
36 - // TODO @puhui999:bizId 直接传递会不会简单点  
37 - // TODO @puhui999:builderPageQuery 应该不仅仅适合于分页查询,应该适用于所有的查询;可以改成 appendPermissionCondition  
38 - public static <T extends MPJLambdaWrapper<?>, S> void builderPageQuery(T query, Integer bizType, SFunction<S, ?> bizId,  
39 - Long userId, Integer sceneType, Boolean pool) { 38 + // TODO @puhui999:bizId 直接传递会不会简单点 回复:还是需要 SFunction 因为分页连表时不知道 bizId 是多少
  39 + public static <T extends MPJLambdaWrapper<?>, S> boolean appendPermissionCondition(T query, Integer bizType, SFunction<S, ?> bizId,
  40 + Long userId, Integer sceneType, Boolean pool) {
40 // 1. 构建数据权限连表条件 41 // 1. 构建数据权限连表条件
41 if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE)) { // 管理员不需要数据权限 42 if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE)) { // 管理员不需要数据权限
42 query.innerJoin(CrmPermissionDO.class, on -> 43 query.innerJoin(CrmPermissionDO.class, on ->
@@ -48,52 +49,55 @@ public class CrmQueryWrapperUtils { @@ -48,52 +49,55 @@ public class CrmQueryWrapperUtils {
48 query.eq("owner_user_id", userId); 49 query.eq("owner_user_id", userId);
49 } 50 }
50 // 2.2 场景二:我参与的数据 51 // 2.2 场景二:我参与的数据
51 - // TODO @puhui999:参与,指的是有读写权限噢;可以把 1. 的合并到 2.2 里;因为 2.1 不需要;  
52 if (CrmSceneTypeEnum.isInvolved(sceneType)) { 52 if (CrmSceneTypeEnum.isInvolved(sceneType)) {
53 - query.ne("owner_user_id", userId); 53 + query
  54 + .ne("owner_user_id", userId)
  55 + .and(q -> q.eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel())
  56 + .or()
  57 + .eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.WRITE.getLevel()));
  58 +
54 } 59 }
55 // 2.3 场景三:下属负责的数据 60 // 2.3 场景三:下属负责的数据
56 if (CrmSceneTypeEnum.isSubordinate(sceneType)) { 61 if (CrmSceneTypeEnum.isSubordinate(sceneType)) {
57 List<AdminUserRespDTO> subordinateUsers = getAdminUserApi().getUserListBySubordinate(userId); 62 List<AdminUserRespDTO> subordinateUsers = getAdminUserApi().getUserListBySubordinate(userId);
58 - // TODO @puhui999:如果为空,不拼接,就是查询了所有数据呀?  
59 - if (CollUtil.isNotEmpty(subordinateUsers)) {  
60 - query.in("owner_user_id", convertSet(subordinateUsers, AdminUserRespDTO::getId)); 63 + if (CollUtil.isEmpty(subordinateUsers)) {
  64 + return false;
61 } 65 }
  66 + query.in("owner_user_id", convertSet(subordinateUsers, AdminUserRespDTO::getId));
62 } 67 }
63 68
64 - // 2. 拼接公海的查询条件 69 + // 3. 拼接公海的查询条件
65 if (ObjUtil.equal(pool, Boolean.TRUE)) { // 情况一:公海 70 if (ObjUtil.equal(pool, Boolean.TRUE)) { // 情况一:公海
66 query.isNull("owner_user_id"); 71 query.isNull("owner_user_id");
67 } else { // 情况二:不是公海 72 } else { // 情况二:不是公海
68 query.isNotNull("owner_user_id"); 73 query.isNotNull("owner_user_id");
69 } 74 }
  75 +
  76 + return true;
70 } 77 }
71 78
72 /** 79 /**
73 * 构造 CRM 数据类型批量数据查询条件 80 * 构造 CRM 数据类型批量数据查询条件
74 * 81 *
75 - * @param query 连表查询对象  
76 - * @param bizType 数据类型 {@link CrmBizTypeEnum}  
77 - * @param bizIds 数据编号  
78 - * @param userId 用户编号 82 + * @param query 连表查询对象
  83 + * @param bizType 数据类型 {@link CrmBizTypeEnum}
  84 + * @param bizIds 数据编号
  85 + * @param userId 用户编号
79 */ 86 */
80 - // TODO @puhui999:可以改成 appendPermissionCondition  
81 - // TODO @puhui999:S 是不是可以删除  
82 - public static <T extends MPJLambdaWrapper<?>, S> void builderListQueryBatch(T query, Integer bizType, Collection<Long> bizIds, Long userId) {  
83 - // TODO @puhui999:这里先 if return 简单点  
84 - if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE)) { // 管理员不需要数据权限  
85 - query.innerJoin(CrmPermissionDO.class, on ->  
86 - on.eq(CrmPermissionDO::getBizType, bizType).in(CrmPermissionDO::getBizId, bizIds)  
87 - .in(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId)); 87 + public static <T extends MPJLambdaWrapper<?>> void appendPermissionCondition(T query, Integer bizType, Collection<Long> bizIds, Long userId) {
  88 + if (ObjUtil.equal(validateAdminUser(userId), Boolean.TRUE)) {// 管理员不需要数据权限
  89 + return;
88 } 90 }
  91 +
  92 + query.innerJoin(CrmPermissionDO.class, on ->
  93 + on.eq(CrmPermissionDO::getBizType, bizType).in(CrmPermissionDO::getBizId, bizIds)
  94 + .in(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId));
89 } 95 }
90 96
91 - // TODO @puhui999:需要加个变量,不用每次都拿哈;  
92 private static AdminUserApi getAdminUserApi() { 97 private static AdminUserApi getAdminUserApi() {
93 - return SpringUtil.getBean(AdminUserApi.class); 98 + return AdminUserApiHolder.ADMIN_USER_API;
94 } 99 }
95 100
96 - // TODO @puhui999:需要实现;  
97 /** 101 /**
98 * 校验用户是否是管理员 102 * 校验用户是否是管理员
99 * 103 *
@@ -101,7 +105,27 @@ public class CrmQueryWrapperUtils { @@ -101,7 +105,27 @@ public class CrmQueryWrapperUtils {
101 * @return 是/否 105 * @return 是/否
102 */ 106 */
103 private static boolean validateAdminUser(Long userId) { 107 private static boolean validateAdminUser(Long userId) {
  108 + // TODO 查询权限配置表用户的角色信息
  109 + //CrmPermissionConfig permissionConfig = crmPermissionConfigService.getPermissionConfigByUserId(userId);
  110 + //if (permissionConfig == null) {
  111 + // return false;
  112 + //}
  113 + //// 校验是否为管理员
  114 + //if (permissionConfig.getIsAdmin()){
  115 + // return true;
  116 + //}
104 return false; 117 return false;
105 } 118 }
106 119
  120 + /**
  121 + * 静态内部类实现 AdminUserApi 单例获取
  122 + *
  123 + * @author HUIHUI
  124 + */
  125 + private static class AdminUserApiHolder {
  126 +
  127 + private static final AdminUserApi ADMIN_USER_API = SpringUtil.getBean(AdminUserApi.class);
  128 +
  129 + }
  130 +
107 } 131 }
yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivablePlanServiceImplTest.java
@@ -7,12 +7,12 @@ import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceiv @@ -7,12 +7,12 @@ import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceiv
7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan.CrmReceivablePlanUpdateReqVO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivablePlanDO;
9 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper; 9 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivablePlanMapper;
  10 +import jakarta.annotation.Resource;
10 import org.junit.jupiter.api.Disabled; 11 import org.junit.jupiter.api.Disabled;
11 import org.junit.jupiter.api.Test; 12 import org.junit.jupiter.api.Test;
12 import org.springframework.context.annotation.Import; 13 import org.springframework.context.annotation.Import;
13 14
14 -import jakarta.annotation.Resource;  
15 - 15 +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
16 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 16 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
17 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 17 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
18 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; 18 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
@@ -22,6 +22,7 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PL @@ -22,6 +22,7 @@ import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.RECEIVABLE_PL
22 import static org.junit.jupiter.api.Assertions.*; 22 import static org.junit.jupiter.api.Assertions.*;
23 23
24 // TODO 芋艿:后续,需要补充测试用例 24 // TODO 芋艿:后续,需要补充测试用例
  25 +
25 /** 26 /**
26 * {@link CrmReceivablePlanServiceImpl} 的单元测试类 27 * {@link CrmReceivablePlanServiceImpl} 的单元测试类
27 * 28 *
@@ -43,7 +44,7 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest { @@ -43,7 +44,7 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest {
43 CrmReceivablePlanCreateReqVO reqVO = randomPojo(CrmReceivablePlanCreateReqVO.class); 44 CrmReceivablePlanCreateReqVO reqVO = randomPojo(CrmReceivablePlanCreateReqVO.class);
44 45
45 // 调用 46 // 调用
46 - Long receivablePlanId = receivablePlanService.createReceivablePlan(reqVO); 47 + Long receivablePlanId = receivablePlanService.createReceivablePlan(reqVO, 1L);
47 // 断言 48 // 断言
48 assertNotNull(receivablePlanId); 49 assertNotNull(receivablePlanId);
49 // 校验记录的属性是否正确 50 // 校验记录的属性是否正确
@@ -87,8 +88,8 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest { @@ -87,8 +88,8 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest {
87 88
88 // 调用 89 // 调用
89 receivablePlanService.deleteReceivablePlan(id); 90 receivablePlanService.deleteReceivablePlan(id);
90 - // 校验数据不存在了  
91 - assertNull(crmReceivablePlanMapper.selectById(id)); 91 + // 校验数据不存在了
  92 + assertNull(crmReceivablePlanMapper.selectById(id));
92 } 93 }
93 94
94 @Test 95 @Test
@@ -103,34 +104,34 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest { @@ -103,34 +104,34 @@ public class CrmCrmReceivablePlanServiceImplTest extends BaseDbUnitTest {
103 @Test 104 @Test
104 @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 105 @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
105 public void testGetReceivablePlanPage() { 106 public void testGetReceivablePlanPage() {
106 - // mock 数据  
107 - CrmReceivablePlanDO dbReceivablePlan = randomPojo(CrmReceivablePlanDO.class, o -> { // 等会查询到  
108 - o.setPeriod(null);  
109 - o.setReturnTime(null);  
110 - o.setRemindDays(null);  
111 - o.setRemindTime(null);  
112 - o.setCustomerId(null);  
113 - o.setContractId(null);  
114 - o.setOwnerUserId(null);  
115 - o.setRemark(null);  
116 - o.setCreateTime(null);  
117 - });  
118 - crmReceivablePlanMapper.insert(dbReceivablePlan);  
119 - // 测试 customerId 不匹配  
120 - crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setCustomerId(null)));  
121 - // 测试 contractId 不匹配  
122 - crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setContractId(null)));  
123 - // 准备参数  
124 - CrmReceivablePlanPageReqVO reqVO = new CrmReceivablePlanPageReqVO();  
125 - reqVO.setCustomerId(null);  
126 - reqVO.setContractId(null);  
127 -  
128 - // 调用  
129 - PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(reqVO);  
130 - // 断言  
131 - assertEquals(1, pageResult.getTotal());  
132 - assertEquals(1, pageResult.getList().size());  
133 - assertPojoEquals(dbReceivablePlan, pageResult.getList().get(0)); 107 + // mock 数据
  108 + CrmReceivablePlanDO dbReceivablePlan = randomPojo(CrmReceivablePlanDO.class, o -> { // 等会查询到
  109 + o.setPeriod(null);
  110 + o.setReturnTime(null);
  111 + o.setRemindDays(null);
  112 + o.setRemindTime(null);
  113 + o.setCustomerId(null);
  114 + o.setContractId(null);
  115 + o.setOwnerUserId(null);
  116 + o.setRemark(null);
  117 + o.setCreateTime(null);
  118 + });
  119 + crmReceivablePlanMapper.insert(dbReceivablePlan);
  120 + // 测试 customerId 不匹配
  121 + crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setCustomerId(null)));
  122 + // 测试 contractId 不匹配
  123 + crmReceivablePlanMapper.insert(cloneIgnoreId(dbReceivablePlan, o -> o.setContractId(null)));
  124 + // 准备参数
  125 + CrmReceivablePlanPageReqVO reqVO = new CrmReceivablePlanPageReqVO();
  126 + reqVO.setCustomerId(null);
  127 + reqVO.setContractId(null);
  128 + reqVO.setPageSize(PAGE_SIZE_NONE);
  129 + // 调用
  130 + PageResult<CrmReceivablePlanDO> pageResult = receivablePlanService.getReceivablePlanPage(reqVO, 1L);
  131 + // 断言
  132 + assertEquals(1, pageResult.getTotal());
  133 + assertEquals(1, pageResult.getList().size());
  134 + assertPojoEquals(dbReceivablePlan, pageResult.getList().get(0));
134 } 135 }
135 136
136 } 137 }
yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/receivable/CrmCrmReceivableServiceImplTest.java
@@ -7,12 +7,11 @@ import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.Crm @@ -7,12 +7,11 @@ import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.Crm
7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableUpdateReqVO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO;
9 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper; 9 import cn.iocoder.yudao.module.crm.dal.mysql.receivable.CrmReceivableMapper;
  10 +import jakarta.annotation.Resource;
10 import org.junit.jupiter.api.Disabled; 11 import org.junit.jupiter.api.Disabled;
11 import org.junit.jupiter.api.Test; 12 import org.junit.jupiter.api.Test;
12 import org.springframework.context.annotation.Import; 13 import org.springframework.context.annotation.Import;
13 14
14 -import jakarta.annotation.Resource;  
15 -  
16 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 15 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
17 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 16 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
18 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; 17 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
@@ -133,7 +132,7 @@ public class CrmCrmReceivableServiceImplTest extends BaseDbUnitTest { @@ -133,7 +132,7 @@ public class CrmCrmReceivableServiceImplTest extends BaseDbUnitTest {
133 reqVO.setCustomerId(null); 132 reqVO.setCustomerId(null);
134 133
135 // 调用 134 // 调用
136 - PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(reqVO); 135 + PageResult<CrmReceivableDO> pageResult = receivableService.getReceivablePage(reqVO, 1L);
137 // 断言 136 // 断言
138 assertEquals(1, pageResult.getTotal()); 137 assertEquals(1, pageResult.getTotal());
139 assertEquals(1, pageResult.getList().size()); 138 assertEquals(1, pageResult.getList().size());
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/api/api.js.vm
@@ -63,7 +63,7 @@ export function export${simpleClassName}Excel(params) { @@ -63,7 +63,7 @@ export function export${simpleClassName}Excel(params) {
63 responseType: 'blob' 63 responseType: 'blob'
64 }) 64 })
65 } 65 }
66 -## 特殊:主子表专属逻辑 TODO @puhui999:下面方法的【空格】不太对 66 +## 特殊:主子表专属逻辑
67 #foreach ($subTable in $subTables) 67 #foreach ($subTable in $subTables)
68 #set ($index = $foreach.count - 1) 68 #set ($index = $foreach.count - 1)
69 #set ($subSimpleClassName = $subSimpleClassNames.get($index)) 69 #set ($subSimpleClassName = $subSimpleClassNames.get($index))
@@ -76,7 +76,7 @@ export function export${simpleClassName}Excel(params) { @@ -76,7 +76,7 @@ export function export${simpleClassName}Excel(params) {
76 76
77 // ==================== 子表($subTable.classComment) ==================== 77 // ==================== 子表($subTable.classComment) ====================
78 ## 情况一:MASTER_ERP 时,需要分查询页子表 78 ## 情况一:MASTER_ERP 时,需要分查询页子表
79 - #if ( $table.templateType == 11 ) 79 + #if ($table.templateType == 11)
80 // 获得${subTable.classComment}分页 80 // 获得${subTable.classComment}分页
81 export function get${subSimpleClassName}Page(params) { 81 export function get${subSimpleClassName}Page(params) {
82 return request({ 82 return request({
@@ -87,7 +87,7 @@ export function export${simpleClassName}Excel(params) { @@ -87,7 +87,7 @@ export function export${simpleClassName}Excel(params) {
87 } 87 }
88 ## 情况二:非 MASTER_ERP 时,需要列表查询子表 88 ## 情况二:非 MASTER_ERP 时,需要列表查询子表
89 #else 89 #else
90 - #if ( $subTable.subJoinMany ) 90 + #if ($subTable.subJoinMany)
91 // 获得${subTable.classComment}列表 91 // 获得${subTable.classComment}列表
92 export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}) { 92 export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}) {
93 return request({ 93 return request({
@@ -106,7 +106,7 @@ export function export${simpleClassName}Excel(params) { @@ -106,7 +106,7 @@ export function export${simpleClassName}Excel(params) {
106 #end 106 #end
107 #end 107 #end
108 ## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作 108 ## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作
109 - #if ( $table.templateType == 11 ) 109 + #if ($table.templateType == 11)
110 // 新增${subTable.classComment} 110 // 新增${subTable.classComment}
111 export function create${subSimpleClassName}(data) { 111 export function create${subSimpleClassName}(data) {
112 return request({ 112 return request({
@@ -115,7 +115,6 @@ export function export${simpleClassName}Excel(params) { @@ -115,7 +115,6 @@ export function export${simpleClassName}Excel(params) {
115 data 115 data
116 }) 116 })
117 } 117 }
118 -  
119 // 修改${subTable.classComment} 118 // 修改${subTable.classComment}
120 export function update${subSimpleClassName}(data) { 119 export function update${subSimpleClassName}(data) {
121 return request({ 120 return request({
@@ -124,7 +123,6 @@ export function export${simpleClassName}Excel(params) { @@ -124,7 +123,6 @@ export function export${simpleClassName}Excel(params) {
124 data 123 data
125 }) 124 })
126 } 125 }
127 -  
128 // 删除${subTable.classComment} 126 // 删除${subTable.classComment}
129 export function delete${subSimpleClassName}(id) { 127 export function delete${subSimpleClassName}(id) {
130 return request({ 128 return request({
@@ -132,7 +130,6 @@ export function export${simpleClassName}Excel(params) { @@ -132,7 +130,6 @@ export function export${simpleClassName}Excel(params) {
132 method: 'delete' 130 method: 'delete'
133 }) 131 })
134 } 132 }
135 -  
136 // 获得${subTable.classComment} 133 // 获得${subTable.classComment}
137 export function get${subSimpleClassName}(id) { 134 export function get${subSimpleClassName}(id) {
138 return request({ 135 return request({
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
@@ -7,11 +7,10 @@ import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivity @@ -7,11 +7,10 @@ import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivity
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
8 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; 8 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
9 import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService; 9 import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
  10 +import jakarta.annotation.Resource;
10 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
11 import org.springframework.validation.annotation.Validated; 12 import org.springframework.validation.annotation.Validated;
12 13
13 -import jakarta.annotation.Resource;  
14 -  
15 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 14 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_RECORD_NOT_EXISTS; 15 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_RECORD_NOT_EXISTS;
17 16
@@ -25,21 +24,21 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINA @@ -25,21 +24,21 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINA
25 public class CombinationRecordApiImpl implements CombinationRecordApi { 24 public class CombinationRecordApiImpl implements CombinationRecordApi {
26 25
27 @Resource 26 @Resource
28 - private CombinationRecordService recordService; 27 + private CombinationRecordService combinationRecordService;
29 28
30 @Override 29 @Override
31 public void validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count) { 30 public void validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count) {
32 - recordService.validateCombinationRecord(userId, activityId, headId, skuId, count); 31 + combinationRecordService.validateCombinationRecord(userId, activityId, headId, skuId, count);
33 } 32 }
34 33
35 @Override 34 @Override
36 public CombinationRecordCreateRespDTO createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) { 35 public CombinationRecordCreateRespDTO createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
37 - return CombinationActivityConvert.INSTANCE.convert4(recordService.createCombinationRecord(reqDTO)); 36 + return CombinationActivityConvert.INSTANCE.convert4(combinationRecordService.createCombinationRecord(reqDTO));
38 } 37 }
39 38
40 @Override 39 @Override
41 public boolean isCombinationRecordSuccess(Long userId, Long orderId) { 40 public boolean isCombinationRecordSuccess(Long userId, Long orderId) {
42 - CombinationRecordDO record = recordService.getCombinationRecord(userId, orderId); 41 + CombinationRecordDO record = combinationRecordService.getCombinationRecord(userId, orderId);
43 if (record == null) { 42 if (record == null) {
44 throw exception(COMBINATION_RECORD_NOT_EXISTS); 43 throw exception(COMBINATION_RECORD_NOT_EXISTS);
45 } 44 }
@@ -48,7 +47,7 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { @@ -48,7 +47,7 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
48 47
49 @Override 48 @Override
50 public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) { 49 public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) {
51 - return recordService.validateJoinCombination(userId, activityId, headId, skuId, count); 50 + return combinationRecordService.validateJoinCombination(userId, activityId, headId, skuId, count);
52 } 51 }
53 52
54 } 53 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
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.collection.MapUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  7 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
7 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO; 8 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO;
8 import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordRespVO; 9 import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordRespVO;
9 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; 10 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO;
@@ -47,10 +48,9 @@ public interface MemberSignInRecordConvert { @@ -47,10 +48,9 @@ public interface MemberSignInRecordConvert {
47 // 1. 计算是第几天签到 48 // 1. 计算是第几天签到
48 configs.sort(Comparator.comparing(MemberSignInConfigDO::getDay)); 49 configs.sort(Comparator.comparing(MemberSignInConfigDO::getDay));
49 MemberSignInConfigDO lastConfig = CollUtil.getLast(configs); // 最大签到天数配置 50 MemberSignInConfigDO lastConfig = CollUtil.getLast(configs); // 最大签到天数配置
50 - // 1.2. 计算今天是第几天签到 51 + // 1.2. 计算今天是第几天签到 (只有连续签到才加否则重置为 1)
51 int day = 1; 52 int day = 1;
52 - // TODO @puhui999:要判断是不是昨天签到的;是否是昨天的判断,可以抽个方法到 util 里  
53 - if (lastRecord != null) { 53 + if (lastRecord != null && DateUtils.isYesterday(lastRecord.getCreateTime())) {
54 day = lastRecord.getDay() + 1; 54 day = lastRecord.getDay() + 1;
55 } 55 }
56 // 1.3 判断是否超出了最大签到配置 56 // 1.3 判断是否超出了最大签到配置
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java
@@ -19,14 +19,12 @@ import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; @@ -19,14 +19,12 @@ import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
19 import cn.iocoder.yudao.module.member.service.level.MemberLevelService; 19 import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
20 import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; 20 import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService;
21 import cn.iocoder.yudao.module.member.service.user.MemberUserService; 21 import cn.iocoder.yudao.module.member.service.user.MemberUserService;
  22 +import jakarta.annotation.Resource;
22 import org.apache.commons.lang3.StringUtils; 23 import org.apache.commons.lang3.StringUtils;
23 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
24 import org.springframework.transaction.annotation.Transactional; 25 import org.springframework.transaction.annotation.Transactional;
25 import org.springframework.validation.annotation.Validated; 26 import org.springframework.validation.annotation.Validated;
26 27
27 -import jakarta.annotation.Resource;  
28 -import java.time.LocalDate;  
29 -import java.util.Comparator;  
30 import java.util.List; 28 import java.util.List;
31 import java.util.Set; 29 import java.util.Set;
32 30
@@ -77,50 +75,15 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService @@ -77,50 +75,15 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService
77 } 75 }
78 summary.setTodaySignIn(DateUtils.isToday(lastRecord.getCreateTime())); 76 summary.setTodaySignIn(DateUtils.isToday(lastRecord.getCreateTime()));
79 77
80 - // 4. 校验今天是否签到,没有签到则直接返回 78 + // 4.1 校验今天是否签到,没有签到则直接返回
81 if (!summary.getTodaySignIn()) { 79 if (!summary.getTodaySignIn()) {
82 return summary; 80 return summary;
83 } 81 }
84 - // 4.1. 判断连续签到天数  
85 - // TODO @puhui999:连续签到,可以基于 lastRecord 的 day 和当前时间判断呀?按 day 统计连续签到天数可能不准确  
86 - // 1. day 只是记录第几天签到的有可能不连续,比如第一次签到是周一,第二次签到是周三这样 lastRecord 的 day 为 2 但是并不是连续的两天  
87 - // 2. day 超出签到规则的最大天数会重置到从第一天开始签到(我理解为开始下一轮,类似一周签到七天七天结束下周又从周一开始签到)  
88 - // 1. 回复:周三签到,day 要归 1 呀。连续签到哈;  
89 - List<MemberSignInRecordDO> signInRecords = signInRecordMapper.selectListByUserId(userId);  
90 - signInRecords.sort(Comparator.comparing(MemberSignInRecordDO::getCreateTime).reversed()); // 根据签到时间倒序  
91 - summary.setContinuousDay(calculateConsecutiveDays(signInRecords)); 82 + // 4.2 连续签到天数
  83 + summary.setContinuousDay(lastRecord.getDay());
92 return summary; 84 return summary;
93 } 85 }
94 86
95 - /**  
96 - * 计算连续签到天数  
97 - *  
98 - * @param signInRecords 签到记录列表  
99 - * @return int 连续签到天数  
100 - */  
101 - public int calculateConsecutiveDays(List<MemberSignInRecordDO> signInRecords) {  
102 - int consecutiveDays = 1; // 初始连续天数为1  
103 - LocalDate previousDate = null;  
104 -  
105 - for (MemberSignInRecordDO record : signInRecords) {  
106 - LocalDate currentDate = record.getCreateTime().toLocalDate();  
107 -  
108 - if (previousDate != null) {  
109 - // 检查相邻两个日期是否连续  
110 - if (currentDate.minusDays(1).isEqual(previousDate)) {  
111 - consecutiveDays++;  
112 - } else {  
113 - // 如果日期不连续,停止遍历  
114 - break;  
115 - }  
116 - }  
117 -  
118 - previousDate = currentDate;  
119 - }  
120 -  
121 - return consecutiveDays;  
122 - }  
123 -  
124 @Override 87 @Override
125 public PageResult<MemberSignInRecordDO> getSignInRecordPage(MemberSignInRecordPageReqVO pageReqVO) { 88 public PageResult<MemberSignInRecordDO> getSignInRecordPage(MemberSignInRecordPageReqVO pageReqVO) {
126 // 根据用户昵称查询出用户ids 89 // 根据用户昵称查询出用户ids
yudao-module-system/yudao-module-system-api/pom.xml
@@ -22,6 +22,23 @@ @@ -22,6 +22,23 @@
22 <artifactId>yudao-common</artifactId> 22 <artifactId>yudao-common</artifactId>
23 </dependency> 23 </dependency>
24 24
  25 + <!-- Springboot-注解-通用操作日志组件 -->
  26 + <!-- 此组件解决的问题是: 「谁」在「什么时间」对「什么」做了「什么事」 -->
  27 + <dependency>
  28 + <groupId>io.github.mouzt</groupId>
  29 + <artifactId>bizlog-sdk</artifactId>
  30 + </dependency>
  31 +
  32 + <!--工具类相关-->
  33 + <dependency>
  34 + <groupId>com.fasterxml.jackson.core</groupId>
  35 + <artifactId>jackson-databind</artifactId>
  36 + </dependency>
  37 + <dependency>
  38 + <groupId>com.fasterxml.jackson.core</groupId>
  39 + <artifactId>jackson-core</artifactId>
  40 + </dependency>
  41 +
25 <!-- 参数校验 --> 42 <!-- 参数校验 -->
26 <dependency> 43 <dependency>
27 <groupId>org.springframework.boot</groupId> 44 <groupId>org.springframework.boot</groupId>
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.module.system.api.logger.dto.OperateLogCreateReqDTO; 3 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogCreateReqDTO;
4 - 4 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
5 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
6 6
  7 +import java.util.List;
  8 +
7 /** 9 /**
8 * 操作日志 API 接口 10 * 操作日志 API 接口
9 * 11 *
@@ -18,4 +20,13 @@ public interface OperateLogApi { @@ -18,4 +20,13 @@ public interface OperateLogApi {
18 */ 20 */
19 void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO); 21 void createOperateLog(@Valid OperateLogCreateReqDTO createReqDTO);
20 22
  23 + /**
  24 + * 获取指定模块的指定数据的操作日志
  25 + *
  26 + * @param module 操作模块
  27 + * @param bizId 操作模块编号
  28 + * @return 操作日志
  29 + */
  30 + List<OperateLogV2RespDTO> getOperateLogByModuleAndBizId(String module, Long bizId);
  31 +
21 } 32 }
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogV2RespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.logger.dto;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import com.fasterxml.jackson.annotation.JsonFormat;
  5 +import lombok.Data;
  6 +
  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 +/**
  13 + * 系统操作日志 Resp DTO
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Data
  18 +public class OperateLogV2RespDTO {
  19 +
  20 + /**
  21 + * 链路追踪编号
  22 + *
  23 + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
  24 + */
  25 + private String traceId;
  26 + /**
  27 + * 用户编号
  28 + *
  29 + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
  30 + */
  31 + private Long userId;
  32 + /**
  33 + * 用户类型
  34 + *
  35 + * 关联 {@link UserTypeEnum}
  36 + */
  37 + private Integer userType;
  38 + /**
  39 + * 操作模块
  40 + */
  41 + private String module;
  42 + /**
  43 + * 操作名
  44 + */
  45 + private String name;
  46 + /**
  47 + * 操作模块业务编号
  48 + */
  49 + private Long bizId;
  50 + /**
  51 + * 操作内容,记录整个操作的明细
  52 + * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  53 + */
  54 + private String content;
  55 + /**
  56 + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
  57 + * 例如说,记录订单编号,{ orderId: "1"}
  58 + */
  59 + private String extra;
  60 +
  61 + /**
  62 + * 请求方法名
  63 + */
  64 + private String requestMethod;
  65 + /**
  66 + * 请求地址
  67 + */
  68 + private String requestUrl;
  69 + /**
  70 + * 用户 IP
  71 + */
  72 + private String userIp;
  73 + /**
  74 + * 浏览器 UA
  75 + */
  76 + private String userAgent;
  77 +
  78 + /**
  79 + * 创建时间
  80 + */
  81 + // TODO puhui999: 木得效果怎么肥事
  82 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  83 + private LocalDateTime createTime;
  84 + /**
  85 + * 创建者,关联 AdminUserDO#getId
  86 + */
  87 + private String creator;
  88 + /**
  89 + * 创建者名称
  90 + */
  91 + private String creatorName;
  92 +
  93 +}
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;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
3 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.OperateLogV2RespDTO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
4 import cn.iocoder.yudao.module.system.service.logger.OperateLogService; 9 import cn.iocoder.yudao.module.system.service.logger.OperateLogService;
  10 +import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  11 +import jakarta.annotation.Resource;
5 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
6 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
7 14
8 -import jakarta.annotation.Resource; 15 +import java.util.Collections;
  16 +import java.util.List;
  17 +import java.util.Map;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  20 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
9 21
10 /** 22 /**
11 * 操作日志 API 实现类 23 * 操作日志 API 实现类
@@ -18,10 +30,31 @@ public class OperateLogApiImpl implements OperateLogApi { @@ -18,10 +30,31 @@ public class OperateLogApiImpl implements OperateLogApi {
18 30
19 @Resource 31 @Resource
20 private OperateLogService operateLogService; 32 private OperateLogService operateLogService;
  33 + @Resource
  34 + private AdminUserService adminUserService;
21 35
22 @Override 36 @Override
23 public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { 37 public void createOperateLog(OperateLogCreateReqDTO createReqDTO) {
24 operateLogService.createOperateLog(createReqDTO); 38 operateLogService.createOperateLog(createReqDTO);
25 } 39 }
26 40
  41 + @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();
  46 + }
  47 +
  48 + // 获取用户
  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 + });
  58 + }
  59 +
27 } 60 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogV2DO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.dataobject.logger;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import com.baomidou.mybatisplus.annotation.KeySequence;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +
  11 +/**
  12 + * 操作日志表 V2
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@TableName(value = "system_operate_log_v2", autoResultMap = true)
  17 +@KeySequence("system_operate_log_seq_v2") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +public class OperateLogV2DO extends BaseDO {
  21 +
  22 + /**
  23 + * 日志主键
  24 + */
  25 + @TableId
  26 + private Long id;
  27 + /**
  28 + * 链路追踪编号
  29 + *
  30 + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
  31 + */
  32 + private String traceId;
  33 + /**
  34 + * 用户编号
  35 + *
  36 + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
  37 + */
  38 + private Long userId;
  39 + /**
  40 + * 用户类型
  41 + *
  42 + * 关联 {@link UserTypeEnum}
  43 + */
  44 + private Integer userType;
  45 + /**
  46 + * 操作模块
  47 + */
  48 + private String module;
  49 + /**
  50 + * 操作名
  51 + */
  52 + private String name;
  53 + /**
  54 + * 操作模块业务编号
  55 + */
  56 + private Long bizId;
  57 + /**
  58 + * 操作内容,记录整个操作的明细
  59 + * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  60 + */
  61 + private String content;
  62 + /**
  63 + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
  64 + * 例如说,记录订单编号,{ orderId: "1"}
  65 + */
  66 + private String extra;
  67 + /**
  68 + * 请求方法名
  69 + */
  70 + private String requestMethod;
  71 + /**
  72 + * 请求地址
  73 + */
  74 + private String requestUrl;
  75 + /**
  76 + * 用户 IP
  77 + */
  78 + private String userIp;
  79 + /**
  80 + * 浏览器 UA
  81 + */
  82 + private String userAgent;
  83 +
  84 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogV2Mapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.mysql.logger;
  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.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
  13 +@Mapper
  14 +public interface OperateLogV2Mapper extends BaseMapperX<OperateLogV2DO> {
  15 +
  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));
  29 + }
  30 +
  31 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/config/YudaoOperateLogV2Configuration.java 0 → 100644
  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 +/**
  7 + * mzt-biz-log 配置类
  8 + *
  9 + * @author HUIHUI
  10 + */
  11 +@Configuration(proxyBeanMethods = false)
  12 +@EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦
  13 +public class YudaoOperateLogV2Configuration {
  14 +
  15 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AdminUserParseFunction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.framework.bizlog.function;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  6 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  7 +import com.mzt.logapi.service.IParseFunction;
  8 +import jakarta.annotation.Resource;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +/**
  13 + * 自定义函数-通过用户编号获取用户信息
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Slf4j
  18 +@Component
  19 +public class AdminUserParseFunction implements IParseFunction {
  20 +
  21 + @Resource
  22 + private AdminUserApi adminUserApi;
  23 +
  24 + @Override
  25 + public String functionName() {
  26 + return "getAdminUserById";
  27 + }
  28 +
  29 + @Override
  30 + public String apply(Object value) {
  31 + if (value == null) {
  32 + //log.warn("(getAdminUserById) 解析异常参数为 null");
  33 + return "";
  34 + }
  35 + if (StrUtil.isEmpty(value.toString())) {
  36 + //log.warn("(getAdminUserById) 解析异常参数为空");
  37 + return "";
  38 + }
  39 +
  40 + // 获取用户信息
  41 + AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(value.toString()));
  42 + if (user == null) {
  43 + log.warn("(getAdminUserById) 获取用户信息失败,参数为:{}", value);
  44 + return "";
  45 + }
  46 + // 返回格式 芋道源码(13888888888)
  47 + String nickname = user.getNickname();
  48 + if (ObjUtil.isNotEmpty(user.getMobile())) {
  49 + return nickname.concat("(").concat(user.getMobile()).concat(")");
  50 + }
  51 + return nickname;
  52 + }
  53 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/function/AreaParseFunction.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.framework.bizlog.function;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
  5 +import com.mzt.logapi.service.IParseFunction;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * 自定义函数-通过区域编号获取区域信息
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@Slf4j
  15 +@Component
  16 +public class AreaParseFunction implements IParseFunction {
  17 +
  18 + @Override
  19 + public boolean executeBefore() {
  20 + return true; // 先转换值后对比
  21 + }
  22 +
  23 + @Override
  24 + public String functionName() {
  25 + return "getAreaById";
  26 + }
  27 +
  28 + @Override
  29 + public String apply(Object value) {
  30 + if (value == null) {
  31 + return "";
  32 + }
  33 + if (StrUtil.isEmpty(value.toString())) {
  34 + return "";
  35 + }
  36 +
  37 + return AreaUtils.format(Integer.parseInt(value.toString()));
  38 + }
  39 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.framework.bizlog;
0 \ No newline at end of file 2 \ No newline at end of file
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/bizlog/service/ILogRecordServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.framework.bizlog.service;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
  4 +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
  5 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
  6 +import cn.iocoder.yudao.module.system.service.logger.OperateLogService;
  7 +import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;
  8 +import com.mzt.logapi.beans.LogRecord;
  9 +import com.mzt.logapi.service.ILogRecordService;
  10 +import jakarta.annotation.Resource;
  11 +import jakarta.servlet.http.HttpServletRequest;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +import org.springframework.stereotype.Service;
  14 +
  15 +import java.util.Collections;
  16 +import java.util.List;
  17 +
  18 +/**
  19 + * 操作日志 ILogRecordService 实现类
  20 + *
  21 + * 基于 {@link OperateLogService} 实现,记录操作日志
  22 + *
  23 + * @author HUIHUI
  24 + */
  25 +@Slf4j
  26 +@Service
  27 +public class ILogRecordServiceImpl implements ILogRecordService {
  28 +
  29 + @Resource
  30 + private OperateLogService operateLogService;
  31 +
  32 + @Override
  33 + public void record(LogRecord logRecord) {
  34 + OperateLogV2CreateReqBO reqBO = new OperateLogV2CreateReqBO();
  35 + // 补全通用字段
  36 + reqBO.setTraceId(TracerUtils.getTraceId());
  37 + // 补充用户信息
  38 + fillUserFields(reqBO);
  39 + // 补全模块信息
  40 + fillModuleFields(reqBO, logRecord);
  41 + // 补全请求信息
  42 + fillRequestFields(reqBO);
  43 + // 异步记录日志
  44 + operateLogService.createOperateLogV2(reqBO);
  45 + log.info("操作日志 ===> {}", reqBO);
  46 + }
  47 +
  48 + private static void fillUserFields(OperateLogV2CreateReqBO reqBO) {
  49 + reqBO.setUserId(WebFrameworkUtils.getLoginUserId());
  50 + reqBO.setUserType(WebFrameworkUtils.getLoginUserType());
  51 + }
  52 +
  53 + public static void fillModuleFields(OperateLogV2CreateReqBO reqBO, LogRecord logRecord) {
  54 + reqBO.setModule(logRecord.getType()); // 大模块类型如 crm-客户
  55 + reqBO.setName(logRecord.getSubType());// 操作名称如 转移客户
  56 + reqBO.setBizId(Long.parseLong(logRecord.getBizNo())); // 操作模块业务编号
  57 + reqBO.setContent(logRecord.getAction());// 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  58 + reqBO.setExtra(logRecord.getExtra()); // 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ),例如说,记录订单编号,{ orderId: "1"}
  59 + }
  60 +
  61 + private static void fillRequestFields(OperateLogV2CreateReqBO reqBO) {
  62 + // 获得 Request 对象
  63 + HttpServletRequest request = ServletUtils.getRequest();
  64 + if (request == null) {
  65 + return;
  66 + }
  67 + // 补全请求信息
  68 + reqBO.setRequestMethod(request.getMethod());
  69 + reqBO.setRequestUrl(request.getRequestURI());
  70 + reqBO.setUserIp(ServletUtils.getClientIP(request));
  71 + reqBO.setUserAgent(ServletUtils.getUserAgent(request));
  72 + }
  73 +
  74 + @Override
  75 + public List<LogRecord> queryLog(String bizNo, String type) {
  76 + return Collections.emptyList();
  77 + }
  78 +
  79 + @Override
  80 + public List<LogRecord> queryLogByBizNo(String bizNo, String type, String subType) {
  81 + return Collections.emptyList();
  82 + }
  83 +
  84 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java
@@ -4,6 +4,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,6 +4,10 @@ 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.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; 5 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
6 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; 6 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
  7 +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;
7 11
8 /** 12 /**
9 * 操作日志 Service 接口 13 * 操作日志 Service 接口
@@ -27,4 +31,22 @@ public interface OperateLogService { @@ -27,4 +31,22 @@ public interface OperateLogService {
27 */ 31 */
28 PageResult<OperateLogDO> getOperateLogPage(OperateLogPageReqVO pageReqVO); 32 PageResult<OperateLogDO> getOperateLogPage(OperateLogPageReqVO pageReqVO);
29 33
  34 + //======================= LOG V2 =======================
  35 +
  36 + /**
  37 + * 记录操作日志 V2
  38 + *
  39 + * @param createReqBO 创建请求
  40 + */
  41 + void createOperateLogV2(OperateLogV2CreateReqBO createReqBO);
  42 +
  43 + /**
  44 + * 获取指定模块的指定数据的操作日志
  45 + *
  46 + * @param module 操作模块
  47 + * @param bizId 操作模块编号
  48 + * @return 操作日志
  49 + */
  50 + List<OperateLogV2DO> getOperateLogByModuleAndBizId(String module, Long bizId);
  51 +
30 } 52 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java
@@ -8,15 +8,19 @@ import cn.iocoder.yudao.framework.common.util.string.StrUtils; @@ -8,15 +8,19 @@ 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.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; 9 import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
10 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; 10 import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO;
  11 +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogV2DO;
11 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; 12 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
12 import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; 13 import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper;
  14 +import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogV2Mapper;
  15 +import cn.iocoder.yudao.module.system.service.logger.bo.OperateLogV2CreateReqBO;
13 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 16 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  17 +import jakarta.annotation.Resource;
14 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
15 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
16 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
17 21
18 -import jakarta.annotation.Resource;  
19 import java.util.Collection; 22 import java.util.Collection;
  23 +import java.util.List;
20 24
21 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 25 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
22 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;
@@ -34,6 +38,8 @@ public class OperateLogServiceImpl implements OperateLogService { @@ -34,6 +38,8 @@ public class OperateLogServiceImpl implements OperateLogService {
34 38
35 @Resource 39 @Resource
36 private OperateLogMapper operateLogMapper; 40 private OperateLogMapper operateLogMapper;
  41 + @Resource
  42 + private OperateLogV2Mapper operateLogV2Mapper;
37 43
38 @Resource 44 @Resource
39 private AdminUserService userService; 45 private AdminUserService userService;
@@ -60,4 +66,17 @@ public class OperateLogServiceImpl implements OperateLogService { @@ -60,4 +66,17 @@ public class OperateLogServiceImpl implements OperateLogService {
60 return operateLogMapper.selectPage(pageReqVO, userIds); 66 return operateLogMapper.selectPage(pageReqVO, userIds);
61 } 67 }
62 68
  69 + //======================= LOG V2 =======================
  70 +
  71 + @Override
  72 + public void createOperateLogV2(OperateLogV2CreateReqBO createReqBO) {
  73 + OperateLogV2DO log = BeanUtils.toBean(createReqBO, OperateLogV2DO.class);
  74 + operateLogV2Mapper.insert(log);
  75 + }
  76 +
  77 + @Override
  78 + public List<OperateLogV2DO> getOperateLogByModuleAndBizId(String module, Long bizId) {
  79 + return operateLogV2Mapper.selectListByModuleAndBizId(module, bizId);
  80 + }
  81 +
63 } 82 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/bo/OperateLogV2CreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.logger.bo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import lombok.Data;
  6 +
  7 +/**
  8 + * 系统操作日志 Create Req BO
  9 + *
  10 + * @author HUIHUI
  11 + */
  12 +@Data
  13 +public class OperateLogV2CreateReqBO {
  14 +
  15 + /**
  16 + * 链路追踪编号
  17 + *
  18 + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
  19 + */
  20 + private String traceId;
  21 + /**
  22 + * 用户编号
  23 + *
  24 + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
  25 + */
  26 + @NotEmpty(message = "用户编号不能为空")
  27 + private Long userId;
  28 + /**
  29 + * 用户类型
  30 + *
  31 + * 关联 {@link UserTypeEnum}
  32 + */
  33 + @NotEmpty(message = "用户类型不能为空")
  34 + private Integer userType;
  35 + /**
  36 + * 操作模块
  37 + */
  38 + @NotEmpty(message = "操作模块不能为空")
  39 + private String module;
  40 + /**
  41 + * 操作名
  42 + */
  43 + @NotEmpty(message = "操作名不能为空")
  44 + private String name;
  45 + /**
  46 + * 操作模块业务编号
  47 + */
  48 + @NotEmpty(message = "操作模块业务编号不能为空")
  49 + private Long bizId;
  50 + /**
  51 + * 操作内容,记录整个操作的明细
  52 + * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。
  53 + */
  54 + @NotEmpty(message = "操作内容不能为空")
  55 + private String content;
  56 + /**
  57 + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 )
  58 + * 例如说,记录订单编号,{ orderId: "1"}
  59 + */
  60 + private String extra;
  61 +
  62 + /**
  63 + * 请求方法名
  64 + */
  65 + @NotEmpty(message = "请求方法名不能为空")
  66 + private String requestMethod;
  67 + /**
  68 + * 请求地址
  69 + */
  70 + @NotEmpty(message = "请求地址不能为空")
  71 + private String requestUrl;
  72 + /**
  73 + * 用户 IP
  74 + */
  75 + @NotEmpty(message = "用户 IP 不能为空")
  76 + private String userIp;
  77 + /**
  78 + * 浏览器 UA
  79 + */
  80 + @NotEmpty(message = "浏览器 UA 不能为空")
  81 + private String userAgent;
  82 +
  83 +}