From 7188f28998d47c286e34cbd525986603cfc19ba9 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 31 七月 2026 17:58:25 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_business' into dev_business

---
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java |  108 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 94 insertions(+), 14 deletions(-)

diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
index 71b1884..0aaa4ec 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
@@ -2,6 +2,7 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.ListUtil;
+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;
@@ -15,6 +16,7 @@
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
 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.business.CrmBusinessMapper;
 import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@@ -27,7 +29,8 @@
 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
-import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import com.mzt.logapi.context.LogRecordContext;
 import com.mzt.logapi.service.impl.DiffParseFunction;
@@ -43,6 +46,8 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -78,7 +83,7 @@
     @Resource
     private CrmContactBusinessService contactBusinessService;
     @Resource
-    private CrmProductService productService;
+    private MdmItemApi mdmItemApi;
 
     @Resource
     private AdminUserApi adminUserApi;
@@ -129,8 +134,9 @@
         updateReqVO.setOwnerUserId(null).setStatusTypeId(null); // 涓嶅厑璁告洿鏂扮殑瀛楁
         // 1.1 鏍¢獙瀛樺湪
         CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
-        // 1.2 鏍¢獙浜у搧椤圭殑鏈夋晥鎬�
-        List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts());
+        // 1.2 鏍¢獙浜у搧椤圭殑鏈夋晥鎬э紙浼犲叆鏃т骇鍝佸垪琛ㄧ敤浜庡悎骞� null 瀛楁锛�
+        List<CrmBusinessProductDO> oldProducts = businessProductMapper.selectListByBusinessId(updateReqVO.getId());
+        List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts(), oldProducts);
         // 1.3 鏍¢獙鍏宠仈瀛楁
         validateRelationDataExists(updateReqVO);
 
@@ -142,7 +148,7 @@
         updateBusinessProduct(updateObj.getId(), businessProducts);
 
         // 3. 璁板綍鎿嶄綔鏃ュ織涓婁笅鏂�
-        updateReqVO.setOwnerUserId(oldBusiness.getOwnerUserId()); // 閬垮厤鎿嶄綔鏃ュ織鍑虹幇鈥滃垹闄よ礋璐d汉鈥濈殑鎯呭喌
+        updateReqVO.setOwnerUserId(oldBusiness.getOwnerUserId()); // 閬垮厤鎿嶄綔鏃ュ織鍑虹幇"鍒犻櫎璐熻矗浜�"鐨勬儏鍐�
         LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessSaveReqVO.class));
         LogRecordContext.putVariable("businessName", oldBusiness.getName());
     }
@@ -194,9 +200,15 @@
         if (saveReqVO.getCustomerId() != null) {
             customerService.validateCustomer(saveReqVO.getCustomerId());
         }
