Commit acb8d3f23b8725c809eb6fd63b38c333866c3692

Authored by 芋道源码
Committed by Gitee
2 parents 6f95f371 4418a7be

!858 CRM-客户: 完善客户导入

Merge pull request !858 from puhui999/develop
Showing 25 changed files with 463 additions and 117 deletions
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
@@ -6,16 +6,15 @@ import cn.iocoder.yudao.framework.common.util.io.IoUtils; @@ -6,16 +6,15 @@ import cn.iocoder.yudao.framework.common.util.io.IoUtils;
6 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*; 6 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
7 import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert; 7 import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
8 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; 8 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
9 -import io.swagger.v3.oas.annotations.tags.Tag;  
10 -import io.swagger.v3.oas.annotations.Parameter;  
11 import io.swagger.v3.oas.annotations.Operation; 9 import io.swagger.v3.oas.annotations.Operation;
  10 +import io.swagger.v3.oas.annotations.Parameter;
  11 +import io.swagger.v3.oas.annotations.tags.Tag;
  12 +import jakarta.annotation.Resource;
  13 +import jakarta.validation.Valid;
12 import org.springframework.security.access.prepost.PreAuthorize; 14 import org.springframework.security.access.prepost.PreAuthorize;
13 import org.springframework.validation.annotation.Validated; 15 import org.springframework.validation.annotation.Validated;
14 import org.springframework.web.bind.annotation.*; 16 import org.springframework.web.bind.annotation.*;
15 17
16 -import jakarta.annotation.Resource;  
17 -import jakarta.validation.Valid;  
18 -  
19 import java.io.IOException; 18 import java.io.IOException;
20 19
21 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -44,6 +43,15 @@ public class BpmModelController { @@ -44,6 +43,15 @@ public class BpmModelController {
44 return success(model); 43 return success(model);
45 } 44 }
46 45
  46 + @GetMapping("/get-by-key")
  47 + @Operation(summary = "获得模型")
  48 + @Parameter(name = "key", description = "流程标识", required = true, example = "oa_leave")
  49 + @PreAuthorize("@ss.hasPermission('bpm:model:query')")
  50 + public CommonResult<BpmModelRespVO> getModelByKey(@RequestParam("key") String key) {
  51 + BpmModelRespVO model = modelService.getBpmnModelByKey(key);
  52 + return success(model);
  53 + }
  54 +
47 @PostMapping("/create") 55 @PostMapping("/create")
48 @Operation(summary = "新建模型") 56 @Operation(summary = "新建模型")
49 @PreAuthorize("@ss.hasPermission('bpm:model:create')") 57 @PreAuthorize("@ss.hasPermission('bpm:model:create')")
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
@@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.bpm.service.definition; @@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.bpm.service.definition;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*; 4 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
5 -import org.flowable.bpmn.model.BpmnModel;  
6 -  
7 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
  6 +import org.flowable.bpmn.model.BpmnModel;
8 7
9 /** 8 /**
10 * Flowable流程模型接口 9 * Flowable流程模型接口
@@ -39,6 +38,14 @@ public interface BpmModelService { @@ -39,6 +38,14 @@ public interface BpmModelService {
39 BpmModelRespVO getModel(String id); 38 BpmModelRespVO getModel(String id);
40 39
41 /** 40 /**
  41 + * 获得流程模块
  42 + *
  43 + * @param key 流程标识
  44 + * @return 流程模型
  45 + */
  46 + BpmModelRespVO getBpmnModelByKey(String key);
  47 +
  48 + /**
42 * 修改流程模型 49 * 修改流程模型
43 * 50 *
44 * @param updateReqVO 更新信息 51 * @param updateReqVO 更新信息
@@ -62,7 +69,7 @@ public interface BpmModelService { @@ -62,7 +69,7 @@ public interface BpmModelService {
62 /** 69 /**
63 * 修改模型的状态,实际更新的部署的流程定义的状态 70 * 修改模型的状态,实际更新的部署的流程定义的状态
64 * 71 *
65 - * @param id 编号 72 + * @param id 编号
66 * @param state 状态 73 * @param state 状态
67 */ 74 */
68 void updateModelState(String id, Integer state); 75 void updateModelState(String id, Integer state);
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -14,6 +14,8 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; @@ -14,6 +14,8 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
14 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; 14 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
15 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO; 15 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmModelMetaInfoRespDTO;
16 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO; 16 import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmProcessDefinitionCreateReqDTO;
  17 +import jakarta.annotation.Resource;
  18 +import jakarta.validation.Valid;
17 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
18 import org.flowable.bpmn.converter.BpmnXMLConverter; 20 import org.flowable.bpmn.converter.BpmnXMLConverter;
19 import org.flowable.bpmn.model.BpmnModel; 21 import org.flowable.bpmn.model.BpmnModel;
@@ -29,8 +31,6 @@ import org.springframework.transaction.annotation.Transactional; @@ -29,8 +31,6 @@ import org.springframework.transaction.annotation.Transactional;
29 import org.springframework.util.ObjectUtils; 31 import org.springframework.util.ObjectUtils;
30 import org.springframework.validation.annotation.Validated; 32 import org.springframework.validation.annotation.Validated;
31 33
32 -import jakarta.annotation.Resource;  
33 -import jakarta.validation.Valid;  
34 import java.util.*; 34 import java.util.*;
35 35
36 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 36 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -135,6 +135,19 @@ public class BpmModelServiceImpl implements BpmModelService { @@ -135,6 +135,19 @@ public class BpmModelServiceImpl implements BpmModelService {
135 } 135 }
136 136
137 @Override 137 @Override
  138 + public BpmModelRespVO getBpmnModelByKey(String key) {
  139 + Model model = getModelByKey(key);
  140 + if (model == null) {
  141 + return null;
  142 + }
  143 + BpmModelRespVO modelRespVO = BpmModelConvert.INSTANCE.convert(model);
  144 + // 拼接 bpmn XML
  145 + byte[] bpmnBytes = repositoryService.getModelEditorSource(model.getId());
  146 + modelRespVO.setBpmnXml(StrUtil.utf8Str(bpmnBytes));
  147 + return modelRespVO;
  148 + }
  149 +
  150 + @Override
