Commit ff7e492887408c68b962075661b30757c061bf72

Authored by 芋道源码
Committed by Gitee
2 parents 206899bf 86a17722

!851 CRM: 完善线索

Merge pull request !851 from puhui999/develop
Showing 22 changed files with 553 additions and 181 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
@@ -14,9 +14,13 @@ public interface LogRecordConstants { @@ -14,9 +14,13 @@ public interface LogRecordConstants {
14 String CRM_LEADS_CREATE_SUB_TYPE = "创建线索"; 14 String CRM_LEADS_CREATE_SUB_TYPE = "创建线索";
15 String CRM_LEADS_CREATE_SUCCESS = "创建了线索{{#clue.name}}"; 15 String CRM_LEADS_CREATE_SUCCESS = "创建了线索{{#clue.name}}";
16 String CRM_LEADS_UPDATE_SUB_TYPE = "更新线索"; 16 String CRM_LEADS_UPDATE_SUB_TYPE = "更新线索";
17 - String CRM_LEADS_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReqVO}}"; 17 + String CRM_LEADS_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReq}}";
18 String CRM_LEADS_DELETE_SUB_TYPE = "删除线索"; 18 String CRM_LEADS_DELETE_SUB_TYPE = "删除线索";
19 String CRM_LEADS_DELETE_SUCCESS = "删除了线索【{{#clueName}}】"; 19 String CRM_LEADS_DELETE_SUCCESS = "删除了线索【{{#clueName}}】";
  20 + String CRM_LEADS_TRANSFER_SUB_TYPE = "转移线索";
  21 + String CRM_LEADS_TRANSFER_SUCCESS = "将线索【{{#clue.name}}】的负责人从【{getAdminUserById{#clue.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
  22 + String CRM_LEADS_TRANSLATE_SUB_TYPE = "线索转化为客户";
  23 + String CRM_LEADS_TRANSLATE_SUCCESS = "将线索【{{#clue.name}}】转化为客户";
