From 624b35988ec2a008190bab1b532af4d7ea6f55c9 Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Thu, 1 Feb 2024 00:45:29 +0800
Subject: [PATCH 1/5] =?UTF-8?q?BPM:=20=E5=AE=9E=E7=8E=B0=E4=B8=9A=E5=8A=A1?=
 =?UTF-8?q?=E6=B5=81=E7=A8=8B=E7=BB=93=E6=9E=9C=E7=9B=91=E5=90=AC=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pom.xml                                       |  2 +-
 .../api/listener/BpmResultListenerApi.java    | 26 ++++++++++++++
 .../dto/BpmResultListenerRespDTO.java         | 31 ++++++++++++++++
 .../listener/BpmServiceResultListener.java    | 35 +++++++++++++++++++
 .../business/CrmBusinessServiceImpl.java      |  2 ++
 .../contact/CrmContactServiceImpl.java        |  2 ++
 .../service/contract/CrmContractService.java  |  7 ++++
 .../contract/CrmContractServiceImpl.java      | 10 ++++++
 .../listener/CrmContractResultListener.java   | 30 ++++++++++++++--
 yudao-server/pom.xml                          | 10 +++---
 10 files changed, 147 insertions(+), 8 deletions(-)
 create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java
 create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java
 create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java

diff --git a/pom.xml b/pom.xml
index 89807a7d0..5cf738c52 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,7 +16,7 @@
         <module>yudao-module-system</module>
         <module>yudao-module-infra</module>
 <!--        <module>yudao-module-member</module>-->
