diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java index e0b739920..ccfeb3917 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/MoneyUtils.java @@ -35,6 +35,22 @@ public class MoneyUtils { return calculateRatePrice(price, rate, 0, RoundingMode.FLOOR).intValue(); } + /** + * 计算百分比金额 + * + * @param price 金额(单位分) + * @param count 数量 + * @param percent 折扣(单位分),列如 60.2%,则传入 6020 + * @return 商品总价 + */ + public static Integer calculator(Integer price, Integer count, Integer percent) { + price = price * count; + if (percent == null) { + return price; + } + return MoneyUtils.calculateRatePriceFloor(price, (double) (percent / 100)); + } + /** * 计算百分比金额 * diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java new file mode 100644 index 000000000..65b038311 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.bpm.api.listener; + +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; + +/** + * 业务流程实例的结果发生变化的监听器 Api + * + * @author HUIHUI + */ +public interface BpmResultListenerApi { + + /** + * 监听的流程定义 Key + * + * @return 返回监听的流程定义 Key + */ + String getProcessDefinitionKey(); + + /** + * 处理事件 + * + * @param event 事件 + */ + void onEvent(BpmResultListenerRespDTO event); + +} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java new file mode 100644 index 000000000..75f9d02f0 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.bpm.api.listener.dto; + +import lombok.Data; + +/** + * 业务流程实例的结果 Response DTO + * + * @author HUIHUI + */ +@Data +public class BpmResultListenerRespDTO { + + /** + * 流程实例的编号 + */ + private String id; + /** + * 流程实例的 key + */ + private String processDefinitionKey; + /** + * 流程实例的结果 + */ + private Integer result; + /** + * 流程实例对应的业务标识 + * 例如说,请假 + */ + private String businessKey; + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java index 861698ac0..32b073c4b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java +++ b/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 { return success(model); } - // TODO @puhui999:这个接口的目的是啥呀? + // TODO @puhui999:这个接口的目的是啥呀?业务表单预览流程🤣 @GetMapping("/get-by-key") @Operation(summary = "获得模型") @Parameter(name = "key", description = "流程标识", required = true, example = "oa_leave") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java new file mode 100644 index 000000000..3081fb360 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.bpm.framework.bpm.listener; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi; +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; +import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent; +import jakarta.annotation.Resource; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 业务流程结果监听器实现类 + * + * @author HUIHUI + */ +@Component +public class BpmServiceResultListener implements ApplicationListener { + + @Resource + private List bpmResultListenerApis; + + @Override + public final void onApplicationEvent(BpmProcessInstanceResultEvent event) { + bpmResultListenerApis.forEach(bpmResultListenerApi -> { + if (!StrUtil.equals(event.getProcessDefinitionKey(), bpmResultListenerApi.getProcessDefinitionKey())) { + return; + } + bpmResultListenerApi.onEvent(BeanUtils.toBean(event, BpmResultListenerRespDTO.class)); + }); + } + +} diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java index 9190880fa..d536d8a40 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java +++ b/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 { // ========== 合同管理 1-020-000-000 ========== ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在"); ErrorCode CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_000_001, "更新合同失败,原因:禁止编辑"); + ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态"); // ========== 线索管理 1-020-001-000 ========== ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java index a0b4559ef..d22e87bed 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java +++ b/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 { String CRM_CUSTOMER_POOL_SUCCESS = "将客户【{{#customerName}}】放入了公海"; String CRM_CUSTOMER_RECEIVE_SUB_TYPE = "{{#ownerUserName != null ? '分配客户' : '领取客户'}}"; String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户【' + #customer.name + '】分配给【' + #ownerUserName + '】' : '领取客户【' + #customer.name + '】'}}"; + String CRM_CUSTOMER_IMPORT_SUB_TYPE = "{{#isUpdate ? '导入并更新客户' : '导入客户'}}"; + String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}"; // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 ======================= diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java index 90b00dd48..c85c151f5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java +++ b/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 { @PutMapping("/transfer") @Operation(summary = "商机转移") @PreAuthorize("@ss.hasPermission('crm:business:update')") - public CommonResult transfer(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) { + public CommonResult transferBusiness(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) { businessService.transferBusiness(reqVO, getLoginUserId()); return success(true); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java index dca364cba..8be62ae26 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java +++ b/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 { @PutMapping("/transfer") @Operation(summary = "线索转移") @PreAuthorize("@ss.hasPermission('crm:clue:update')") - public CommonResult transfer(@Valid @RequestBody CrmClueTransferReqVO reqVO) { + public CommonResult transferClue(@Valid @RequestBody CrmClueTransferReqVO reqVO) { clueService.transferClue(reqVO, getLoginUserId()); return success(true); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java index e3998834d..766adb02d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java +++ b/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 { List customerList = customerService.getCustomerList( Collections.singletonList(contact.getCustomerId())); // 3. 直属上级 - List parentContactList = contactService.getContactList( + List parentContactList = contactService.getContactListByIds( Collections.singletonList(contact.getParentId()), getLoginUserId()); return success(CrmContactConvert.INSTANCE.convert(contact, userMap, customerList, parentContactList)); } @@ -112,7 +112,7 @@ public class CrmContactController { @Parameter(name = "ids", description = "编号", required = true, example = "[1024]") @PreAuthorize("@ss.hasPermission('crm:contact:query')") public CommonResult> getContactListByIds(@RequestParam("ids") List ids) { - return success(BeanUtils.toBean(contactService.getContactList(ids, getLoginUserId()), CrmContactRespVO.class)); + return success(BeanUtils.toBean(contactService.getContactListByIds(ids, getLoginUserId()), CrmContactRespVO.class)); } @GetMapping("/simple-all-list") @@ -170,7 +170,7 @@ public class CrmContactController { Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); // 3. 直属上级 - List parentContactList = contactService.getContactList( + List parentContactList = contactService.getContactListByIds( convertSet(contactList, CrmContactDO::getParentId), getLoginUserId()); return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList); } @@ -178,7 +178,7 @@ public class CrmContactController { @PutMapping("/transfer") @Operation(summary = "联系人转移") @PreAuthorize("@ss.hasPermission('crm:contact:update')") - public CommonResult transfer(@Valid @RequestBody CrmContactTransferReqVO reqVO) { + public CommonResult transferContact(@Valid @RequestBody CrmContactTransferReqVO reqVO) { contactService.transferContact(reqVO, getLoginUserId()); return success(true); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java index 328695b8b..c050f6f57 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java +++ b/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; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*; import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService; +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; +import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; +import cn.iocoder.yudao.module.crm.service.product.CrmProductService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import io.swagger.v3.oas.annotations.Operation; @@ -28,13 +36,14 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @@ -48,6 +57,15 @@ public class CrmContractController { private CrmContractService contractService; @Resource private CrmCustomerService customerService; + @Resource + private CrmContactService contactService; + @Resource + private CrmBusinessService businessService; + @Resource + @Lazy + private CrmBusinessProductService businessProductService; + @Resource + private CrmProductService productService; @Resource private AdminUserApi adminUserApi; @@ -82,7 +100,19 @@ public class CrmContractController { @PreAuthorize("@ss.hasPermission('crm:contract:query')") public CommonResult getContract(@RequestParam("id") Long id) { CrmContractDO contract = contractService.getContract(id); - return success(BeanUtils.toBean(contract, CrmContractRespVO.class)); + List respVOList = buildContractDetail(Collections.singletonList(contract)); + CrmContractRespVO respVO = respVOList.getFirst(); + List businessProductList = businessProductService.getBusinessProductListByContractId(id); + Map businessProductMap = convertMap(businessProductList, CrmBusinessProductDO::getProductId); + List productList = productService.getProductListByIds(convertSet(businessProductList, CrmBusinessProductDO::getProductId)); + respVO.setProductItems(convertList(productList, product -> { + CrmContractRespVO.CrmContractProductItemRespVO productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.CrmContractProductItemRespVO.class); + findAndThen(businessProductMap, product.getId(), businessProduct -> { + productItemRespVO.setCount(businessProduct.getCount()).setDiscountPercent(businessProduct.getDiscountPercent()); + }); + return productItemRespVO; + })); + return success(respVO); } @GetMapping("/page") @@ -90,15 +120,15 @@ public class CrmContractController { @PreAuthorize("@ss.hasPermission('crm:contract:query')") public CommonResult> getContractPage(@Valid CrmContractPageReqVO pageVO) { PageResult pageResult = contractService.getContractPage(pageVO, getLoginUserId()); - return success(buildContractDetailPage(pageResult)); + return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetail(pageResult.getList()))); } @GetMapping("/page-by-customer") - @Operation(summary = "获得联系人分页,基于指定客户") + @Operation(summary = "获得合同分页,基于指定客户") public CommonResult> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) { Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空"); PageResult pageResult = contractService.getContractPageByCustomerId(pageVO); - return success(buildContractDetailPage(pageResult)); + return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetail(pageResult.getList()))); } @GetMapping("/export-excel") @@ -114,40 +144,43 @@ public class CrmContractController { } /** - * 构建详细的合同分页结果 + * 构建详细的合同结果 * - * @param pageResult 简单的合同分页结果 - * @return 详细的合同分页结果 + * @param contractList 原始合同信息 + * @return 细的合同结果 */ - private PageResult buildContractDetailPage(PageResult pageResult) { - List contactList = pageResult.getList(); - if (CollUtil.isEmpty(contactList)) { - return PageResult.empty(pageResult.getTotal()); + private List buildContractDetail(List contractList) { + if (CollUtil.isEmpty(contractList)) { + return Collections.emptyList(); } // 1. 获取客户列表 List customerList = customerService.getCustomerList( - convertSet(contactList, CrmContractDO::getCustomerId)); + convertSet(contractList, CrmContractDO::getCustomerId)); // 2. 获取创建人、负责人列表 - Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList, + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(contractList, contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId()))); - return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList); + // 3. 获取联系人 + Map contactMap = convertMap(contactService.getContactListByIds(convertSet(contractList, + CrmContractDO::getContactId)), CrmContactDO::getId); + // 4. 获取商机 + Map businessMap = convertMap(businessService.getBusinessList(convertSet(contractList, + CrmContractDO::getBusinessId)), CrmBusinessDO::getId); + return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap); } - // TODO @puhui999:transferContract @PutMapping("/transfer") @Operation(summary = "合同转移") @PreAuthorize("@ss.hasPermission('crm:contract:update')") - public CommonResult transfer(@Valid @RequestBody CrmContractTransferReqVO reqVO) { + public CommonResult transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) { contractService.transferContract(reqVO, getLoginUserId()); return success(true); } - // TODO @puhui999:方法名不对哈;要不改成 submit?提交审核的意思 - @PutMapping("/approve") - @Operation(summary = "发起合同审批流程") + @PutMapping("/submit") + @Operation(summary = "提交合同审批") @PreAuthorize("@ss.hasPermission('crm:contract:update')") - public CommonResult transfer(@RequestParam("id") Long id) { - contractService.handleApprove(id, getLoginUserId()); + public CommonResult submitContract(@RequestParam("id") Long id) { + contractService.submitContract(id, getLoginUserId()); return success(true); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java index 1164f4a0c..238789794 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java +++ b/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; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; +import java.util.List; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -26,10 +29,16 @@ public class CrmContractRespVO { @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336") @ExcelProperty("客户编号") private Long customerId; + @Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "18336") + @ExcelProperty("客户名称") + private String customerName; @Schema(description = "商机编号", example = "10864") @ExcelProperty("商机编号") private Long businessId; + @Schema(description = "商机名称", example = "10864") + @ExcelProperty("商机名称") + private String businessName; @Schema(description = "工作流编号", example = "1043") @ExcelProperty("工作流编号") @@ -74,10 +83,16 @@ public class CrmContractRespVO { @Schema(description = "联系人编号", example = "18546") @ExcelProperty("联系人编号") private Long contactId; + @Schema(description = "联系人编号", example = "18546") + @ExcelProperty("联系人编号") + private String contactName; @Schema(description = "公司签约人", example = "14036") @ExcelProperty("公司签约人") private Long signUserId; + @Schema(description = "公司签约人", example = "14036") + @ExcelProperty("公司签约人") + private String signUserName; @Schema(description = "最后跟进时间") @ExcelProperty("最后跟进时间") @@ -101,10 +116,6 @@ public class CrmContractRespVO { @ExcelProperty("创建人名字") private String creatorName; - @Schema(description = "客户名字", example = "test") - @ExcelProperty("客户名字") - private String customerName; - @Schema(description = "负责人", example = "test") @ExcelProperty("负责人") private String ownerUserName; @@ -113,4 +124,36 @@ public class CrmContractRespVO { @ExcelProperty("审批状态") private Integer auditStatus; + @Schema(description = "产品列表") + private List productItems; + + @Schema(description = "产品列表") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class CrmContractProductItemRespVO { + + @Schema(description = "产品编号", example = "20529") + private Long id; + + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + private String name; + + @Schema(description = "产品编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + private String no; + + @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + private Integer unit; + + @Schema(description = "价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + private Integer price; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911") + private Integer count; + + @Schema(description = "产品折扣") + private Integer discountPercent; + + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java index f1ab8c581..20b20580e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractSaveReqVO.java +++ b/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 { @DiffLogField(name = "备注") private String remark; - // TODO @puhui999:这个字段,按道理不用传递? - @Schema(description = "审批状态", example = "1") - private Integer auditStatus; @Schema(description = "产品列表") private List productItems; - @Schema(description = "商品属性") + @Schema(description = "产品列表") @Data @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index 09057fd78..407b2d0c8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/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 { @PutMapping("/transfer") @Operation(summary = "转移客户") @PreAuthorize("@ss.hasPermission('crm:customer:update')") - public CommonResult transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { + public CommonResult transferCustomer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) { customerService.transferCustomer(reqVO, getLoginUserId()); return success(true); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java index 735f2e887..f0b726353 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java +++ b/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 { public CommonResult> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) { PageResult pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO); /// 拼接数据 - Map contactMap = convertMap(contactService.getContactList( + Map contactMap = convertMap(contactService.getContactListByIds( convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream())), CrmContactDO::getId); Map businessMap = convertMap(businessService.getBusinessList( convertSetByFlatMap(pageResult.getList(), item -> item.getBusinessIds().stream())), CrmBusinessDO::getId); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java index 599d998a6..c9247e6a5 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java @@ -1,9 +1,10 @@ package cn.iocoder.yudao.module.crm.convert.contract; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; @@ -31,17 +32,21 @@ public interface CrmContractConvert { @Mapping(target = "bizId", source = "reqVO.id") CrmPermissionTransferReqBO convert(CrmContractTransferReqVO reqVO, Long userId); - default PageResult convertPage(PageResult pageResult, Map userMap, - List customerList) { - PageResult voPageResult = BeanUtils.toBean(pageResult, CrmContractRespVO.class); + default List convertList(List contractList, Map userMap, + List customerList, Map contactMap, + Map businessMap) { + List respVOList = BeanUtils.toBean(contractList, CrmContractRespVO.class); // 拼接关联字段 Map customerMap = convertMap(customerList, CrmCustomerDO::getId); - voPageResult.getList().forEach(contract -> { + respVOList.forEach(contract -> { findAndThen(userMap, contract.getOwnerUserId(), user -> contract.setOwnerUserName(user.getNickname())); findAndThen(userMap, Long.parseLong(contract.getCreator()), user -> contract.setCreatorName(user.getNickname())); + findAndThen(userMap, contract.getSignUserId(), user -> contract.setSignUserName(user.getNickname())); findAndThen(customerMap, contract.getCustomerId(), customer -> contract.setCustomerName(customer.getName())); + findAndThen(contactMap, contract.getContactId(), contact -> contract.setContactName(contact.getName())); + findAndThen(businessMap, contract.getBusinessId(), business -> contract.setBusinessName(business.getName())); }); - return voPageResult; + return respVOList; } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java index 83121dbdd..2d801ff5b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessProductDO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.dal.dataobject.business; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -40,6 +41,12 @@ public class CrmBusinessProductDO extends BaseDO { * 关联 {@link CrmProductDO#getId()} */ private Long productId; + /** + * 合同编号 + * + * 关联 {@link CrmContractDO#getId()} + */ + private Long contractId; /** * 产品单价 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java index 6523782dd..a3c56ccc9 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/product/CrmProductDO.java +++ b/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 { /** * 价格,单位:分 */ - private Long price; + private Integer price; /** * 状态 * diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java index 14732641d..29cd47c66 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java +++ b/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 { return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); } - default List selectListByLockAndDealStatusAndNotPool(Boolean lockStatus, Boolean dealStatus) { + default List selectListByLockAndNotPool(Boolean lockStatus) { return selectList(new LambdaQueryWrapper() .eq(CrmCustomerDO::getLockStatus, lockStatus) - .eq(CrmCustomerDO::getDealStatus, dealStatus) .gt(CrmCustomerDO::getOwnerUserId, 0)); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java index 30a07eec2..3d696de7e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/product/CrmProductMapper.java +++ b/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; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; @@ -9,6 +10,9 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; import org.apache.ibatis.annotations.Mapper; +import java.util.Collection; +import java.util.List; + /** * CRM 产品 Mapper * @@ -34,4 +38,8 @@ public interface CrmProductMapper extends BaseMapperX { return selectOne(CrmProductDO::getNo, no); } + default List selectListByIds(Collection ids) { + return selectList(new LambdaQueryWrapperX().in(CrmProductDO::getId, ids)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductService.java index 010be4a92..f819b5907 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductService.java +++ b/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; import java.util.List; // TODO @lzxhqs:方法名上,带下 BusinessProduct;主要考虑不精简的原因,是因为一个逻辑,可能会出现一些超越它自身方法,省略不容易懂; + /** * 商机产品关联表 Service 接口 * @@ -14,31 +15,45 @@ public interface CrmBusinessProductService { /** * 批量新增商机产品关联数据 + * * @param list 商机产品集合 */ - void insertBatch(List list); + void createBusinessProductBatch(List list); /** * 根据商机id获取商机产品关联数据集合 + * * @param businessId 商机id */ - List selectListByBusinessId(Long businessId); + List getBusinessProductListByBusinessId(Long businessId); /** * 批量更新商机产品表 + * * @param list 商机产品数据集合 */ - void updateBatch(List list); + void updateBusinessProductBatch(List list); /** * 批量删除 + * * @param list 需要删除的商机产品集合 */ - void deleteBatch(List list); + void deleteBusinessProductBatch(List list); /** - *根据商机id删除商机产品关联数据 + * 根据商机id删除商机产品关联数据 + * * @param businessId 商机id */ - void deleteByBusinessId(Long businessId); + void deleteBusinessProductByBusinessId(Long businessId); + + /** + * 获得合同关联的商品列表 + * + * @param contractId 合同编号 + * @return 关联的商品列表 + */ + List getBusinessProductListByContractId(Long contractId); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductServiceImpl.java index afad6ae31..88115a86a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessProductServiceImpl.java +++ b/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 private CrmBusinessProductMapper businessProductMapper; @Override - public void insertBatch(List list) { + public void createBusinessProductBatch(List list) { businessProductMapper.insertBatch(list); } @Override - public List selectListByBusinessId(Long businessId) { - return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId,businessId); + public List getBusinessProductListByBusinessId(Long businessId) { + return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId, businessId); } @Override - public void updateBatch(List list) { + public void updateBusinessProductBatch(List list) { businessProductMapper.updateBatch(list); } // TODO @lzxhqs:这个方法,可以直接调用 deleteList 方法,然后传递 ids 就好了; @Override - public void deleteBatch(List list) { + public void deleteBusinessProductBatch(List list) { businessProductMapper.deleteBatchIds(CollectionUtils.convertList(list, CrmBusinessProductDO::getId)); } @Override - public void deleteByBusinessId(Long businessId) { + public void deleteBusinessProductByBusinessId(Long businessId) { businessProductMapper.deleteByBusinessId(businessId); } + @Override + public List getBusinessProductListByContractId(Long contractId) { + return businessProductMapper.selectList(CrmBusinessProductDO::getContractId, contractId); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java index aa0839d12..1daad016a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java +++ b/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 { CrmBusinessProductConvert.INSTANCE.convert(product).setBusinessId(businessId)); if (Boolean.TRUE.equals(updateFlag)) { // 根据商机 id从商机产品关联表中获取已存在的数据集合 - List oldProducts = businessProductService.selectListByBusinessId(businessId); + List oldProducts = businessProductService.getBusinessProductListByBusinessId(businessId); List> diffList = CollectionUtils.diffList(oldProducts, list, (oldValue, newValue) -> ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId())); if (CollUtil.isNotEmpty(diffList.getFirst())) { - businessProductService.insertBatch(diffList.getFirst()); + businessProductService.createBusinessProductBatch(diffList.getFirst()); } if (CollUtil.isNotEmpty(diffList.get(1))) { - businessProductService.updateBatch(diffList.get(1)); + businessProductService.updateBusinessProductBatch(diffList.get(1)); } if (CollUtil.isNotEmpty(diffList.get(2))) { - businessProductService.deleteBatch(diffList.get(2)); + businessProductService.deleteBusinessProductBatch(diffList.get(2)); } } else { - businessProductService.insertBatch(list); + businessProductService.createBusinessProductBatch(list); } } @@ -154,7 +154,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { if (CollUtil.isNotEmpty(updateReqVO.getProducts())) { createBusinessProducts(updateReqVO.getProducts(), updateReqVO.getId(), true); } else { - businessProductService.deleteByBusinessId(updateReqVO.getId()); + businessProductService.deleteBusinessProductByBusinessId(updateReqVO.getId()); } // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表 @@ -196,7 +196,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { */ private void validateContractExists(Long businessId) { // TODO @lzxhqs:保持风格的统一,selectCountByBusinessId 改成 getContractCountByBusinessId;另外,可以不用声明 count,因为就一次性使用,直接把 197 和 198 合并成一行; - Long count = contractService.selectCountByBusinessId(businessId); + Long count = contractService.getContractCountByBusinessId(businessId); if (count > 0) { throw exception(BUSINESS_CONTRACT_EXISTS); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java index a43ed2e65..d7688b8fb 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java +++ b/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 { * @param userId 用户编号 * @return 联系人列表 */ - List getContactList(Collection ids, Long userId); + List getContactListByIds(Collection ids, Long userId); /** * 获得联系人列表 @@ -88,7 +88,7 @@ public interface CrmContactService { * @param ids 编号 * @return 联系人列表 */ - List getContactList(Collection ids); + List getContactListByIds(Collection ids); /** * 获得联系人列表 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java index 52f2d6cf7..08ce78b81 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java +++ b/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; import com.mzt.logapi.service.impl.DiffParseFunction; import com.mzt.logapi.starter.annotation.LogRecord; import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -56,6 +57,7 @@ public class CrmContactServiceImpl implements CrmContactService { @Resource private CrmPermissionService permissionService; @Resource + @Lazy private CrmContractService contractService; @Resource private CrmContactBusinessService contactBusinessService; @@ -208,7 +210,7 @@ public class CrmContactServiceImpl implements CrmContactService { } @Override - public List getContactList(Collection ids, Long userId) { + public List getContactListByIds(Collection ids, Long userId) { if (CollUtil.isEmpty(ids)) { return ListUtil.empty(); } @@ -216,7 +218,7 @@ public class CrmContactServiceImpl implements CrmContactService { } @Override - public List getContactList(Collection ids) { + public List getContactListByIds(Collection ids) { if (CollUtil.isEmpty(ids)) { return ListUtil.empty(); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java index ea7972228..cb06e99e0 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.crm.service.contract; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; @@ -63,8 +64,14 @@ public interface CrmContractService { * @param id 合同编号 * @param userId 用户编号 */ - void handleApprove(Long id, Long userId); + void submitContract(Long id, Long userId); + /** + * 更新合同流程审批结果 + * + * @param event 审批结果 + */ + void updateContractAuditStatus(BpmResultListenerRespDTO event); /** * 获得合同 * @@ -118,13 +125,12 @@ public interface CrmContractService { */ Long getContractCountByCustomerId(Long customerId); - // TODO @puhui999:要不改成 getContractCountByBusinessId /** * 根据商机ID获取关联客户的合同数量 * * @param businessId 商机编号 * @return 数量 */ - Long selectCountByBusinessId(Long businessId); + Long getContractCountByBusinessId(Long businessId); } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java index 130f0f709..87f1c20e4 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; @@ -87,8 +90,11 @@ public class CrmContractServiceImpl implements CrmContractService { CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null); contractMapper.insert(contract); // 1.2 插入商机关联商品 - List businessProduct = convertBusinessProductList(createReqVO); - businessProductService.insertBatch(businessProduct); + if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话 + List businessProduct = convertBusinessProductList(createReqVO, contract.getId()); + businessProductService.createBusinessProductBatch(businessProduct); + // 更新合同商品总金额 + } // 2. 创建数据权限 crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId) @@ -106,9 +112,11 @@ public class CrmContractServiceImpl implements CrmContractService { success = CRM_CONTRACT_UPDATE_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTRACT, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) public void updateContract(CrmContractSaveReqVO updateReqVO) { - // TODO @合同待定:只有草稿、审批中,可以编辑; - if (ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) || - ObjUtil.notEqual(updateReqVO.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) { + Assert.notNull(updateReqVO.getId(), "合同编号不能为空"); + CrmContractDO contract = validateContractExists(updateReqVO.getId()); + // 只有草稿、审批中,可以编辑; + if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), + CrmAuditStatusEnum.PROCESS.getStatus())) { throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED); } validateRelationDataExists(updateReqVO); @@ -117,21 +125,41 @@ public class CrmContractServiceImpl implements CrmContractService { // 更新合同 CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class); contractMapper.updateById(updateObj); - - // TODO puhui999: @芋艿:合同变更关联的商机后商品怎么处理? - // TODO @puhui999:和商品 spu、sku 编辑一样;新增的插入;修改的更新;删除的删除 - //List businessProduct = convertBusinessProductList(updateReqVO); - //businessProductService.selectListByBusinessId() - //diffList() + // 更新合同关联商品 + updateContractProduct(updateReqVO, updateObj.getId()); // 3. 记录操作日志上下文 LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class)); LogRecordContext.putVariable("contractName", oldContract.getName()); } + private void updateContractProduct(CrmContractSaveReqVO updateReqVO, Long contractId) { + if (CollUtil.isEmpty(updateReqVO.getProductItems())) { + return; + } + List newProductList = convertBusinessProductList(updateReqVO, contractId); + List oldProductList = businessProductService.getBusinessProductListByContractId(contractId); + List> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> { + if (ObjUtil.notEqual(oldObj.getProductId(), newObj.getProductId())) { + return false; + } + newObj.setId(oldObj.getId()); // 设置一下老的编号更新时需要使用 + return true; + }); + if (CollUtil.isNotEmpty(diffList.getFirst())) { + businessProductService.createBusinessProductBatch(diffList.getFirst()); + } + if (CollUtil.isNotEmpty(diffList.get(1))) { + businessProductService.updateBusinessProductBatch(diffList.get(1)); + } + if (CollUtil.isNotEmpty(diffList.get(2))) { + businessProductService.deleteBusinessProductBatch(diffList.get(2)); + } + } + // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum - private List convertBusinessProductList(CrmContractSaveReqVO reqVO) { + private List convertBusinessProductList(CrmContractSaveReqVO reqVO, Long contractId) { // 校验商品存在 Set productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId); List productList = productService.getProductList(productIds); @@ -140,29 +168,14 @@ public class CrmContractServiceImpl implements CrmContractService { } Map productMap = convertMap(productList, CrmProductDO::getId); return convertList(reqVO.getProductItems(), productItem -> { - // TODO @puhui999:这里可以改成直接 return,不用弄一个 businessProduct 变量哈; - CrmBusinessProductDO businessProduct = BeanUtils.toBean(productMap.get(productItem.getId()), CrmBusinessProductDO.class); - businessProduct.setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId()) - .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()).setTotalPrice(calculator(businessProduct)); - return businessProduct; + CrmProductDO product = productMap.get(productItem.getId()); + return BeanUtils.toBean(product, CrmBusinessProductDO.class) + .setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId()).setContractId(contractId) + .setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent()) + .setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent())); }); } - /** - * 计算商品总价 - * - * @param businessProduct 关联商品 - * @return 商品总价 - */ - // TODO @puhui999:这个逻辑的计算,是不是可以封装到 calculateRatePriceFloor 里; - private Integer calculator(CrmBusinessProductDO businessProduct) { - int price = businessProduct.getPrice() * businessProduct.getCount(); - if (businessProduct.getDiscountPercent() == null) { - return price; - } - return MoneyUtils.calculateRatePriceFloor(price, (double) (businessProduct.getDiscountPercent() / 100)); - } - /** * 校验关联数据是否存在 * @@ -235,9 +248,12 @@ public class CrmContractServiceImpl implements CrmContractService { @Override @Transactional(rollbackFor = Exception.class) - public void handleApprove(Long id, Long userId) { - // TODO @puhui999:需要做状态检查 - + public void submitContract(Long id, Long userId) { + // 1. 校验合同是否在审批 + CrmContractDO contract = validateContractExists(id); + if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) { + throw exception(CONTRACT_SUBMIT_FAIL_NOT_DRAFT); + } // 创建合同审批流程实例 String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() .setProcessDefinitionKey(CONTRACT_APPROVE).setBusinessKey(String.valueOf(id))); @@ -247,6 +263,12 @@ public class CrmContractServiceImpl implements CrmContractService { .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus())); } + @Override + public void updateContractAuditStatus(BpmResultListenerRespDTO event) { + contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey())) + .setAuditStatus(event.getResult())); + } + //======================= 查询相关 ======================= @Override @@ -285,7 +307,7 @@ public class CrmContractServiceImpl implements CrmContractService { } @Override - public Long selectCountByBusinessId(Long businessId) { + public Long getContractCountByBusinessId(Long businessId) { return contractMapper.selectCountByBusinessId(businessId); } // TODO @合同待定:需要新增一个 ContractConfigDO 表,合同配置,重点是到期提醒; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java index e393c2664..783579fc6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java @@ -1,5 +1,59 @@ package cn.iocoder.yudao.module.crm.service.contract.listener; -public class CrmContractResultListener { - // TODO puhui999: @芋艿: 艿艿写一下这个,没研究明白哈哈 +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi; +import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; +import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; +import cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +/** + * 合同审批的结果的监听器实现类 + * + * @author HUIHUI + */ +@Component +public class CrmContractResultListener implements BpmResultListenerApi { + + @Resource + private CrmContractService contractService; + + @Override + public String getProcessDefinitionKey() { + return CrmContractServiceImpl.CONTRACT_APPROVE; + } + + @Override + public void onEvent(BpmResultListenerRespDTO event) { + boolean currentTaskFinish = isEndResult(event.getResult()); + if (!currentTaskFinish) { + return; + } + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) { + event.setResult(CrmAuditStatusEnum.APPROVE.getStatus()); + } + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) { + event.setResult(CrmAuditStatusEnum.REJECT.getStatus()); + } + if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) { + event.setResult(CrmAuditStatusEnum.CANCEL.getStatus()); + } + contractService.updateContractAuditStatus(event); + } + + /** + * 判断该结果是否处于 End 最终结果 + * + * @param result 结果 + * @return 是否 + */ + public static boolean isEndResult(Integer result) { + return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(), + BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult()); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java index a7ddb2da9..6a38973c8 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java +++ b/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 { validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1); // 2. 插入客户 - CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class).setOwnerUserId(userId) - .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); + CrmCustomerDO customer = initCustomer(createReqVO, userId); customerMapper.insert(customer); // 3. 创建数据权限 @@ -232,7 +231,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { return customer.getId(); } - // TODO @puhui999:操作日志 @Override public CrmCustomerImportRespVO importCustomerList(List importCustomers, Boolean isUpdateSupport, Long userId) { @@ -253,14 +251,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { // 判断如果不存在,在进行插入 CrmCustomerDO existCustomer = customerMapper.selectByCustomerName(importCustomer.getName()); if (existCustomer == null) { - // TODO @puhui999:可以搞个 initCustomer 方法;这样可以把 create 和导入复用下这个方法; - CrmCustomerDO customer = BeanUtils.toBean(importCustomer, CrmCustomerDO.class).setOwnerUserId(userId) - .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); + CrmCustomerDO customer = initCustomer(importCustomer, userId); customerMapper.insert(customer); respVO.getCreateCustomerNames().add(importCustomer.getName()); // 创建数据权限 permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人 + // 记录操作日志 + getSelf().importCustomerLog(customer, false); return; } // 如果存在,判断是否允许更新 @@ -273,10 +271,25 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { updateCustomer.setId(existCustomer.getId()); customerMapper.updateById(updateCustomer); respVO.getUpdateCustomerNames().add(importCustomer.getName()); + // 记录操作日志 + getSelf().importCustomerLog(updateCustomer, true); }); return respVO; } + private static CrmCustomerDO initCustomer(Object customer, Long userId) { + return BeanUtils.toBean(customer, CrmCustomerDO.class).setOwnerUserId(userId) + .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now()); + } + + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_IMPORT_SUB_TYPE, bizNo = "{{#customer.id}}", + success = CRM_CUSTOMER_IMPORT_SUCCESS) + public void importCustomerLog(CrmCustomerDO customer, boolean isUpdate) { + // 记录操作日志上下文 + LogRecordContext.putVariable("customer", customer); + LogRecordContext.putVariable("isUpdate", isUpdate); + } + private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) { // 校验客户名称不能为空 if (StrUtil.isEmptyIfStr(importCustomer.getName())) { @@ -367,15 +380,15 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { if (poolConfig == null || !poolConfig.getEnabled()) { return 0; } - // 1.1 获取没有锁定的不在公海的客户且没有成交的 - List notDealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.FALSE); - // 1.2 获取没有锁定的不在公海的客户且成交的 - // TODO @puhui999:下面也搞到 sql 里去哈;写 or 查询,问题不大的; - List dealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.TRUE); + // 1.1 获取没有锁定的不在公海的客户 + List customerList = customerMapper.selectListByLockAndNotPool(Boolean.FALSE); List poolCustomerList = new ArrayList<>(); - poolCustomerList.addAll(filterList(notDealCustomerList, customer -> - (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0)); - poolCustomerList.addAll(filterList(dealCustomerList, customer -> { + poolCustomerList.addAll(filterList(customerList, customer -> + !customer.getDealStatus() && (poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime())) <= 0)); + poolCustomerList.addAll(filterList(customerList, customer -> { + if (!customer.getDealStatus()) { // 这里只处理成交的 + return false; + } LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime()); return (poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime)) <= 0; })); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java index e4eabd15a..6d2dd4943 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductService.java +++ b/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 { * @return 产品 */ CrmProductDO getProductByCategoryId(Long categoryId); + + /** + * 获得产品列表 + * + * @param ids 产品编号 + * @return 产品列表 + */ + List getProductListByIds(Collection ids); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java index 80c38af3c..55f8a3593 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java +++ b/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 { return productMapper.selectOne(new LambdaQueryWrapper().eq(CrmProductDO::getCategoryId, categoryId)); } + @Override + public List getProductListByIds(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return productMapper.selectListByIds(ids); + } + }