-        // 鏍¢獙鑱旂郴浜�
+        // 鏍¢獙鑱旂郴浜猴紙涓斿繀椤讳笌鍟嗘満灞炰簬鍚屼竴瀹㈡埛锛岄伩鍏嶈法瀹㈡埛鍏宠仈锛�
         if (saveReqVO.getContactId() != null) {
             contactService.validateContact(saveReqVO.getContactId());
+            if (saveReqVO.getCustomerId() != null) {
+                CrmContactDO contact = contactService.getContact(saveReqVO.getContactId());
+                if (ObjUtil.notEqual(saveReqVO.getCustomerId(), contact.getCustomerId())) {
+                    throw exception(BUSINESS_CONTACT_CUSTOMER_NOT_MATCH);
+                }
+            }
         }
         // 鏍¢獙璐熻矗浜�
         if (saveReqVO.getOwnerUserId() != null) {
@@ -205,17 +217,67 @@
     }
 
     private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.BusinessProduct> list) {
-        // 1. 鏍¢獙浜у搧瀛樺湪
-        productService.validProductList(convertSet(list, CrmBusinessSaveReqVO.BusinessProduct::getProductId));
-        // 2. 杞寲涓� CrmBusinessProductDO 鍒楄〃
-        return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class,
-                item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount()))));
+        return validateBusinessProducts(list, Collections.emptyList());
+    }
+
+    /**
+     * 鏍¢獙浜у搧椤圭殑鏈夋晥鎬э紙鏇存柊鍦烘櫙鍚堝苟鏃ф暟鎹級
+     *
+     * @param list       鍓嶇浼犲叆鐨勪骇鍝佸垪琛�
+     * @param oldProducts 宸叉湁浜у搧鍒楄〃锛堢敤浜庡悎骞� null 瀛楁锛�
+     * @return 澶勭悊鍚庣殑浜у搧 DO 鍒楄〃
+     */
+    private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.BusinessProduct> list,
+                                                                  List<CrmBusinessProductDO> oldProducts) {
+        // 1. 鏍¢獙鐗╂枡瀛樺湪
+        Set<Long> itemIds = convertSet(list, CrmBusinessSaveReqVO.BusinessProduct::getItemId);
+        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(itemIds);
+        for (Long itemId : itemIds) {
+            if (itemMap.get(itemId) == null) {
+                throw exception(PRODUCT_NOT_EXISTS);
+            }
+        }
+        // 2. 鏋勫缓鏃т骇鍝� Map锛堟寜 id 绱㈠紩锛�
+        Map<Long, CrmBusinessProductDO> oldProductMap = convertMap(oldProducts, CrmBusinessProductDO::getId);
+        // 3. 杞寲涓� CrmBusinessProductDO 鍒楄〃锛堝悎骞舵棫鏁版嵁锛�
+        return convertList(list, o -> {
+            CrmBusinessProductDO product = BeanUtils.toBean(o, CrmBusinessProductDO.class);
+            // 鏇存柊鍦烘櫙锛氬鏋滀骇鍝佸凡鏈� id 涓旀湁鏃ф暟鎹紝鍒欏悎骞� null 瀛楁
+            if (o.getId() != null && oldProductMap.containsKey(o.getId())) {
+                CrmBusinessProductDO old = oldProductMap.get(o.getId());
+                if (product.getBusinessPrice() == null) {
+                    product.setBusinessPrice(old.getBusinessPrice());
+                }
+                if (product.getItemPrice() == null) {
+                    product.setItemPrice(old.getItemPrice());
+                }
+                if (product.getCount() == null) {
+                    product.setCount(old.getCount());
+                }
+                if (product.getItemUnitId() == null) {
+                    product.setItemUnitId(old.getItemUnitId());
+                }
+            }
+            // 浼樺厛浣跨敤鍓嶇浼犲叆鐨� totalPrice锛屽惁鍒欓�氳繃 businessPrice * count 璁$畻
+            if (o.getTotalPrice() != null) {
+                product.setTotalPrice(o.getTotalPrice());
+            } else {
+                product.setTotalPrice(MoneyUtils.priceMultiply(product.getBusinessPrice(), product.getCount()));
+            }
+            return product;
+        });
     }
 
     private void calculateTotalPrice(CrmBusinessDO business, List<CrmBusinessProductDO> businessProducts) {
-        business.setTotalProductPrice(getSumValue(businessProducts, CrmBusinessProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
-        BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(business.getTotalProductPrice(), business.getDiscountPercent());
-        business.setTotalPrice(business.getTotalProductPrice().subtract(discountPrice));
+        // totalProductPrice锛氫紭鍏堜娇鐢ㄥ墠绔紶鍏ュ�硷紝鍚﹀垯浠庝骇鍝佹眹鎬�
+        if (business.getTotalProductPrice() == null) {
+            business.setTotalProductPrice(getSumValue(businessProducts, CrmBusinessProductDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
+        }
+        // totalPrice锛氫紭鍏堜娇鐢ㄥ墠绔紶鍏ュ�硷紝鍚﹀垯鎸夋姌鎵h绠�
+        if (business.getTotalPrice() == null) {
+            BigDecimal discountPrice = MoneyUtils.priceMultiplyPercent(business.getTotalProductPrice(), business.getDiscountPercent());
+            business.setTotalPrice(business.getTotalProductPrice().subtract(discountPrice));
+        }
     }
 
     @Override
@@ -378,6 +440,24 @@
     }
 
     @Override
+    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#customerId", level = CrmPermissionLevelEnum.READ)
+    public List<CrmBusinessDO> getBusinessListByCustomerId(Long customerId) {
+        return businessMapper.selectListByCustomerId(customerId);
+    }
+
+    @Override
+    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.READ)
+    public List<CrmBusinessDO> getBusinessListByContact(Long contactId) {
+        // 1. 鏌ヨ鍏宠仈鐨勫晢鏈虹紪鍙�
+        List<CrmContactBusinessDO> contactBusinessList = contactBusinessService.getContactBusinessListByContactId(contactId);
+        if (CollUtil.isEmpty(contactBusinessList)) {
+            return ListUtil.empty();
+        }
+        // 2. 鏌ヨ鍟嗘満鍒楄〃
+        return businessMapper.selectByIds(convertSet(contactBusinessList, CrmContactBusinessDO::getBusinessId));
+    }
+
+    @Override
     public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {
         return businessMapper.selectPage(pageVO);
     }

--
Gitblit v1.9.3