-<!--        <module>yudao-module-bpm</module>-->
+        <module>yudao-module-bpm</module>
 <!--        <module>yudao-module-report</module>-->
 <!--        <module>yudao-module-mp</module>-->
 <!--        <module>yudao-module-pay</module>-->
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/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<BpmProcessInstanceResultEvent> {
+
+    @Resource
+    private List<BpmResultListenerApi> 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-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 716485007..877971435 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
@@ -28,6 +28,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 CrmBusinessServiceImpl implements CrmBusinessService {
     @Resource
     private CrmBusinessProductService businessProductService;
     @Resource
+    @Lazy
     private CrmContractService contractService;
     @Resource
     private CrmPermissionService permissionService;
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..4c1a7fd95 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;
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 9847af328..9d3cdb85b 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;
@@ -65,6 +66,12 @@ public interface CrmContractService {
      */
     void handleApprove(Long id, Long userId);
 
+    /**
+     * 更新合同流程审批结果
+     *
+     * @param event 审批结果
+     */
+    void updateContractAuditStatus(BpmResultListenerRespDTO event);
     /**
      * 获得合同
      *
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 59274b522..c58ce15f0 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
@@ -6,6 +6,7 @@ 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.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;
@@ -33,6 +34,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;
@@ -73,8 +75,10 @@ public class CrmContractServiceImpl implements CrmContractService {
     @Resource
     private CrmCustomerService customerService;
     @Resource
+    @Lazy
     private CrmContactService contactService;
     @Resource
+    @Lazy
     private CrmBusinessService businessService;
     @Resource
     private AdminUserApi adminUserApi;
@@ -244,6 +248,12 @@ public class CrmContractServiceImpl implements CrmContractService {
                 .setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateContractAuditStatus(BpmResultListenerRespDTO event) {
+        contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey())).setAuditStatus(event.getResult()));
+    }
+
     //======================= 查询相关 =======================
 
     @Override
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..f175bd43d 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,31 @@
 package cn.iocoder.yudao.module.crm.service.contract.listener;
 
-public class CrmContractResultListener {
-    // TODO puhui999: @芋艿: 艿艿写一下这个,没研究明白哈哈
+import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi;
+import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;
+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) {
+        contractService.updateContractAuditStatus(event);
+    }
+
 }
diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml
index 39d6c14c7..bf640588b 100644
--- a/yudao-server/pom.xml
+++ b/yudao-server/pom.xml
@@ -46,11 +46,11 @@
 <!--            <version>${revision}</version>-->
 <!--        </dependency>-->
         <!-- 工作流。默认注释,保证编译速度 -->
-<!--        <dependency>-->
-<!--            <groupId>cn.iocoder.boot</groupId>-->
-<!--            <artifactId>yudao-module-bpm-biz</artifactId>-->
-<!--            <version>${revision}</version>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-bpm-biz</artifactId>
+            <version>${revision}</version>
+        </dependency>
         <!-- 支付服务。默认注释,保证编译速度 -->
 <!--        <dependency>-->
 <!--            <groupId>cn.iocoder.boot</groupId>-->

From 59e6f23788af5cd79888eaeada5519b5f952a2ed Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Sat, 3 Feb 2024 12:29:28 +0800
Subject: [PATCH 2/5] =?UTF-8?q?CRM-CONTRACT:=20=E5=AE=8C=E5=96=84=20TODO?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../common/util/number/MoneyUtils.java        | 16 ++++
 .../admin/definition/BpmModelController.java  |  2 +-
 .../module/crm/enums/ErrorCodeConstants.java  |  1 +
 .../module/crm/enums/LogRecordConstants.java  |  2 +
 .../admin/contract/CrmContractController.java | 14 ++-
 .../contract/vo/CrmContractSaveReqVO.java     |  3 -
 .../business/CrmBusinessProductDO.java        |  7 ++
 .../dal/dataobject/product/CrmProductDO.java  |  2 +-
 .../dal/mysql/customer/CrmCustomerMapper.java |  3 +-
 .../business/CrmBusinessProductService.java   | 17 +++-
 .../CrmBusinessProductServiceImpl.java        |  7 +-
 .../business/CrmBusinessServiceImpl.java      |  2 +-
 .../service/contract/CrmContractService.java  |  5 +-
 .../contract/CrmContractServiceImpl.java      | 92 +++++++++++--------
 .../listener/CrmContractResultListener.java   | 28 ++++++
 .../customer/CrmCustomerServiceImpl.java      | 41 ++++++---
 16 files changed, 171 insertions(+), 71 deletions(-)

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-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-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/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 28c71c4e2..d9b181029 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
@@ -132,21 +132,19 @@ public class CrmContractController {
         return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList);
     }
 
-    // TODO @puhui999:transferContract
-    @PutMapping("/transfer")
+    @PutMapping("/transfer-contract")
     @Operation(summary = "合同转移")
     @PreAuthorize("@ss.hasPermission('crm:contract:update')")
-    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmContractTransferReqVO reqVO) {
+    public CommonResult<Boolean> 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<Boolean> transfer(@RequestParam("id") Long id) {
-        contractService.handleApprove(id, getLoginUserId());
+    public CommonResult<Boolean> 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/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..a2908a3ac 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,9 +89,6 @@ public class CrmContractSaveReqVO {
     @DiffLogField(name = "备注")
     private String remark;
 
-    // TODO @puhui999:这个字段,按道理不用传递?
-    @Schema(description = "审批状态", example = "1")
-    private Integer auditStatus;
 
     @Schema(description = "产品列表")
     private List<CrmContractProductItem> productItems;
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<CrmCustomerDO> {
         return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
     }
 
-    default List<CrmCustomerDO> selectListByLockAndDealStatusAndNotPool(Boolean lockStatus, Boolean dealStatus) {
+    default List<CrmCustomerDO> selectListByLockAndNotPool(Boolean lockStatus) {
         return selectList(new LambdaQueryWrapper<CrmCustomerDO>()
                 .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/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..f5a040488 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<CrmBusinessProductDO> list);
 
     /**
      * 根据商机id获取商机产品关联数据集合
+     *
      * @param businessId 商机id
      */
     List<CrmBusinessProductDO> selectListByBusinessId(Long businessId);
 
     /**
      * 批量更新商机产品表
+     *
      * @param list 商机产品数据集合
      */
     void updateBatch(List<CrmBusinessProductDO> list);
 
     /**
      * 批量删除
+     *
      * @param list 需要删除的商机产品集合
      */
     void deleteBatch(List<CrmBusinessProductDO> list);
 
     /**
-     *根据商机id删除商机产品关联数据
+     * 根据商机id删除商机产品关联数据
+     *
      * @param businessId 商机id
      */
     void deleteByBusinessId(Long businessId);
+
+    /**
+     * 获得合同关联的商品列表
+     *
+     * @param contractId 合同编号
+     * @return 关联的商品列表
+     */
+    List<CrmBusinessProductDO> selectListByContractId(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..0b65019b1 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
@@ -28,7 +28,7 @@ public class CrmBusinessProductServiceImpl implements CrmBusinessProductService
 
     @Override
     public List<CrmBusinessProductDO> selectListByBusinessId(Long businessId) {
-        return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId,businessId);
+        return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId, businessId);
     }
 
     @Override
@@ -47,4 +47,9 @@ public class CrmBusinessProductServiceImpl implements CrmBusinessProductService
         businessProductMapper.deleteByBusinessId(businessId);
     }
 
+    @Override
+    public List<CrmBusinessProductDO> selectListByContractId(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 877971435..3b6b46aed 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
@@ -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/contract/CrmContractService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
index 10ea9235d..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
@@ -64,7 +64,7 @@ public interface CrmContractService {
      * @param id     合同编号
      * @param userId 用户编号
      */
-    void handleApprove(Long id, Long userId);
+    void submitContract(Long id, Long userId);
 
     /**
      * 更新合同流程审批结果
@@ -125,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..5c0b2ede1 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,12 @@ 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.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 +89,10 @@ public class CrmContractServiceImpl implements CrmContractService {
         CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null);
         contractMapper.insert(contract);
         // 1.2 插入商机关联商品
-        List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO);
-        businessProductService.insertBatch(businessProduct);
+        if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
+            List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO, contract.getId());
+            businessProductService.insertBatch(businessProduct);
+        }
 
         // 2. 创建数据权限
         crmPermissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(userId)
@@ -106,9 +110,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 (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) ||
+                ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
             throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED);
         }
         validateRelationDataExists(updateReqVO);
@@ -117,21 +123,41 @@ public class CrmContractServiceImpl implements CrmContractService {
         // 更新合同
         CrmContractDO updateObj = BeanUtils.toBean(updateReqVO, CrmContractDO.class);
         contractMapper.updateById(updateObj);
-
-        // TODO puhui999: @芋艿:合同变更关联的商机后商品怎么处理?
-        // TODO @puhui999:和商品 spu、sku 编辑一样;新增的插入;修改的更新;删除的删除
-        //List<CrmBusinessProductDO> 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<CrmBusinessProductDO> newProductList = convertBusinessProductList(updateReqVO, contractId);
+        List<CrmBusinessProductDO> oldProductList = businessProductService.selectListByContractId(contractId);
+        List<List<CrmBusinessProductDO>> 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.insertBatch(diffList.getFirst());
+        }
+        if (CollUtil.isNotEmpty(diffList.get(1))) {
+            businessProductService.updateBatch(diffList.get(1));
+        }
+        if (CollUtil.isNotEmpty(diffList.get(2))) {
+            businessProductService.deleteBatch(diffList.get(2));
+        }
+    }
+
     // TODO @合同待定:缺一个取消合同的接口;只有草稿、审批中可以取消;CrmAuditStatusEnum
 
-    private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO) {
+    private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO, Long contractId) {
         // 校验商品存在
         Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId);
         List<CrmProductDO> productList = productService.getProductList(productIds);
@@ -140,29 +166,14 @@ public class CrmContractServiceImpl implements CrmContractService {
         }
         Map<Long, CrmProductDO> 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 +246,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 +261,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 +305,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 f175bd43d..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,7 +1,11 @@
 package cn.iocoder.yudao.module.crm.service.contract.listener;
 
+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;
@@ -25,7 +29,31 @@ public class CrmContractResultListener implements BpmResultListenerApi {
 
     @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<CrmCustomerImportExcelVO> 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<CrmCustomerDO> notDealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.FALSE);
-        // 1.2 获取没有锁定的不在公海的客户且成交的
-        // TODO @puhui999:下面也搞到 sql 里去哈;写 or 查询,问题不大的;
-        List<CrmCustomerDO> dealCustomerList = customerMapper.selectListByLockAndDealStatusAndNotPool(Boolean.FALSE, Boolean.TRUE);
+        // 1.1 获取没有锁定的不在公海的客户
+        List<CrmCustomerDO> customerList = customerMapper.selectListByLockAndNotPool(Boolean.FALSE);
         List<CrmCustomerDO> 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;
         }));

From 6a63f2f078759025905e160893a672119d78b1ee Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Sat, 3 Feb 2024 14:08:06 +0800
Subject: [PATCH 3/5] =?UTF-8?q?CRM-CONTRACT:=20=E5=AE=8C=E5=96=84=E5=90=88?=
 =?UTF-8?q?=E5=90=8C=E7=BC=96=E8=BE=91=E5=9B=9E=E6=98=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../admin/contract/CrmContractController.java | 25 +++++++++++--
 .../admin/contract/vo/CrmContractRespVO.java  | 35 +++++++++++++++++++
 .../contract/vo/CrmContractSaveReqVO.java     |  2 +-
 .../dal/mysql/product/CrmProductMapper.java   |  8 +++++
 .../business/CrmBusinessProductService.java   | 12 +++----
 .../CrmBusinessProductServiceImpl.java        | 12 +++----
 .../business/CrmBusinessServiceImpl.java      | 12 +++----
 .../contract/CrmContractServiceImpl.java      | 16 +++++----
 .../service/product/CrmProductService.java    |  9 +++++
 .../product/CrmProductServiceImpl.java        |  8 +++++
 10 files changed, 110 insertions(+), 29 deletions(-)

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 d9b181029..e6180d83d 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,14 @@ 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.CrmBusinessProductDO;
 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.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;
@@ -32,8 +36,8 @@ 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;
 
@@ -47,6 +51,10 @@ public class CrmContractController {
     private CrmContractService contractService;
     @Resource
     private CrmCustomerService customerService;
+    @Resource
+    private CrmBusinessProductService businessProductService;
+    @Resource
+    private CrmProductService productService;
 
     @Resource
     private AdminUserApi adminUserApi;
@@ -81,7 +89,18 @@ public class CrmContractController {
     @PreAuthorize("@ss.hasPermission('crm:contract:query')")
     public CommonResult<CrmContractRespVO> getContract(@RequestParam("id") Long id) {
         CrmContractDO contract = contractService.getContract(id);
-        return success(BeanUtils.toBean(contract, CrmContractRespVO.class));
+        CrmContractRespVO respVO = BeanUtils.toBean(contract, CrmContractRespVO.class);
+        List<CrmBusinessProductDO> businessProductList = businessProductService.getBusinessProductListByContractId(id);
+        Map<Long, CrmBusinessProductDO> businessProductMap = convertMap(businessProductList, CrmBusinessProductDO::getProductId);
+        List<CrmProductDO> 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")
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..9e6c39a3a 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;
 
@@ -113,4 +116,36 @@ public class CrmContractRespVO {
     @ExcelProperty("审批状态")
     private Integer auditStatus;
 
+    @Schema(description = "产品列表")
+    private List<CrmContractProductItemRespVO> 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 a2908a3ac..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
@@ -93,7 +93,7 @@ public class CrmContractSaveReqVO {
     @Schema(description = "产品列表")
     private List<CrmContractProductItem> 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/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<CrmProductDO> {
         return selectOne(CrmProductDO::getNo, no);
     }
 
+    default List<CrmProductDO> selectListByIds(Collection<Long> ids) {
+        return selectList(new LambdaQueryWrapperX<CrmProductDO>().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 f5a040488..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
@@ -18,35 +18,35 @@ public interface CrmBusinessProductService {
      *
      * @param list 商机产品集合
      */
-    void insertBatch(List<CrmBusinessProductDO> list);
+    void createBusinessProductBatch(List<CrmBusinessProductDO> list);
 
     /**
      * 根据商机id获取商机产品关联数据集合
      *
      * @param businessId 商机id
      */
-    List<CrmBusinessProductDO> selectListByBusinessId(Long businessId);
+    List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId);
 
     /**
      * 批量更新商机产品表
      *
      * @param list 商机产品数据集合
      */
-    void updateBatch(List<CrmBusinessProductDO> list);
+    void updateBusinessProductBatch(List<CrmBusinessProductDO> list);
 
     /**
      * 批量删除
      *
      * @param list 需要删除的商机产品集合
      */
-    void deleteBatch(List<CrmBusinessProductDO> list);
+    void deleteBusinessProductBatch(List<CrmBusinessProductDO> list);
 
     /**
      * 根据商机id删除商机产品关联数据
      *
      * @param businessId 商机id
      */
-    void deleteByBusinessId(Long businessId);
+    void deleteBusinessProductByBusinessId(Long businessId);
 
     /**
      * 获得合同关联的商品列表
@@ -54,6 +54,6 @@ public interface CrmBusinessProductService {
      * @param contractId 合同编号
      * @return 关联的商品列表
      */
-    List<CrmBusinessProductDO> selectListByContractId(Long contractId);
+    List<CrmBusinessProductDO> 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 0b65019b1..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,33 +22,33 @@ public class CrmBusinessProductServiceImpl implements CrmBusinessProductService
     private CrmBusinessProductMapper businessProductMapper;
 
     @Override
-    public void insertBatch(List<CrmBusinessProductDO> list) {
+    public void createBusinessProductBatch(List<CrmBusinessProductDO> list) {
         businessProductMapper.insertBatch(list);
     }
 
     @Override
-    public List<CrmBusinessProductDO> selectListByBusinessId(Long businessId) {
+    public List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId) {
         return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId, businessId);
     }
 
     @Override
-    public void updateBatch(List<CrmBusinessProductDO> list) {
+    public void updateBusinessProductBatch(List<CrmBusinessProductDO> list) {
         businessProductMapper.updateBatch(list);
     }
 
     // TODO @lzxhqs:这个方法,可以直接调用 deleteList 方法,然后传递 ids 就好了;
     @Override
-    public void deleteBatch(List<CrmBusinessProductDO> list) {
+    public void deleteBusinessProductBatch(List<CrmBusinessProductDO> 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<CrmBusinessProductDO> selectListByContractId(Long contractId) {
+    public List<CrmBusinessProductDO> 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 3b6b46aed..f40a15bc1 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<CrmBusinessProductDO> oldProducts = businessProductService.selectListByBusinessId(businessId);
+            List<CrmBusinessProductDO> oldProducts = businessProductService.getBusinessProductListByBusinessId(businessId);
             List<List<CrmBusinessProductDO>> 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 @商机待定:如果状态发生变化,插入商机状态变更记录表
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 5c0b2ede1..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
@@ -7,6 +7,7 @@ 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;
@@ -91,7 +92,8 @@ public class CrmContractServiceImpl implements CrmContractService {
         // 1.2 插入商机关联商品
         if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
             List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO, contract.getId());
-            businessProductService.insertBatch(businessProduct);
+            businessProductService.createBusinessProductBatch(businessProduct);
+            // 更新合同商品总金额
         }
 
         // 2. 创建数据权限
@@ -113,8 +115,8 @@ public class CrmContractServiceImpl implements CrmContractService {
         Assert.notNull(updateReqVO.getId(), "合同编号不能为空");
         CrmContractDO contract = validateContractExists(updateReqVO.getId());
         // 只有草稿、审批中,可以编辑;
-        if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus()) ||
-                ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
+        if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
+                CrmAuditStatusEnum.PROCESS.getStatus())) {
             throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED);
         }
         validateRelationDataExists(updateReqVO);
@@ -136,7 +138,7 @@ public class CrmContractServiceImpl implements CrmContractService {
             return;
         }
         List<CrmBusinessProductDO> newProductList = convertBusinessProductList(updateReqVO, contractId);
-        List<CrmBusinessProductDO> oldProductList = businessProductService.selectListByContractId(contractId);
+        List<CrmBusinessProductDO> oldProductList = businessProductService.getBusinessProductListByContractId(contractId);
         List<List<CrmBusinessProductDO>> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> {
             if (ObjUtil.notEqual(oldObj.getProductId(), newObj.getProductId())) {
                 return false;
@@ -145,13 +147,13 @@ public class CrmContractServiceImpl implements CrmContractService {
             return true;
         });
         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));
         }
     }
 
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<CrmProductDO> getProductListByIds(Collection<Long> 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<CrmProductDO>().eq(CrmProductDO::getCategoryId, categoryId));
     }
 