138 @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 151 @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
139 public void updateModel(@Valid BpmModelUpdateReqVO updateReqVO) { 152 public void updateModel(@Valid BpmModelUpdateReqVO updateReqVO) {
140 // 校验流程模型存在 153 // 校验流程模型存在
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -11,6 +11,7 @@ public interface ErrorCodeConstants { @@ -11,6 +11,7 @@ public interface ErrorCodeConstants {
11 11
12 // ========== 合同管理 1-020-000-000 ========== 12 // ========== 合同管理 1-020-000-000 ==========
13 ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在"); 13 ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
  14 + ErrorCode CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_000_001, "更新合同失败,原因:禁止编辑");
14 15
15 // ========== 线索管理 1-020-001-000 ========== 16 // ========== 线索管理 1-020-001-000 ==========
16 ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); 17 ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
@@ -48,6 +49,9 @@ public interface ErrorCodeConstants { @@ -48,6 +49,9 @@ public interface ErrorCodeConstants {
48 ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "锁定客户失败,超出锁定规则上限"); 49 ErrorCode CUSTOMER_LOCK_EXCEED_LIMIT = new ErrorCode(1_020_006_009, "锁定客户失败,超出锁定规则上限");
49 ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "操作失败,超出客户数拥有上限"); 50 ErrorCode CUSTOMER_OWNER_EXCEED_LIMIT = new ErrorCode(1_020_006_010, "操作失败,超出客户数拥有上限");
50 ErrorCode CUSTOMER_DELETE_FAIL_HAVE_REFERENCE = new ErrorCode(1_020_006_011, "删除客户失败,有关联{}"); 51 ErrorCode CUSTOMER_DELETE_FAIL_HAVE_REFERENCE = new ErrorCode(1_020_006_011, "删除客户失败,有关联{}");
  52 + ErrorCode CUSTOMER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_020_006_012, "导入客户数据不能为空!");
  53 + ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, "客户名称不能为空!");
  54 + ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, "已存在名为【{}】的客户!");
51 55
52 // ========== 权限管理 1_020_007_000 ========== 56 // ========== 权限管理 1_020_007_000 ==========
53 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在"); 57 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
yudao-module-crm/yudao-module-crm-biz/pom.xml
@@ -27,6 +27,11 @@ @@ -27,6 +27,11 @@
27 <artifactId>yudao-module-crm-api</artifactId> 27 <artifactId>yudao-module-crm-api</artifactId>
28 <version>${revision}</version> 28 <version>${revision}</version>
29 </dependency> 29 </dependency>
  30 + <dependency>
  31 + <groupId>cn.iocoder.boot</groupId>
  32 + <artifactId>yudao-module-bpm-api</artifactId>
  33 + <version>${revision}</version>
  34 + </dependency>
30 35
31 <!-- 业务组件 --> 36 <!-- 业务组件 -->
32 <dependency> 37 <dependency>
@@ -37,6 +42,10 @@ @@ -37,6 +42,10 @@
37 <groupId>cn.iocoder.boot</groupId> 42 <groupId>cn.iocoder.boot</groupId>
38 <artifactId>yudao-spring-boot-starter-biz-ip</artifactId> 43 <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
39 </dependency> 44 </dependency>
  45 + <dependency>
  46 + <groupId>cn.iocoder.boot</groupId>
  47 + <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
  48 + </dependency>
40 49
41 <!-- Web 相关 --> 50 <!-- Web 相关 -->
42 <dependency> 51 <dependency>
@@ -70,11 +79,5 @@ @@ -70,11 +79,5 @@
70 <groupId>cn.iocoder.boot</groupId> 79 <groupId>cn.iocoder.boot</groupId>
71 <artifactId>yudao-spring-boot-starter-test</artifactId> 80 <artifactId>yudao-spring-boot-starter-test</artifactId>
72 </dependency> 81 </dependency>
73 -  
74 - <!-- TODO @puhui999:放的位置,要整齐哈。 -->  
75 - <dependency>  
76 - <groupId>cn.iocoder.boot</groupId>  
77 - <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>  
78 - </dependency>  
79 </dependencies> 82 </dependencies>
80 </project> 83 </project>
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
@@ -140,4 +140,12 @@ public class CrmContractController { @@ -140,4 +140,12 @@ public class CrmContractController {
140 return success(true); 140 return success(true);
141 } 141 }
142 142
  143 + @PutMapping("/approve")
  144 + @Operation(summary = "发起合同审批流程")
  145 + @PreAuthorize("@ss.hasPermission('crm:contract:update')")
  146 + public CommonResult<Boolean> transfer(@RequestParam("id") Long id) {
  147 + contractService.handleApprove(id, getLoginUserId());
  148 + return success(true);
  149 + }
  150 +
143 } 151 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java
@@ -7,10 +7,13 @@ import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFu @@ -7,10 +7,13 @@ import cn.iocoder.yudao.module.crm.framework.operatelog.core.SysAdminUserParseFu
7 import com.mzt.logapi.starter.annotation.DiffLogField; 7 import com.mzt.logapi.starter.annotation.DiffLogField;
8 import io.swagger.v3.oas.annotations.media.Schema; 8 import io.swagger.v3.oas.annotations.media.Schema;
9 import jakarta.validation.constraints.NotNull; 9 import jakarta.validation.constraints.NotNull;
  10 +import lombok.AllArgsConstructor;
10 import lombok.Data; 11 import lombok.Data;
  12 +import lombok.NoArgsConstructor;
11 import org.springframework.format.annotation.DateTimeFormat; 13 import org.springframework.format.annotation.DateTimeFormat;
12 14
13 import java.time.LocalDateTime; 15 import java.time.LocalDateTime;
  16 +import java.util.List;
