| | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.collection.ListUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.hutool.extra.spring.SpringUtil; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | 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.crm.controller.admin.business.vo.business.CrmBusinessImportExcelVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessImportReqVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessImportRespVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO; |
| | |
| | | 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.dataobject.customer.CrmCustomerDO; |
| | | 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.dal.mysql.customer.CrmCustomerMapper; |
| | | 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; |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | 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.*; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; |
| | | import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; |
| | | |
| | |
| | | private CrmBusinessMapper businessMapper; |
| | | @Resource |
| | | private CrmBusinessProductMapper businessProductMapper; |
| | | @Resource |
| | | private CrmCustomerMapper customerMapper; |
| | | |
| | | @Resource |
| | | private CrmBusinessStatusService businessStatusService; |
| | |
| | | permissionService.createPermission(new CrmPermissionCreateReqBO().setUserId(business.getOwnerUserId()) |
| | | .setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()).setBizId(business.getId()) |
| | | .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); |
| | | // 3.1 客户负责人自动拥有该商机的读写权限 |
| | | permissionService.createPermissionForCustomerOwner(CrmBizTypeEnum.CRM_BUSINESS.getType(), |
| | | business.getId(), business.getCustomerId(), business.getOwnerUserId()); |
| | | |
| | | // 4. 在联系人的详情页,如果直接【新建商机】,则需要关联下 |
| | | if (createReqVO.getContactId() != null) { |
| | |
| | | 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); |
| | | |
| | |
| | | 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()); |
| | | } |
| | |
| | | } |
| | | |
| | | private List<CrmBusinessProductDO> validateBusinessProducts(List<CrmBusinessSaveReqVO.BusinessProduct> list) { |
| | | 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); |
| | |
| | | throw exception(PRODUCT_NOT_EXISTS); |
| | | } |
| | | } |
| | | // 2. 转化为 CrmBusinessProductDO 列表 |
| | | return convertList(list, o -> BeanUtils.toBean(o, CrmBusinessProductDO.class, |
| | | item -> item.setTotalPrice(MoneyUtils.priceMultiply(item.getBusinessPrice(), item.getCount())))); |
| | | // 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()); |
| | | } |
| | | } else { |
| | | // 新产品:清除前端生成的临时 ID,让数据库 AUTO_INCREMENT 接管 |
| | | product.setId(null); |
| | | } |
| | | // 优先使用前端传入的 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 |
| | |
| | | return businessMapper.selectPage(pageVO); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public CrmBusinessImportRespVO importBusinessList(List<CrmBusinessImportExcelVO> importBusinesses, |
| | | CrmBusinessImportReqVO importReqVO) { |
| | | // 1. 校验非空 |
| | | importBusinesses = filterList(importBusinesses, item -> StrUtil.isNotBlank(item.getName())); |
| | | if (CollUtil.isEmpty(importBusinesses)) { |
| | | throw exception(BUSINESS_IMPORT_LIST_IS_EMPTY); |
| | | } |
| | | // 2. 校验负责人(为空则默认当前操作人) |
| | | Long ownerUserId = importReqVO.getOwnerUserId() != null ? importReqVO.getOwnerUserId() : getLoginUserId(); |
| | | adminUserApi.validateUser(ownerUserId); |
| | | // 3. 默认商机状态组 + 状态 |
| | | Long defaultStatusTypeId = businessStatusService.getBusinessStatusTypeList().get(0).getId(); |
| | | Long defaultStatusId = businessStatusService.getBusinessStatusListByTypeId(defaultStatusTypeId).get(0).getId(); |
| | | |
| | | // 4. 逐条处理 |
| | | CrmBusinessImportRespVO respVO = CrmBusinessImportRespVO.builder() |
| | | .createBusinessNames(new ArrayList<>()) |
| | | .updateBusinessNames(new ArrayList<>()) |
| | | .failureBusinessNames(new LinkedHashMap<>()) |
| | | .build(); |
| | | importBusinesses.forEach(importBusiness -> { |
| | | try { |
| | | // 4.1 解析客户 |
| | | CrmCustomerDO customer = customerMapper.selectByCustomerName(importBusiness.getCustomerName()); |
| | | if (customer == null) { |
| | | throw exception(BUSINESS_IMPORT_CUSTOMER_NOT_EXISTS, importBusiness.getCustomerName()); |
| | | } |
| | | // 4.2 判断是否存在 |
| | | CrmBusinessDO existBusiness = businessMapper.selectByNameAndCustomerId( |
| | | importBusiness.getName(), customer.getId()); |
| | | if (existBusiness == null) { |
| | | // 4.2.1 新增 |
| | | CrmBusinessDO business = BeanUtils.toBean(importBusiness, CrmBusinessDO.class) |
| | | .setCustomerId(customer.getId()).setOwnerUserId(ownerUserId) |
| | | .setStatusTypeId(defaultStatusTypeId).setStatusId(defaultStatusId); |
| | | if (business.getDiscountPercent() == null) { |
| | | business.setDiscountPercent(BigDecimal.ZERO); |
| | | } |
| | | calculateTotalPrice(business, Collections.emptyList()); |
| | | businessMapper.insert(business); |
| | | // 创建数据权限 |
| | | permissionService.createPermission(new CrmPermissionCreateReqBO() |
| | | .setBizType(CrmBizTypeEnum.CRM_BUSINESS.getType()).setBizId(business.getId()) |
| | | .setUserId(ownerUserId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); |
| | | // 客户负责人自动拥有该商机的读写权限 |
| | | permissionService.createPermissionForCustomerOwner(CrmBizTypeEnum.CRM_BUSINESS.getType(), |
| | | business.getId(), customer.getId(), ownerUserId); |
| | | respVO.getCreateBusinessNames().add(importBusiness.getName()); |
| | | // 记录操作日志 |
| | | getSelf().importBusinessLog(business, false); |
| | | return; |
| | | } |
| | | // 4.2.2 已存在,判断是否允许更新 |
| | | if (!importReqVO.getUpdateSupport()) { |
| | | respVO.getFailureBusinessNames().put(importBusiness.getName(), |
| | | StrUtil.format(BUSINESS_NAME_EXISTS.getMsg(), importBusiness.getName())); |
| | | return; |
| | | } |
| | | CrmBusinessDO updateBusiness = BeanUtils.toBean(importBusiness, CrmBusinessDO.class) |
| | | .setId(existBusiness.getId()).setCustomerId(customer.getId()); |
| | | businessMapper.updateById(updateBusiness); |
| | | respVO.getUpdateBusinessNames().add(importBusiness.getName()); |
| | | // 记录操作日志 |
| | | getSelf().importBusinessLog(updateBusiness, true); |
| | | } catch (ServiceException ex) { |
| | | respVO.getFailureBusinessNames().put(importBusiness.getName(), ex.getMessage()); |
| | | } |
| | | }); |
| | | return respVO; |
| | | } |
| | | |
| | | /** |
| | | * 记录导入商机时的操作日志 |
| | | * |
| | | * @param business 商机信息 |
| | | * @param isUpdate 是否更新;true - 更新,false - 新增 |
| | | */ |
| | | @LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_IMPORT_SUB_TYPE, bizNo = "{{#business.id}}", |
| | | success = CRM_BUSINESS_IMPORT_SUCCESS) |
| | | public void importBusinessLog(CrmBusinessDO business, boolean isUpdate) { |
| | | LogRecordContext.putVariable("business", business); |
| | | LogRecordContext.putVariable("isUpdate", isUpdate); |
| | | } |
| | | |
| | | /** |
| | | * 获得自身的代理对象,解决 AOP 生效问题 |
| | | * |
| | | * @return 自己 |
| | | */ |
| | | private CrmBusinessServiceImpl getSelf() { |
| | | return SpringUtil.getBean(getClass()); |
| | | } |
| | | |
| | | } |