Commit bb9173f7f01a6064b55720ff321a4fc8dc0b7929

Authored by 芋道源码
Committed by Gitee
2 parents 9fbbce4e ba6218b8

!864 CRM-合同:完善 TODO

Merge pull request !864 from puhui999/develop
Showing 31 changed files with 464 additions and 127 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java
@@ -38,6 +38,22 @@ public class MoneyUtils { @@ -38,6 +38,22 @@ public class MoneyUtils {
38 /** 38 /**
39 * 计算百分比金额 39 * 计算百分比金额
40 * 40 *
  41 + * @param price 金额(单位分)
  42 + * @param count 数量
  43 + * @param percent 折扣(单位分),列如 60.2%,则传入 6020
  44 + * @return 商品总价
  45 + */
  46 + public static Integer calculator(Integer price, Integer count, Integer percent) {
  47 + price = price * count;
  48 + if (percent == null) {
  49 + return price;
  50 + }
  51 + return MoneyUtils.calculateRatePriceFloor(price, (double) (percent / 100));
  52 + }
  53 +
  54 + /**
  55 + * 计算百分比金额
  56 + *
41 * @param price 金额 57 * @param price 金额
42 * @param rate 百分比,例如说 56.77% 则传入 56.77 58 * @param rate 百分比,例如说 56.77% 则传入 56.77
43 * @param scale 保留小数位数 59 * @param scale 保留小数位数
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.api.listener;
  2 +
  3 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
  4 +
  5 +/**
  6 + * 业务流程实例的结果发生变化的监听器 Api
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +public interface BpmResultListenerApi {
  11 +
  12 + /**
  13 + * 监听的流程定义 Key
  14 + *
  15 + * @return 返回监听的流程定义 Key
  16 + */
  17 + String getProcessDefinitionKey();
  18 +
  19 + /**
  20 + * 处理事件
  21 + *
  22 + * @param event 事件
  23 + */
  24 + void onEvent(BpmResultListenerRespDTO event);
  25 +
  26 +}
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.api.listener.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * 业务流程实例的结果 Response DTO
  7 + *
  8 + * @author HUIHUI
  9 + */
  10 +@Data
  11 +public class BpmResultListenerRespDTO {
  12 +
  13 + /**
  14 + * 流程实例的编号
  15 + */
  16 + private String id;
  17 + /**
  18 + * 流程实例的 key
  19 + */
  20 + private String processDefinitionKey;
  21 + /**
  22 + * 流程实例的结果
  23 + */
  24 + private Integer result;
  25 + /**
  26 + * 流程实例对应的业务标识
  27 + * 例如说,请假
  28 + */
  29 + private String businessKey;
  30 +
  31 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
@@ -43,7 +43,7 @@ public class BpmModelController { @@ -43,7 +43,7 @@ public class BpmModelController {
43 return success(model); 43 return success(model);
44 } 44 }
45 45
46 - // TODO @puhui999:这个接口的目的是啥呀? 46 + // TODO @puhui999:这个接口的目的是啥呀?业务表单预览流程🤣
47 @GetMapping("/get-by-key") 47 @GetMapping("/get-by-key")
48 @Operation(summary = "获得模型") 48 @Operation(summary = "获得模型")
49 @Parameter(name = "key", description = "流程标识", required = true, example = "oa_leave") 49 @Parameter(name = "key", description = "流程标识", required = true, example = "oa_leave")
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.framework.bpm.listener;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi;
  6 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
  7 +import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;
  8 +import jakarta.annotation.Resource;
  9 +import org.springframework.context.ApplicationListener;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * 业务流程结果监听器实现类
  16 + *
  17 + * @author HUIHUI
  18 + */
  19 +@Component
  20 +public class BpmServiceResultListener implements ApplicationListener<BpmProcessInstanceResultEvent> {
  21 +
  22 + @Resource
  23 + private List<BpmResultListenerApi> bpmResultListenerApis;
  24 +
  25 + @Override
  26 + public final void onApplicationEvent(BpmProcessInstanceResultEvent event) {
  27 + bpmResultListenerApis.forEach(bpmResultListenerApi -> {
  28 + if (!StrUtil.equals(event.getProcessDefinitionKey(), bpmResultListenerApi.getProcessDefinitionKey())) {
  29 + return;
  30 + }
  31 + bpmResultListenerApi.onEvent(BeanUtils.toBean(event, BpmResultListenerRespDTO.class));
  32 + });
  33 + }
  34 +
  35 +}
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -12,6 +12,7 @@ public interface ErrorCodeConstants { @@ -12,6 +12,7 @@ public interface ErrorCodeConstants {
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 ErrorCode CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_000_001, "更新合同失败,原因:禁止编辑");
  15 + ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态");
15 16
16 // ========== 线索管理 1-020-001-000 ========== 17 // ========== 线索管理 1-020-001-000 ==========
17 ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); 18 ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
@@ -39,6 +39,8 @@ public interface LogRecordConstants { @@ -39,6 +39,8 @@ public interface LogRecordConstants {
39 String CRM_CUSTOMER_POOL_SUCCESS = "将客户【{{#customerName}}】放入了公海"; 39 String CRM_CUSTOMER_POOL_SUCCESS = "将客户【{{#customerName}}】放入了公海";
40 String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分配客户' : '领取客户'}}"; 40 String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分配客户' : '领取客户'}}";
41 String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户【' + #customer.name + '】分配给【' + #ownerUserName + '】' : '领取客户【' + #customer.name + '】'}}"; 41 String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户【' + #customer.name + '】分配给【' + #ownerUserName + '】' : '领取客户【' + #customer.name + '】'}}";
  42 + String CRM_CUSTOMER_IMPORT_SUB_TYPE = "{{#isUpdate ? '导入并更新客户' : '导入客户'}}";
  43 + String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}";
42 44
43 // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 ======================= 45 // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 =======================
44 46
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
@@ -169,7 +169,7 @@ public class CrmBusinessController { @@ -169,7 +169,7 @@ public class CrmBusinessController {
169 @PutMapping("/transfer") 169 @PutMapping("/transfer")
170 @Operation(summary = "商机转移") 170 @Operation(summary = "商机转移")
171 @PreAuthorize("@ss.hasPermission('crm:business:update')") 171 @PreAuthorize("@ss.hasPermission('crm:business:update')")
172 - public CommonResult<Boolean> transfer(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) { 172 + public CommonResult<Boolean> transferBusiness(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) {
173 businessService.transferBusiness(reqVO, getLoginUserId()); 173 businessService.transferBusiness(reqVO, getLoginUserId());
174 return success(true); 174 return success(true);
175 } 175 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
@@ -91,7 +91,7 @@ public class CrmClueController { @@ -91,7 +91,7 @@ public class CrmClueController {
91 @PutMapping("/transfer") 91 @PutMapping("/transfer")
92 @Operation(summary = "线索转移") 92 @Operation(summary = "线索转移")
93 @PreAuthorize("@ss.hasPermission('crm:clue:update')") 93 @PreAuthorize("@ss.hasPermission('crm:clue:update')")
94 - public CommonResult<Boolean> transfer(@Valid @RequestBody CrmClueTransferReqVO reqVO) { 94 + public CommonResult<Boolean> transferClue(@Valid @RequestBody CrmClueTransferReqVO reqVO) {
95 clueService.transferClue(reqVO, getLoginUserId()); 95 clueService.transferClue(reqVO, getLoginUserId());
96 return success(true); 96 return success(true);
97 } 97 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
@@ -102,7 +102,7 @@ public class CrmContactController { @@ -102,7 +102,7 @@ public class CrmContactController {
102 List<CrmCustomerDO> customerList = customerService.getCustomerList( 102 List<CrmCustomerDO> customerList = customerService.getCustomerList(
103 Collections.singletonList(contact.getCustomerId())); 103 Collections.singletonList(contact.getCustomerId()));
104 // 3. 直属上级 104 // 3. 直属上级
105 - List<CrmContactDO> parentContactList = contactService.getContactList( 105 + List<CrmContactDO> parentContactList = contactService.getContactListByIds(
106 Collections.singletonList(contact.getParentId()), getLoginUserId()); 106 Collections.singletonList(contact.getParentId()), getLoginUserId());
107 return success(CrmContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList)); 107 return success(CrmContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList));
108 } 108 }
@@ -112,7 +112,7 @@ public class CrmContactController { @@ -112,7 +112,7 @@ public class CrmContactController {
112 @Parameter(name = "ids", description = "编号", required = true, example = "[1024]") 112 @Parameter(name = "ids", description = "编号", required = true, example = "[1024]")
113 @PreAuthorize("@ss.hasPermission('crm:contact:query')") 113 @PreAuthorize("@ss.hasPermission('crm:contact:query')")
114 public CommonResult<List<CrmContactRespVO>> getContactListByIds(@RequestParam("ids") List<Long> ids) { 114 public CommonResult<List<CrmContactRespVO>> getContactListByIds(@RequestParam("ids") List<Long> ids) {
115 - return success(BeanUtils.toBean(contactService.getContactList(ids, getLoginUserId()), CrmContactRespVO.class)); 115 + return success(BeanUtils.toBean(contactService.getContactListByIds(ids, getLoginUserId()), CrmContactRespVO.class));
116 } 116 }
117 117
118 @GetMapping("/simple-all-list") 118 @GetMapping("/simple-all-list")
@@ -170,7 +170,7 @@ public class CrmContactController { @@ -170,7 +170,7 @@ public class CrmContactController {
170 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, 170 Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList,
171 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 171 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
172 // 3. 直属上级 172 // 3. 直属上级
173 - List<CrmContactDO> parentContactList = contactService.getContactList( 173 + List<CrmContactDO> parentContactList = contactService.getContactListByIds(
174 convertSet(contactList, CrmContactDO::getParentId), getLoginUserId()); 174 convertSet(contactList, CrmContactDO::getParentId), getLoginUserId());
175 return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList); 175 return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList);
176 } 176 }
@@ -178,7 +178,7 @@ public class CrmContactController { @@ -178,7 +178,7 @@ public class CrmContactController {
178 @PutMapping("/transfer") 178 @PutMapping("/transfer")
179 @Operation(summary = "联系人转移") 179 @Operation(summary = "联系人转移")
180 @PreAuthorize("@ss.hasPermission('crm:contact:update')") 180 @PreAuthorize("@ss.hasPermission('crm:contact:update')")
181 - public CommonResult<Boolean> transfer(@Valid @RequestBody CrmContactTransferReqVO reqVO) { 181 + public CommonResult<Boolean> transferContact(@Valid @RequestBody CrmContactTransferReqVO reqVO) {
182 contactService.transferContact(reqVO, getLoginUserId()); 182 contactService.transferContact(reqVO, getLoginUserId());
183 return success(true); 183 return success(true);
184 } 184 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
@@ -10,10 +10,18 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; @@ -10,10 +10,18 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
11 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*; 11 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
12 import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert; 12 import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
  13 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  14 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
  15 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
13 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  18 +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
  19 +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService;
  20 +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
  21 +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
15 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 22 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
16 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; 23 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
  24 +import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
17 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 25 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
18 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 26 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
19 import io.swagger.v3.oas.annotations.Operation; 27 import io.swagger.v3.oas.annotations.Operation;
@@ -28,13 +36,14 @@ import org.springframework.validation.annotation.Validated; @@ -28,13 +36,14 @@ import org.springframework.validation.annotation.Validated;
28 import org.springframework.web.bind.annotation.*; 36 import org.springframework.web.bind.annotation.*;
29 37
30 import java.io.IOException; 38 import java.io.IOException;
  39 +import java.util.Collections;
31 import java.util.List; 40 import java.util.List;
32 import java.util.Map; 41 import java.util.Map;
33 import java.util.stream.Stream; 42 import java.util.stream.Stream;
34 43
35 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 44 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
36 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;  
37 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 45 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
  46 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
38 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 47 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
39 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 48 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
40 49
@@ -48,6 +57,15 @@ public class CrmContractController { @@ -48,6 +57,15 @@ public class CrmContractController {
48 private CrmContractService contractService; 57 private CrmContractService contractService;
49 @Resource 58 @Resource
50 private CrmCustomerService customerService; 59 private CrmCustomerService customerService;
  60 + @Resource
  61 + private CrmContactService contactService;
  62 + @Resource
  63 + private CrmBusinessService businessService;
  64 + @Resource
  65 + @Lazy
  66 + private CrmBusinessProductService businessProductService;
  67 + @Resource
  68 + private CrmProductService productService;
51 69
52 @Resource 70 @Resource
53 private AdminUserApi adminUserApi; 71 private AdminUserApi adminUserApi;
@@ -82,7 +100,19 @@ public class CrmContractController { @@ -82,7 +100,19 @@ public class CrmContractController {
82 @PreAuthorize("@ss.hasPermission('crm:contract:query')") 100 @PreAuthorize("@ss.hasPermission('crm:contract:query')")
83 public CommonResult<CrmContractRespVO> getContract(@RequestParam("id") Long id) { 101 public CommonResult<CrmContractRespVO> getContract(@RequestParam("id") Long id) {
84 CrmContractDO contract = contractService.getContract(id); 102 CrmContractDO contract = contractService.getContract(id);
85 - return success(BeanUtils.toBean(contract, CrmContractRespVO.class)); 103 + List<CrmContractRespVO> respVOList = buildContractDetail(Collections.singletonList(contract));
  104 + CrmContractRespVO respVO = respVOList.getFirst();
  105 + List<CrmBusinessProductDO> businessProductList = businessProductService.getBusinessProductListByContractId(id);
  106 + Map<Long, CrmBusinessProductDO> businessProductMap = convertMap(businessProductList, CrmBusinessProductDO::getProductId);
  107 + List<CrmProductDO> productList = productService.getProductListByIds(convertSet(businessProductList, CrmBusinessProductDO::getProductId));
  108 + respVO.setProductItems(convertList(productList, product -> {
  109 + CrmContractRespVO.CrmContractProductItemRespVO productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.CrmContractProductItemRespVO.class);
  110 + findAndThen(businessProductMap, product.getId(), businessProduct -> {
  111 + productItemRespVO.setCount(businessProduct.getCount()).setDiscountPercent(businessProduct.getDiscountPercent());
  112 + });
  113 + return productItemRespVO;
  114 + }));
  115 + return success(respVO);
86 } 116 }
87 117
88 @GetMapping("/page") 118 @GetMapping("/page")
@@ -90,15 +120,15 @@ public class CrmContractController { @@ -90,15 +120,15 @@ public class CrmContractController {
90 @PreAuthorize("@ss.hasPermission('crm:contract:query')") 120 @PreAuthorize("@ss.hasPermission('crm:contract:query')")
91 public CommonResult<PageResult<CrmContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) { 121 public CommonResult<PageResult<CrmContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) {
92 PageResult<CrmContractDO> pageResult = contractService.getContractPage(pageVO, getLoginUserId()); 122 PageResult<CrmContractDO> pageResult = contractService.getContractPage(pageVO, getLoginUserId());
93 - return success(buildContractDetailPage(pageResult)); 123 + return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetail(pageResult.getList())));
94 } 124 }
95 125
96 @GetMapping("/page-by-customer") 126 @GetMapping("/page-by-customer")
97 - @Operation(summary = "获得联系人分页,基于指定客户") 127 + @Operation(summary = "获得合同分页,基于指定客户")
98 public CommonResult<PageResult<CrmContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) { 128 public CommonResult<PageResult<CrmContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) {
99 Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空"); 129 Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
100 PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO); 130 PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO);
101 - return success(buildContractDetailPage(pageResult)); 131 + return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetail(pageResult.getList())));
102 } 132 }
103 133
104 @GetMapping("/export-excel") 134 @GetMapping("/export-excel")
@@ -114,40 +144,43 @@ public class CrmContractController { @@ -114,40 +144,43 @@ public class CrmContractController {
114 } 144 }
115 145
116 /** 146 /**
117 - * 构建详细的合同分页结果 147 + * 构建详细的合同结果
118 * 148 *
119 - * @param pageResult 简单的合同分页结果  
120 - * @return 详细的合同分页结果 149 + * @param contractList 原始合同信息
  150 + * @return 细的合同结果
121 */ 151 */
122 - private PageResult<CrmContractRespVO> buildContractDetailPage(PageResult<CrmContractDO> pageResult) {  
123 - List<CrmContractDO> contactList = pageResult.getList();  
124 - if (CollUtil.isEmpty(contactList)) {  
125 - return PageResult.empty(pageResult.getTotal()); 152 + private List<CrmContractRespVO> buildContractDetail(List<CrmContractDO> contractList) {
  153 + if (CollUtil.isEmpty(contractList)) {
  154 + return Collections.emptyList();
126 } 155 }
127 // 1. 获取客户列表 156 // 1. 获取客户列表
128 List<CrmCustomerDO> customerList = customerService.getCustomerList( 157 List<CrmCustomerDO> customerList = customerService.getCustomerList(
129 - convertSet(contactList, CrmContractDO::getCustomerId)); 158 + convertSet(contractList, CrmContractDO::getCustomerId));
130 // 2. 获取创建人、负责人列表 159 // 2. 获取创建人、负责人列表
131 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, 160 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contractList,
132 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); 161 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
133 - return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList); 162 + // 3. 获取联系人
  163 + Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactListByIds(convertSet(contractList,
  164 + CrmContractDO::getContactId)), CrmContactDO::getId);
  165 + // 4. 获取商机
  166 + Map<Long, CrmBusinessDO> businessMap = convertMap(businessService.getBusinessList(convertSet(contractList,
  167 + CrmContractDO::getBusinessId)), CrmBusinessDO::getId);
  168 + return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap);
134 } 169 }
135 170
136 - // TODO @puhui999:transferContract  
137 @PutMapping("/transfer") 171 @PutMapping("/transfer")
138 @Operation(summary = "合同转移") 172 @Operation(summary = "合同转移")
139 @PreAuthorize("@ss.hasPermission('crm:contract:update')") 173 @PreAuthorize("@ss.hasPermission('crm:contract:update')")
140 - public CommonResult<Boolean> transfer(@Valid @RequestBody CrmContractTransferReqVO reqVO) { 174 + public CommonResult<Boolean> transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) {
141 contractService.transferContract(reqVO, getLoginUserId()); 175 contractService.transferContract(reqVO, getLoginUserId());
142 return success(true); 176 return success(true);
143 } 177 }
144 178
145 - // TODO @puhui999:方法名不对哈;要不改成 submit?提交审核的意思  
146 - @PutMapping("/approve")  
147 - @Operation(summary = "发起合同审批流程") 179 + @PutMapping("/submit")
  180 + @Operation(summary = "提交合同审批")