14 17
15 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;
16 19
@@ -35,10 +38,6 @@ public class CrmContractSaveReqVO { @@ -35,10 +38,6 @@ public class CrmContractSaveReqVO {
35 @DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME) 38 @DiffLogField(name = "商机", function = CrmBusinessParseFunction.NAME)
36 private Long businessId; 39 private Long businessId;
37 40
38 - @Schema(description = "工作流编号", example = "1043")  
39 - @DiffLogField(name = "工作流编号")  
40 - private Long processInstanceId;  
41 -  
42 @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED) 41 @Schema(description = "下单日期", requiredMode = Schema.RequiredMode.REQUIRED)
43 @DiffLogField(name = "下单日期") 42 @DiffLogField(name = "下单日期")
44 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 43 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@@ -86,16 +85,33 @@ public class CrmContractSaveReqVO { @@ -86,16 +85,33 @@ public class CrmContractSaveReqVO {
86 @DiffLogField(name = "公司签约人", function = SysAdminUserParseFunction.NAME) 85 @DiffLogField(name = "公司签约人", function = SysAdminUserParseFunction.NAME)
87 private Long signUserId; 86 private Long signUserId;
88 87
89 - @Schema(description = "最后跟进时间")  
90 - @DiffLogField(name = "最后跟进时间")  
91 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
92 - private LocalDateTime contactLastTime;  
93 -  
94 @Schema(description = "备注", example = "你猜") 88 @Schema(description = "备注", example = "你猜")
95 @DiffLogField(name = "备注") 89 @DiffLogField(name = "备注")
96 private String remark; 90 private String remark;
97 91
98 - // TODO @dhb52:增加一个 status 字段:具体有哪些值,你来枚举下;主要页面上有个【草稿】【提交审核】的流程,可以看看。然后要对接工作流,这块也可以看看,不确定的地方问我。 92 + @Schema(description = "审批状态", example = "1")
  93 + private Integer auditStatus;
  94 +
  95 + @Schema(description = "产品列表")
  96 + private List<CrmContractProductItem> productItems;
  97 +
  98 + @Schema(description = "商品属性")
  99 + @Data
  100 + @NoArgsConstructor
  101 + @AllArgsConstructor
  102 + public static class CrmContractProductItem {
  103 +
  104 + @Schema(description = "产品编号", example = "20529")
  105 + @NotNull(message = "产品编号不能为空")
  106 + private Long id;
  107 +
  108 + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  109 + @NotNull(message = "产品数量不能为空")
  110 + private Integer count;
  111 +
  112 + @Schema(description = "产品折扣")
  113 + private Integer discountPercent;
99 114
  115 + }
100 116
101 } 117 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
21 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 21 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
22 import io.swagger.v3.oas.annotations.Operation; 22 import io.swagger.v3.oas.annotations.Operation;
23 import io.swagger.v3.oas.annotations.Parameter; 23 import io.swagger.v3.oas.annotations.Parameter;
  24 +import io.swagger.v3.oas.annotations.Parameters;
24 import io.swagger.v3.oas.annotations.tags.Tag; 25 import io.swagger.v3.oas.annotations.tags.Tag;
25 import jakarta.annotation.Resource; 26 import jakarta.annotation.Resource;
26 import jakarta.servlet.http.HttpServletResponse; 27 import jakarta.servlet.http.HttpServletResponse;
@@ -29,9 +30,11 @@ import org.mapstruct.ap.internal.util.Collections; @@ -29,9 +30,11 @@ import org.mapstruct.ap.internal.util.Collections;
29 import org.springframework.security.access.prepost.PreAuthorize; 30 import org.springframework.security.access.prepost.PreAuthorize;
30 import org.springframework.validation.annotation.Validated; 31 import org.springframework.validation.annotation.Validated;
31 import org.springframework.web.bind.annotation.*; 32 import org.springframework.web.bind.annotation.*;
  33 +import org.springframework.web.multipart.MultipartFile;
32 34
33 import java.io.IOException; 35 import java.io.IOException;
34 import java.time.LocalDateTime; 36 import java.time.LocalDateTime;
  37 +import java.util.Arrays;
35 import java.util.List; 38 import java.util.List;
36 import java.util.Map; 39 import java.util.Map;
37 import java.util.stream.Stream; 40 import java.util.stream.Stream;
@@ -198,6 +201,36 @@ public class CrmCustomerController { @@ -198,6 +201,36 @@ public class CrmCustomerController {
198 BeanUtils.toBean(list, CrmCustomerRespVO.class)); 201 BeanUtils.toBean(list, CrmCustomerRespVO.class));
199 } 202 }
200 203
  204 + @GetMapping("/get-import-template")
  205 + @Operation(summary = "获得导入客户模板")
  206 + public void importTemplate(HttpServletResponse response) throws IOException {
  207 + // 手动创建导出 demo
  208 + List<CrmCustomerImportExcelVO> list = Arrays.asList(
  209 + CrmCustomerImportExcelVO.builder().name("芋道").industryId(1).level(1).source(1).mobile("15601691300").telephone("")
  210 + .website("https://doc.iocoder.cn/").qq("").wechat("").email("yunai@iocoder.cn").description("").remark("")
  211 + .areaId(null).detailAddress("").build(),
  212 + CrmCustomerImportExcelVO.builder().name("源码").industryId(1).level(1).source(1).mobile("15601691300").telephone("")
  213 + .website("https://doc.iocoder.cn/").qq("").wechat("").email("yunai@iocoder.cn").description("").remark("")
  214 + .areaId(null).detailAddress("").build()
  215 + );
  216 + // 输出
  217 + ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list);
  218 + }
  219 +
  220 + @PostMapping("/import")
  221 + @Operation(summary = "导入客户")
  222 + @Parameters({
  223 + @Parameter(name = "file", description = "Excel 文件", required = true),
  224 + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
  225 + })
  226 + @PreAuthorize("@ss.hasPermission('system:customer:import')")
  227 + public CommonResult<CrmCustomerImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
  228 + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
  229 + List<CrmCustomerImportExcelVO> list = ExcelUtils.read(file, CrmCustomerImportExcelVO.class);
  230 + return success(customerService.importCustomerList(list, updateSupport, getLoginUserId()));
  231 + }
  232 +
  233 +
