فهرست منبع

CRM:完善 review 提到的问题

puhui999 1 سال پیش
والد
کامیت
02b63de808
18فایلهای تغییر یافته به همراه88 افزوده شده و 76 حذف شده
  1. 1 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
  2. 2 0
      yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
  3. 2 3
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java
  4. 2 3
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java
  5. 2 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java
  6. 3 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java
  7. 1 12
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java
  8. 3 3
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactBusinessMapper.java
  9. 4 0
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
  10. 0 9
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java
  11. 8 18
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
  12. 2 2
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java
  13. 9 7
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java
  14. 17 6
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java
  15. 5 4
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
  16. 25 3
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
  17. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
  18. 1 1
      yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java

+ 1 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java

@@ -28,6 +28,7 @@ public interface ErrorCodeConstants {
     ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
     ErrorCode CONTACT_BUSINESS_LINK_NOT_EXISTS = new ErrorCode(1_020_003_001, "联系人商机关联不存在");
     ErrorCode CONTACT_DELETE_FAIL_CONTRACT_LINK_EXISTS = new ErrorCode(1_020_003_002, "联系人已关联合同,不能删除");
+    ErrorCode CONTACT_UPDATE_OWNER_USER_FAIL = new ErrorCode(1_020_003_003, "更新联系人负责人失败");
 
     // ========== 回款 1-020-004-000 ==========
     ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款不存在");

+ 2 - 0
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java

@@ -77,6 +77,8 @@ public interface LogRecordConstants {
     String CRM_CONTACT_TRANSFER_SUCCESS = "将联系人【{{#contact.name}}】的负责人从【{getAdminUserById{#contact.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】";
     String CRM_CONTACT_FOLLOW_UP_SUB_TYPE = "联系人跟进";
     String CRM_CONTACT_FOLLOW_UP_SUCCESS = "联系人跟进【{{#contactName}}】";
+    String CRM_CONTACT_UPDATE_OWNER_USER_SUB_TYPE = "更新联系人负责人";
+    String CRM_CONTACT_UPDATE_OWNER_USER_SUCCESS = "将联系人【{{#contact.name}}】的负责人从【{getAdminUserById{#contact.ownerUserId}}】变更为了【{getAdminUserById{#ownerUserId}}】";
 
     // ======================= CRM_BUSINESS 商机 =======================
 

+ 2 - 3
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessSaveReqVO.java

@@ -77,15 +77,14 @@ public class CrmBusinessSaveReqVO {
     @Schema(description = "联系人编号", example = "110")
     private Long contactId; // 使用场景,在【联系人详情】添加商机时,如果需要关联两者,需要传递 contactId 字段
 
-    // TODO @puhui999:传递 items 就行啦;
     @Schema(description = "产品列表")
-    private List<CrmBusinessProductItem> productItems;
+    private List<Item> items;
 
     @Schema(description = "产品列表")
     @Data
     @NoArgsConstructor
     @AllArgsConstructor
-    public static class CrmBusinessProductItem {
+    public static class Item {
 
         @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
         @NotNull(message = "产品编号不能为空")

+ 2 - 3
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java

@@ -130,14 +130,13 @@ public class CrmContractRespVO {
     private Integer auditStatus;
 
     @Schema(description = "产品列表")
-    private List<CrmContractProductItemRespVO> productItems;
+    private List<Item> items;
 
-    // TODO @puhui999:可以直接叫 Item
     @Schema(description = "产品列表")
     @Data
     @NoArgsConstructor
     @AllArgsConstructor
-    public static class CrmContractProductItemRespVO {
+    public static class Item {
 
         @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20529")
         private Long id;

+ 2 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/CrmContractConvert.java

@@ -59,8 +59,8 @@ public interface CrmContractConvert {
 
     default void setContractRespVOProductItems(CrmContractRespVO respVO, Map<Long, CrmContractProductDO> contractProductMap,
                                                List<CrmProductDO> productList) {
-        respVO.setProductItems(CollectionUtils.convertList(productList, product -> {
-            CrmContractRespVO.CrmContractProductItemRespVO productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.CrmContractProductItemRespVO.class);
+        respVO.setItems(CollectionUtils.convertList(productList, product -> {
+            CrmContractRespVO.Item productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.Item.class);
             findAndThen(contractProductMap, product.getId(), contractProduct ->
                     productItemRespVO.setCount(contractProduct.getCount()).setDiscountPercent(contractProduct.getDiscountPercent()));
             return productItemRespVO;

+ 3 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractProductDO.java

@@ -57,8 +57,9 @@ public class CrmContractProductDO extends BaseDO {
     private Integer discountPercent;
     /**
      * 总计价格(折扣后价格)
-     *
-     * TODO @puhui999:可以写下计算公式哈;
+     * = {@link #price}
+     * * {@link #count}
+     * * ({@link #discountPercent / 100})
      */
     private Integer totalPrice;
 

+ 1 - 12
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessProductMapper.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.dal.mysql.business;
 
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
 import org.apache.ibatis.annotations.Mapper;
 
@@ -16,18 +15,8 @@ import java.util.List;
 @Mapper
 public interface CrmBusinessProductMapper extends BaseMapperX<CrmBusinessProductDO> {
 
-    // TODO @puhui999:用不到的方法,看看是不是删除哈
-    default void deleteByBusinessId(Long getBusinessId) { // TODO @lzxhqs:第一个方法,和类之间最好空一行;
-        delete(CrmBusinessProductDO::getBusinessId, getBusinessId);
-    }
-
-    default CrmBusinessProductDO selectByBusinessId(Long getBusinessId) {
-        return selectOne(CrmBusinessProductDO::getBusinessId, getBusinessId);
-    }
-
     default List<CrmBusinessProductDO> selectListByBusinessId(Long businessId) {
-        // TODO @puhui999:可以简化,selectList(CrmBusinessProductDO::getBusinessId, businessId)
-        return selectList(new LambdaQueryWrapperX<CrmBusinessProductDO>().eq(CrmBusinessProductDO::getBusinessId, businessId));
+        return selectList(CrmBusinessProductDO::getBusinessId, businessId);
     }
 
 }

+ 3 - 3
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contactbusinesslink/CrmContactBusinessMapper.java → yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactBusinessMapper.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink;
+package cn.iocoder.yudao.module.crm.dal.mysql.contact;
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
@@ -9,7 +9,7 @@ import java.util.Collection;
 import java.util.List;
 
 /**
- * CRM 联系人商机关联 Mapper
+ * CRM 联系人商机关联 Mapper
  *
  * @author 芋道源码
  */
@@ -31,4 +31,4 @@ public interface CrmContactBusinessMapper extends BaseMapperX<CrmContactBusiness
         return selectList(CrmContactBusinessDO::getContactId, contactId);
     }
 
-}
+}

+ 4 - 0
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java

@@ -65,4 +65,8 @@ public interface CrmContactMapper extends BaseMapperX<CrmContactDO> {
         return selectJoinList(CrmContactDO.class, query);
     }
 
+    default List<CrmContactDO> selectListByCustomerId(Long customerId) {
+        return selectList(CrmContactDO::getCustomerId, customerId);
+    }
+
 }

+ 0 - 9
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractProductMapper.java

@@ -16,15 +16,6 @@ import java.util.List;
 @Mapper
 public interface CrmContractProductMapper extends BaseMapperX<CrmContractProductDO> {
 
-    // TODO @puhui999:用不到的方法,看看是不是删除哈
-    default void deleteByContractId(Long contractId) { // TODO @lzxhqs:第一个方法,和类之间最好空一行;
-        delete(CrmContractProductDO::getContractId, contractId);
-    }
-
-    default CrmContractProductDO selectByContractId(Long contractId) {
-        return selectOne(CrmContractProductDO::getContractId, contractId);
-    }
-
     default List<CrmContractProductDO> selectListByContractId(Long contractId) {
         return selectList(new LambdaQueryWrapperX<CrmContractProductDO>().eq(CrmContractProductDO::getContractId, contractId));
     }

+ 8 - 18
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java

@@ -80,16 +80,15 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
         CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class).setOwnerUserId(userId);
         businessMapper.insert(business);
         // 1.2 插入商机关联商品
-        if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
-            List<CrmBusinessProductDO> productList = buildBusinessProductList(createReqVO.getProductItems(), business.getId());
+        if (CollUtil.isNotEmpty(createReqVO.getItems())) { // 如果有的话
+            List<CrmBusinessProductDO> productList = buildBusinessProductList(createReqVO.getItems(), business.getId());
             businessProductMapper.insertBatch(productList);
             // 更新合同商品总金额
             businessMapper.updateById(new CrmBusinessDO().setId(business.getId()).setProductPrice(
                     getSumValue(productList, CrmBusinessProductDO::getTotalPrice, Integer::sum)));
         }
-        // TODO @puhui999:在联系人的详情页,如果直接【新建商机】,则需要关联下。这里要搞个 CrmContactBusinessDO 表
-        createContactBusiness(business.getId(), createReqVO.getContactId());
-
+        // 在联系人的详情页,如果直接【新建商机】,则需要关联下。
+        contactBusinessService.createContactBusiness(createReqVO.getContactId(), business.getId());
         // 2. 创建数据权限
         // 设置当前操作的人为负责人
         permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType())
@@ -100,14 +99,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
         return business.getId();
     }
 
-    // TODO @lzxhqs:CrmContactBusinessService 调用这个;这样逻辑才能收敛哈;
-    private void createContactBusiness(Long businessId, Long contactId) {
-        CrmContactBusinessDO contactBusiness = new CrmContactBusinessDO();
-        contactBusiness.setBusinessId(businessId);
-        contactBusiness.setContactId(contactId);
-        contactBusinessService.insert(contactBusiness);
-    }
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
@@ -121,7 +112,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
         CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
         businessMapper.updateById(updateObj);
         // 2.2 更新商机关联商品
-        List<CrmBusinessProductDO> productList = buildBusinessProductList(updateReqVO.getProductItems(), updateObj.getId());
+        List<CrmBusinessProductDO> productList = buildBusinessProductList(updateReqVO.getItems(), updateObj.getId());
         updateBusinessProduct(productList, updateObj.getId());
 
         // TODO @商机待定:如果状态发生变化,插入商机状态变更记录表
@@ -175,10 +166,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
         }
     }
 
-    private List<CrmBusinessProductDO> buildBusinessProductList(List<CrmBusinessSaveReqVO.CrmBusinessProductItem> productItems,
-                                                                Long businessId) {
+    private List<CrmBusinessProductDO> buildBusinessProductList(List<CrmBusinessSaveReqVO.Item> productItems, Long businessId) {
         // 校验商品存在
-        Set<Long> productIds = convertSet(productItems, CrmBusinessSaveReqVO.CrmBusinessProductItem::getId);
+        Set<Long> productIds = convertSet(productItems, CrmBusinessSaveReqVO.Item::getId);
         List<CrmProductDO> productList = productService.getProductList(productIds);
         if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) {
             throw exception(PRODUCT_NOT_EXISTS);
@@ -237,7 +227,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
     public void updateBusinessProduct(CrmBusinessUpdateProductReqBO updateProductReqBO) {
         // 更新商机关联商品
         List<CrmBusinessProductDO> productList = buildBusinessProductList(
-                BeanUtils.toBean(updateProductReqBO.getProductItems(), CrmBusinessSaveReqVO.CrmBusinessProductItem.class), updateProductReqBO.getId());
+                BeanUtils.toBean(updateProductReqBO.getItems(), CrmBusinessSaveReqVO.Item.class), updateProductReqBO.getId());
         updateBusinessProduct(productList, updateProductReqBO.getId());
     }
 

+ 2 - 2
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/business/bo/CrmBusinessUpdateProductReqBO.java

@@ -25,13 +25,13 @@ public class CrmBusinessUpdateProductReqBO {
 
     // TODO @芋艿:再想想
     @NotEmpty(message = "产品列表不能为空")
-    private List<CrmBusinessProductItem> productItems;
+    private List<Item> items;
 
     @Schema(description = "产品列表")
     @Data
     @NoArgsConstructor
     @AllArgsConstructor
-    public static class CrmBusinessProductItem {
+    public static class Item {
 
         @Schema(description = "产品编号", example = "20529")
         @NotNull(message = "产品编号不能为空")

+ 9 - 7
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessService.java

@@ -13,6 +13,15 @@ import java.util.List;
  */
 public interface CrmContactBusinessService {
 
+    /**
+     * 创建联系人人商机关联
+     *
+     * @param contactId  联系人编号
+     * @param businessId 商机编号
+     */
+    void createContactBusiness(Long contactId, Long businessId);
+
+
     /**
      * 创建联系人与商机的关联
      *
@@ -42,11 +51,4 @@ public interface CrmContactBusinessService {
      */
     List<CrmContactBusinessDO> getContactBusinessListByContactId(Long contactId);
 
-    /**
-     * 新增联系人与商机的关联
-     *
-     * @param contactBusiness 新增联系人与商机的对象
-     */
-    void insert(CrmContactBusinessDO contactBusiness);
-
 }

+ 17 - 6
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java

@@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.CrmContactBusines
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
-import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper;
+import cn.iocoder.yudao.module.crm.dal.mysql.contact.CrmContactBusinessMapper;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
@@ -41,6 +41,22 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
     @Lazy // 延迟加载,为了解决延迟加载
     private CrmContactService contactService;
 
+    @Override
+    public void createContactBusiness(Long contactId, Long businessId) {
+        // 校验存在
+        CrmContactDO contact = contactService.getContact(contactId);
+        if (contact == null) {
+            throw exception(CONTACT_NOT_EXISTS);
+        }
+        CrmBusinessDO business = businessService.getBusiness(businessId);
+        if (business == null) {
+            throw exception(BUSINESS_NOT_EXISTS);
+        }
+
+        // 插入
+        contactBusinessMapper.insert(new CrmContactBusinessDO(null, contactId, businessId));
+    }
+
     @Override
     @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#createReqVO.contactId", level = CrmPermissionLevelEnum.WRITE)
     public void createContactBusinessList(CrmContactBusinessReqVO createReqVO) {
@@ -91,9 +107,4 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
         return contactBusinessMapper.selectListByContactId(contactId);
     }
 
-    @Override
-    public void insert(CrmContactBusinessDO contactBusiness) {
-        contactBusinessMapper.insert(contactBusiness);
-    }
-
 }

+ 5 - 4
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java

@@ -55,6 +55,7 @@ public interface CrmContactService {
 
     /**
      * 更新指定客户的联系人的负责人
+     * 数据权限基于 【客户】
      *
      * @param customerId  客户编号
      * @param ownerUserId 用户编号
@@ -64,8 +65,8 @@ public interface CrmContactService {
     /**
      * 更新联系人相关跟进信息
      *
-     * @param id 编号
-     * @param contactNextTime 下次联系时间
+     * @param id                 编号
+     * @param contactNextTime    下次联系时间
      * @param contactLastContent 最后联系内容
      */
     void updateContactFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent);
@@ -73,8 +74,8 @@ public interface CrmContactService {
     /**
      * 更新联系人相关跟进信息
      *
-     * @param ids 编号数组
-     * @param contactNextTime 下次联系时间
+     * @param ids                编号数组
+     * @param contactNextTime    下次联系时间
      * @param contactLastContent 最后联系内容
      */
     void updateContactFollowUpBatch(Collection<Long> ids, LocalDateTime contactNextTime, String contactLastContent);

+ 25 - 3
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java

@@ -184,7 +184,7 @@ public class CrmContactServiceImpl implements CrmContactService {
 
         // 2.1 数据权限转移
         permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CONTACT.getType(),
-                        reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
+                reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
         // 2.2 设置新的负责人
         contactMapper.updateById(new CrmContactDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId()));
 
@@ -193,9 +193,31 @@ public class CrmContactServiceImpl implements CrmContactService {
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
+    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#customerId", level = CrmPermissionLevelEnum.OWNER)
     public void updateOwnerUserIdByCustomerId(Long customerId, Long ownerUserId) {
-        contactMapper.updateOwnerUserIdByCustomerId(customerId, ownerUserId);
-        // TODO @puhui999:操作日志、数据权限;
+        // 1. 校验存在
+        List<CrmContactDO> contacts = contactMapper.selectListByCustomerId(customerId);
+        if (CollUtil.isEmpty(contacts)) {
+            return;
+        }
+        int count = contactMapper.updateOwnerUserIdByCustomerId(customerId, ownerUserId);
+        if (count == 0) {
+            throw exception(CONTACT_UPDATE_OWNER_USER_FAIL);
+        }
+
+        // 2. 记录操作日志
+        for (CrmContactDO contact : contacts) {
+            receiveContactLog(contact, ownerUserId);
+        }
+    }
+
+    @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_UPDATE_OWNER_USER_SUB_TYPE, bizNo = "{{#contact.id}",
+            success = CRM_CONTACT_UPDATE_OWNER_USER_SUCCESS)
+    public void receiveContactLog(CrmContactDO contact, Long ownerUserId) {
+        // 记录操作日志上下文
+        LogRecordContext.putVariable("contact", contact);
+        LogRecordContext.putVariable("ownerUserId", ownerUserId);
     }
 
     @Override

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java

@@ -104,7 +104,7 @@ public class CrmContractServiceImpl implements CrmContractService {
             // 如果存在合同关联了商机则更新商机商品关联
             if (contract.getBusinessId() != null) {
                 businessService.updateBusinessProduct(new CrmBusinessUpdateProductReqBO().setId(contract.getBusinessId())
-                        .setProductItems(BeanUtils.toBean(createReqVO.getProductItems(), CrmBusinessUpdateProductReqBO.CrmBusinessProductItem.class)));
+                        .setItems(BeanUtils.toBean(createReqVO.getProductItems(), CrmBusinessUpdateProductReqBO.Item.class)));
             }
         }
 

+ 1 - 1
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java

@@ -412,7 +412,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
         return count;
     }
 
-    @Transactional // 需要 protected 修饰,因为需要在事务中调用
+    @Transactional(rollbackFor = Exception.class) // 需要 protected 修饰,因为需要在事务中调用
     protected void putCustomerPool(CrmCustomerDO customer) {
         // 1. 设置负责人为 NULL
         int updateOwnerUserIncr = customerMapper.updateOwnerUserIdById(customer.getId(), null);