148 @PreAuthorize("@ss.hasPermission('crm:contract:update')") 181 @PreAuthorize("@ss.hasPermission('crm:contract:update')")
149 - public CommonResult<Boolean> transfer(@RequestParam("id") Long id) {  
150 - contractService.handleApprove(id, getLoginUserId()); 182 + public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) {
  183 + contractService.submitContract(id, getLoginUserId());
151 return success(true); 184 return success(true);
152 } 185 }
153 186
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java
@@ -3,10 +3,13 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract.vo; @@ -3,10 +3,13 @@ package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
3 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; 3 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
4 import com.alibaba.excel.annotation.ExcelProperty; 4 import com.alibaba.excel.annotation.ExcelProperty;
5 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
  6 +import lombok.AllArgsConstructor;
6 import lombok.Data; 7 import lombok.Data;
  8 +import lombok.NoArgsConstructor;
7 import org.springframework.format.annotation.DateTimeFormat; 9 import org.springframework.format.annotation.DateTimeFormat;
8 10
9 import java.time.LocalDateTime; 11 import java.time.LocalDateTime;
  12 +import java.util.List;
10 13
11 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 14 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
12 15
@@ -26,10 +29,16 @@ public class CrmContractRespVO { @@ -26,10 +29,16 @@ public class CrmContractRespVO {
26 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336") 29 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336")
27 @ExcelProperty("客户编号") 30 @ExcelProperty("客户编号")
28 private Long customerId; 31 private Long customerId;
  32 + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336")
  33 + @ExcelProperty("客户名称")
  34 + private String customerName;
29 35
30 @Schema(description = "商机编号", example = "10864") 36 @Schema(description = "商机编号", example = "10864")
31 @ExcelProperty("商机编号") 37 @ExcelProperty("商机编号")
32 private Long businessId; 38 private Long businessId;
  39 + @Schema(description = "商机名称", example = "10864")
  40 + @ExcelProperty("商机名称")
  41 + private String businessName;
33 42
34 @Schema(description = "工作流编号", example = "1043") 43 @Schema(description = "工作流编号", example = "1043")
35 @ExcelProperty("工作流编号") 44 @ExcelProperty("工作流编号")
@@ -74,10 +83,16 @@ public class CrmContractRespVO { @@ -74,10 +83,16 @@ public class CrmContractRespVO {
74 @Schema(description = "联系人编号", example = "18546") 83 @Schema(description = "联系人编号", example = "18546")
75 @ExcelProperty("联系人编号") 84 @ExcelProperty("联系人编号")
76 private Long contactId; 85 private Long contactId;
  86 + @Schema(description = "联系人编号", example = "18546")
  87 + @ExcelProperty("联系人编号")
  88 + private String contactName;
77 89
78 @Schema(description = "公司签约人", example = "14036") 90 @Schema(description = "公司签约人", example = "14036")
79 @ExcelProperty("公司签约人") 91 @ExcelProperty("公司签约人")
80 private Long signUserId; 92 private Long signUserId;
  93 + @Schema(description = "公司签约人", example = "14036")
  94 + @ExcelProperty("公司签约人")
  95 + private String signUserName;
81 96
82 @Schema(description = "最后跟进时间") 97 @Schema(description = "最后跟进时间")
83 @ExcelProperty("最后跟进时间") 98 @ExcelProperty("最后跟进时间")
@@ -101,10 +116,6 @@ public class CrmContractRespVO { @@ -101,10 +116,6 @@ public class CrmContractRespVO {
101 @ExcelProperty("创建人名字") 116 @ExcelProperty("创建人名字")
102 private String creatorName; 117 private String creatorName;
103 118
104 - @Schema(description = "客户名字", example = "test")  
105 - @ExcelProperty("客户名字")  
106 - private String customerName;  
107 -  
108 @Schema(description = "负责人", example = "test") 119 @Schema(description = "负责人", example = "test")
109 @ExcelProperty("负责人") 120 @ExcelProperty("负责人")
110 private String ownerUserName; 121 private String ownerUserName;
@@ -113,4 +124,36 @@ public class CrmContractRespVO { @@ -113,4 +124,36 @@ public class CrmContractRespVO {
113 @ExcelProperty("审批状态") 124 @ExcelProperty("审批状态")
114 private Integer auditStatus; 125 private Integer auditStatus;
115 126
  127 + @Schema(description = "产品列表")
  128 + private List<CrmContractProductItemRespVO> productItems;
  129 +
  130 + @Schema(description = "产品列表")
  131 + @Data
  132 + @NoArgsConstructor
  133 + @AllArgsConstructor
  134 + public static class CrmContractProductItemRespVO {
  135 +
  136 + @Schema(description = "产品编号", example = "20529")
  137 + private Long id;
  138 +
  139 + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  140 + private String name;
  141 +
  142 + @Schema(description = "产品编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  143 + private String no;
  144 +
  145 + @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  146 + private Integer unit;
  147 +
  148 + @Schema(description = "价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  149 + private Integer price;
  150 +
  151 + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
  152 + private Integer count;
  153 +
  154 + @Schema(description = "产品折扣")
  155 + private Integer discountPercent;
  156 +
  157 + }
  158 +
116 } 159 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java
@@ -89,14 +89,11 @@ public class CrmContractSaveReqVO { @@ -89,14 +89,11 @@ public class CrmContractSaveReqVO {
89 @DiffLogField(name = "备注") 89 @DiffLogField(name = "备注")
90 private String remark; 90 private String remark;
91 91
92 - // TODO @puhui999:这个字段,按道理不用传递?  
93 - @Schema(description = "审批状态", example = "1")  
94 - private Integer auditStatus;  
95 92
96 @Schema(description = "产品列表") 93 @Schema(description = "产品列表")
97 private List<CrmContractProductItem> productItems; 94 private List<CrmContractProductItem> productItems;
98 95
99 - @Schema(description = "商品属性") 96 + @Schema(description = "产品列表")
100 @Data 97 @Data
101 @NoArgsConstructor 98 @NoArgsConstructor
102 @AllArgsConstructor 99 @AllArgsConstructor
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -235,7 +235,7 @@ public class CrmCustomerController { @@ -235,7 +235,7 @@ public class CrmCustomerController {
235 @PutMapping("/transfer") 235 @PutMapping("/transfer")
236 @Operation(summary = "转移客户") 236 @Operation(summary = "转移客户")
237 @PreAuthorize("@ss.hasPermission('crm:customer:update')") 237 @PreAuthorize("@ss.hasPermission('crm:customer:update')")
238 - public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { 238 + public CommonResult<Boolean> transferCustomer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
239 customerService.transferCustomer(reqVO, getLoginUserId()); 239 customerService.transferCustomer(reqVO, getLoginUserId());
240 return success(true); 240 return success(true);
241 } 241 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java
@@ -75,7 +75,7 @@ public class CrmFollowUpRecordController { @@ -75,7 +75,7 @@ public class CrmFollowUpRecordController {
75 public CommonResult<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) { 75 public CommonResult<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) {
76 PageResult<CrmFollowUpRecordDO> pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO); 76 PageResult<CrmFollowUpRecordDO> pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO);
77 /// 拼接数据 77 /// 拼接数据
78 - Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactList( 78 + Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactListByIds(
79 convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream())), CrmContactDO::getId); 79 convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream())), CrmContactDO::getId);
80 Map<Long, CrmBusinessDO> businessMap = convertMap(businessService.getBusinessList( 80 Map<Long, CrmBusinessDO> businessMap = convertMap(businessService.getBusinessList(
81 convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream())), CrmBusinessDO::getId); 81 convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream())), CrmBusinessDO::getId);
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java
1 package cn.iocoder.yudao.module.crm.convert.contract; 1 package cn.iocoder.yudao.module.crm.convert.contract;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO; 4 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
  6 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  7 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
9 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; 10 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
@@ -31,17 +32,21 @@ public interface CrmContractConvert { @@ -31,17 +32,21 @@ public interface CrmContractConvert {
31 @Mapping(target = "bizId", source = "reqVO.id") 32 @Mapping(target = "bizId", source = "reqVO.id")
32 CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId); 33 CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId);
33 34
34 - default PageResult<CrmContractRespVO> convertPage(PageResult<CrmContractDO> pageResult, Map<Long, AdminUserRespDTO> userMap,  
35 - List<CrmCustomerDO> customerList) {  
36 - PageResult<CrmContractRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmContractRespVO.class); 35 + default List<CrmContractRespVO> convertList(List<CrmContractDO> contractList, Map<Long, AdminUserRespDTO> userMap,
  36 + List<CrmCustomerDO> customerList, Map<Long, CrmContactDO> contactMap,
  37 + Map<Long, CrmBusinessDO> businessMap) {
  38 + List<CrmContractRespVO> respVOList = BeanUtils.toBean(contractList, CrmContractRespVO.class);
37 // 拼接关联字段 39 // 拼接关联字段
38 Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId); 40 Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);
39 - voPageResult.getList().forEach(contract -> { 41 + respVOList.forEach(contract -> {
40 findAndThen(userMap, contract.getOwnerUserId(), user -> contract.setOwnerUserName(user.getNickname())); 42 findAndThen(userMap, contract.getOwnerUserId(), user -> contract.setOwnerUserName(user.getNickname()));
41 findAndThen(userMap, Long.parseLong(contract.getCreator()), user -> contract.setCreatorName(user.getNickname())); 43 findAndThen(userMap, Long.parseLong(contract.getCreator()), user -> contract.setCreatorName(user.getNickname()));
  44 + findAndThen(userMap, contract.getSignUserId(), user -> contract.setSignUserName(user.getNickname()));
42 findAndThen(customerMap, contract.getCustomerId(), customer -> contract.setCustomerName(customer.getName())); 45 findAndThen(customerMap, contract.getCustomerId(), customer -> contract.setCustomerName(customer.getName()));
  46 + findAndThen(contactMap, contract.getContactId(), contact -> contract.setContactName(contact.getName()));
  47 + findAndThen(businessMap, contract.getBusinessId(), business -> contract.setBusinessName(business.getName()));
43 }); 48 });
44 - return voPageResult; 49 + return respVOList;
45 } 50 }
46 51
47 } 52 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java
1 package cn.iocoder.yudao.module.crm.dal.dataobject.business; 1 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.contract.CrmContractDO;
4 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; 5 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
5 import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; 6 import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
6 import com.baomidou.mybatisplus.annotation.KeySequence; 7 import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -40,6 +41,12 @@ public class CrmBusinessProductDO extends BaseDO { @@ -40,6 +41,12 @@ public class CrmBusinessProductDO extends BaseDO {
40 * 关联 {@link CrmProductDO#getId()} 41 * 关联 {@link CrmProductDO#getId()}
41 */ 42 */
42 private Long productId; 43 private Long productId;
  44 + /**
  45 + * 合同编号
  46 + *
  47 + * 关联 {@link CrmContractDO#getId()}
  48 + */
  49 + private Long contractId;
43 50
44 /** 51 /**
45 * 产品单价 52 * 产品单价
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java
@@ -45,7 +45,7 @@ public class CrmProductDO extends BaseDO { @@ -45,7 +45,7 @@ public class CrmProductDO extends BaseDO {
45 /** 45 /**
46 * 价格,单位:分 46 * 价格,单位:分
47 */ 47 */
48 - private Long price; 48 + private Integer price;
49 /** 49 /**
50 * 状态 50 * 状态
51 * 51 *
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -101,10 +101,9 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -101,10 +101,9 @@ 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> selectListByLockAndDealStatusAndNotPool(Boolean lockStatus, Boolean dealStatus) { 104 + default List<CrmCustomerDO> selectListByLockAndNotPool(Boolean lockStatus) {
105 return selectList(new LambdaQueryWrapper<CrmCustomerDO>() 105 return selectList(new LambdaQueryWrapper<CrmCustomerDO>()
106 .eq(CrmCustomerDO::getLockStatus, lockStatus) 106 .eq(CrmCustomerDO::getLockStatus, lockStatus)
107 - .eq(CrmCustomerDO::getDealStatus, dealStatus)  
108 .gt(CrmCustomerDO::getOwnerUserId, 0)); 107 .gt(CrmCustomerDO::getOwnerUserId, 0));
109 } 108 }
110 109
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.product; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.dal.mysql.product;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
6 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; 8 import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
@@ -9,6 +10,9 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; @@ -9,6 +10,9 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
9 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; 10 import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
10 import org.apache.ibatis.annotations.Mapper; 11 import org.apache.ibatis.annotations.Mapper;
11 12
  13 +import java.util.Collection;
  14 +import java.util.List;
  15 +
12 /** 16 /**
13 * CRM 产品 Mapper 17 * CRM 产品 Mapper
14 * 18 *
@@ -34,4 +38,8 @@ public interface CrmProductMapper extends BaseMapperX&lt;CrmProductDO&gt; { @@ -34,4 +38,8 @@ public interface CrmProductMapper extends BaseMapperX&lt;CrmProductDO&gt; {
34 return selectOne(CrmProductDO::getNo, no); 38 return selectOne(CrmProductDO::getNo, no);
35 } 39 }
36 40
  41 + default List<CrmProductDO> selectListByIds(Collection<Long> ids) {
  42 + return selectList(new LambdaQueryWrapperX<CrmProductDO>().in(CrmProductDO::getId, ids));
  43 + }
  44 +
37 } 45 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductService.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
5 import java.util.List; 5 import java.util.List;
6 6
7 // TODO @lzxhqs:方法名上,带下 BusinessProduct;主要考虑不精简的原因,是因为一个逻辑,可能会出现一些超越它自身方法,省略不容易懂; 7 // TODO @lzxhqs:方法名上,带下 BusinessProduct;主要考虑不精简的原因,是因为一个逻辑,可能会出现一些超越它自身方法,省略不容易懂;
  8 +
8 /** 9 /**
9 * 商机产品关联表 Service 接口 10 * 商机产品关联表 Service 接口
10 * 11 *
@@ -14,31 +15,45 @@ public interface CrmBusinessProductService { @@ -14,31 +15,45 @@ public interface CrmBusinessProductService {
14 15
15 /** 16 /**
16 * 批量新增商机产品关联数据 17 * 批量新增商机产品关联数据
  18 + *
17 * @param list 商机产品集合 19 * @param list 商机产品集合
18 */ 20 */
19 - void insertBatch(List<CrmBusinessProductDO> list); 21 + void createBusinessProductBatch(List<CrmBusinessProductDO> list);
20 22
21 /** 23 /**
22 * 根据商机id获取商机产品关联数据集合 24 * 根据商机id获取商机产品关联数据集合
  25 + *
23 * @param businessId 商机id 26 * @param businessId 商机id
24 */ 27 */
25 - List<CrmBusinessProductDO> selectListByBusinessId(Long businessId); 28 + List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId);
26 29
27 /** 30 /**
28 * 批量更新商机产品表 31 * 批量更新商机产品表
  32 + *
29 * @param list 商机产品数据集合 33 * @param list 商机产品数据集合
30 */ 34 */
31 - void updateBatch(List<CrmBusinessProductDO> list); 35 + void updateBusinessProductBatch(List<CrmBusinessProductDO> list);
32 36
33 /** 37 /**
34 * 批量删除 38 * 批量删除
  39 + *
35 * @param list 需要删除的商机产品集合 40 * @param list 需要删除的商机产品集合
36 */ 41 */
37 - void deleteBatch(List<CrmBusinessProductDO> list); 42 + void deleteBusinessProductBatch(List<CrmBusinessProductDO> list);
38 43
39 /** 44 /**
40 - *根据商机id删除商机产品关联数据 45 + * 根据商机id删除商机产品关联数据
  46 + *
41 * @param businessId 商机id 47 * @param businessId 商机id
42 */ 48 */
43 - void deleteByBusinessId(Long businessId); 49 + void deleteBusinessProductByBusinessId(Long businessId);
  50 +
  51 + /**
  52 + * 获得合同关联的商品列表
  53 + *
  54 + * @param contractId 合同编号
  55 + * @return 关联的商品列表
  56 + */
  57 + List<CrmBusinessProductDO> getBusinessProductListByContractId(Long contractId);
  58 +
44 } 59 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductServiceImpl.java
@@ -22,29 +22,34 @@ public class CrmBusinessProductServiceImpl implements CrmBusinessProductService @@ -22,29 +22,34 @@ public class CrmBusinessProductServiceImpl implements CrmBusinessProductService
22 private CrmBusinessProductMapper businessProductMapper; 22 private CrmBusinessProductMapper businessProductMapper;
23 23
24 @Override 24 @Override
25 - public void insertBatch(List<CrmBusinessProductDO> list) { 25 + public void createBusinessProductBatch(List<CrmBusinessProductDO> list) {
26 businessProductMapper.insertBatch(list); 26 businessProductMapper.insertBatch(list);
27 } 27 }
28 28
29 @Override 29 @Override
30 - public List<CrmBusinessProductDO> selectListByBusinessId(Long businessId) {  
31 - return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId,businessId); 30 + public List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId) {
  31 + return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId, businessId);
32 } 32 }
33 33
34 @Override 34 @Override
35 - public void updateBatch(List<CrmBusinessProductDO> list) { 35 + public void updateBusinessProductBatch(List<CrmBusinessProductDO> list) {
36 businessProductMapper.updateBatch(list); 36 businessProductMapper.updateBatch(list);
37 } 37 }
38 38
39 // TODO @lzxhqs:这个方法,可以直接调用 deleteList 方法,然后传递 ids 就好了; 39 // TODO @lzxhqs:这个方法,可以直接调用 deleteList 方法,然后传递 ids 就好了;
40 @Override 40 @Override
41 - public void deleteBatch(List<CrmBusinessProductDO> list) { 41 + public void deleteBusinessProductBatch(List<CrmBusinessProductDO> list) {
42 businessProductMapper.deleteBatchIds(CollectionUtils.convertList(list, CrmBusinessProductDO::getId)); 42 businessProductMapper.deleteBatchIds(CollectionUtils.convertList(list, CrmBusinessProductDO::getId));
43 } 43 }
44 44
45 @Override 45 @Override
46 - public void deleteByBusinessId(Long businessId) { 46 + public void deleteBusinessProductByBusinessId(Long businessId) {
47 businessProductMapper.deleteByBusinessId(businessId); 47 businessProductMapper.deleteByBusinessId(businessId);
48 } 48 }
49 49
  50 + @Override
  51 + public List<CrmBusinessProductDO> getBusinessProductListByContractId(Long contractId) {
  52 + return businessProductMapper.selectList(CrmBusinessProductDO::getContractId, contractId);
  53 + }
  54 +