201 @PutMapping("/transfer") 234 @PutMapping("/transfer")
202 @Operation(summary = "转移客户") 235 @Operation(summary = "转移客户")
203 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 236 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
  4 +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
  5 +import com.alibaba.excel.annotation.ExcelProperty;
  6 +import jakarta.validation.constraints.Size;
  7 +import lombok.AllArgsConstructor;
  8 +import lombok.Builder;
  9 +import lombok.Data;
  10 +import lombok.NoArgsConstructor;
  11 +import lombok.experimental.Accessors;
  12 +
  13 +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
  14 +
  15 +/**
  16 + * 客户 Excel 导入 VO
  17 + */
  18 +@Data
  19 +@Builder
  20 +@AllArgsConstructor
  21 +@NoArgsConstructor
  22 +@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
  23 +public class CrmCustomerImportExcelVO {
  24 +
  25 + @ExcelProperty("客户名称")
  26 + private String name;
  27 +
  28 + @ExcelProperty(value = "所属行业", converter = DictConvert.class)
  29 + @DictFormat(CRM_CUSTOMER_INDUSTRY)
  30 + private Integer industryId;
  31 +
  32 + @ExcelProperty(value = "客户等级", converter = DictConvert.class)
  33 + @DictFormat(CRM_CUSTOMER_LEVEL)
  34 + private Integer level;
  35 +
  36 + @ExcelProperty(value = "客户来源", converter = DictConvert.class)
  37 + @DictFormat(CRM_CUSTOMER_SOURCE)
  38 + private Integer source;
  39 +
  40 + @ExcelProperty("手机")
  41 + private String mobile;
  42 +
  43 + @ExcelProperty("电话")
  44 + private String telephone;
  45 +
  46 + @ExcelProperty("网址")
  47 + private String website;
  48 +
  49 + @Size(max = 20, message = "QQ长度不能超过 20 个字符")
  50 + @ExcelProperty("QQ")
  51 + private String qq;
  52 +
  53 + @Size(max = 255, message = "微信长度不能超过 255 个字符")
  54 + @ExcelProperty("微信")
  55 + private String wechat;
  56 +
  57 + @Size(max = 255, message = "邮箱长度不能超过 255 个字符")
  58 + @ExcelProperty("邮箱")
  59 + private String email;
  60 +
  61 + @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
  62 + @ExcelProperty("客户描述")
  63 + private String description;
  64 +
  65 + @ExcelProperty("备注")
  66 + private String remark;
  67 +
  68 + @ExcelProperty("地区编号")
  69 + private Integer areaId;
  70 +
  71 + @ExcelProperty("详细地址")
  72 + private String detailAddress;
  73 +
  74 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Builder;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +import java.util.Map;
  9 +
  10 +@Schema(description = "管理后台 - 客户导入 Response VO")
  11 +@Data
  12 +@Builder
  13 +public class CrmCustomerImportRespVO {
  14 +
  15 + @Schema(description = "创建成功的客户名数组", requiredMode = Schema.RequiredMode.REQUIRED)
  16 + private List<String> createCustomerNames;
  17 +
  18 + @Schema(description = "更新成功的客户名数组", requiredMode = Schema.RequiredMode.REQUIRED)
  19 + private List<String> updateCustomerNames;
  20 +
  21 + @Schema(description = "导入失败的客户集合,key 为客户名,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED)
  22 + private Map<String, String> failureCustomerNames;
  23 +
  24 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java
@@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.business; @@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.business;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; 4 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
  5 +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
5 import com.baomidou.mybatisplus.annotation.KeySequence; 6 import com.baomidou.mybatisplus.annotation.KeySequence;
6 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 8 import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.*; 9 import lombok.*;
9 10
10 -import java.math.BigDecimal;  
11 -  
12 /** 11 /**
13 * 商机产品关联表 DO 12 * 商机产品关联表 DO
14 * 13 *
@@ -29,14 +28,12 @@ public class CrmBusinessProductDO extends BaseDO { @@ -29,14 +28,12 @@ public class CrmBusinessProductDO extends BaseDO {
29 */ 28 */
30 @TableId 29 @TableId
31 private Long id; 30 private Long id;
32 -  
33 /** 31 /**
34 * 商机编号 32 * 商机编号
35 * 33 *
36 * 关联 {@link CrmBusinessDO#getId()} 34 * 关联 {@link CrmBusinessDO#getId()}
37 */ 35 */
38 private Long businessId; 36 private Long businessId;
39 -  
40 /** 37 /**
41 * 产品编号 38 * 产品编号
42 * 39 *
@@ -50,29 +47,27 @@ public class CrmBusinessProductDO extends BaseDO { @@ -50,29 +47,27 @@ public class CrmBusinessProductDO extends BaseDO {
50 private Integer price; 47 private Integer price;
51 48
52 /** 49 /**
53 - * 销售价格 50 + * 销售价格, 单位:分
54 */ 51 */
55 - private BigDecimal salesPrice;  
56 - 52 + private Integer salesPrice;
57 /** 53 /**
58 * 数量 54 * 数量
59 */ 55 */
60 - private BigDecimal count;  
61 -  
62 - // TODO @lzxhqs:改成 discountPercent 56 + private Integer count;
63 /** 57 /**
64 * 折扣 58 * 折扣
65 */ 59 */
66 - private BigDecimal discountPercent;  
67 -  
68 - // TODO @lzxhqs:改成 totalPrice;总计价格,和现有项目风格一致; 60 + private Integer discountPercent;
69 /** 61 /**
70 - * 小计(折扣后价格) 62 + * 总计价格(折扣后价格)
71 */ 63 */
72 - private BigDecimal totalPrice; 64 + private Integer totalPrice;
73 65
74 /** 66 /**
75 * 单位 67 * 单位
  68 + *
  69 + * 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT}
76 */ 70 */
77 - private String unit; 71 + private Integer unit;
  72 +
78 } 73 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java
@@ -45,7 +45,7 @@ public class CrmContractDO extends BaseDO { @@ -45,7 +45,7 @@ public class CrmContractDO extends BaseDO {
45 /** 45 /**
46 * 工作流编号 46 * 工作流编号
47 */ 47 */
48 - private Long processInstanceId; 48 + private String processInstanceId;
49 /** 49 /**
50 * 下单日期 50 * 下单日期
51 */ 51 */
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java
1 package cn.iocoder.yudao.module.crm.dal.dataobject.product; 1 package cn.iocoder.yudao.module.crm.dal.dataobject.product;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
4 import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum; 5 import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum;
5 import com.baomidou.mybatisplus.annotation.KeySequence; 6 import com.baomidou.mybatisplus.annotation.KeySequence;
6 import com.baomidou.mybatisplus.annotation.TableId; 7 import com.baomidou.mybatisplus.annotation.TableId;
@@ -38,7 +39,7 @@ public class CrmProductDO extends BaseDO { @@ -38,7 +39,7 @@ public class CrmProductDO extends BaseDO {
38 /** 39 /**
39 * 单位 40 * 单位
40 * 41 *
41 - * 字典 {@link cn.iocoder.yudao.module.crm.enums.DictTypeConstants#CRM_PRODUCT_UNIT} 42 + * 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT}
42 */ 43 */
43 private Integer unit; 44 private Integer unit;
44 /** 45 /**
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
@@ -41,7 +41,7 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; { @@ -41,7 +41,7 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; {
41 default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) { 41 default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) {
42 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); 42 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
43 // 拼接数据权限的查询条件 43 // 拼接数据权限的查询条件
44 - CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(), 44 + CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTRACT.getType(),
45 CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE); 45 CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
46 // 拼接自身的查询条件 46 // 拼接自身的查询条件
47 mpjLambdaWrapperX.selectAll(CrmContractDO.class) 47 mpjLambdaWrapperX.selectAll(CrmContractDO.class)
@@ -56,7 +56,7 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; { @@ -56,7 +56,7 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; {
56 default List<CrmContractDO> selectBatchIds(Collection<Long> ids, Long userId) { 56 default List<CrmContractDO> selectBatchIds(Collection<Long> ids, Long userId) {
57 MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>(); 57 MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
58 // 构建数据权限连表条件 58 // 构建数据权限连表条件
59 - CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(), ids, userId); 59 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(), ids, userId);
60 // 拼接自身的查询条件 60 // 拼接自身的查询条件
61 query.selectAll(CrmContractDO.class).in(CrmContractDO::getId, ids).orderByDesc(CrmContractDO::getId); 61 query.selectAll(CrmContractDO.class).in(CrmContractDO::getId, ids).orderByDesc(CrmContractDO::getId);
62 return selectJoinList(CrmContractDO.class, query); 62 return selectJoinList(CrmContractDO.class, query);
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -101,11 +101,15 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -101,11 +101,15 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
101 return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); 101 return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
102 } 102 }
103 103
104 - default List<CrmCustomerDO> selectListByLockStatusAndOwnerUserIdNotNull(Boolean lockStatus) { 104 + default List<CrmCustomerDO> selectListByLockAndDealStatusAndNotPool(Boolean lockStatus, Boolean dealStatus) {
105 return selectList(new LambdaQueryWrapper<CrmCustomerDO>() 105 return selectList(new LambdaQueryWrapper<CrmCustomerDO>()
106 .eq(CrmCustomerDO::getLockStatus, lockStatus) 106 .eq(CrmCustomerDO::getLockStatus, lockStatus)
107 - // TODO @puhui999:not null 可以转化成大于 0  
108 - .isNotNull(CrmCustomerDO::getOwnerUserId)); 107 + .eq(CrmCustomerDO::getDealStatus, dealStatus)
  108 + .gt(CrmCustomerDO::getOwnerUserId, 0));
  109 + }
  110 +
  111 + default CrmCustomerDO selectByCustomerName(String name) {
  112 + return selectOne(CrmCustomerDO::getName, name);
109 } 113 }
110 114
111 default PageResult<CrmCustomerDO> selectPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO, 115 default PageResult<CrmCustomerDO> selectPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java
1 package cn.iocoder.yudao.module.crm.framework.permission.core.util; 1 package cn.iocoder.yudao.module.crm.framework.permission.core.util;
2 2
3 -import cn.hutool.core.util.ObjUtil;  
4 import cn.hutool.extra.spring.SpringUtil; 3 import cn.hutool.extra.spring.SpringUtil;
5 -import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;  
6 -import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;  
7 -import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;  
8 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum; 4 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum;
9 -import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;  
10 import cn.iocoder.yudao.module.system.api.permission.PermissionApi; 5 import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
11 6
12 -import java.util.List;  
13 -  
14 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;  
15 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 7 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
16 8
17 /** 9 /**
@@ -30,22 +22,6 @@ public class CrmPermissionUtils { @@ -30,22 +22,6 @@ public class CrmPermissionUtils {
30 return SingletonManager.getPermissionApi().hasAnyRoles(getLoginUserId(), CrmPermissionRoleCodeEnum.CRM_ADMIN.getCode()); 22 return SingletonManager.getPermissionApi().hasAnyRoles(getLoginUserId(), CrmPermissionRoleCodeEnum.CRM_ADMIN.getCode());
31 } 23 }
32 24
33 - // TODO @puhui999:这个貌似直接放到 CrmPermissionService 会更好?  
34 - /**  
35 - * 校验权限  
36 - *  
37 - * @param bizType 数据类型,关联 {@link CrmBizTypeEnum}  
38 - * @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()  
39 - * @param userId 用户编号  
40 - * @param levelEnum 权限级别  
41 - * @return boolean  
42 - */  
43 - public static boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum) {  
44 - List<CrmPermissionDO> permissionList = SingletonManager.getCrmPermissionService().getPermissionListByBiz(bizType, bizId);  
45 - return anyMatch(permissionList, permission ->  
46 - ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), levelEnum.getLevel()));  
47 - }  
48 -  
49 /** 25 /**
50 * 静态内部类实现单例获取 26 * 静态内部类实现单例获取
51 * 27 *
@@ -54,16 +30,11 @@ public class CrmPermissionUtils { @@ -54,16 +30,11 @@ public class CrmPermissionUtils {
54 private static class SingletonManager { 30 private static class SingletonManager {
55 31
56 private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class); 32 private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class);
57 - private static final CrmPermissionService CRM_PERMISSION_SERVICE = SpringUtil.getBean(CrmPermissionService.class);  
58 33
59 public static PermissionApi getPermissionApi() { 34 public static PermissionApi getPermissionApi() {
60 return PERMISSION_API; 35 return PERMISSION_API;
61 } 36 }
62 37
63 - public static CrmPermissionService getCrmPermissionService() {  
64 - return CRM_PERMISSION_SERVICE;  
65 - }  
66 -  
67 } 38 }
68 39
69 } 40 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/job/customer/CrmCustomerAutoPutPoolJob.java
@@ -20,7 +20,7 @@ public class CrmCustomerAutoPutPoolJob implements JobHandler { @@ -20,7 +20,7 @@ public class CrmCustomerAutoPutPoolJob implements JobHandler {
20 @Override 20 @Override
21 @TenantJob 21 @TenantJob
22 public String execute(String param) { 22 public String execute(String param) {
23 - int count = customerService.customerAutoPutPoolBySystem(); 23 + int count = customerService.autoPutCustomerPool();
24 return String.format("掉入公海客户 %s 个", count); 24 return String.format("掉入公海客户 %s 个", count);
25 } 25 }
26 26
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
@@ -58,6 +58,14 @@ public interface CrmContractService { @@ -58,6 +58,14 @@ public interface CrmContractService {
58 void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO); 58 void updateContractFollowUp(CrmUpdateFollowUpReqBO contractUpdateFollowUpReqBO);
59 59
60 /** 60 /**
  61 + * 发起合同审批流程
  62 + *
  63 + * @param id 合同编号
  64 + * @param userId 用户编号
  65 + */
  66 + void handleApprove(Long id, Long userId);
  67 +
  68 + /**
61 * 获得合同 69 * 获得合同
62 * 70 *
63 * @param id 编号 71 * @param id 编号
@@ -111,9 +119,11 @@ public interface CrmContractService { @@ -111,9 +119,11 @@ public interface CrmContractService {
111 Long getContractCountByCustomerId(Long customerId); 119 Long getContractCountByCustomerId(Long customerId);
112 120
113 /** 121 /**
114 - * 根据商机ID获取关联客户的合同数量 TODO @lzxhqs:1)方法注释,和参数注释之间要有空行;2)中英文之间有空格,更清晰,例如说 商机 ID  
115 - * @param businessId 商机ID 122 + * 根据商机ID获取关联客户的合同数量
  123 + *
  124 + * @param businessId 商机编号
116 * @return 数量 125 * @return 数量
117 */ 126 */
118 Long selectCountByBusinessId(Long businessId); 127 Long selectCountByBusinessId(Long businessId);
  128 +
119 } 129 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -2,20 +2,33 @@ package cn.iocoder.yudao.module.crm.service.contract; @@ -2,20 +2,33 @@ package cn.iocoder.yudao.module.crm.service.contract;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.ListUtil; 4 import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.util.ObjUtil;
5 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.number.MoneyUtils;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  9 +import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
  10 +import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
7 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; 11 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; 12 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
9 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; 13 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
10 import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert; 14 import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
  15 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
11 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
  17 +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
12 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper; 18 import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
  19 +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 20 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 21 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
15 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; 22 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
  23 +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService;
  24 +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
  25 +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
  26 +import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
16 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; 27 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
17 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 28 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
18 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 29 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  30 +import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
  31 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
19 import com.mzt.logapi.context.LogRecordContext; 32 import com.mzt.logapi.context.LogRecordContext;
20 import com.mzt.logapi.service.impl.DiffParseFunction; 33 import com.mzt.logapi.service.impl.DiffParseFunction;
21 import com.mzt.logapi.starter.annotation.LogRecord; 34 import com.mzt.logapi.starter.annotation.LogRecord;
@@ -26,10 +39,14 @@ import org.springframework.validation.annotation.Validated; @@ -26,10 +39,14 @@ import org.springframework.validation.annotation.Validated;
26 39
27 import java.util.Collection; 40 import java.util.Collection;
28 import java.util.List; 41 import java.util.List;
  42 +import java.util.Map;
  43 +import java.util.Set;
29 44
30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 45 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
31 -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTRACT_NOT_EXISTS; 46 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  47 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
32 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 48 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
  49 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
33 50
34 /** 51 /**
35 * CRM 合同 Service 实现类 52 * CRM 合同 Service 实现类
@@ -40,21 +57,36 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; @@ -40,21 +57,36 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
40 @Validated 57 @Validated
41 public class CrmContractServiceImpl implements CrmContractService { 58 public class CrmContractServiceImpl implements CrmContractService {
42 59
  60 + public static final String CONTRACT_APPROVE = "contract-approve"; // 合同审批流程标识
  61 +
43 @Resource 62 @Resource
44 private CrmContractMapper contractMapper; 63 private CrmContractMapper contractMapper;
45 64
46 @Resource 65 @Resource
47 private CrmPermissionService crmPermissionService; 66 private CrmPermissionService crmPermissionService;
  67 + @Resource
  68 + private CrmBusinessProductService businessProductService;
  69 + @Resource
  70 + private CrmProductService productService;
  71 + @Resource
  72 + private BpmProcessInstanceApi bpmProcessInstanceApi;
  73 + @Resource
  74 + private CrmCustomerService customerService;
  75 + @Resource
  76 + private CrmContactService contactService;
  77 + @Resource
  78 + private CrmBusinessService businessService;
  79 + @Resource
  80 + private AdminUserApi adminUserApi;
48 81
49 @Override 82 @Override
50 @Transactional(rollbackFor = Exception.class) 83 @Transactional(rollbackFor = Exception.class)
51 @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}", 84 @LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_CREATE_SUB_TYPE, bizNo = "{{#contract.id}}",
52 success = CRM_CONTRACT_CREATE_SUCCESS) 85 success = CRM_CONTRACT_CREATE_SUCCESS)
53 public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) { 86 public Long createContract(CrmContractSaveReqVO createReqVO, Long userId) {
54 - createReqVO.setId(null);  
55 - // TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO 87 + validateRelationDataExists(createReqVO);
56 // 插入合同 88 // 插入合同
57 - CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class); 89 + CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null);
58 contractMapper.insert(contract); 90 contractMapper.insert(contract);
59 91
60 // 创建数据权限 92 // 创建数据权限
@@ -62,6 +94,9 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -62,6 +94,9 @@ public class CrmContractServiceImpl implements CrmContractService {
62 .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId()) 94 .setBizType(CrmBizTypeEnum.CRM_CONTRACT.getType()).setBizId(contract.getId())
63 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); 95 .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
64 96
  97 + // 插入商机关联商品
  98 + List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO);
  99 + businessProductService.insertBatch(businessProduct);
65 // 4. 记录操作日志上下文 100 // 4. 记录操作日志上下文
66 LogRecordContext.putVariable("contract", contract); 101 LogRecordContext.putVariable("contract", contract);
67 return contract.getId(); 102 return contract.getId();
@@ -74,12 +109,21 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -74,12 +109,21 @@ public class CrmContractServiceImpl implements CrmContractService {
74 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 109 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
75 public void updateContract(CrmContractSaveReqVO updateReqVO) { 110 public void updateContract(CrmContractSaveReqVO updateReqVO) {
76 // TODO @合同待定:只有草稿、审批中,可以编辑; 111 // TODO @合同待定:只有草稿、审批中,可以编辑;
  112 + if (ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) ||
  113 + ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
  114 + throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED);
  115 + }
  116 + validateRelationDataExists(updateReqVO);
77 // 校验存在 117 // 校验存在
78 CrmContractDO oldContract = validateContractExists(updateReqVO.getId()); 118 CrmContractDO oldContract = validateContractExists(updateReqVO.getId());
79 // 更新合同 119 // 更新合同
80 CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class); 120 CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class);
81 contractMapper.updateById(updateObj); 121 contractMapper.updateById(updateObj);
82 - // TODO @合同待定:插入合同商品;需要搞个 BusinessProductDO 122 +
  123 + // TODO puhui999: @芋艿:合同变更关联的商机后商品怎么处理?
  124 + //List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(updateReqVO);
  125 + //businessProductService.selectListByBusinessId()
  126 + //diffList()
83 127
84 // 3. 记录操作日志上下文 128 // 3. 记录操作日志上下文
85 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class)); 129 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class));
@@ -88,8 +132,55 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -88,8 +132,55 @@ public class CrmContractServiceImpl implements CrmContractService {
88 132
89 // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum 133 // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
90 134
91 - // TODO @合同待定:缺一个发起审批的接口;只有草稿可以发起审批;CrmAuditStatusEnum 135 + private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO) {
  136 + // 校验商品存在
  137 + Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId);
  138 + List<CrmProductDO> productList = productService.getProductList(productIds);
  139 + if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) {
  140 + throw exception(PRODUCT_NOT_EXISTS);
  141 + }
  142 + Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
  143 + return convertList(reqVO.getProductItems(), productItem -> {
  144 + CrmBusinessProductDO businessProduct = BeanUtils.toBean(productMap.get(productItem.getId()), CrmBusinessProductDO.class);
  145 + businessProduct.setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId())
  146 + .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()).setTotalPrice(calculator(businessProduct));
  147 + return businessProduct;
  148 + });
  149 + }
92 150
  151 + /**
  152 + * 计算商品总价
  153 + *
  154 + * @param businessProduct 关联商品
  155 + * @return 商品总价
  156 + */
  157 + private Integer calculator(CrmBusinessProductDO businessProduct) {
  158 + int price = businessProduct.getPrice() * businessProduct.getCount();
  159 + if (businessProduct.getDiscountPercent() == null) {
  160 + return price;
  161 + }
  162 + return MoneyUtils.calculateRatePriceFloor(price, (double) (businessProduct.getDiscountPercent() / 100));
  163 + }
  164 +
  165 + /**
  166 + * 校验关联数据是否存在
  167 + *
  168 + * @param reqVO 请求
  169 + */
  170 + private void validateRelationDataExists(CrmContractSaveReqVO reqVO) {
  171 + // 1. 校验客户
  172 + if (reqVO.getCustomerId() != null && customerService.getCustomer(reqVO.getCustomerId()) == null) {
  173 + throw exception(CUSTOMER_NOT_EXISTS);
  174 + }
  175 + // 2. 校验负责人
  176 + if (reqVO.getOwnerUserId() != null && adminUserApi.getUser(reqVO.getOwnerUserId()) == null) {
  177 + throw exception(USER_NOT_EXISTS);
  178 + }
  179 + // 4. 如果有关联商机,则需要校验存在
  180 + if (reqVO.getBusinessId() != null && businessService.getBusiness(reqVO.getBusinessId()) == null) {
  181 + throw exception(BUSINESS_NOT_EXISTS);
  182 + }
  183 + }
93 184
94 @Override 185 @Override
95 @Transactional(rollbackFor = Exception.class) 186 @Transactional(rollbackFor = Exception.class)
@@ -141,6 +232,18 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -141,6 +232,18 @@ public class CrmContractServiceImpl implements CrmContractService {
141 contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class).setId(contractUpdateFollowUpReqBO.getBizId())); 232 contractMapper.updateById(BeanUtils.toBean(contractUpdateFollowUpReqBO, CrmContractDO.class).setId(contractUpdateFollowUpReqBO.getBizId()));
142 } 233 }
143 234
  235 + @Override
  236 + @Transactional(rollbackFor = Exception.class)
  237 + public void handleApprove(Long id, Long userId) {
  238 + // 创建合同审批流程实例
  239 + String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
  240 + .setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id)));
  241 +
  242 + // 更新合同工作流编号
  243 + contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId)
  244 + .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
  245 + }
  246 +