20 24
21 // ======================= CRM_CUSTOMER 客户 ======================= 25 // ======================= CRM_CUSTOMER 客户 =======================
22 26
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
@@ -39,7 +39,7 @@ public class CrmClueController { @@ -39,7 +39,7 @@ public class CrmClueController {
39 @Operation(summary = "创建线索") 39 @Operation(summary = "创建线索")
40 @PreAuthorize("@ss.hasPermission('crm:clue:create')") 40 @PreAuthorize("@ss.hasPermission('crm:clue:create')")
41 public CommonResult<Long> createClue(@Valid @RequestBody CrmClueSaveReqVO createReqVO) { 41 public CommonResult<Long> createClue(@Valid @RequestBody CrmClueSaveReqVO createReqVO) {
42 - return success(clueService.createClue(createReqVO)); 42 + return success(clueService.createClue(createReqVO, getLoginUserId()));
43 } 43 }
44 44
45 @PutMapping("/update") 45 @PutMapping("/update")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2 2
3 import cn.iocoder.yudao.framework.common.validation.Mobile; 3 import cn.iocoder.yudao.framework.common.validation.Mobile;
4 import cn.iocoder.yudao.framework.common.validation.Telephone; 4 import cn.iocoder.yudao.framework.common.validation.Telephone;
  5 +import com.mzt.logapi.starter.annotation.DiffLogField;
5 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
6 import jakarta.validation.constraints.NotEmpty; 7 import jakarta.validation.constraints.NotEmpty;
7 import lombok.Data; 8 import lombok.Data;
@@ -19,32 +20,39 @@ public class CrmClueSaveReqVO { @@ -19,32 +20,39 @@ public class CrmClueSaveReqVO {
19 private Long id; 20 private Long id;
20 21
21 @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx") 22 @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
  23 + @DiffLogField(name = "线索名称")
22 @NotEmpty(message = "线索名称不能为空") 24 @NotEmpty(message = "线索名称不能为空")
23 private String name; 25 private String name;
24 26
25 @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00") 27 @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
  28 + @DiffLogField(name = "下次联系时间")
26 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 29 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
27 private LocalDateTime contactNextTime; 30 private LocalDateTime contactNextTime;
28 31
29 @Schema(description = "电话", example = "18000000000") 32 @Schema(description = "电话", example = "18000000000")
  33 + @DiffLogField(name = "电话")
30 @Telephone 34 @Telephone
31 private String telephone; 35 private String telephone;
32 36
33 @Schema(description = "手机号", example = "18000000000") 37 @Schema(description = "手机号", example = "18000000000")
  38 + @DiffLogField(name = "手机号")
34 @Mobile 39 @Mobile
35 private String mobile; 40 private String mobile;
36 41
37 @Schema(description = "地址", example = "北京市海淀区") 42 @Schema(description = "地址", example = "北京市海淀区")
  43 + @DiffLogField(name = "地址")
38 private String address; 44 private String address;
39 45
40 @Schema(description = "最后跟进时间") 46 @Schema(description = "最后跟进时间")
41 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 47 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  48 + @DiffLogField(name = "最后跟进时间")
42 private LocalDateTime contactLastTime; 49 private LocalDateTime contactLastTime;
43 50
44 @Schema(description = "负责人编号", example = "2048") 51 @Schema(description = "负责人编号", example = "2048")
45 private Long ownerUserId; 52 private Long ownerUserId;
46 53
47 @Schema(description = "备注", example = "随便") 54 @Schema(description = "备注", example = "随便")
  55 + @DiffLogField(name = "备注")
48 private String remark; 56 private String remark;
49 57
50 } 58 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
@@ -17,9 +17,6 @@ import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants; @@ -17,9 +17,6 @@ import cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants;
17 import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService; 17 import cn.iocoder.yudao.module.crm.service.contact.CrmContactBusinessService;
18 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; 18 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
20 -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;  
21 -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;  
22 -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;  
23 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 20 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
24 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 21 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
25 import com.google.common.collect.Lists; 22 import com.google.common.collect.Lists;
@@ -46,7 +43,6 @@ import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; @@ -46,7 +43,6 @@ import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
46 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 43 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
47 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 44 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
48 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 45 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
49 -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CONTACT_TYPE;  
50 46
51 @Tag(name = "管理后台 - CRM 联系人") 47 @Tag(name = "管理后台 - CRM 联系人")
52 @RestController 48 @RestController
@@ -64,8 +60,6 @@ public class CrmContactController { @@ -64,8 +60,6 @@ public class CrmContactController {
64 60
65 @Resource 61 @Resource
66 private AdminUserApi adminUserApi; 62 private AdminUserApi adminUserApi;
67 - @Resource  
68 - private OperateLogApi operateLogApi;  
69 63
70 @PostMapping("/create") 64 @PostMapping("/create")
71 @Operation(summary = "创建联系人") 65 @Operation(summary = "创建联系人")
@@ -158,17 +152,6 @@ public class CrmContactController { @@ -158,17 +152,6 @@ public class CrmContactController {
158 buildContactDetailPage(pageResult).getList()); 152 buildContactDetailPage(pageResult).getList());
159 } 153 }
160 154
161 - @GetMapping("/operate-log-page")  
162 - @Operation(summary = "获得客户操作日志")  
163 - @PreAuthorize("@ss.hasPermission('crm:customer:query')")  
164 - public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(@RequestParam("bizId") Long bizId) {  
165 - OperateLogV2PageReqDTO reqVO = new OperateLogV2PageReqDTO();  
166 - reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页  
167 - reqVO.setBizType(CRM_CONTACT_TYPE);  
168 - reqVO.setBizId(bizId);  
169 - return success(operateLogApi.getOperateLogPage(BeanUtils.toBean(reqVO, OperateLogV2PageReqDTO.class)));  
170 - }  
171 -  
172 /** 155 /**
173 * 构建详细的联系人分页结果 156 * 构建详细的联系人分页结果
174 * 157 *
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -17,9 +17,6 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService @@ -17,9 +17,6 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerPoolConfigService
17 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 17 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
18 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 18 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
19 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 19 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
20 -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;  
21 -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;  
22 -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;  
23 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 20 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
24 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 21 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
25 import io.swagger.v3.oas.annotations.Operation; 22 import io.swagger.v3.oas.annotations.Operation;
@@ -44,7 +41,6 @@ import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; @@ -44,7 +41,6 @@ import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
44 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
45 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 42 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
46 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 43 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
47 -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_CUSTOMER_TYPE;  
48 44
49 @Tag(name = "管理后台 - CRM 客户") 45 @Tag(name = "管理后台 - CRM 客户")
50 @RestController 46 @RestController
@@ -60,8 +56,7 @@ public class CrmCustomerController { @@ -60,8 +56,7 @@ public class CrmCustomerController {
60 private DeptApi deptApi; 56 private DeptApi deptApi;
61 @Resource 57 @Resource
62 private AdminUserApi adminUserApi; 58 private AdminUserApi adminUserApi;
63 - @Resource  
64 - private OperateLogApi operateLogApi; 59 +
65 60
66 @PostMapping("/create") 61 @PostMapping("/create")
67 @Operation(summary = "创建客户") 62 @Operation(summary = "创建客户")
@@ -182,19 +177,6 @@ public class CrmCustomerController { @@ -182,19 +177,6 @@ public class CrmCustomerController {
182 return success(true); 177 return success(true);
183 } 178 }
184 179
185 - // TODO @puhui999:要不搞个通用的 CrmOperateLogController,之后所有业务都调用它?  
186 - @GetMapping("/operate-log-page")  
187 - @Operation(summary = "获得客户操作日志")  
188 - @Parameter(name = "id", description = "客户编号", required = true)  
189 - @PreAuthorize("@ss.hasPermission('crm:customer:query')")  
190 - public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(@RequestParam("id") Long id) {  
191 - OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO();  
192 - reqDTO.setPageSize(PAGE_SIZE_NONE); // 不分页  
193 - reqDTO.setBizType(CRM_CUSTOMER_TYPE);  
194 - reqDTO.setBizId(id);  
195 - return success(operateLogApi.getOperateLogPage(reqDTO));  
196 - }  
197 -  
198 @PutMapping("/lock") 180 @PutMapping("/lock")
199 @Operation(summary = "锁定/解锁客户") 181 @Operation(summary = "锁定/解锁客户")
200 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 182 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.operatelog;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO;
  6 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  7 +import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
  8 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
  9 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
  10 +import io.swagger.v3.oas.annotations.Operation;
  11 +import io.swagger.v3.oas.annotations.Parameter;
  12 +import io.swagger.v3.oas.annotations.tags.Tag;
  13 +import jakarta.annotation.Resource;
  14 +import jakarta.validation.Valid;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.validation.annotation.Validated;
  17 +import org.springframework.web.bind.annotation.GetMapping;
  18 +import org.springframework.web.bind.annotation.RequestMapping;
  19 +import org.springframework.web.bind.annotation.RestController;
  20 +
  21 +import java.util.HashMap;
  22 +import java.util.Map;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
  26 +import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
  27 +
  28 +@Tag(name = "管理后台 - 操作日志")
  29 +@RestController
  30 +@RequestMapping("/crm/operate-log")
  31 +@Validated
  32 +public class CrmOperateLogController {
  33 +
  34 + @Resource
  35 + private OperateLogApi operateLogApi;
  36 +
  37 + private static final Map<Integer, String> BIZ_TYPE_MAP = new HashMap<>();
  38 +
  39 + static {
  40 + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_LEADS.getType(), CRM_LEADS_TYPE);
  41 + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CUSTOMER.getType(), CRM_CUSTOMER_TYPE);
  42 + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTACT.getType(), CRM_CONTACT_TYPE);
  43 + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_BUSINESS.getType(), CRM_BUSINESS_TYPE);
  44 + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTRACT.getType(), CRM_CONTRACT_TYPE);
  45 + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_PRODUCT.getType(), CRM_PRODUCT_TYPE);
  46 + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), CRM_RECEIVABLE_TYPE);
  47 + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), CRM_RECEIVABLE_PLAN_TYPE);
  48 + }
  49 +
  50 + @GetMapping("/page")
  51 + @Operation(summary = "获得操作日志")
  52 + @Parameter(name = "id", description = "客户编号", required = true)
  53 + @PreAuthorize("@ss.hasPermission('crm:customer:query')")
  54 + public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) {
  55 + OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO();
  56 + reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释
  57 + reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType()));
  58 + reqDTO.setBizId(pageReqVO.getBizId());
  59 + return success(operateLogApi.getOperateLogPage(reqDTO));
  60 + }
  61 +
  62 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +
  12 +@Schema(description = "管理后台 - 跟进记录分页 Request VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class CrmOperateLogPageReqVO extends PageParam {
  17 +
  18 + @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  19 + @InEnum(CrmBizTypeEnum.class)
  20 + @NotNull(message = "数据类型不能为空")
  21 + private Integer bizType;
  22 +
  23 + @Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  24 + @NotNull(message = "数据编号不能为空")
  25 + private Long bizId;
  26 +
  27 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.permission; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.permission;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
@@ -11,6 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; @@ -11,6 +12,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
11 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 12 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
12 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; 13 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
13 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 14 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
  15 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
14 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 16 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
15 import cn.iocoder.yudao.module.system.api.dept.PostApi; 17 import cn.iocoder.yudao.module.system.api.dept.PostApi;
16 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 18 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@@ -55,7 +57,7 @@ public class CrmPermissionController { @@ -55,7 +57,7 @@ public class CrmPermissionController {
55 @PreAuthorize("@ss.hasPermission('crm:permission:create')") 57 @PreAuthorize("@ss.hasPermission('crm:permission:create')")
56 @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER) 58 @CrmPermission(bizTypeValue = "#reqVO.bizType", bizId = "#reqVO.bizId", level = CrmPermissionLevelEnum.OWNER)
57 public CommonResult<Boolean> addPermission(@Valid @RequestBody CrmPermissionCreateReqVO reqVO) { 59 public CommonResult<Boolean> addPermission(@Valid @RequestBody CrmPermissionCreateReqVO reqVO) {
58 - permissionService.createPermission(CrmPermissionConvert.INSTANCE.convert(reqVO)); 60 + permissionService.createPermission(BeanUtils.toBean(reqVO, CrmPermissionCreateReqBO.class));
59 return success(true); 61 return success(true);
60 } 62 }
61 63
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java
@@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageParam; 5 import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.collection.SetUtils; 7 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
8 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 8 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 9 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
11 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; 10 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
@@ -16,9 +15,6 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; @@ -16,9 +15,6 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
16 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; 15 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
17 import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService; 16 import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService;
18 import cn.iocoder.yudao.module.crm.service.product.CrmProductService; 17 import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
19 -import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;  
20 -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;  
21 -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;  
22 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 18 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
23 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 19 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
24 import io.swagger.v3.oas.annotations.Operation; 20 import io.swagger.v3.oas.annotations.Operation;
@@ -38,12 +34,10 @@ import java.util.Map; @@ -38,12 +34,10 @@ import java.util.Map;
38 import java.util.stream.Stream; 34 import java.util.stream.Stream;
39 35
40 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 36 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
41 -import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;  
42 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 37 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
43 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; 38 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
44 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 39 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
45 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 40 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
46 -import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.CRM_PRODUCT_TYPE;  
47 41
48 @Tag(name = "管理后台 - CRM 产品") 42 @Tag(name = "管理后台 - CRM 产品")
49 @RestController 43 @RestController
@@ -56,8 +50,6 @@ public class CrmProductController { @@ -56,8 +50,6 @@ public class CrmProductController {
56 @Resource 50 @Resource
57 private CrmProductCategoryService productCategoryService; 51 private CrmProductCategoryService productCategoryService;
58 @Resource 52 @Resource
59 - private OperateLogApi operateLogApi;  
60 - @Resource  
61 private AdminUserApi adminUserApi; 53 private AdminUserApi adminUserApi;
62 54
63 @PostMapping("/create") 55 @PostMapping("/create")
@@ -131,14 +123,4 @@ public class CrmProductController { @@ -131,14 +123,4 @@ public class CrmProductController {
131 return CrmProductConvert.INSTANCE.convertList(list, userMap, productCategoryList); 123 return CrmProductConvert.INSTANCE.convertList(list, userMap, productCategoryList);
132 } 124 }
133 125
134 - @GetMapping("/operate-log-page")  
135 - @Operation(summary = "获得产品操作日志")  
136 - @PreAuthorize("@ss.hasPermission('crm:product:query')")  
137 - public CommonResult<PageResult<OperateLogV2RespDTO>> getProductOperateLog(@RequestParam("bizId") Long bizId) {  
138 - OperateLogV2PageReqDTO reqVO = new OperateLogV2PageReqDTO();  
139 - reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页  
140 - reqVO.setBizType(CRM_PRODUCT_TYPE).setBizId(bizId);  
141 - return success(operateLogApi.getOperateLogPage(BeanUtils.toBean(reqVO, OperateLogV2PageReqDTO.class)));  
142 - }  
143 -  
144 } 126 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java
@@ -3,12 +3,9 @@ package cn.iocoder.yudao.module.crm.convert.permission; @@ -3,12 +3,9 @@ package cn.iocoder.yudao.module.crm.convert.permission;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
6 -import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionCreateReqVO;  
7 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
9 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
10 -import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;  
11 -import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionUpdateReqBO;  
12 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 9 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
13 import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; 10 import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO;
14 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 11 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -32,14 +29,6 @@ public interface CrmPermissionConvert { @@ -32,14 +29,6 @@ public interface CrmPermissionConvert {
32 29
33 CrmPermissionConvert INSTANCE = Mappers.getMapper(CrmPermissionConvert.class); 30 CrmPermissionConvert INSTANCE = Mappers.getMapper(CrmPermissionConvert.class);
34 31
35 - CrmPermissionDO convert(CrmPermissionCreateReqBO createBO);  
36 -  
37 - CrmPermissionDO convert(CrmPermissionUpdateReqBO updateBO);  
38 -  
39 - CrmPermissionCreateReqBO convert(CrmPermissionCreateReqVO reqVO);  
40 -  
41 - CrmPermissionUpdateReqBO convert(CrmPermissionUpdateReqVO updateReqVO);  
42 -  
43 List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permission); 32 List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permission);
44 33
45 default List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permission, List<AdminUserRespDTO> userList, 34 default List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permission, List<AdminUserRespDTO> userList,
@@ -65,7 +54,4 @@ public interface CrmPermissionConvert { @@ -65,7 +54,4 @@ public interface CrmPermissionConvert {
65 id -> new CrmPermissionDO().setId(id).setLevel(updateReqVO.getLevel())); 54 id -> new CrmPermissionDO().setId(id).setLevel(updateReqVO.getLevel()));
66 } 55 }
67 56
68 - // TODO @puhui999:搞成 BeanUtils  
69 - List<CrmPermissionDO> convertList(List<CrmPermissionCreateReqBO> list);  
70 -  
71 } 57 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java
@@ -7,6 +7,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecor @@ -7,6 +7,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecor
7 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 9
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
10 /** 13 /**
11 * 跟进记录 Mapper 14 * 跟进记录 Mapper
12 * 15 *
@@ -22,4 +25,14 @@ public interface CrmFollowUpRecordMapper extends BaseMapperX&lt;CrmFollowUpRecordDO @@ -22,4 +25,14 @@ public interface CrmFollowUpRecordMapper extends BaseMapperX&lt;CrmFollowUpRecordDO
22 .orderByDesc(CrmFollowUpRecordDO::getId)); 25 .orderByDesc(CrmFollowUpRecordDO::getId));
23 } 26 }
24 27
  28 + default void deleteByBiz(Integer bizType, Long bizId) {
  29 + delete(new LambdaQueryWrapperX<CrmFollowUpRecordDO>().eq(CrmFollowUpRecordDO::getBizType, bizType)
  30 + .eq(CrmFollowUpRecordDO::getBizId, bizId));
  31 + }
  32 +
  33 + default List<CrmFollowUpRecordDO> selectListByBiz(Integer bizType, Collection<Long> bizIds) {
  34 + return selectList(new LambdaQueryWrapperX<CrmFollowUpRecordDO>().eq(CrmFollowUpRecordDO::getBizType, bizType)
  35 + .in(CrmFollowUpRecordDO::getBizId, bizIds));
  36 + }
  37 +
25 } 38 }
26 \ No newline at end of file 39 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
@@ -23,9 +23,10 @@ public interface CrmClueService { @@ -23,9 +23,10 @@ public interface CrmClueService {
23 * 创建线索 23 * 创建线索
24 * 24 *
25 * @param createReqVO 创建信息 25 * @param createReqVO 创建信息
  26 + * @param userId 用户编号
26 * @return 编号 27 * @return 编号
27 */ 28 */
28 - Long createClue(@Valid CrmClueSaveReqVO createReqVO); 29 + Long createClue(@Valid CrmClueSaveReqVO createReqVO, Long userId);
29 30
30 /** 31 /**
31 * 更新线索 32 * 更新线索
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
@@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.crm.service.clue; @@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.crm.service.clue;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.ListUtil; 4 import cn.hutool.core.collection.ListUtil;
5 import cn.hutool.core.lang.Assert; 5 import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.util.ObjUtil;
6 import cn.hutool.core.util.ObjectUtil; 7 import cn.hutool.core.util.ObjectUtil;
7 -import cn.hutool.core.util.StrUtil; 8 +import cn.hutool.extra.spring.SpringUtil;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; 11 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
12 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; 12 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;
@@ -15,11 +15,16 @@ import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqV @@ -15,11 +15,16 @@ import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqV
15 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; 15 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
16 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert; 16 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
17 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
  18 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  19 +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
18 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; 20 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
19 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 21 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
20 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 22 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
21 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; 23 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
22 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 24 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
  25 +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
  26 +import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
  27 +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
23 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; 28 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
24 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 29 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
25 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 30 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
@@ -32,17 +37,14 @@ import org.springframework.stereotype.Service; @@ -32,17 +37,14 @@ import org.springframework.stereotype.Service;
32 import org.springframework.transaction.annotation.Transactional; 37 import org.springframework.transaction.annotation.Transactional;
33 import org.springframework.validation.annotation.Validated; 38 import org.springframework.validation.annotation.Validated;
34 39
35 -import java.util.Collection;  
36 -import java.util.List;  
37 -import java.util.Objects;  
38 -import java.util.Set; 40 +import java.util.*;
39 41
40 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 42 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
41 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;  
42 -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 43 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
43 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 44 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
44 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 45 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
45 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; 46 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
  47 +import static java.util.Collections.singletonList;
46 48
47 /** 49 /**
48 * 线索 Service 实现类 50 * 线索 Service 实现类
@@ -61,7 +63,8 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -61,7 +63,8 @@ public class CrmClueServiceImpl implements CrmClueService {
61 63
62 @Resource 64 @Resource
63 private CrmPermissionService crmPermissionService; 65 private CrmPermissionService crmPermissionService;
64 - 66 + @Resource
  67 + private CrmFollowUpRecordService followUpRecordService;
65 @Resource 68 @Resource
66 private AdminUserApi adminUserApi; 69 private AdminUserApi adminUserApi;
67 70
@@ -69,21 +72,23 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -69,21 +72,23 @@ public class CrmClueServiceImpl implements CrmClueService {
69 @Transactional(rollbackFor = Exception.class) 72 @Transactional(rollbackFor = Exception.class)
70 @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_CREATE_SUB_TYPE, bizNo = "{{#clue.id}}", 73 @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_CREATE_SUB_TYPE, bizNo = "{{#clue.id}}",
71 success = CRM_LEADS_CREATE_SUCCESS) 74 success = CRM_LEADS_CREATE_SUCCESS)
72 - public Long createClue(CrmClueSaveReqVO createReqVO) { 75 + public Long createClue(CrmClueSaveReqVO createReqVO, Long userId) {
73 // 1. 校验关联数据 76 // 1. 校验关联数据
74 validateRelationDataExists(createReqVO); 77 validateRelationDataExists(createReqVO);
75 78
76 // 2. 插入 79 // 2. 插入
77 - CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class); 80 + CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class).setId(null);
  81 + if (ObjUtil.isNull(createReqVO.getOwnerUserId())) {
  82 + clue.setOwnerUserId(userId); // 如果没有设置负责人那么默认操作人为负责人
  83 + } else {
  84 + // 校验负责人是否存在
  85 + adminUserApi.validateUserList(singletonList(createReqVO.getOwnerUserId()));
  86 + }
78 clueMapper.insert(clue); 87 clueMapper.insert(clue);
79 88
80 // 3. 创建数据权限 89 // 3. 创建数据权限
81 - CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO()  
82 - .setBizType(CrmBizTypeEnum.CRM_LEADS.getType())  
83 - .setBizId(clue.getId())  
84 - // 设置当前操作的人为负责人  
85 - .setUserId(getLoginUserId())  
86 - .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()); 90 + CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_LEADS.getType())
  91 + .setBizId(clue.getId()).setUserId(clue.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel());
87 crmPermissionService.createPermission(createReqBO); 92 crmPermissionService.createPermission(createReqBO);
88 93
89 // 4. 记录操作日志上下文 94 // 4. 记录操作日志上下文
@@ -95,16 +100,16 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -95,16 +100,16 @@ public class CrmClueServiceImpl implements CrmClueService {
95 @Transactional(rollbackFor = Exception.class) 100 @Transactional(rollbackFor = Exception.class)
96 @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", 101 @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
97 success = CRM_LEADS_UPDATE_SUCCESS) 102 success = CRM_LEADS_UPDATE_SUCCESS)
98 - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)  
99 - public void updateClue(CrmClueSaveReqVO updateReqVO) {  
100 - Assert.notNull(updateReqVO.getId(), "线索编号不能为空"); 103 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReq.id", level = CrmPermissionLevelEnum.OWNER)
  104 + public void updateClue(CrmClueSaveReqVO updateReq) {
  105 + Assert.notNull(updateReq.getId(), "线索编号不能为空");
101 // 1. 校验线索是否存在 106 // 1. 校验线索是否存在
102 - CrmClueDO oldClue = validateClueExists(updateReqVO.getId()); 107 + CrmClueDO oldClue = validateClueExists(updateReq.getId());
103 // 2. 校验关联数据 108 // 2. 校验关联数据
104 - validateRelationDataExists(updateReqVO); 109 + validateRelationDataExists(updateReq);
105 110
106 // 3. 更新 111 // 3. 更新
107 - CrmClueDO updateObj = BeanUtils.toBean(updateReqVO, CrmClueDO.class); 112 + CrmClueDO updateObj = BeanUtils.toBean(updateReq, CrmClueDO.class);
108 clueMapper.updateById(updateObj); 113 clueMapper.updateById(updateObj);
109 114
110 // 3. 记录操作日志上下文 115 // 3. 记录操作日志上下文
@@ -113,8 +118,19 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -113,8 +118,19 @@ public class CrmClueServiceImpl implements CrmClueService {
113 } 118 }
114 119
115 @Override 120 @Override
116 - public void updateClueFollowUp(CrmUpdateFollowUpReqBO clueUpdateFollowUpReqBO) {  
117 - clueMapper.updateById(BeanUtils.toBean(clueUpdateFollowUpReqBO, CrmClueDO.class).setId(clueUpdateFollowUpReqBO.getBizId())); 121 + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReq.bizId}",
  122 + success = CRM_LEADS_UPDATE_SUCCESS)
  123 + public void updateClueFollowUp(CrmUpdateFollowUpReqBO updateReq) {
  124 + // 校验线索是否存在
  125 + CrmClueDO oldClue = validateClueExists(updateReq.getBizId());
  126 +
  127 + // 更新
  128 + clueMapper.updateById(BeanUtils.toBean(updateReq, CrmClueDO.class).setId(updateReq.getBizId()));
  129 +
  130 + // 3. 记录操作日志上下文
  131 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmUpdateFollowUpReqBO.class));
  132 + LogRecordContext.putVariable("clueName", oldClue.getName());
  133 +
118 } 134 }
119 135
120 @Override 136 @Override
@@ -132,78 +148,89 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -132,78 +148,89 @@ public class CrmClueServiceImpl implements CrmClueService {
132 // 3. 删除数据权限 148 // 3. 删除数据权限
133 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_LEADS.getType(), id); 149 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_LEADS.getType(), id);
134 150
135 - // 4. 记录操作日志上下文  
136 - LogRecordContext.putVariable("clueName", clue.getName());  
137 - }  
138 -  
139 - private CrmClueDO validateClueExists(Long id) {  
140 - CrmClueDO crmClueDO = clueMapper.selectById(id);  
141 - if (crmClueDO == null) {  
142 - throw exception(CLUE_NOT_EXISTS);  
143 - }  
144 - return crmClueDO;  
145 - }  
146 -  
147 - @Override  
148 - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.READ)  
149 - public CrmClueDO getClue(Long id) {  
150 - return clueMapper.selectById(id);  
151 - } 151 + // 4. 删除跟进
  152 + followUpRecordService.deleteFollowUpRecordByBiz(CrmBizTypeEnum.CRM_LEADS.getType(), id);
152 153
153 - @Override  
154 - public List<CrmClueDO> getClueList(Collection<Long> ids, Long userId) {  
155 - if (CollUtil.isEmpty(ids)) {  
156 - return ListUtil.empty();  
157 - }  
158 - return clueMapper.selectBatchIds(ids, userId); 154 + // 记录操作日志上下文
  155 + LogRecordContext.putVariable("clueName", clue.getName());
159 } 156 }
160 157
161 - @Override  
162 - public PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId) {  
163 - return clueMapper.selectPage(pageReqVO, userId);  
164 - }  
165 158
166 @Override 159 @Override
  160 + @Transactional(rollbackFor = Exception.class)
  161 + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
  162 + success = CRM_LEADS_TRANSFER_SUCCESS)
  163 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
167 public void transferClue(CrmClueTransferReqVO reqVO, Long userId) { 164 public void transferClue(CrmClueTransferReqVO reqVO, Long userId) {
168 // 1 校验线索是否存在 165 // 1 校验线索是否存在
169 - validateClueExists(reqVO.getId()); 166 + CrmClueDO clue = validateClueExists(reqVO.getId());
170 167
171 // 2.1 数据权限转移 168 // 2.1 数据权限转移
172 crmPermissionService.transferPermission(CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType())); 169 crmPermissionService.transferPermission(CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType()));
173 // 2.2 设置新的负责人 170 // 2.2 设置新的负责人
174 clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 171 clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
175 172
176 - // 3. TODO 记录转移日志 173 + // 3. 记录转移日志
  174 + LogRecordContext.putVariable("clue", clue);
177 } 175 }
178 176
179 @Override 177 @Override
180 @Transactional(rollbackFor = Exception.class) 178 @Transactional(rollbackFor = Exception.class)
  179 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
181 public void translateCustomer(CrmClueTransformReqVO reqVO, Long userId) { 180 public void translateCustomer(CrmClueTransformReqVO reqVO, Long userId) {
182 - // 校验线索都存在 181 + // 1.1 校验线索都存在
183 Set<Long> clueIds = reqVO.getIds(); 182 Set<Long> clueIds = reqVO.getIds();
184 List<CrmClueDO> clues = getClueList(clueIds, userId); 183 List<CrmClueDO> clues = getClueList(clueIds, userId);
185 if (CollUtil.isEmpty(clues) || ObjectUtil.notEqual(clues.size(), clueIds.size())) { 184 if (CollUtil.isEmpty(clues) || ObjectUtil.notEqual(clues.size(), clueIds.size())) {
186 clueIds.removeAll(convertSet(clues, CrmClueDO::getId)); 185 clueIds.removeAll(convertSet(clues, CrmClueDO::getId));
187 - throw exception(CLUE_ANY_CLUE_NOT_EXISTS, StrUtil.join(",", clueIds)); 186 + throw exception(CLUE_ANY_CLUE_NOT_EXISTS, clueIds);
188 } 187 }
189 -  
190 - // 存在已经转化的,直接提示哈。避免操作的用户,以为都转化成功了  
191 - List<CrmClueDO> translatedClues = CollectionUtils.filterList(clues, 188 + // 1.2 存在已经转化的,直接提示哈。避免操作的用户,以为都转化成功了
  189 + List<CrmClueDO> translatedClues = filterList(clues,
192 clue -> ObjectUtil.equal(Boolean.TRUE, clue.getTransformStatus())); 190 clue -> ObjectUtil.equal(Boolean.TRUE, clue.getTransformStatus()));
193 if (CollUtil.isNotEmpty(translatedClues)) { 191 if (CollUtil.isNotEmpty(translatedClues)) {
194 - throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, StrUtil.join(",", convertSet(translatedClues, CrmClueDO::getId))); 192 + throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, convertSet(translatedClues, CrmClueDO::getId));
  193 + }
  194 +
  195 + // 2. 遍历线索(未转化的线索),创建对应的客户
  196 + List<CrmClueDO> translateClues = filterList(clues, clue -> ObjUtil.equal(Boolean.FALSE, clue.getTransformStatus()));
  197 + List<CrmCustomerDO> customers = customerService.createCustomerBatch(convertList(translateClues, clue ->
  198 + BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class)), userId);
  199 +
  200 + Map<Long, Long> clueCustomerIdMap = new HashMap<>(translateClues.size());
  201 + // 2.1 更新线索
  202 + clueMapper.updateBatch(convertList(customers, customer -> {
  203 + CrmClueDO firstClue = findFirst(translateClues, clue -> ObjUtil.equal(clue.getName(), customer.getName()));
  204 + clueCustomerIdMap.put(firstClue.getId(), customer.getId());
  205 + return new CrmClueDO().setId(firstClue.getId()).setTransformStatus(Boolean.TRUE).setCustomerId(customer.getId());
  206 + }));
  207 + // 2.3 复制跟进
  208 + updateFollowUpRecords(clueCustomerIdMap);
  209 +
  210 + // 3. 记录操作日志
  211 + for (CrmClueDO clue : translateClues) {
  212 + getSelf().receiveClueLog(clue);
  213 + }
  214 + }
  215 +
  216 + private void updateFollowUpRecords(Map<Long, Long> clueCustomerIdMap) {
  217 + List<CrmFollowUpRecordDO> followUpRecords = followUpRecordService.getFollowUpRecordByBiz(
  218 + CrmBizTypeEnum.CRM_LEADS.getType(), clueCustomerIdMap.keySet());
  219 + if (CollUtil.isEmpty(followUpRecords)) {
  220 + return;
195 } 221 }
196 222
197 - // 遍历线索(未转化的线索),创建对应的客户  
198 - reqVO.getIds().forEach(id -> {  
199 - // 1. 创建客户  
200 - CrmCustomerSaveReqVO customerSaveReqVO = BeanUtils.toBean(id, CrmCustomerSaveReqVO.class).setId(null);  
201 - Long customerId = customerService.createCustomer(customerSaveReqVO, userId);  
202 - // TODO @puhui999:如果有跟进记录,需要一起转过去;提问:艿艿这里是复制线索所有的跟进吗?还是直接把线索相关的跟进 bizType、bizId 全改为关联客户?  
203 - // 2. 更新线索  
204 - clueMapper.updateById(new CrmClueDO().setId(id)  
205 - .setTransformStatus(Boolean.TRUE).setCustomerId(customerId));  
206 - }); 223 + // 创建跟进
  224 + followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, followUpRecord ->
  225 + BeanUtils.toBean(followUpRecord, CrmFollowUpCreateReqBO.class).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
  226 + .setBizId(clueCustomerIdMap.get(followUpRecord.getBizId()))));
  227 + }
  228 +
  229 + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSLATE_SUB_TYPE, bizNo = "{{#clue.id}}",
  230 + success = CRM_LEADS_TRANSLATE_SUCCESS)
  231 + public void receiveClueLog(CrmClueDO clue) {
  232 + // 记录操作日志上下文
  233 + LogRecordContext.putVariable("clue", clue);
207 } 234 }
208 235
209 private void validateRelationDataExists(CrmClueSaveReqVO reqVO) { 236 private void validateRelationDataExists(CrmClueSaveReqVO reqVO) {
@@ -214,4 +241,40 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -214,4 +241,40 @@ public class CrmClueServiceImpl implements CrmClueService {
214 } 241 }
215 } 242 }
216 243
  244 + private CrmClueDO validateClueExists(Long id) {
  245 + CrmClueDO crmClueDO = clueMapper.selectById(id);
  246 + if (crmClueDO == null) {
  247 + throw exception(CLUE_NOT_EXISTS);
  248 + }
  249 + return crmClueDO;
  250 + }
  251 +
  252 + @Override
  253 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.READ)
  254 + public CrmClueDO getClue(Long id) {
  255 + return clueMapper.selectById(id);
  256 + }
  257 +
  258 + @Override
  259 + public List<CrmClueDO> getClueList(Collection<Long> ids, Long userId) {
  260 + if (CollUtil.isEmpty(ids)) {
  261 + return ListUtil.empty();
  262 + }
  263 + return clueMapper.selectBatchIds(ids, userId);
  264 + }
  265 +
  266 + @Override
  267 + public PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId) {
  268 + return clueMapper.selectPage(pageReqVO, userId);
  269 + }
  270 +
  271 + /**
  272 + * 获得自身的代理对象,解决 AOP 生效问题
  273 + *
  274 + * @return 自己
  275 + */
  276 + private CrmClueServiceImpl getSelf() {
  277 + return SpringUtil.getBean(getClass());
  278 + }
  279 +
217 } 280 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR
6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  9 +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
9 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; 10 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
10 import jakarta.validation.Valid; 11 import jakarta.validation.Valid;
11 12
@@ -98,6 +99,15 @@ public interface CrmCustomerService { @@ -98,6 +99,15 @@ public interface CrmCustomerService {
98 */ 99 */
99 void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO); 100 void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO);
100 101
  102 + /**
  103 + * 批量创建客户
  104 + *
  105 + * @param customerCreateReqBOs 请求
  106 + * @param userId 用户编号
  107 + * @return 客户列表
  108 + */
  109 + List<CrmCustomerDO> createCustomerBatch(List<CrmCustomerCreateReqBO> customerCreateReqBOs, Long userId);
  110 +
101 // ==================== 公海相关操作 ==================== 111 // ==================== 公海相关操作 ====================
102 112
103 /** 113 /**
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionU @@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionU
21 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; 21 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
22 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; 22 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
23 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 23 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
  24 +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
24 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; 25 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
25 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 26 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
26 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 27 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
@@ -42,10 +43,12 @@ import java.util.Collections; @@ -42,10 +43,12 @@ import java.util.Collections;
42 import java.util.List; 43 import java.util.List;
43 44
44 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 45 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  46 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
45 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 47 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
46 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 48 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
47 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT; 49 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
48 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT; 50 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT;
  51 +import static java.util.Collections.emptyList;
49 import static java.util.Collections.singletonList; 52 import static java.util.Collections.singletonList;
50 53
51 /** 54 /**
@@ -209,6 +212,25 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -209,6 +212,25 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
209 customerMapper.updateById(BeanUtils.toBean(customerUpdateFollowUpReqBO, CrmCustomerDO.class).setId(customerUpdateFollowUpReqBO.getBizId())); 212 customerMapper.updateById(BeanUtils.toBean(customerUpdateFollowUpReqBO, CrmCustomerDO.class).setId(customerUpdateFollowUpReqBO.getBizId()));
210 } 213 }
211 214
  215 + @Override
  216 + @Transactional(rollbackFor = Exception.class)
  217 + public List<CrmCustomerDO> createCustomerBatch(List<CrmCustomerCreateReqBO> customerCreateReqBOs, Long userId) {
  218 + if (CollUtil.isEmpty(customerCreateReqBOs)) {
  219 + return emptyList();
  220 + }
  221 +
  222 + // 创建客户
  223 + List<CrmCustomerDO> customers = convertList(customerCreateReqBOs, customerBO ->
  224 + BeanUtils.toBean(customerBO, CrmCustomerDO.class).setOwnerUserId(userId));
  225 + customerMapper.insertBatch(customers);
  226 +
  227 + // 创建负责人数据权限
  228 + permissionService.createPermissionBatch(convertList(customers, customer -> new CrmPermissionCreateReqBO()
  229 + .setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()).setBizId(customer.getId()).setUserId(userId)
  230 + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())));
  231 + return customers;
  232 + }
  233 +
212 // ==================== 公海相关操作 ==================== 234 // ==================== 公海相关操作 ====================
213 235
214 @Override 236 @Override
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.customer.bo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.Mobile;
  4 +import cn.iocoder.yudao.framework.common.validation.Telephone;
  5 +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
  6 +import jakarta.validation.constraints.Email;
  7 +import jakarta.validation.constraints.NotEmpty;
  8 +import jakarta.validation.constraints.Size;
  9 +import lombok.Data;
  10 +
  11 +import java.time.LocalDateTime;
  12 +
  13 +/**
  14 + * 客户创建 Create Req BO
  15 + *
  16 + * @author HUIHUI
  17 + */
  18 +@Data
  19 +public class CrmCustomerCreateReqBO {
  20 +
  21 + /**
  22 + * 客户名称
  23 + */
  24 + @NotEmpty(message = "客户名称不能为空")
  25 + private String name;
  26 + /**
  27 + * 跟进状态
  28 + */
  29 + private Boolean followUpStatus;
  30 + /**
  31 + * 锁定状态
  32 + */
  33 + private Boolean lockStatus;
  34 + /**
  35 + * 成交状态
  36 + */
  37 + private Boolean dealStatus;
  38 + /**
  39 + * 所属行业
  40 + *
  41 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY}
  42 + */
  43 + private Integer industryId;
  44 + /**
  45 + * 客户等级
  46 + *
  47 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL}
  48 + */
  49 + private Integer level;
  50 + /**
  51 + * 客户来源
  52 + *
  53 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE}
  54 + */
  55 + private Integer source;
  56 +
  57 + /**
  58 + * 手机
  59 + */
  60 + @Mobile
  61 + private String mobile;
  62 + /**
  63 + * 电话
  64 + */
  65 + @Telephone
  66 + private String telephone;
  67 + /**
  68 + * 网址
  69 + */
  70 + private String website;
  71 + /**
  72 + * QQ
  73 + */
  74 + private String qq;
  75 + /**
  76 + * wechat
  77 + */
  78 + private String wechat;
  79 +
  80 + /**
  81 + * 邮箱
  82 + */
  83 + @Email(message = "邮箱格式不正确")
  84 + private String email;
  85 +
  86 + /**
  87 + * 客户描述
  88 + */
  89 + @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
  90 + private String description;
  91 + /**
  92 + * 备注
  93 + */
  94 + private String remark;
  95 + /**
  96 + * 负责人的用户编号
  97 + *
  98 + * 关联 AdminUserDO 的 id 字段
  99 + */
  100 + private Long ownerUserId;
  101 + /**
  102 + * 所在地
  103 + *
  104 + * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段
  105 + */
  106 + private Integer areaId;
  107 + /**
  108 + * 详细地址
  109 + */
  110 + private String detailAddress;
  111 +
  112 + /**
  113 + * 最后跟进时间
  114 + */
  115 + private LocalDateTime contactLastTime;
  116 + /**
  117 + * 最后跟进内容
  118 + */
  119 + private String contactLastContent;
  120 + /**
  121 + * 下次联系时间
  122 + */
  123 + private LocalDateTime contactNextTime;
  124 +
  125 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java
@@ -4,8 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,8 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO; 4 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
6 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; 6 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
  7 +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
7 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
8 9
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
9 /** 13 /**
10 * 跟进记录 Service 接口 14 * 跟进记录 Service 接口
11 * 15 *
@@ -22,6 +26,13 @@ public interface CrmFollowUpRecordService { @@ -22,6 +26,13 @@ public interface CrmFollowUpRecordService {
22 Long createFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO createReqVO); 26 Long createFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO createReqVO);
23 27
24 /** 28 /**
  29 + * 创建更进
  30 + *
  31 + * @param followUpCreateReqBOs 请求
  32 + */
  33 + void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> followUpCreateReqBOs);
  34 +
  35 + /**
25 * 删除跟进记录 (数据权限基于 bizType、 bizId) 36 * 删除跟进记录 (数据权限基于 bizType、 bizId)
26 * 37 *
27 * @param id 编号 38 * @param id 编号
@@ -30,6 +41,14 @@ public interface CrmFollowUpRecordService { @@ -30,6 +41,14 @@ public interface CrmFollowUpRecordService {
30 void deleteFollowUpRecord(Long id, Long userId); 41 void deleteFollowUpRecord(Long id, Long userId);
31 42
32 /** 43 /**
  44 + * 删除跟进
  45 + *
  46 + * @param bizType 模块类型
  47 + * @param bizId 模块数据编号
  48 + */
  49 + void deleteFollowUpRecordByBiz(Integer bizType, Long bizId);
  50 +
  51 + /**
33 * 获得跟进记录 52 * 获得跟进记录
34 * 53 *
35 * @param id 编号 54 * @param id 编号
@@ -45,4 +64,13 @@ public interface CrmFollowUpRecordService { @@ -45,4 +64,13 @@ public interface CrmFollowUpRecordService {
45 */ 64 */
46 PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO); 65 PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO);
47 66
  67 + /**
  68 + * 获取跟进记录
  69 + *
  70 + * @param bizType 模块类型
  71 + * @param bizIds 模块数据编号
  72 + * @return 跟进列表
  73 + */
  74 + List<CrmFollowUpRecordDO> getFollowUpRecordByBiz(Integer bizType, Collection<Long> bizIds);
  75 +