50 } 55 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
@@ -120,20 +120,20 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -120,20 +120,20 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
120 CrmBusinessProductConvert.INSTANCE.convert(product).setBusinessId(businessId)); 120 CrmBusinessProductConvert.INSTANCE.convert(product).setBusinessId(businessId));
121 if (Boolean.TRUE.equals(updateFlag)) { 121 if (Boolean.TRUE.equals(updateFlag)) {
122 // 根据商机 id从商机产品关联表中获取已存在的数据集合 122 // 根据商机 id从商机产品关联表中获取已存在的数据集合
123 - List<CrmBusinessProductDO> oldProducts = businessProductService.selectListByBusinessId(businessId); 123 + List<CrmBusinessProductDO> oldProducts = businessProductService.getBusinessProductListByBusinessId(businessId);
124 List<List<CrmBusinessProductDO>> diffList = CollectionUtils.diffList(oldProducts, list, (oldValue, newValue) -> 124 List<List<CrmBusinessProductDO>> diffList = CollectionUtils.diffList(oldProducts, list, (oldValue, newValue) ->
125 ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId())); 125 ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId()));
126 if (CollUtil.isNotEmpty(diffList.getFirst())) { 126 if (CollUtil.isNotEmpty(diffList.getFirst())) {
127 - businessProductService.insertBatch(diffList.getFirst()); 127 + businessProductService.createBusinessProductBatch(diffList.getFirst());
128 } 128 }
129 if (CollUtil.isNotEmpty(diffList.get(1))) { 129 if (CollUtil.isNotEmpty(diffList.get(1))) {
130 - businessProductService.updateBatch(diffList.get(1)); 130 + businessProductService.updateBusinessProductBatch(diffList.get(1));
131 } 131 }
132 if (CollUtil.isNotEmpty(diffList.get(2))) { 132 if (CollUtil.isNotEmpty(diffList.get(2))) {
133 - businessProductService.deleteBatch(diffList.get(2)); 133 + businessProductService.deleteBusinessProductBatch(diffList.get(2));
134 } 134 }
135 } else { 135 } else {
136 - businessProductService.insertBatch(list); 136 + businessProductService.createBusinessProductBatch(list);
137 } 137 }
138 } 138 }
139 139
@@ -154,7 +154,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -154,7 +154,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
154 if (CollUtil.isNotEmpty(updateReqVO.getProducts())) { 154 if (CollUtil.isNotEmpty(updateReqVO.getProducts())) {
155 createBusinessProducts(updateReqVO.getProducts(), updateReqVO.getId(), true); 155 createBusinessProducts(updateReqVO.getProducts(), updateReqVO.getId(), true);
156 } else { 156 } else {
157 - businessProductService.deleteByBusinessId(updateReqVO.getId()); 157 + businessProductService.deleteBusinessProductByBusinessId(updateReqVO.getId());
158 } 158 }
159 159
160 // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表 160 // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表
@@ -196,7 +196,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { @@ -196,7 +196,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
196 */ 196 */
197 private void validateContractExists(Long businessId) { 197 private void validateContractExists(Long businessId) {
198 // TODO @lzxhqs:保持风格的统一,selectCountByBusinessId 改成 getContractCountByBusinessId;另外,可以不用声明 count,因为就一次性使用,直接把 197 和 198 合并成一行; 198 // TODO @lzxhqs:保持风格的统一,selectCountByBusinessId 改成 getContractCountByBusinessId;另外,可以不用声明 count,因为就一次性使用,直接把 197 和 198 合并成一行;
199 - Long count = contractService.selectCountByBusinessId(businessId); 199 + Long count = contractService.getContractCountByBusinessId(businessId);
200 if (count > 0) { 200 if (count > 0) {
201 throw exception(BUSINESS_CONTRACT_EXISTS); 201 throw exception(BUSINESS_CONTRACT_EXISTS);
202 } 202 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
@@ -80,7 +80,7 @@ public interface CrmContactService { @@ -80,7 +80,7 @@ public interface CrmContactService {
80 * @param userId 用户编号 80 * @param userId 用户编号
81 * @return 联系人列表 81 * @return 联系人列表
82 */ 82 */
83 - List<CrmContactDO> getContactList(Collection<Long> ids, Long userId); 83 + List<CrmContactDO> getContactListByIds(Collection<Long> ids, Long userId);
84 84
85 /** 85 /**
86 * 获得联系人列表 86 * 获得联系人列表
@@ -88,7 +88,7 @@ public interface CrmContactService { @@ -88,7 +88,7 @@ public interface CrmContactService {
88 * @param ids 编号 88 * @param ids 编号
89 * @return 联系人列表 89 * @return 联系人列表
90 */ 90 */
91 - List<CrmContactDO> getContactList(Collection<Long> ids); 91 + List<CrmContactDO> getContactListByIds(Collection<Long> ids);
92 92
93 /** 93 /**
94 * 获得联系人列表 94 * 获得联系人列表
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
@@ -25,6 +25,7 @@ import com.mzt.logapi.context.LogRecordContext; @@ -25,6 +25,7 @@ import com.mzt.logapi.context.LogRecordContext;
25 import com.mzt.logapi.service.impl.DiffParseFunction; 25 import com.mzt.logapi.service.impl.DiffParseFunction;
26 import com.mzt.logapi.starter.annotation.LogRecord; 26 import com.mzt.logapi.starter.annotation.LogRecord;
27 import jakarta.annotation.Resource; 27 import jakarta.annotation.Resource;
  28 +import org.springframework.context.annotation.Lazy;
28 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
29 import org.springframework.transaction.annotation.Transactional; 30 import org.springframework.transaction.annotation.Transactional;
30 import org.springframework.validation.annotation.Validated; 31 import org.springframework.validation.annotation.Validated;
@@ -56,6 +57,7 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -56,6 +57,7 @@ public class CrmContactServiceImpl implements CrmContactService {
56 @Resource 57 @Resource
57 private CrmPermissionService permissionService; 58 private CrmPermissionService permissionService;
58 @Resource 59 @Resource
  60 + @Lazy
59 private CrmContractService contractService; 61 private CrmContractService contractService;
60 @Resource 62 @Resource
61 private CrmContactBusinessService contactBusinessService; 63 private CrmContactBusinessService contactBusinessService;
@@ -208,7 +210,7 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -208,7 +210,7 @@ public class CrmContactServiceImpl implements CrmContactService {
208 } 210 }
209 211
210 @Override 212 @Override
211 - public List<CrmContactDO> getContactList(Collection<Long> ids, Long userId) { 213 + public List<CrmContactDO> getContactListByIds(Collection<Long> ids, Long userId) {
212 if (CollUtil.isEmpty(ids)) { 214 if (CollUtil.isEmpty(ids)) {
213 return ListUtil.empty(); 215 return ListUtil.empty();
214 } 216 }
@@ -216,7 +218,7 @@ public class CrmContactServiceImpl implements CrmContactService { @@ -216,7 +218,7 @@ public class CrmContactServiceImpl implements CrmContactService {
216 } 218 }
217 219
218 @Override 220 @Override
219 - public List<CrmContactDO> getContactList(Collection<Long> ids) { 221 + public List<CrmContactDO> getContactListByIds(Collection<Long> ids) {
220 if (CollUtil.isEmpty(ids)) { 222 if (CollUtil.isEmpty(ids)) {
221 return ListUtil.empty(); 223 return ListUtil.empty();
222 } 224 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
1 package cn.iocoder.yudao.module.crm.service.contract; 1 package cn.iocoder.yudao.module.crm.service.contract;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
4 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
5 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
@@ -63,9 +64,15 @@ public interface CrmContractService { @@ -63,9 +64,15 @@ public interface CrmContractService {
63 * @param id 合同编号 64 * @param id 合同编号
64 * @param userId 用户编号 65 * @param userId 用户编号
65 */ 66 */
66 - void handleApprove(Long id, Long userId); 67 + void submitContract(Long id, Long userId);
67 68
68 /** 69 /**
  70 + * 更新合同流程审批结果
  71 + *
  72 + * @param event 审批结果
  73 + */
  74 + void updateContractAuditStatus(BpmResultListenerRespDTO event);
  75 + /**
69 * 获得合同 76 * 获得合同
70 * 77 *
71 * @param id 编号 78 * @param id 编号
@@ -118,13 +125,12 @@ public interface CrmContractService { @@ -118,13 +125,12 @@ public interface CrmContractService {
118 */ 125 */
119 Long getContractCountByCustomerId(Long customerId); 126 Long getContractCountByCustomerId(Long customerId);
120 127
121 - // TODO @puhui999:要不改成 getContractCountByBusinessId  
122 /** 128 /**
123 * 根据商机ID获取关联客户的合同数量 129 * 根据商机ID获取关联客户的合同数量
124 * 130 *
125 * @param businessId 商机编号 131 * @param businessId 商机编号
126 * @return 数量 132 * @return 数量
127 */ 133 */
128 - Long selectCountByBusinessId(Long businessId); 134 + Long getContractCountByBusinessId(Long businessId);
129 135
130 } 136 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -2,10 +2,13 @@ package cn.iocoder.yudao.module.crm.service.contract; @@ -2,10 +2,13 @@ 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.lang.Assert;
5 import cn.hutool.core.util.ObjUtil; 6 import cn.hutool.core.util.ObjUtil;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; 8 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  10 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  11 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
9 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; 12 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
10 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; 13 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
11 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; 14 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
@@ -87,8 +90,11 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -87,8 +90,11 @@ public class CrmContractServiceImpl implements CrmContractService {
87 CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null); 90 CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null);
88 contractMapper.insert(contract); 91 contractMapper.insert(contract);
89 // 1.2 插入商机关联商品 92 // 1.2 插入商机关联商品
90 - List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO);  
91 - businessProductService.insertBatch(businessProduct); 93 + if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
  94 + List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO, contract.getId());
  95 + businessProductService.createBusinessProductBatch(businessProduct);
  96 + // 更新合同商品总金额
  97 + }
92 98
93 // 2. 创建数据权限 99 // 2. 创建数据权限
94 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) 100 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
@@ -106,9 +112,11 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -106,9 +112,11 @@ public class CrmContractServiceImpl implements CrmContractService {
106 success = CRM_CONTRACT_UPDATE_SUCCESS) 112 success = CRM_CONTRACT_UPDATE_SUCCESS)
107 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) 113 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
108 public void updateContract(CrmContractSaveReqVO updateReqVO) { 114 public void updateContract(CrmContractSaveReqVO updateReqVO) {
109 - // TODO @合同待定:只有草稿、审批中,可以编辑;  
110 - if (ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) ||  
111 - ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) { 115 + Assert.notNull(updateReqVO.getId(), "合同编号不能为空");
  116 + CrmContractDO contract = validateContractExists(updateReqVO.getId());
  117 + // 只有草稿、审批中,可以编辑;
  118 + if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
  119 + CrmAuditStatusEnum.PROCESS.getStatus())) {
112 throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED); 120 throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED);
113 } 121 }
114 validateRelationDataExists(updateReqVO); 122 validateRelationDataExists(updateReqVO);
@@ -117,21 +125,41 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -117,21 +125,41 @@ public class CrmContractServiceImpl implements CrmContractService {
117 // 更新合同 125 // 更新合同
118 CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class); 126 CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class);
119 contractMapper.updateById(updateObj); 127 contractMapper.updateById(updateObj);
120 -  
121 - // TODO puhui999: @芋艿:合同变更关联的商机后商品怎么处理?  
122 - // TODO @puhui999:和商品 spu、sku 编辑一样;新增的插入;修改的更新;删除的删除  
123 - //List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(updateReqVO);  
124 - //businessProductService.selectListByBusinessId()  
125 - //diffList() 128 + // 更新合同关联商品
  129 + updateContractProduct(updateReqVO, updateObj.getId());