144 //======================= 查询相关 ======================= 247 //======================= 查询相关 =======================
145 248
146 @Override 249 @Override
@@ -182,6 +285,5 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -182,6 +285,5 @@ public class CrmContractServiceImpl implements CrmContractService {
182 public Long selectCountByBusinessId(Long businessId) { 285 public Long selectCountByBusinessId(Long businessId) {
183 return contractMapper.selectCountByBusinessId(businessId); 286 return contractMapper.selectCountByBusinessId(businessId);
184 } 287 }
185 -  
186 // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒; 288 // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒;
187 } 289 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.contract.listener;
  2 +
  3 +public class CrmContractResultListener {
  4 + // TODO puhui999: @芋艿: 艿艿写一下这个,没研究明白哈哈
  5 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
1 package cn.iocoder.yudao.module.crm.service.customer; 1 package cn.iocoder.yudao.module.crm.service.customer;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO;  
5 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; 4 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
9 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; 6 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
10 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; 7 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
@@ -109,6 +106,16 @@ public interface CrmCustomerService { @@ -109,6 +106,16 @@ public interface CrmCustomerService {
109 */ 106 */
110 Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId); 107 Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId);
111 108
  109 + /**
  110 + * 批量导入客户
  111 + *
  112 + * @param importCustomers 导入客户列表
  113 + * @param isUpdateSupport 是否支持更新
  114 + * @param userId 用户编号
  115 + * @return 导入结果
  116 + */
  117 + CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, Boolean isUpdateSupport, Long userId);
  118 +
