package cn.iocoder.yudao.module.erp.service.product; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO; import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO; import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO; import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemCreateReqDTO; import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemUpdateReqDTO; import cn.iocoder.yudao.module.mdm.api.category.MdmItemCategoryApi; import cn.iocoder.yudao.module.mdm.api.category.dto.MdmItemCategoryRespDTO; import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi; import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.*; 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.module.erp.enums.ErrorCodeConstants.*; /** * ERP 产品 Service 实现类 * * 改造说明:ERP 产品数据来源于 MDM 物料主数据 * - 所有产品操作均调用 MDM API * - ERP 产品对应 MDM 物料类型 = 成品(itemType = 2) * * @author 芋道源码 */ @Service @Validated public class ErpProductServiceImpl implements ErpProductService { /** ERP 产品对应的 MDM 物料类型:成品 */ private static final int MDM_ITEM_TYPE_PRODUCT = 2; @Resource private MdmItemApi mdmItemApi; @Resource private MdmItemCategoryApi mdmItemCategoryApi; @Resource private MdmUnitMeasureApi mdmUnitMeasureApi; @Override public Long createProduct(ProductSaveReqVO createReqVO) { // 1. 校验条码唯一 validateProductBarCodeUnique(null, createReqVO.getBarCode()); // 2. 构建创建请求并调用 MDM API MdmItemCreateReqDTO createReqDTO = buildCreateReqDTO(createReqVO); return mdmItemApi.createItem(createReqDTO).getCheckedData(); } @Override public void updateProduct(ProductSaveReqVO updateReqVO) { // 1. 校验存在 validateProductExists(updateReqVO.getId()); // 2. 校验条码唯一 validateProductBarCodeUnique(updateReqVO.getId(), updateReqVO.getBarCode()); // 3. 构建更新请求并调用 MDM API MdmItemUpdateReqDTO updateReqDTO = buildUpdateReqDTO(updateReqVO); mdmItemApi.updateItem(updateReqDTO); } @Override public void deleteProduct(Long id) { // 校验存在 validateProductExists(id); // ERP 产品删除实际为禁用(MDM 物料不能物理删除) mdmItemApi.updateItemStatus(id, CommonStatusEnum.DISABLE.getStatus()); } @Override public List validProductList(Collection ids) { if (CollUtil.isEmpty(ids)) { return Collections.emptyList(); } // 1. 调用 MDM API 获取物料列表 List itemList = mdmItemApi.getItemList(ids).getCheckedData(); Map itemMap = convertMap(itemList, MdmItemRespDTO::getId); // 2. 校验每个产品 for (Long id : ids) { MdmItemRespDTO item = itemMap.get(id); if (item == null) { throw exception(PRODUCT_NOT_EXISTS); } if (CommonStatusEnum.isDisable(item.getStatus())) { throw exception(PRODUCT_NOT_ENABLE, item.getName()); } } // 3. 转换为 ERP Product DO(手动映射字段) return convertToErpProductDOList(itemList); } @Override public ErpProductDO getProduct(Long id) { MdmItemRespDTO item = mdmItemApi.getItem(id).getCheckedData(); return convertToErpProductDO(item); } @Override public List getProductVOListByStatus(Integer status) { List itemList = mdmItemApi.getItemListByStatus(status).getCheckedData(); return buildProductVOList(itemList); } @Override public List getProductVOList(Collection ids) { if (CollUtil.isEmpty(ids)) { return Collections.emptyList(); } List itemList = mdmItemApi.getItemList(ids).getCheckedData(); return buildProductVOList(itemList); } @Override public PageResult getProductVOPage(ErpProductPageReqVO pageReqVO) { // 调用 MDM API 分页查询 PageResult mdmPageResult = mdmItemApi.getItemPage( MDM_ITEM_TYPE_PRODUCT, pageReqVO.getStatus(), pageReqVO.getPageNo(), pageReqVO.getPageSize() ).getCheckedData(); List productVOList = buildProductVOList(mdmPageResult.getList()); return new PageResult<>(productVOList, mdmPageResult.getTotal()); } @Override public Long getProductCountByCategoryId(Long categoryId) { return mdmItemApi.getItemCountByCategoryId(categoryId).getCheckedData(); } @Override public Long getProductCountByUnitId(Long unitId) { return mdmItemApi.getItemCountByUnitMeasureId(unitId).getCheckedData(); } // ==================== 私有方法 ==================== /** * 校验产品存在 */ private void validateProductExists(Long id) { mdmItemApi.validateItemExists(id).getCheckedData(); } /** * 校验产品条码唯一 */ private void validateProductBarCodeUnique(Long id, String barCode) { if (barCode == null) { return; } MdmItemRespDTO existItem = mdmItemApi.getItemByBarCode(barCode).getCheckedData(); if (existItem != null && !existItem.getId().equals(id)) { throw exception(PRODUCT_BAR_CODE_EXISTS); } } /** * 构建创建请求 DTO * 注意:ProductSaveReqVO 使用 standard 字段作为规格,MDM 使用 specification */ private MdmItemCreateReqDTO buildCreateReqDTO(ProductSaveReqVO saveReqVO) { MdmItemCreateReqDTO createReqDTO = new MdmItemCreateReqDTO(); createReqDTO.setCode(saveReqVO.getBarCode()); // 使用条码作为编码 createReqDTO.setName(saveReqVO.getName()); createReqDTO.setBarCode(saveReqVO.getBarCode()); createReqDTO.setSpecification(saveReqVO.getStandard()); // standard -> specification createReqDTO.setCategoryId(saveReqVO.getCategoryId()); createReqDTO.setUnitMeasureId(saveReqVO.getUnitId()); // unitId -> unitMeasureId createReqDTO.setItemType(MDM_ITEM_TYPE_PRODUCT); createReqDTO.setStatus(saveReqVO.getStatus() != null ? saveReqVO.getStatus() : CommonStatusEnum.ENABLE.getStatus()); createReqDTO.setPurchasePrice(saveReqVO.getPurchasePrice()); createReqDTO.setSalesPrice(saveReqVO.getSalePrice()); createReqDTO.setCostPrice(saveReqVO.getMinPrice()); // minPrice -> costPrice createReqDTO.setExpiryDay(saveReqVO.getExpiryDay()); createReqDTO.setWeight(saveReqVO.getWeight()); createReqDTO.setRemark(saveReqVO.getRemark()); return createReqDTO; } /** * 构建更新请求 DTO */ private MdmItemUpdateReqDTO buildUpdateReqDTO(ProductSaveReqVO saveReqVO) { MdmItemUpdateReqDTO updateReqDTO = new MdmItemUpdateReqDTO(); updateReqDTO.setId(saveReqVO.getId()); updateReqDTO.setCode(saveReqVO.getBarCode()); updateReqDTO.setName(saveReqVO.getName()); updateReqDTO.setBarCode(saveReqVO.getBarCode()); updateReqDTO.setSpecification(saveReqVO.getStandard()); // standard -> specification updateReqDTO.setCategoryId(saveReqVO.getCategoryId()); updateReqDTO.setUnitMeasureId(saveReqVO.getUnitId()); // unitId -> unitMeasureId updateReqDTO.setStatus(saveReqVO.getStatus()); updateReqDTO.setPurchasePrice(saveReqVO.getPurchasePrice()); updateReqDTO.setSalesPrice(saveReqVO.getSalePrice()); updateReqDTO.setCostPrice(saveReqVO.getMinPrice()); updateReqDTO.setExpiryDay(saveReqVO.getExpiryDay()); updateReqDTO.setWeight(saveReqVO.getWeight()); updateReqDTO.setRemark(saveReqVO.getRemark()); return updateReqDTO; } /** * 构建产品 VO 列表(包含分类和单位名称) * 注意:MDM 和 ERP 字段名有差异,需要手动映射 */ private List buildProductVOList(List itemList) { if (CollUtil.isEmpty(itemList)) { return Collections.emptyList(); } // 1. 获取分类和单位信息 Set categoryIds = convertSet(itemList, MdmItemRespDTO::getCategoryId); Set unitIds = convertSet(itemList, MdmItemRespDTO::getUnitMeasureId); Map categoryMap = CollUtil.isEmpty(categoryIds) ? Collections.emptyMap() : convertMap(mdmItemCategoryApi.getItemCategoryList(categoryIds).getCheckedData(), MdmItemCategoryRespDTO::getId); Map unitMap = CollUtil.isEmpty(unitIds) ? Collections.emptyMap() : convertMap(mdmUnitMeasureApi.getUnitMeasureList(unitIds).getCheckedData(), MdmUnitMeasureRespDTO::getId); // 2. 手动转换并填充关联信息(处理字段名差异) List result = new ArrayList<>(); for (MdmItemRespDTO mdmItem : itemList) { ErpProductRespVO product = new ErpProductRespVO(); product.setId(mdmItem.getId()); product.setName(mdmItem.getName()); product.setBarCode(mdmItem.getBarCode()); product.setStandard(mdmItem.getSpecification()); // specification -> standard product.setCategoryId(mdmItem.getCategoryId()); product.setUnitId(mdmItem.getUnitMeasureId()); // unitMeasureId -> unitId product.setStatus(mdmItem.getStatus()); product.setPurchasePrice(mdmItem.getPurchasePrice()); product.setSalePrice(mdmItem.getSalesPrice()); // salesPrice -> salePrice product.setMinPrice(mdmItem.getCostPrice()); // costPrice -> minPrice product.setExpiryDay(mdmItem.getExpiryDay()); product.setWeight(mdmItem.getWeight()); product.setRemark(mdmItem.getRemark()); // 设置分类和单位名称 MapUtils.findAndThen(categoryMap, mdmItem.getCategoryId(), category -> product.setCategoryName(category.getName())); MapUtils.findAndThen(unitMap, mdmItem.getUnitMeasureId(), unit -> product.setUnitName(unit.getName())); result.add(product); } return result; } /** * 转换 MDM 物料为 ERP 产品 DO * 注意:字段名差异需要手动映射 */ private ErpProductDO convertToErpProductDO(MdmItemRespDTO mdmItem) { if (mdmItem == null) { return null; } ErpProductDO product = new ErpProductDO(); product.setId(mdmItem.getId()); product.setName(mdmItem.getName()); product.setBarCode(mdmItem.getBarCode()); product.setStandard(mdmItem.getSpecification()); // specification -> standard product.setCategoryId(mdmItem.getCategoryId()); product.setUnitId(mdmItem.getUnitMeasureId()); // unitMeasureId -> unitId product.setStatus(mdmItem.getStatus()); product.setPurchasePrice(mdmItem.getPurchasePrice()); product.setSalePrice(mdmItem.getSalesPrice()); // salesPrice -> salePrice product.setMinPrice(mdmItem.getCostPrice()); // costPrice -> minPrice product.setExpiryDay(mdmItem.getExpiryDay()); product.setWeight(mdmItem.getWeight()); product.setRemark(mdmItem.getRemark()); return product; } /** * 批量转换 MDM 物料列表为 ERP 产品列表 */ private List convertToErpProductDOList(List mdmItemList) { if (CollUtil.isEmpty(mdmItemList)) { return Collections.emptyList(); } List result = new ArrayList<>(); for (MdmItemRespDTO mdmItem : mdmItemList) { result.add(convertToErpProductDO(mdmItem)); } return result; } }