126 130
127 // 3. 记录操作日志上下文 131 // 3. 记录操作日志上下文
128 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class)); 132 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class));
129 LogRecordContext.putVariable("contractName", oldContract.getName()); 133 LogRecordContext.putVariable("contractName", oldContract.getName());
130 } 134 }
131 135
  136 + private void updateContractProduct(CrmContractSaveReqVO updateReqVO, Long contractId) {
  137 + if (CollUtil.isEmpty(updateReqVO.getProductItems())) {
  138 + return;
  139 + }
  140 + List<CrmBusinessProductDO> newProductList = convertBusinessProductList(updateReqVO, contractId);
  141 + List<CrmBusinessProductDO> oldProductList = businessProductService.getBusinessProductListByContractId(contractId);
  142 + List<List<CrmBusinessProductDO>> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> {
  143 + if (ObjUtil.notEqual(oldObj.getProductId(), newObj.getProductId())) {
  144 + return false;
  145 + }
  146 + newObj.setId(oldObj.getId()); // 设置一下老的编号更新时需要使用
  147 + return true;
  148 + });
  149 + if (CollUtil.isNotEmpty(diffList.getFirst())) {
  150 + businessProductService.createBusinessProductBatch(diffList.getFirst());
  151 + }
  152 + if (CollUtil.isNotEmpty(diffList.get(1))) {
  153 + businessProductService.updateBusinessProductBatch(diffList.get(1));
  154 + }
  155 + if (CollUtil.isNotEmpty(diffList.get(2))) {
  156 + businessProductService.deleteBusinessProductBatch(diffList.get(2));
  157 + }
  158 + }
  159 +