112 // ==================== 公海相关操作 ==================== 119 // ==================== 公海相关操作 ====================
113 120
114 /** 121 /**
@@ -127,13 +134,12 @@ public interface CrmCustomerService { @@ -127,13 +134,12 @@ public interface CrmCustomerService {
127 */ 134 */
128 void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive); 135 void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive);
129 136
130 - // TODO @puhui999:autoPutCustomerPool,注释说明是系统就好哈;  
131 /** 137 /**
132 * 【系统】客户自动掉入公海 138 * 【系统】客户自动掉入公海
133 * 139 *
134 * @return 掉入公海数量 140 * @return 掉入公海数量
135 */ 141 */
136 - int customerAutoPutPoolBySystem(); 142 + int autoPutCustomerPool();
137 143
138 PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO, 144 PageResult<CrmCustomerDO> getPutInPoolRemindCustomerPage(CrmCustomerPageReqVO pageVO,
139 CrmCustomerPoolConfigDO poolConfigDO, 145 CrmCustomerPoolConfigDO poolConfigDO,
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -3,15 +3,14 @@ package cn.iocoder.yudao.module.crm.service.customer; @@ -3,15 +3,14 @@ package cn.iocoder.yudao.module.crm.service.customer;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.core.util.StrUtil;
6 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
  8 +import cn.iocoder.yudao.framework.common.exception.ServiceException;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 10 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
9 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 11 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 12 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerLockReqVO;  
12 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;  
13 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;  
14 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; 13 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
15 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; 14 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 15 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
@@ -41,12 +40,10 @@ import org.springframework.transaction.annotation.Transactional; @@ -41,12 +40,10 @@ import org.springframework.transaction.annotation.Transactional;
41 import org.springframework.validation.annotation.Validated; 40 import org.springframework.validation.annotation.Validated;
42 41
43 import java.time.LocalDateTime; 42 import java.time.LocalDateTime;
44 -import java.util.ArrayList;  
45 -import java.util.Collection;  
46 -import java.util.Collections;  
47 -import java.util.List; 43 +import java.util.*;
48 44
49 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.filterList;
50 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 47 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
51 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 48 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
52 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;
@@ -96,7 +93,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -96,7 +93,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
96 validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1); 93 validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);
97 94
98 // 2. 插入客户 95 // 2. 插入客户
99 - CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class) 96 + CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class).setOwnerUserId(userId)
100 .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); 97 .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
101 customerMapper.insert(customer); 98 customerMapper.insert(customer);
102 99
@@ -235,7 +232,55 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -235,7 +232,55 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
235 return customer.getId(); 232 return customer.getId();
236 } 233 }
237 234
238 -// ==================== 公海相关操作 ==================== 235 + @Override
  236 + public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, Boolean isUpdateSupport, Long userId) {
  237 + if (CollUtil.isEmpty(importCustomers)) {
  238 + throw exception(CUSTOMER_IMPORT_LIST_IS_EMPTY);
  239 + }
  240 + CrmCustomerImportRespVO respVO = CrmCustomerImportRespVO.builder().createCustomerNames(new ArrayList<>())
  241 + .updateCustomerNames(new ArrayList<>()).failureCustomerNames(new LinkedHashMap<>()).build();
  242 + importCustomers.forEach(importCustomer -> {
  243 + // 校验,判断是否有不符合的原因
  244 + try {
  245 + validateCustomerForCreate(importCustomer);
  246 + } catch (ServiceException ex) {
  247 + respVO.getFailureCustomerNames().put(importCustomer.getName(), ex.getMessage());
  248 + return;
  249 + }
  250 + // 判断如果不存在,在进行插入
  251 + CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName());
  252 + if (existCustomer == null) {
  253 + CrmCustomerDO customer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class).setOwnerUserId(userId)
  254 + .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
  255 + customerMapper.insert(customer);
  256 + respVO.getCreateCustomerNames().add(importCustomer.getName());
  257 + // 创建数据权限
  258 + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
  259 + .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
  260 + return;
  261 + }
  262 + // 如果存在,判断是否允许更新
  263 + if (!isUpdateSupport) {
  264 + respVO.getFailureCustomerNames().put(importCustomer.getName(),
  265 + StrUtil.format(CUSTOMER_NAME_EXISTS.getMsg(), importCustomer.getName()));
  266 + return;
  267 + }
  268 + CrmCustomerDO updateCustomer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class);
  269 + updateCustomer.setId(existCustomer.getId());
  270 + customerMapper.updateById(updateCustomer);
  271 + respVO.getUpdateCustomerNames().add(importCustomer.getName());
  272 + });
  273 + return respVO;
  274 + }
  275 +
  276 + private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) {
  277 + // 校验客户名称不能为空
  278 + if (StrUtil.isEmptyIfStr(importCustomer.getName())) {
  279 + throw exception(CUSTOMER_CREATE_NAME_NOT_NULL);
  280 + }
  281 + }
  282 +
  283 + // ==================== 公海相关操作 ====================