48 } 76 }
49 \ No newline at end of file 77 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
@@ -17,13 +17,16 @@ import cn.iocoder.yudao.module.crm.service.clue.CrmClueService; @@ -17,13 +17,16 @@ import cn.iocoder.yudao.module.crm.service.clue.CrmClueService;
17 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; 17 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
18 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 18 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
  20 +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
20 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; 21 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
21 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 22 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
22 import jakarta.annotation.Resource; 23 import jakarta.annotation.Resource;
  24 +import org.springframework.context.annotation.Lazy;
23 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
24 import org.springframework.validation.annotation.Validated; 26 import org.springframework.validation.annotation.Validated;
25 27
26 import java.time.LocalDateTime; 28 import java.time.LocalDateTime;
  29 +import java.util.Collection;
27 import java.util.Collections; 30 import java.util.Collections;
28 import java.util.List; 31 import java.util.List;
29 32
@@ -46,16 +49,22 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -46,16 +49,22 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
46 private CrmFollowUpRecordMapper crmFollowUpRecordMapper; 49 private CrmFollowUpRecordMapper crmFollowUpRecordMapper;
47 50
48 @Resource 51 @Resource
  52 + @Lazy
49 private CrmPermissionService permissionService; 53 private CrmPermissionService permissionService;
50 @Resource 54 @Resource
  55 + @Lazy