132 // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum 160 // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
133 161
134 - private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO) { 162 + private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO, Long contractId) {
135 // 校验商品存在 163 // 校验商品存在
136 Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId); 164 Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId);
137 List<CrmProductDO> productList = productService.getProductList(productIds); 165 List<CrmProductDO> productList = productService.getProductList(productIds);
@@ -140,30 +168,15 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -140,30 +168,15 @@ public class CrmContractServiceImpl implements CrmContractService {
140 } 168 }
141 Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId); 169 Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
142 return convertList(reqVO.getProductItems(), productItem -> { 170 return convertList(reqVO.getProductItems(), productItem -> {
143 - // TODO @puhui999:这里可以改成直接 return,不用弄一个 businessProduct 变量哈;  
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; 171 + CrmProductDO product = productMap.get(productItem.getId());
  172 + return BeanUtils.toBean(product, CrmBusinessProductDO.class)
  173 + .setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId()).setContractId(contractId)
  174 + .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent())
  175 + .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent()));
148 }); 176 });
149 } 177 }
150 178
151 /** 179 /**
152 - * 计算商品总价  
153 - *  
154 - * @param businessProduct 关联商品  
155 - * @return 商品总价  
156 - */  
157 - // TODO @puhui999:这个逻辑的计算,是不是可以封装到 calculateRatePriceFloor 里;  
158 - private Integer calculator(CrmBusinessProductDO businessProduct) {  
159 - int price = businessProduct.getPrice() * businessProduct.getCount();  
160 - if (businessProduct.getDiscountPercent() == null) {  
161 - return price;  
162 - }  
163 - return MoneyUtils.calculateRatePriceFloor(price, (double) (businessProduct.getDiscountPercent() / 100));  
164 - }  
165 -  
166 - /**  
167 * 校验关联数据是否存在 180 * 校验关联数据是否存在
168 * 181 *
169 * @param reqVO 请求 182 * @param reqVO 请求
@@ -235,9 +248,12 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -235,9 +248,12 @@ public class CrmContractServiceImpl implements CrmContractService {
235 248
236 @Override 249 @Override
237 @Transactional(rollbackFor = Exception.class) 250 @Transactional(rollbackFor = Exception.class)
238 - public void handleApprove(Long id, Long userId) {  
239 - // TODO @puhui999:需要做状态检查  
240 - 251 + public void submitContract(Long id, Long userId) {
  252 + // 1. 校验合同是否在审批
  253 + CrmContractDO contract = validateContractExists(id);
  254 + if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
  255 + throw exception(CONTRACT_SUBMIT_FAIL_NOT_DRAFT);
  256 + }
241 // 创建合同审批流程实例 257 // 创建合同审批流程实例
242 String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() 258 String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
243 .setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id))); 259 .setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id)));
@@ -247,6 +263,12 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -247,6 +263,12 @@ public class CrmContractServiceImpl implements CrmContractService {
247 .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus())); 263 .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
248 } 264 }
249 265
  266 + @Override
  267 + public void updateContractAuditStatus(BpmResultListenerRespDTO event) {
  268 + contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey()))
  269 + .setAuditStatus(event.getResult()));
  270 + }
  271 +
250 //======================= 查询相关 ======================= 272 //======================= 查询相关 =======================
251 273
252 @Override 274 @Override
@@ -285,7 +307,7 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -285,7 +307,7 @@ public class CrmContractServiceImpl implements CrmContractService {
285 } 307 }
286 308
287 @Override 309 @Override
288 - public Long selectCountByBusinessId(Long businessId) { 310 + public Long getContractCountByBusinessId(Long businessId) {
289 return contractMapper.selectCountByBusinessId(businessId); 311 return contractMapper.selectCountByBusinessId(businessId);
290 } 312 }
291 // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒; 313 // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java
1 package cn.iocoder.yudao.module.crm.service.contract.listener; 1 package cn.iocoder.yudao.module.crm.service.contract.listener;
2 2
3 -public class CrmContractResultListener {  
4 - // TODO puhui999: @芋艿: 艿艿写一下这个,没研究明白哈哈 3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  5 +import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi;
  6 +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
  7 +import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
  8 +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
  9 +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
  10 +import cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl;
  11 +import jakarta.annotation.Resource;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +/**
  15 + * 合同审批的结果的监听器实现类
  16 + *
  17 + * @author HUIHUI
  18 + */
  19 +@Component
  20 +public class CrmContractResultListener implements BpmResultListenerApi {
  21 +
  22 + @Resource
  23 + private CrmContractService contractService;
  24 +
  25 + @Override
  26 + public String getProcessDefinitionKey() {
  27 + return CrmContractServiceImpl.CONTRACT_APPROVE;
  28 + }
  29 +
  30 + @Override
  31 + public void onEvent(BpmResultListenerRespDTO event) {
  32 + boolean currentTaskFinish = isEndResult(event.getResult());
  33 + if (!currentTaskFinish) {
  34 + return;
  35 + }
  36 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) {
  37 + event.setResult(CrmAuditStatusEnum.APPROVE.getStatus());
  38 + }
  39 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) {
  40 + event.setResult(CrmAuditStatusEnum.REJECT.getStatus());
  41 + }
  42 + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) {
  43 + event.setResult(CrmAuditStatusEnum.CANCEL.getStatus());
  44 + }
  45 + contractService.updateContractAuditStatus(event);
  46 + }
  47 +
  48 + /**
  49 + * 判断该结果是否处于 End 最终结果
  50 + *
  51 + * @param result 结果
  52 + * @return 是否
  53 + */
  54 + public static boolean isEndResult(Integer result) {
  55 + return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(),
  56 + BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult());
  57 + }
  58 +