+    @Override
+    public List<CrmProductDO> getProductListByIds(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return productMapper.selectListByIds(ids);
+    }
+
 }

From 53badfc3673d97beb6ee7dafa7bdc78659e202ca Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Sat, 3 Feb 2024 20:49:13 +0800
Subject: [PATCH 4/5] =?UTF-8?q?CRM-CONTRACT:=20=E5=AE=8C=E5=96=84=E5=90=88?=
 =?UTF-8?q?=E5=90=8C=E7=BC=96=E8=BE=91=E5=9B=9E=E6=98=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../admin/business/CrmBusinessController.java |  4 +-
 .../admin/clue/CrmClueController.java         |  4 +-
 .../admin/contact/CrmContactController.java   | 10 ++---
 .../admin/contract/CrmContractController.java | 45 +++++++++++++------
 .../admin/contract/vo/CrmContractRespVO.java  | 16 +++++--
 .../admin/customer/CrmCustomerController.java |  4 +-
 .../followup/CrmFollowUpRecordController.java |  2 +-
 .../convert/contract/CrmContractConvert.java  | 17 ++++---
 .../service/contact/CrmContactService.java    |  4 +-
 .../contact/CrmContactServiceImpl.java        |  4 +-
 10 files changed, 70 insertions(+), 40 deletions(-)

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..85fe5b3f3 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
@@ -166,10 +166,10 @@ public class CrmBusinessController {
         return CrmBusinessConvert.INSTANCE.convertPage(pageResult, customerList, statusTypeList, statusList);
     }
 
