5 天以前 7188f28998d47c286e34cbd525986603cfc19ba9
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()); // 避免操作日志出现“删除负责人”的情况
        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:优先使用前端传入值,否则按折扣计算
        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);
    }