239 284
240 @Override 285 @Override
241 @Transactional(rollbackFor = Exception.class) 286 @Transactional(rollbackFor = Exception.class)
@@ -313,23 +358,22 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -313,23 +358,22 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
313 } 358 }
314 359
315 @Override 360 @Override
316 - public int customerAutoPutPoolBySystem() { 361 + public int autoPutCustomerPool() {
317 CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig(); 362 CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
318 if (poolConfig == null || !poolConfig.getEnabled()) { 363 if (poolConfig == null || !poolConfig.getEnabled()) {
319 return 0; 364 return 0;
320 } 365 }
321 - // 1. 获取没有锁定的不在公海的客户  
322 - List<CrmCustomerDO> customerList = customerMapper.selectListByLockStatusAndOwnerUserIdNotNull(Boolean.FALSE);  
323 - List<CrmCustomerDO> poolCustomerList = CollectionUtils.filterList(customerList, customer -> {  
324 - // TODO @puhui999:建议这里作为一个查询条件哈,不放内存里过滤;  
325 - // 1.1 未成交放入公海  
326 - if (!customer.getDealStatus()) {  
327 - return (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0;  
328 - }  
329 - // 1.2 未跟进放入公海 366 + // 1.1 获取没有锁定的不在公海的客户且没有成交的
  367 + List<CrmCustomerDO> notDealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.FALSE);
  368 + // 1.2 获取没有锁定的不在公海的客户且成交的
  369 + List<CrmCustomerDO> dealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.TRUE);
  370 + List<CrmCustomerDO> poolCustomerList = new ArrayList<>();
  371 + poolCustomerList.addAll(filterList(notDealCustomerList, customer ->
  372 + (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0));
  373 + poolCustomerList.addAll(filterList(dealCustomerList, customer -> {
330 LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime()); 374 LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
331 return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0; 375 return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0;
332 - }); 376 + }));
333 377
334 // 2. 逐个放入公海 378 // 2. 逐个放入公海
335 int count = 0; 379 int count = 0;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
@@ -33,7 +33,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -33,7 +33,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
34 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED; 34 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED;
35 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS; 35 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
36 -import static cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils.hasPermission;  
37 36
38 /** 37 /**
39 * 跟进记录 Service 实现类 38 * 跟进记录 Service 实现类
@@ -119,7 +118,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -119,7 +118,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
119 // 校验存在 118 // 校验存在
120 CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id); 119 CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id);
121 // 校验权限 120 // 校验权限
122 - if (!hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) { 121 + if (!permissionService.hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) {
123 throw exception(FOLLOW_UP_RECORD_DELETE_DENIED); 122 throw exception(FOLLOW_UP_RECORD_DELETE_DENIED);
124 } 123 }
125 124
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java
@@ -108,4 +108,15 @@ public interface CrmPermissionService { @@ -108,4 +108,15 @@ public interface CrmPermissionService {
108 */ 108 */
109 List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId); 109 List<CrmPermissionDO> getPermissionListByBizTypeAndUserId(Integer bizType, Long userId);
110 110
  111 + /**
  112 + * 校验权限
  113 + *
  114 + * @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
  115 + * @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
  116 + * @param userId 用户编号
  117 + * @param levelEnum 权限级别
  118 + * @return boolean
  119 + */
  120 + boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum);
  121 +
111 } 122 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
@@ -24,6 +24,7 @@ import java.util.List; @@ -24,6 +24,7 @@ import java.util.List;
24 import java.util.Set; 24 import java.util.Set;
25 25
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
28 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 29 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
29 import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner; 30 import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner;
@@ -211,4 +212,11 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { @@ -211,4 +212,11 @@ public class CrmPermissionServiceImpl implements CrmPermissionService {
211 return permissionMapper.selectListByBizTypeAndUserId(bizType, userId); 212 return permissionMapper.selectListByBizTypeAndUserId(bizType, userId);
212 } 213 }
213 214
  215 + @Override
  216 + public boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum) {
  217 + List<CrmPermissionDO> permissionList = permissionMapper.selectByBizTypeAndBizId(bizType, bizId);
  218 + return anyMatch(permissionList, permission ->
  219 + ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), levelEnum.getLevel()));
  220 + }
  221 +
214 } 222 }