-    @PutMapping("/transfer")
+    @PutMapping("/transfer-business")
     @Operation(summary = "商机转移")
     @PreAuthorize("@ss.hasPermission('crm:business:update')")
-    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) {
+    public CommonResult<Boolean> 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..a2ddf3742 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
@@ -88,10 +88,10 @@ public class CrmClueController {
         ExcelUtils.write(response, "线索.xls", "数据", CrmClueRespVO.class, datas);
     }
 
-    @PutMapping("/transfer")
+    @PutMapping("/transfer-clue")
     @Operation(summary = "线索转移")
     @PreAuthorize("@ss.hasPermission('crm:clue:update')")
-    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmClueTransferReqVO reqVO) {
+    public CommonResult<Boolean> 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..c00d57a6c 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<CrmCustomerDO> customerList = customerService.getCustomerList(
                 Collections.singletonList(contact.getCustomerId()));
         // 3. 直属上级
-        List<CrmContactDO> parentContactList = contactService.getContactList(
+        List<CrmContactDO> 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<List<CrmContactRespVO>> getContactListByIds(@RequestParam("ids") List<Long> 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,15 +170,15 @@ public class CrmContactController {
         Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList,
                 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
         // 3. 直属上级
-        List<CrmContactDO> parentContactList = contactService.getContactList(
+        List<CrmContactDO> parentContactList = contactService.getContactListByIds(
                 convertSet(contactList, CrmContactDO::getParentId), getLoginUserId());
         return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList);
     }
 
-    @PutMapping("/transfer")
+    @PutMapping("/transfer-contact")
     @Operation(summary = "联系人转移")
     @PreAuthorize("@ss.hasPermission('crm:contact:update')")
-    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmContactTransferReqVO reqVO) {
+    public CommonResult<Boolean> 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 e6180d83d..43bd49ea2 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,11 +10,15 @@ 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;
@@ -26,11 +30,13 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.Valid;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.security.access.prepost.PreAuthorize;
 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;
@@ -52,6 +58,11 @@ public class CrmContractController {
     @Resource
     private CrmCustomerService customerService;
     @Resource
+    private CrmContactService contactService;
+    @Resource
+    private CrmBusinessService businessService;
+    @Resource
+    @Lazy
     private CrmBusinessProductService businessProductService;
     @Resource
     private CrmProductService productService;
@@ -89,7 +100,8 @@ public class CrmContractController {
     @PreAuthorize("@ss.hasPermission('crm:contract:query')")
     public CommonResult<CrmContractRespVO> getContract(@RequestParam("id") Long id) {
         CrmContractDO contract = contractService.getContract(id);
-        CrmContractRespVO respVO = BeanUtils.toBean(contract, CrmContractRespVO.class);
+        List<CrmContractRespVO> respVOList = buildContractDetail(Collections.singletonList(contract));
+        CrmContractRespVO respVO = respVOList.getFirst();
         List<CrmBusinessProductDO> businessProductList = businessProductService.getBusinessProductListByContractId(id);
         Map<Long, CrmBusinessProductDO> businessProductMap = convertMap(businessProductList, CrmBusinessProductDO::getProductId);
         List<CrmProductDO> productList = productService.getProductListByIds(convertSet(businessProductList, CrmBusinessProductDO::getProductId));
@@ -108,15 +120,15 @@ public class CrmContractController {
     @PreAuthorize("@ss.hasPermission('crm:contract:query')")
     public CommonResult<PageResult<CrmContractRespVO>> getContractPage(@Valid CrmContractPageReqVO pageVO) {
         PageResult<CrmContractDO> 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<PageResult<CrmContractRespVO>> getContractPageByCustomer(@Valid CrmContractPageReqVO pageVO) {
         Assert.notNull(pageVO.getCustomerId(), "客户编号不能为空");
         PageResult<CrmContractDO> pageResult = contractService.getContractPageByCustomerId(pageVO);
-        return success(buildContractDetailPage(pageResult));
+        return success(BeanUtils.toBean(pageResult, CrmContractRespVO.class).setList(buildContractDetail(pageResult.getList())));
     }
 
     @GetMapping("/export-excel")
@@ -132,23 +144,28 @@ public class CrmContractController {
     }
 
     /**
-     * 构建详细的合同分页结果
+     * 构建详细的合同结果
      *
-     * @param pageResult 简单的合同分页结果
-     * @return 详细的合同分页结果
+     * @param contractList 原始合同信息
+     * @return 细的合同结果
      */
-    private PageResult<CrmContractRespVO> buildContractDetailPage(PageResult<CrmContractDO> pageResult) {
-        List<CrmContractDO> contactList = pageResult.getList();
-        if (CollUtil.isEmpty(contactList)) {
-            return PageResult.empty(pageResult.getTotal());
+    private List<CrmContractRespVO> buildContractDetail(List<CrmContractDO> contractList) {
+        if (CollUtil.isEmpty(contractList)) {
+            return Collections.emptyList();
         }
         // 1. 获取客户列表
         List<CrmCustomerDO> customerList = customerService.getCustomerList(
-                convertSet(contactList, CrmContractDO::getCustomerId));
+                convertSet(contractList, CrmContractDO::getCustomerId));
         // 2. 获取创建人、负责人列表
-        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contactList,
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(contractList,
                 contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
-        return CrmContractConvert.INSTANCE.convertPage(pageResult, userMap, customerList);
+        // 3. 获取联系人
+        Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactListByIds(convertSet(contractList,
+                CrmContractDO::getContactId)), CrmContactDO::getId);
+        // 4. 获取商机
+        Map<Long, CrmBusinessDO> businessMap = convertMap(businessService.getBusinessList(convertSet(contractList,
+                CrmContractDO::getBusinessId)), CrmBusinessDO::getId);
+        return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap);
     }
 
     @PutMapping("/transfer-contract")
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 9e6c39a3a..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
@@ -29,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("工作流编号")
@@ -77,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("最后跟进时间")
@@ -104,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;
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..d8c2f0289 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
@@ -232,10 +232,10 @@ public class CrmCustomerController {
         return success(customerService.importCustomerList(list, updateSupport, getLoginUserId()));
     }
 
-    @PutMapping("/transfer")
+    @PutMapping("/transfer-customer")
     @Operation(summary = "转移客户")
     @PreAuthorize("@ss.hasPermission('crm:customer:update')")
-    public CommonResult<Boolean> transfer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {
+    public CommonResult<Boolean> 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<PageResult<CrmFollowUpRecordRespVO>> getFollowUpRecordPage(@Valid CrmFollowUpRecordPageReqVO pageReqVO) {
         PageResult<CrmFollowUpRecordDO> pageResult = followUpRecordService.getFollowUpRecordPage(pageReqVO);
         /// 拼接数据
-        Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactList(
+        Map<Long, CrmContactDO> contactMap = convertMap(contactService.getContactListByIds(
                 convertSetByFlatMap(pageResult.getList(), item -> item.getContactIds().stream())), CrmContactDO::getId);
         Map<Long, CrmBusinessDO> 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<CrmContractRespVO> convertPage(PageResult<CrmContractDO> pageResult, Map<Long, AdminUserRespDTO> userMap,
-                                                      List<CrmCustomerDO> customerList) {
-        PageResult<CrmContractRespVO> voPageResult = BeanUtils.toBean(pageResult, CrmContractRespVO.class);
+    default List<CrmContractRespVO> convertList(List<CrmContractDO> contractList, Map<Long, AdminUserRespDTO> userMap,
+                                                List<CrmCustomerDO> customerList, Map<Long, CrmContactDO> contactMap,
+                                                Map<Long, CrmBusinessDO> businessMap) {
+        List<CrmContractRespVO> respVOList = BeanUtils.toBean(contractList, CrmContractRespVO.class);
         // 拼接关联字段
         Map<Long, CrmCustomerDO> 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/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<CrmContactDO> getContactList(Collection<Long> ids, Long userId);
+    List<CrmContactDO> getContactListByIds(Collection<Long> ids, Long userId);
 
     /**
      * 获得联系人列表
@@ -88,7 +88,7 @@ public interface CrmContactService {
      * @param ids 编号
      * @return 联系人列表
      */
-    List<CrmContactDO> getContactList(Collection<Long> ids);
+    List<CrmContactDO> getContactListByIds(Collection<Long> 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 4c1a7fd95..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
@@ -210,7 +210,7 @@ public class CrmContactServiceImpl implements CrmContactService {
     }
 
     @Override
-    public List<CrmContactDO> getContactList(Collection<Long> ids, Long userId) {
+    public List<CrmContactDO> getContactListByIds(Collection<Long> ids, Long userId) {
         if (CollUtil.isEmpty(ids)) {
             return ListUtil.empty();
         }
@@ -218,7 +218,7 @@ public class CrmContactServiceImpl implements CrmContactService {
     }
 
     @Override
-    public List<CrmContactDO> getContactList(Collection<Long> ids) {
+    public List<CrmContactDO> getContactListByIds(Collection<Long> ids) {
         if (CollUtil.isEmpty(ids)) {
             return ListUtil.empty();
         }

From ba6218b8351b5950fe4f86b23647d4a6ed0c2b6f Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Sat, 3 Feb 2024 22:13:02 +0800
Subject: [PATCH 5/5] =?UTF-8?q?CRM-=E5=90=88=E5=90=8C:=20transfer?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../crm/controller/admin/business/CrmBusinessController.java    | 2 +-
 .../module/crm/controller/admin/clue/CrmClueController.java     | 2 +-
 .../crm/controller/admin/contact/CrmContactController.java      | 2 +-
 .../crm/controller/admin/contract/CrmContractController.java    | 2 +-
 .../crm/controller/admin/customer/CrmCustomerController.java    | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

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 85fe5b3f3..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
@@ -166,7 +166,7 @@ public class CrmBusinessController {
         return CrmBusinessConvert.INSTANCE.convertPage(pageResult, customerList, statusTypeList, statusList);
     }
 
-    @PutMapping("/transfer-business")
+    @PutMapping("/transfer")
     @Operation(summary = "商机转移")
     @PreAuthorize("@ss.hasPermission('crm:business:update')")
     public CommonResult<Boolean> transferBusiness(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) {
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 a2ddf3742..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
@@ -88,7 +88,7 @@ public class CrmClueController {
         ExcelUtils.write(response, "线索.xls", "数据", CrmClueRespVO.class, datas);
     }
 
-    @PutMapping("/transfer-clue")
+    @PutMapping("/transfer")
     @Operation(summary = "线索转移")
     @PreAuthorize("@ss.hasPermission('crm:clue:update')")
     public CommonResult<Boolean> transferClue(@Valid @RequestBody CrmClueTransferReqVO reqVO) {
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 c00d57a6c..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
@@ -175,7 +175,7 @@ public class CrmContactController {
         return CrmContactConvert.INSTANCE.convertPage(pageResult, userMap, crmCustomerDOList, parentContactList);
     }
 
-    @PutMapping("/transfer-contact")
+    @PutMapping("/transfer")
     @Operation(summary = "联系人转移")
     @PreAuthorize("@ss.hasPermission('crm:contact:update')")
     public CommonResult<Boolean> transferContact(@Valid @RequestBody CrmContactTransferReqVO reqVO) {
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 43bd49ea2..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
@@ -168,7 +168,7 @@ public class CrmContractController {
         return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap);
     }
 
-    @PutMapping("/transfer-contract")
+    @PutMapping("/transfer")
     @Operation(summary = "合同转移")
     @PreAuthorize("@ss.hasPermission('crm:contract:update')")
     public CommonResult<Boolean> transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) {
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 d8c2f0289..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
@@ -232,7 +232,7 @@ public class CrmCustomerController {
         return success(customerService.importCustomerList(list, updateSupport, getLoginUserId()));
     }
 
-    @PutMapping("/transfer-customer")
+    @PutMapping("/transfer")
     @Operation(summary = "转移客户")
     @PreAuthorize("@ss.hasPermission('crm:customer:update')")
     public CommonResult<Boolean> transferCustomer(@Valid @RequestBody CrmCustomerTransferReqVO reqVO) {