5 } 59 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -93,8 +93,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -93,8 +93,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
93 validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1); 93 validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);
94 94
95 // 2. 插入客户 95 // 2. 插入客户
96 - CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class).setOwnerUserId(userId)  
97 - .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); 96 + CrmCustomerDO customer = initCustomer(createReqVO, userId);
98 customerMapper.insert(customer); 97 customerMapper.insert(customer);
99 98
100 // 3. 创建数据权限 99 // 3. 创建数据权限
@@ -232,7 +231,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -232,7 +231,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
232 return customer.getId(); 231 return customer.getId();
233 } 232 }
234 233
235 - // TODO @puhui999:操作日志  
236 @Override 234 @Override
237 public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, 235 public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers,
238 Boolean isUpdateSupport, Long userId) { 236 Boolean isUpdateSupport, Long userId) {
@@ -253,14 +251,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -253,14 +251,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
253 // 判断如果不存在,在进行插入 251 // 判断如果不存在,在进行插入
254 CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName()); 252 CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName());
255 if (existCustomer == null) { 253 if (existCustomer == null) {
256 - // TODO @puhui999:可以搞个 initCustomer 方法;这样可以把 create 和导入复用下这个方法;  
257 - CrmCustomerDO customer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class).setOwnerUserId(userId)  
258 - .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); 254 + CrmCustomerDO customer = initCustomer(importCustomer, userId);
259 customerMapper.insert(customer); 255 customerMapper.insert(customer);
260 respVO.getCreateCustomerNames().add(importCustomer.getName()); 256 respVO.getCreateCustomerNames().add(importCustomer.getName());
261 // 创建数据权限 257 // 创建数据权限
262 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) 258 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
263 .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 259 .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
  260 + // 记录操作日志
  261 + getSelf().importCustomerLog(customer, false);