51 private CrmBusinessService businessService; 56 private CrmBusinessService businessService;
52 @Resource 57 @Resource
  58 + @Lazy
53 private CrmClueService clueService; 59 private CrmClueService clueService;
54 @Resource 60 @Resource
  61 + @Lazy
55 private CrmContactService contactService; 62 private CrmContactService contactService;
56 @Resource 63 @Resource
  64 + @Lazy
57 private CrmContractService contractService; 65 private CrmContractService contractService;
58 @Resource 66 @Resource
  67 + @Lazy
59 private CrmCustomerService customerService; 68 private CrmCustomerService customerService;
60 69
61 @Override 70 @Override
@@ -97,6 +106,16 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -97,6 +106,16 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
97 } 106 }
98 107
99 @Override 108 @Override
  109 + public void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> followUpCreateReqBOs) {
  110 + if (CollUtil.isEmpty(followUpCreateReqBOs)) {
  111 + return;
  112 + }
  113 +
  114 + List<CrmFollowUpRecordDO> followUpRecords = BeanUtils.toBean(followUpCreateReqBOs, CrmFollowUpRecordDO.class);
  115 + crmFollowUpRecordMapper.insertBatch(followUpRecords);
  116 + }
  117 +
  118 + @Override
100 public void deleteFollowUpRecord(Long id, Long userId) { 119 public void deleteFollowUpRecord(Long id, Long userId) {
101 // 校验存在 120 // 校验存在
102 CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id); 121 CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id);
@@ -113,6 +132,12 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -113,6 +132,12 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
113 crmFollowUpRecordMapper.deleteById(id); 132 crmFollowUpRecordMapper.deleteById(id);
114 } 133 }
115 134
  135 + @Override
  136 + public void deleteFollowUpRecordByBiz(Integer bizType, Long bizId) {
  137 + // 删除
  138 + crmFollowUpRecordMapper.deleteByBiz(bizType, bizId);
  139 + }
  140 +
