Commit 59e6f23788af5cd79888eaeada5519b5f952a2ed
1 parent
1a96fb2f
CRM-CONTRACT: 完善 TODO
Showing
16 changed files
with
171 additions
and
71 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 | 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 | 57 | * @param price 金额 |
| 42 | 58 | * @param rate 百分比,例如说 56.77% 则传入 56.77 |
| 43 | 59 | * @param scale 保留小数位数 | ... | ... |
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 | 43 | return success(model); |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | - // TODO @puhui999:这个接口的目的是啥呀? | |
| 46 | + // TODO @puhui999:这个接口的目的是啥呀?业务表单预览流程🤣 | |
| 47 | 47 | @GetMapping("/get-by-key") |
| 48 | 48 | @Operation(summary = "获得模型") |
| 49 | 49 | @Parameter(name = "key", description = "流程标识", required = true, example = "oa_leave") | ... | ... |
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 | 12 | // ========== 合同管理 1-020-000-000 ========== |
| 13 | 13 | ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在"); |
| 14 | 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 | 17 | // ========== 线索管理 1-020-001-000 ========== |
| 17 | 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 | 39 | String CRM_CUSTOMER_POOL_SUCCESS = "将客户【{{#customerName}}】放入了公海"; |
| 40 | 40 | String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分配客户' : '领取客户'}}"; |
| 41 | 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 | 45 | // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 ======================= |
| 44 | 46 | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
| ... | ... | @@ -132,21 +132,19 @@ public class CrmContractController { |
| 132 | 132 | return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList); |
| 133 | 133 | } |
| 134 | 134 | |
| 135 | - // TODO @puhui999:transferContract | |
| 136 | - @PutMapping("/transfer") | |
| 135 | + @PutMapping("/transfer-contract") | |
| 137 | 136 | @Operation(summary = "合同转移") |
| 138 | 137 | @PreAuthorize("@ss.hasPermission('crm:contract:update')") |
| 139 | - public CommonResult<Boolean> transfer(@Valid @RequestBody CrmContractTransferReqVO reqVO) { | |
| 138 | + public CommonResult<Boolean> transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) { | |
| 140 | 139 | contractService.transferContract(reqVO, getLoginUserId()); |
| 141 | 140 | return success(true); |
| 142 | 141 | } |
| 143 | 142 | |
| 144 | - // TODO @puhui999:方法名不对哈;要不改成 submit?提交审核的意思 | |
| 145 | - @PutMapping("/approve") | |
| 146 | - @Operation(summary = "发起合同审批流程") | |
| 143 | + @PutMapping("/submit") | |
| 144 | + @Operation(summary = "提交合同审批") | |
| 147 | 145 | @PreAuthorize("@ss.hasPermission('crm:contract:update')") |
| 148 | - public CommonResult<Boolean> transfer(@RequestParam("id") Long id) { | |
| 149 | - contractService.handleApprove(id, getLoginUserId()); | |
| 146 | + public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) { | |
| 147 | + contractService.submitContract(id, getLoginUserId()); | |
| 150 | 148 | return success(true); |
| 151 | 149 | } |
| 152 | 150 | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java
| ... | ... | @@ -89,9 +89,6 @@ public class CrmContractSaveReqVO { |
| 89 | 89 | @DiffLogField(name = "备注") |
| 90 | 90 | private String remark; |
| 91 | 91 | |
| 92 | - // TODO @puhui999:这个字段,按道理不用传递? | |
| 93 | - @Schema(description = "审批状态", example = "1") | |
| 94 | - private Integer auditStatus; | |
| 95 | 92 | |
| 96 | 93 | @Schema(description = "产品列表") |
| 97 | 94 | private List<CrmContractProductItem> productItems; | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.dal.dataobject.business; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; | |
| 4 | 5 | import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; |
| 5 | 6 | import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; |
| 6 | 7 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| ... | ... | @@ -40,6 +41,12 @@ public class CrmBusinessProductDO extends BaseDO { |
| 40 | 41 | * 关联 {@link CrmProductDO#getId()} |
| 41 | 42 | */ |
| 42 | 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
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<CrmCustomerDO> { |
| 101 | 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 | 105 | return selectList(new LambdaQueryWrapper<CrmCustomerDO>() |
| 106 | 106 | .eq(CrmCustomerDO::getLockStatus, lockStatus) |
| 107 | - .eq(CrmCustomerDO::getDealStatus, dealStatus) | |
| 108 | 107 | .gt(CrmCustomerDO::getOwnerUserId, 0)); |
| 109 | 108 | } |
| 110 | 109 | ... | ... |
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 | 5 | import java.util.List; |
| 6 | 6 | |
| 7 | 7 | // TODO @lzxhqs:方法名上,带下 BusinessProduct;主要考虑不精简的原因,是因为一个逻辑,可能会出现一些超越它自身方法,省略不容易懂; |
| 8 | + | |
| 8 | 9 | /** |
| 9 | 10 | * 商机产品关联表 Service 接口 |
| 10 | 11 | * |
| ... | ... | @@ -14,31 +15,45 @@ public interface CrmBusinessProductService { |
| 14 | 15 | |
| 15 | 16 | /** |
| 16 | 17 | * 批量新增商机产品关联数据 |
| 18 | + * | |
| 17 | 19 | * @param list 商机产品集合 |
| 18 | 20 | */ |
| 19 | 21 | void insertBatch(List<CrmBusinessProductDO> list); |
| 20 | 22 | |
| 21 | 23 | /** |
| 22 | 24 | * 根据商机id获取商机产品关联数据集合 |
| 25 | + * | |
| 23 | 26 | * @param businessId 商机id |
| 24 | 27 | */ |
| 25 | 28 | List<CrmBusinessProductDO> selectListByBusinessId(Long businessId); |
| 26 | 29 | |
| 27 | 30 | /** |
| 28 | 31 | * 批量更新商机产品表 |
| 32 | + * | |
| 29 | 33 | * @param list 商机产品数据集合 |
| 30 | 34 | */ |
| 31 | 35 | void updateBatch(List<CrmBusinessProductDO> list); |
| 32 | 36 | |
| 33 | 37 | /** |
| 34 | 38 | * 批量删除 |
| 39 | + * | |
| 35 | 40 | * @param list 需要删除的商机产品集合 |
| 36 | 41 | */ |
| 37 | 42 | void deleteBatch(List<CrmBusinessProductDO> list); |
| 38 | 43 | |
| 39 | 44 | /** |
| 40 | - *根据商机id删除商机产品关联数据 | |
| 45 | + * 根据商机id删除商机产品关联数据 | |
| 46 | + * | |
| 41 | 47 | * @param businessId 商机id |
| 42 | 48 | */ |
| 43 | 49 | void deleteByBusinessId(Long businessId); |
| 50 | + | |
| 51 | + /** | |
| 52 | + * 获得合同关联的商品列表 | |
| 53 | + * | |
| 54 | + * @param contractId 合同编号 | |
| 55 | + * @return 关联的商品列表 | |
| 56 | + */ | |
| 57 | + List<CrmBusinessProductDO> selectListByContractId(Long contractId); | |
| 58 | + | |
| 44 | 59 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductServiceImpl.java
| ... | ... | @@ -28,7 +28,7 @@ public class CrmBusinessProductServiceImpl implements CrmBusinessProductService |
| 28 | 28 | |
| 29 | 29 | @Override |
| 30 | 30 | public List<CrmBusinessProductDO> selectListByBusinessId(Long businessId) { |
| 31 | - return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId,businessId); | |
| 31 | + return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId, businessId); | |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | 34 | @Override |
| ... | ... | @@ -47,4 +47,9 @@ public class CrmBusinessProductServiceImpl implements CrmBusinessProductService |
| 47 | 47 | businessProductMapper.deleteByBusinessId(businessId); |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | + @Override | |
| 51 | + public List<CrmBusinessProductDO> selectListByContractId(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
| ... | ... | @@ -196,7 +196,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { |
| 196 | 196 | */ |
| 197 | 197 | private void validateContractExists(Long businessId) { |
| 198 | 198 | // TODO @lzxhqs:保持风格的统一,selectCountByBusinessId 改成 getContractCountByBusinessId;另外,可以不用声明 count,因为就一次性使用,直接把 197 和 198 合并成一行; |
| 199 | - Long count = contractService.selectCountByBusinessId(businessId); | |
| 199 | + Long count = contractService.getContractCountByBusinessId(businessId); | |
| 200 | 200 | if (count > 0) { |
| 201 | 201 | throw exception(BUSINESS_CONTRACT_EXISTS); |
| 202 | 202 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
| ... | ... | @@ -64,7 +64,7 @@ public interface CrmContractService { |
| 64 | 64 | * @param id 合同编号 |
| 65 | 65 | * @param userId 用户编号 |
| 66 | 66 | */ |
| 67 | - void handleApprove(Long id, Long userId); | |
| 67 | + void submitContract(Long id, Long userId); | |
| 68 | 68 | |
| 69 | 69 | /** |
| 70 | 70 | * 更新合同流程审批结果 |
| ... | ... | @@ -125,13 +125,12 @@ public interface CrmContractService { |
| 125 | 125 | */ |
| 126 | 126 | Long getContractCountByCustomerId(Long customerId); |
| 127 | 127 | |
| 128 | - // TODO @puhui999:要不改成 getContractCountByBusinessId | |
| 129 | 128 | /** |
| 130 | 129 | * 根据商机ID获取关联客户的合同数量 |
| 131 | 130 | * |
| 132 | 131 | * @param businessId 商机编号 |
| 133 | 132 | * @return 数量 |
| 134 | 133 | */ |
| 135 | - Long selectCountByBusinessId(Long businessId); | |
| 134 | + Long getContractCountByBusinessId(Long businessId); | |
| 136 | 135 | |
| 137 | 136 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
| ... | ... | @@ -2,10 +2,12 @@ package cn.iocoder.yudao.module.crm.service.contract; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.collection.ListUtil; |
| 5 | +import cn.hutool.core.lang.Assert; | |
| 5 | 6 | import cn.hutool.core.util.ObjUtil; |
| 6 | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; |
| 8 | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 10 | +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; | |
| 9 | 11 | import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; |
| 10 | 12 | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| 11 | 13 | import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; |
| ... | ... | @@ -87,8 +89,10 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 87 | 89 | CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null); |
| 88 | 90 | contractMapper.insert(contract); |
| 89 | 91 | // 1.2 插入商机关联商品 |
| 90 | - List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO); | |
| 91 | - businessProductService.insertBatch(businessProduct); | |
| 92 | + if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话 | |
| 93 | + List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO, contract.getId()); | |
| 94 | + businessProductService.insertBatch(businessProduct); | |
| 95 | + } | |
| 92 | 96 | |
| 93 | 97 | // 2. 创建数据权限 |
| 94 | 98 | crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) |
| ... | ... | @@ -106,9 +110,11 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 106 | 110 | success = CRM_CONTRACT_UPDATE_SUCCESS) |
| 107 | 111 | @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) |
| 108 | 112 | public void updateContract(CrmContractSaveReqVO updateReqVO) { |
| 109 | - // TODO @合同待定:只有草稿、审批中,可以编辑; | |
| 110 | - if (ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) || | |
| 111 | - ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) { | |
| 113 | + Assert.notNull(updateReqVO.getId(), "合同编号不能为空"); | |
| 114 | + CrmContractDO contract = validateContractExists(updateReqVO.getId()); | |
| 115 | + // 只有草稿、审批中,可以编辑; | |
| 116 | + if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) || | |
| 117 | + ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) { | |
| 112 | 118 | throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED); |
| 113 | 119 | } |
| 114 | 120 | validateRelationDataExists(updateReqVO); |
| ... | ... | @@ -117,21 +123,41 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 117 | 123 | // 更新合同 |
| 118 | 124 | CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class); |
| 119 | 125 | contractMapper.updateById(updateObj); |
| 120 | - | |
| 121 | - // TODO puhui999: @芋艿:合同变更关联的商机后商品怎么处理? | |
| 122 | - // TODO @puhui999:和商品 spu、sku 编辑一样;新增的插入;修改的更新;删除的删除 | |
| 123 | - //List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(updateReqVO); | |
| 124 | - //businessProductService.selectListByBusinessId() | |
| 125 | - //diffList() | |
| 126 | + // 更新合同关联商品 | |
| 127 | + updateContractProduct(updateReqVO, updateObj.getId()); | |
| 126 | 128 | |
| 127 | 129 | // 3. 记录操作日志上下文 |
| 128 | 130 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class)); |
| 129 | 131 | LogRecordContext.putVariable("contractName", oldContract.getName()); |
| 130 | 132 | } |
| 131 | 133 | |
| 134 | + private void updateContractProduct(CrmContractSaveReqVO updateReqVO, Long contractId) { | |
| 135 | + if (CollUtil.isEmpty(updateReqVO.getProductItems())) { | |
| 136 | + return; | |
| 137 | + } | |
| 138 | + List<CrmBusinessProductDO> newProductList = convertBusinessProductList(updateReqVO, contractId); | |
| 139 | + List<CrmBusinessProductDO> oldProductList = businessProductService.selectListByContractId(contractId); | |
| 140 | + List<List<CrmBusinessProductDO>> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> { | |
| 141 | + if (ObjUtil.notEqual(oldObj.getProductId(), newObj.getProductId())) { | |
| 142 | + return false; | |
| 143 | + } | |
| 144 | + newObj.setId(oldObj.getId()); // 设置一下老的编号更新时需要使用 | |
| 145 | + return true; | |
| 146 | + }); | |
| 147 | + if (CollUtil.isNotEmpty(diffList.getFirst())) { | |
| 148 | + businessProductService.insertBatch(diffList.getFirst()); | |
| 149 | + } | |
| 150 | + if (CollUtil.isNotEmpty(diffList.get(1))) { | |
| 151 | + businessProductService.updateBatch(diffList.get(1)); | |
| 152 | + } | |
| 153 | + if (CollUtil.isNotEmpty(diffList.get(2))) { | |
| 154 | + businessProductService.deleteBatch(diffList.get(2)); | |
| 155 | + } | |
| 156 | + } | |
| 157 | + | |
| 132 | 158 | // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum |
| 133 | 159 | |
| 134 | - private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO) { | |
| 160 | + private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO, Long contractId) { | |
| 135 | 161 | // 校验商品存在 |
| 136 | 162 | Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId); |
| 137 | 163 | List<CrmProductDO> productList = productService.getProductList(productIds); |
| ... | ... | @@ -140,30 +166,15 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 140 | 166 | } |
| 141 | 167 | Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId); |
| 142 | 168 | 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; | |
| 169 | + CrmProductDO product = productMap.get(productItem.getId()); | |
| 170 | + return BeanUtils.toBean(product, CrmBusinessProductDO.class) | |
| 171 | + .setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId()).setContractId(contractId) | |
| 172 | + .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()) | |
| 173 | + .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent())); | |
| 148 | 174 | }); |
| 149 | 175 | } |
| 150 | 176 | |
| 151 | 177 | /** |
| 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 | 178 | * 校验关联数据是否存在 |
| 168 | 179 | * |
| 169 | 180 | * @param reqVO 请求 |
| ... | ... | @@ -235,9 +246,12 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 235 | 246 | |
| 236 | 247 | @Override |
| 237 | 248 | @Transactional(rollbackFor = Exception.class) |
| 238 | - public void handleApprove(Long id, Long userId) { | |
| 239 | - // TODO @puhui999:需要做状态检查 | |
| 240 | - | |
| 249 | + public void submitContract(Long id, Long userId) { | |
| 250 | + // 1. 校验合同是否在审批 | |
| 251 | + CrmContractDO contract = validateContractExists(id); | |
| 252 | + if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) { | |
| 253 | + throw exception(CONTRACT_SUBMIT_FAIL_NOT_DRAFT); | |
| 254 | + } | |
| 241 | 255 | // 创建合同审批流程实例 |
| 242 | 256 | String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() |
| 243 | 257 | .setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id))); |
| ... | ... | @@ -247,6 +261,12 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 247 | 261 | .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus())); |
| 248 | 262 | } |
| 249 | 263 | |
| 264 | + @Override | |
| 265 | + public void updateContractAuditStatus(BpmResultListenerRespDTO event) { | |
| 266 | + contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey())) | |
| 267 | + .setAuditStatus(event.getResult())); | |
| 268 | + } | |
| 269 | + | |
| 250 | 270 | //======================= 查询相关 ======================= |
| 251 | 271 | |
| 252 | 272 | @Override |
| ... | ... | @@ -285,7 +305,7 @@ public class CrmContractServiceImpl implements CrmContractService { |
| 285 | 305 | } |
| 286 | 306 | |
| 287 | 307 | @Override |
| 288 | - public Long selectCountByBusinessId(Long businessId) { | |
| 308 | + public Long getContractCountByBusinessId(Long businessId) { | |
| 289 | 309 | return contractMapper.selectCountByBusinessId(businessId); |
| 290 | 310 | } |
| 291 | 311 | // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒; | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java
| 1 | 1 | package cn.iocoder.yudao.module.crm.service.contract.listener; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ObjUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | |
| 3 | 5 | import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi; |
| 4 | 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; | |
| 5 | 9 | import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; |
| 6 | 10 | import cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl; |
| 7 | 11 | import jakarta.annotation.Resource; |
| ... | ... | @@ -25,7 +29,31 @@ public class CrmContractResultListener implements BpmResultListenerApi { |
| 25 | 29 | |
| 26 | 30 | @Override |
| 27 | 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 | + } | |
| 28 | 45 | contractService.updateContractAuditStatus(event); |
| 29 | 46 | } |
| 30 | 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 | + | |
| 31 | 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 | 93 | validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1); |
| 94 | 94 | |
| 95 | 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 | 97 | customerMapper.insert(customer); |
| 99 | 98 | |
| 100 | 99 | // 3. 创建数据权限 |
| ... | ... | @@ -232,7 +231,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 232 | 231 | return customer.getId(); |
| 233 | 232 | } |
| 234 | 233 | |
| 235 | - // TODO @puhui999:操作日志 | |
| 236 | 234 | @Override |
| 237 | 235 | public CrmCustomerImportRespVO importCustomerList(List<CrmCustomerImportExcelVO> importCustomers, |
| 238 | 236 | Boolean isUpdateSupport, Long userId) { |
| ... | ... | @@ -253,14 +251,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 253 | 251 | // 判断如果不存在,在进行插入 |
| 254 | 252 | CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName()); |
| 255 | 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 | 255 | customerMapper.insert(customer); |
| 260 | 256 | respVO.getCreateCustomerNames().add(importCustomer.getName()); |
| 261 | 257 | // 创建数据权限 |
| 262 | 258 | permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) |
| 263 | 259 | .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 |
| 260 | + // 记录操作日志 | |
| 261 | + getSelf().importCustomerLog(customer, false); | |
| 264 | 262 | return; |
| 265 | 263 | } |
| 266 | 264 | // 如果存在,判断是否允许更新 |
| ... | ... | @@ -273,10 +271,25 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 273 | 271 | updateCustomer.setId(existCustomer.getId()); |
| 274 | 272 | customerMapper.updateById(updateCustomer); |
| 275 | 273 | respVO.getUpdateCustomerNames().add(importCustomer.getName()); |
| 274 | + // 记录操作日志 | |
| 275 | + getSelf().importCustomerLog(updateCustomer, true); | |
| 276 | 276 | }); |
| 277 | 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 | 293 | private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) { |
| 281 | 294 | // 校验客户名称不能为空 |
| 282 | 295 | if (StrUtil.isEmptyIfStr(importCustomer.getName())) { |
| ... | ... | @@ -367,15 +380,15 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 367 | 380 | if (poolConfig == null || !poolConfig.getEnabled()) { |
| 368 | 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 | 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 | 392 | LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime()); |
| 380 | 393 | return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0; |
| 381 | 394 | })); | ... | ... |