264 return; 262 return;
265 } 263 }
266 // 如果存在,判断是否允许更新 264 // 如果存在,判断是否允许更新
@@ -273,10 +271,25 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -273,10 +271,25 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
273 updateCustomer.setId(existCustomer.getId()); 271 updateCustomer.setId(existCustomer.getId());
274 customerMapper.updateById(updateCustomer); 272 customerMapper.updateById(updateCustomer);
275 respVO.getUpdateCustomerNames().add(importCustomer.getName()); 273 respVO.getUpdateCustomerNames().add(importCustomer.getName());
  274 + // 记录操作日志
  275 + getSelf().importCustomerLog(updateCustomer, true);
276 }); 276 });
277 return respVO; 277 return respVO;
278 } 278 }
279 279
  280 + private static CrmCustomerDO initCustomer(Object customer, Long userId) {
  281 + return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(userId)
  282 + .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
  283 + }
  284 +
  285 + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_IMPORT_SUB_TYPE, bizNo = "{{#customer.id}}",
  286 + success = CRM_CUSTOMER_IMPORT_SUCCESS)
  287 + public void importCustomerLog(CrmCustomerDO customer, boolean isUpdate) {
  288 + // 记录操作日志上下文
  289 + LogRecordContext.putVariable("customer", customer);
  290 + LogRecordContext.putVariable("isUpdate", isUpdate);
  291 + }
  292 +