116 private CrmFollowUpRecordDO validateFollowUpRecordExists(Long id) { 141 private CrmFollowUpRecordDO validateFollowUpRecordExists(Long id) {
117 CrmFollowUpRecordDO followUpRecord = crmFollowUpRecordMapper.selectById(id); 142 CrmFollowUpRecordDO followUpRecord = crmFollowUpRecordMapper.selectById(id);
118 if (followUpRecord == null) { 143 if (followUpRecord == null) {
@@ -132,4 +157,9 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -132,4 +157,9 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
132 return crmFollowUpRecordMapper.selectPage(pageReqVO); 157 return crmFollowUpRecordMapper.selectPage(pageReqVO);
133 } 158 }
134 159
  160 + @Override
  161 + public List<CrmFollowUpRecordDO> getFollowUpRecordByBiz(Integer bizType, Collection<Long> bizIds) {
  162 + return crmFollowUpRecordMapper.selectListByBiz(bizType, bizIds);
  163 + }
  164 +
135 } 165 }
136 \ No newline at end of file 166 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.followup.bo;
  2 +
  3 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  4 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
  5 +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
  6 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  7 +import jakarta.validation.constraints.NotEmpty;
  8 +import jakarta.validation.constraints.NotNull;
  9 +import lombok.Data;
  10 +
  11 +import java.time.LocalDateTime;
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * 跟进信息 Create Req BO
  16 + *
  17 + * @author HUIHUI
  18 + */
  19 +@Data
  20 +public class CrmFollowUpCreateReqBO {
  21 +
  22 + /**
  23 + * 数据类型
  24 + *
  25 + * 枚举 {@link CrmBizTypeEnum}
  26 + */
  27 + @NotNull(message = "数据类型不能为空")
  28 + private Integer bizType;
  29 + /**
  30 + * 数据编号
  31 + *
  32 + * 关联 {@link CrmBizTypeEnum} 对应模块 DO 的 id 字段
  33 + */
  34 + @NotNull(message = "数据编号不能为空")
  35 + private Long bizId;
  36 +
  37 + /**
  38 + * 跟进类型
  39 + *
  40 + * 关联 {@link DictTypeConstants#CRM_FOLLOW_UP_TYPE} 字典
  41 + */
  42 + @NotNull(message = "跟进类型不能为空")
  43 + private Integer type;
  44 + /**
  45 + * 跟进内容
  46 + */
  47 + @NotEmpty(message = "跟进内容不能为空")
  48 + private String content;
  49 + /**
  50 + * 下次联系时间
  51 + */
  52 + @NotNull(message = "下次联系时间不能为空")
  53 + private LocalDateTime nextTime;
  54 +
  55 + /**
  56 + * 图片
  57 + */
  58 + private List<String> picUrls;
  59 + /**
  60 + * 附件
  61 + */
  62 + private List<String> fileUrls;
  63 +
  64 + /**
  65 + * 关联的商机编号数组
  66 + *
  67 + * 关联 {@link CrmBusinessDO#getId()}
  68 + */
  69 + private List<Long> businessIds;
  70 +
  71 + /**
  72 + * 关联的联系人编号数组
  73 + *
  74 + * 关联 {@link CrmContactDO#getId()}
  75 + */
  76 + private List<Long> contactIds;
  77 +
  78 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.permission; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.service.permission;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
6 import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert; 7 import cn.iocoder.yudao.module.crm.convert.permission.CrmPermissionConvert;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
@@ -50,7 +51,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { @@ -50,7 +51,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
50 adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId())); 51 adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId()));
51 52
52 // 2. 创建 53 // 2. 创建
53 - CrmPermissionDO permission = CrmPermissionConvert.INSTANCE.convert(createReqBO); 54 + CrmPermissionDO permission = BeanUtils.toBean(createReqBO, CrmPermissionDO.class);
54 permissionMapper.insert(permission); 55 permissionMapper.insert(permission);
55 return permission.getId(); 56 return permission.getId();
56 } 57 }
@@ -62,7 +63,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { @@ -62,7 +63,7 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
62 adminUserApi.validateUserList(convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId)); 63 adminUserApi.validateUserList(convertSet(createReqBOs, CrmPermissionCreateReqBO::getUserId));
63 64
64 // 2. 创建 65 // 2. 创建
65 - List<CrmPermissionDO> permissions = CrmPermissionConvert.INSTANCE.convertList(createReqBOs); 66 + List<CrmPermissionDO> permissions = BeanUtils.toBean(createReqBOs, CrmPermissionDO.class);
66 permissionMapper.insertBatch(permissions); 67 permissionMapper.insertBatch(permissions);
67 } 68 }
68 69
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionUpdateReqBO.java deleted
1 -package cn.iocoder.yudao.module.crm.service.permission.bo;  
2 -  
3 -import cn.iocoder.yudao.framework.common.validation.InEnum;  
4 -import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;  
5 -import lombok.Data;  
6 -  
7 -import jakarta.validation.constraints.NotNull;  
8 -  
9 -/**  
10 - * crm 数据权限 Update Req BO  
11 - *  
12 - * @author HUIHUI  
13 - */  
14 -@Data  
15 -public class CrmPermissionUpdateReqBO {  
16 -  
17 - /**  
18 - * 数据权限编号  
19 - */  
20 - @NotNull(message = "数据权限编号不能为空")  
21 - private Long id;  
22 -  
23 - /**  
24 - * 当前登录用户编号  
25 - */  
26 - @NotNull(message = "用户编号不能为空")  
27 - private Long userId;  
28 -  
29 - /**  
30 - * 权限级别  
31 - */  
32 - @NotNull(message = "权限级别不能为空")  
33 - @InEnum(CrmPermissionLevelEnum.class)  
34 - private Integer level;  
35 -  
36 -}  
yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java
@@ -15,6 +15,7 @@ import java.util.List; @@ -15,6 +15,7 @@ import java.util.List;
15 15
16 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; 16 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
17 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 17 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  18 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
18 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 19 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
19 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; 20 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
20 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; 21 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
@@ -45,7 +46,7 @@ public class CrmClueServiceImplTest extends BaseDbUnitTest { @@ -45,7 +46,7 @@ public class CrmClueServiceImplTest extends BaseDbUnitTest {
45 CrmClueSaveReqVO reqVO = randomPojo(CrmClueSaveReqVO.class); 46 CrmClueSaveReqVO reqVO = randomPojo(CrmClueSaveReqVO.class);
46 47
47 // 调用 48 // 调用
48 - Long clueId = clueService.createClue(reqVO); 49 + Long clueId = clueService.createClue(reqVO, getLoginUserId());
49 // 断言 50 // 断言
50 assertNotNull(clueId); 51 assertNotNull(clueId);
51 // 校验记录的属性是否正确 52 // 校验记录的属性是否正确