280 private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) { 293 private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) {
281 // 校验客户名称不能为空 294 // 校验客户名称不能为空
282 if (StrUtil.isEmptyIfStr(importCustomer.getName())) { 295 if (StrUtil.isEmptyIfStr(importCustomer.getName())) {
@@ -367,15 +380,15 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -367,15 +380,15 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
367 if (poolConfig == null || !poolConfig.getEnabled()) { 380 if (poolConfig == null || !poolConfig.getEnabled()) {
368 return 0; 381 return 0;
369 } 382 }
370 - // 1.1 获取没有锁定的不在公海的客户且没有成交的  
371 - List<CrmCustomerDO> notDealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.FALSE);  
372 - // 1.2 获取没有锁定的不在公海的客户且成交的  
373 - // TODO @puhui999:下面也搞到 sql 里去哈;写 or 查询,问题不大的;  
374 - List<CrmCustomerDO> dealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.TRUE); 383 + // 1.1 获取没有锁定的不在公海的客户
  384 + List<CrmCustomerDO> customerList = customerMapper.selectListByLockAndNotPool(Boolean.FALSE);
375 List<CrmCustomerDO> poolCustomerList = new ArrayList<>(); 385 List<CrmCustomerDO> poolCustomerList = new ArrayList<>();
376 - poolCustomerList.addAll(filterList(notDealCustomerList, customer ->  
377 - (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0));  
378 - poolCustomerList.addAll(filterList(dealCustomerList, customer -> { 386 + poolCustomerList.addAll(filterList(customerList, customer ->
  387 + !customer.getDealStatus() && (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0));
  388 + poolCustomerList.addAll(filterList(customerList, customer -> {
  389 + if (!customer.getDealStatus()) { // 这里只处理成交的
  390 + return false;
  391 + }
379 LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime()); 392 LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
380 return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0; 393 return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0;
381 })); 394 }));
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java
@@ -69,4 +69,13 @@ public interface CrmProductService { @@ -69,4 +69,13 @@ public interface CrmProductService {
69 * @return 产品 69 * @return 产品
70 */ 70 */
71 CrmProductDO getProductByCategoryId(Long categoryId); 71 CrmProductDO getProductByCategoryId(Long categoryId);
  72 +
  73 + /**
  74 + * 获得产品列表
  75 + *
  76 + * @param ids 产品编号
  77 + * @return 产品列表
  78 + */
  79 + List<CrmProductDO> getProductListByIds(Collection<Long> ids);
  80 +
72 } 81 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java
@@ -155,4 +155,12 @@ public class CrmProductServiceImpl implements CrmProductService { @@ -155,4 +155,12 @@ public class CrmProductServiceImpl implements CrmProductService {
155 return productMapper.selectOne(new LambdaQueryWrapper<CrmProductDO>().eq(CrmProductDO::getCategoryId, categoryId)); 155 return productMapper.selectOne(new LambdaQueryWrapper<CrmProductDO>().eq(CrmProductDO::getCategoryId, categoryId));
156 } 156 }
157 157
  158 + @Override
  159 + public List<CrmProductDO> getProductListByIds(Collection<Long> ids) {
  160 + if (CollUtil.isEmpty(ids)) {
  161 + return Collections.emptyList();
  162 + }
  163 + return productMapper.selectListByIds(ids);
  164 + }
  165 +
158 } 166 }