| | |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemPageReqVO; |
| | | import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemSaveReqVO; |
| | | import cn.iocoder.yudao.module.mdm.dal.dataobject.category.MdmItemCategoryDO; |
| | | import cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemDO; |
| | | import cn.iocoder.yudao.module.mdm.dal.dataobject.unit.MdmUnitMeasureDO; |
| | | import cn.iocoder.yudao.module.mdm.dal.mysql.item.MdmItemBatchConfigMapper; |
| | | import cn.iocoder.yudao.module.mdm.dal.mysql.item.MdmItemMapper; |
| | | import cn.iocoder.yudao.module.mdm.service.brand.MdmBrandService; |
| | | import cn.iocoder.yudao.module.mdm.service.category.MdmItemCategoryService; |
| | | import cn.iocoder.yudao.module.mdm.service.unit.MdmUnitMeasureService; |
| | | import cn.iocoder.yudao.module.mdm.service.brand.MdmBrandService; |
| | | import cn.iocoder.yudao.module.mes.api.item.MesMdItemApi; |
| | | import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | |
| | | @Resource |
| | | private MdmBrandService brandService; |
| | | |
| | | @Resource |
| | | @Lazy |
| | | private MesMdItemApi mesMdItemApi; |
| | | @Resource |
| | | private MdmItemBatchConfigMapper itemBatchConfigMapper; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createItem(MdmItemSaveReqVO createReqVO) { |
| | | // 校验数据 |
| | | validateItemSaveData(createReqVO); |
| | | |
| | | // 插入 |
| | | // 插入 MDM 物料 |
| | | MdmItemDO item = BeanUtils.toBean(createReqVO, MdmItemDO.class); |
| | | itemMapper.insert(item); |
| | | |
| | | // 保存批次配置(如果有) |
| | | saveBatchConfig(item.getId(), createReqVO); |
| | | |
| | | // 同步到 MES(勾选 syncMes 时) |
| | | if (Boolean.TRUE.equals(createReqVO.getSyncMes())) { |
| | | syncItemToMes(createReqVO); |
| | | } |
| | | return item.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateItem(MdmItemSaveReqVO updateReqVO) { |
| | | // 校验存在 |
| | | validateItemExists(updateReqVO.getId()); |
| | | // 校验数据 |
| | | validateItemSaveData(updateReqVO); |
| | | |
| | | // 更新 |
| | | // 更新 MDM 物料 |
| | | MdmItemDO updateObj = BeanUtils.toBean(updateReqVO, MdmItemDO.class); |
| | | itemMapper.updateById(updateObj); |
| | | |
| | | // 保存批次配置(如果有) |
| | | saveBatchConfig(updateReqVO.getId(), updateReqVO); |
| | | |
| | | // 同步到 MES(勾选 syncMes 时) |
| | | if (Boolean.TRUE.equals(updateReqVO.getSyncMes())) { |
| | | syncItemToMes(updateReqVO); |
| | | } |
| | | } |
| | | |
| | | private void validateItemSaveData(MdmItemSaveReqVO reqVO) { |
| | | // 校验物料编码的唯一性 |
| | | validateItemCodeUnique(reqVO.getId(), reqVO.getCode()); |
| | | // 校验物料分类存在 |
| | | validateCategoryExists(reqVO.getCategoryId()); |
| | | // 校验计量单位存在 |
| | | validateUnitMeasureExists(reqVO.getUnitMeasureId()); |
| | | // 校验品牌存在(如果有) |
| | | if (reqVO.getBrandId() != null) { |
| | | validateBrandExists(reqVO.getBrandId()); |
| | | } |
| | | |
| | | /** |
| | | * 同步物料到 MES(单位/分类按 code 映射) |
| | | * |
| | | * MDM 的 unitMeasureId/categoryId 与 MES 的 unitMeasureId/itemTypeId 分属两套主数据, |
| | | * 这里先在 MDM 侧解析出 code,再交由 MesMdItemApi 在 MES 侧解析为对应 id。 |
| | | * 同步失败将抛出异常,触发外层 @Transactional 整体回滚(MDM 操作一并回滚)。 |
| | | */ |
| | | private void syncItemToMes(MdmItemSaveReqVO reqVO) { |
| | | // 解析计量单位 id -> code |
| | | String unitCode = null; |
| | | if (reqVO.getUnitMeasureId() != null) { |
| | | MdmUnitMeasureDO unitMeasure = unitMeasureService.getUnitMeasure(reqVO.getUnitMeasureId()); |
| | | if (unitMeasure == null) { |
| | | throw exception(MDM_UNIT_MEASURE_NOT_EXISTS); |
| | | } |
| | | unitCode = unitMeasure.getCode(); |
| | | } |
| | | // 解析分类 id -> code(使用 MDM 本地分类服务) |
| | | String itemTypeCode = null; |
| | | if (reqVO.getCategoryId() != null) { |
| | | MdmItemCategoryDO category = itemCategoryService.getItemCategory(reqVO.getCategoryId()); |
| | | if (category != null) { |
| | | itemTypeCode = category.getCode(); |
| | | } |
| | | } else if (reqVO.getItemType() != null) { |
| | | // 根据 itemType 查找对应分类编码 |
| | | // itemType: 1原料, 2半成品, 3成品, 4辅料 |
| | | String itemTypeName = convertItemTypeToName(reqVO.getItemType()); |
| | | List<MdmItemCategoryDO> categories = itemCategoryService.getItemCategoryList( |
| | | itemCategoryService.getItemCategoryChildrenList(null).stream().map(MdmItemCategoryDO::getId).collect(Collectors.toSet())); |
| | | itemTypeCode = categories.stream() |
| | | .filter(c -> c.getName() != null && c.getName().equals(itemTypeName)) |
| | | .findFirst() |
| | | .map(MdmItemCategoryDO::getCode) |
| | | .orElse(null); |
| | | } |
| | | // 组装同步 DTO |
| | | MesMdItemSyncReqDTO syncReqDTO = new MesMdItemSyncReqDTO(); |
| | | syncReqDTO.setCode(reqVO.getCode()); |
| | | syncReqDTO.setName(reqVO.getName()); |
| | | syncReqDTO.setSpecification(reqVO.getSpecification()); |
| | | syncReqDTO.setUnitCode(unitCode); |
| | | syncReqDTO.setItemTypeCode(itemTypeCode); |
| | | syncReqDTO.setSafeStockFlag(Boolean.TRUE); // 保留 MDM 的 minStock/maxStock,避免被 MES 清零 |
| | | syncReqDTO.setMinStock(reqVO.getMinStock()); |
| | | syncReqDTO.setMaxStock(reqVO.getMaxStock()); |
| | | syncReqDTO.setHighValue(reqVO.getIsHighValue()); |
| | | syncReqDTO.setBatchFlag(reqVO.getIsBatchManaged()); |
| | | syncReqDTO.setRemark(reqVO.getRemark()); |
| | | // 批次配置字段 |
| | | syncReqDTO.setProduceDateFlag(reqVO.getProduceDateFlag()); |
| | | syncReqDTO.setExpireDateFlag(reqVO.getExpireDateFlag()); |
| | | syncReqDTO.setReceiptDateFlag(reqVO.getReceiptDateFlag()); |
| | | syncReqDTO.setVendorFlag(reqVO.getVendorFlag()); |
| | | syncReqDTO.setPurchaseOrderCodeFlag(reqVO.getPurchaseOrderCodeFlag()); |
| | | syncReqDTO.setLotNumberFlag(reqVO.getLotNumberFlag()); |
| | | syncReqDTO.setQualityStatusFlag(reqVO.getQualityStatusFlag()); |
| | | // create 走 createItem,update 走 updateItem(MES 侧按 code upsert) |
| | | if (reqVO.getId() == null) { |
| | | mesMdItemApi.createItem(syncReqDTO); |
| | | } else { |
| | | mesMdItemApi.updateItem(syncReqDTO); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteItem(Long id) { |
| | | // 校验存在 |
| | | validateItemExists(id); |
| | | // 删除 |
| | | MdmItemDO item = validateItemExists(id); |
| | | // 解决软删除唯一键冲突:如果已存在相同 code 且已软删除的记录,先物理删除它 |
| | | MdmItemDO existingDeleted = itemMapper.selectByCodeAndDeleted(item.getCode(), true); |
| | | if (existingDeleted != null && !existingDeleted.getId().equals(id)) { |
| | | itemMapper.physicalDeleteById(existingDeleted.getId()); |
| | | } |
| | | // 软删除当前记录 |
| | | itemMapper.deleteById(id); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | private void validateCategoryExists(Long categoryId) { |
| | | if (itemCategoryService.getItemCategory(categoryId) == null) { |
| | | if (categoryId == null) { |
| | | return; |
| | | } |
| | | // 使用 MDM 本地分类服务校验 |
| | | MdmItemCategoryDO category = itemCategoryService.getItemCategory(categoryId); |
| | | if (category == null) { |
| | | throw exception(MDM_ITEM_CATEGORY_NOT_EXISTS); |
| | | } |
| | | } |
| | |
| | | private void validateBrandExists(Long brandId) { |
| | | if (brandService.getBrand(brandId) == null) { |
| | | throw exception(MDM_BRAND_NOT_EXISTS); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将 itemType 转换为中文名称 |
| | | * |
| | | * @param itemType 物料类型:1原料, 2半成品, 3成品, 4辅料 |
| | | * @return 中文名称 |
| | | */ |
| | | private String convertItemTypeToName(Integer itemType) { |
| | | if (itemType == null) { |
| | | return null; |
| | | } |
| | | switch (itemType) { |
| | | case 1: |
| | | return "原料"; |
| | | case 2: |
| | | return "半成品"; |
| | | case 3: |
| | | return "成品"; |
| | | case 4: |
| | | return "辅料"; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | |
| | | return itemMapper.selectByCode(code); |
| | | } |
| | | |
| | | /** |
| | | * 保存批次配置(到 MDM 本地表) |
| | | */ |
| | | private void saveBatchConfig(Long itemId, MdmItemSaveReqVO reqVO) { |
| | | // 检查是否有批次配置数据 |
| | | if (reqVO.getProduceDateFlag() == null |
| | | && reqVO.getExpireDateFlag() == null |
| | | && reqVO.getReceiptDateFlag() == null |
| | | && reqVO.getVendorFlag() == null |
| | | && reqVO.getPurchaseOrderCodeFlag() == null |
| | | && reqVO.getLotNumberFlag() == null |
| | | && reqVO.getQualityStatusFlag() == null) { |
| | | return; |
| | | } |
| | | |
| | | // 查询已有配置 |
| | | cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemBatchConfigDO existing = |
| | | itemBatchConfigMapper.selectOne( |
| | | cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemBatchConfigDO::getItemId, itemId); |
| | | |
| | | cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemBatchConfigDO config = |
| | | new cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemBatchConfigDO(); |
| | | config.setItemId(itemId); |
| | | config.setProduceDateFlag(Boolean.TRUE.equals(reqVO.getProduceDateFlag())); |
| | | config.setExpireDateFlag(Boolean.TRUE.equals(reqVO.getExpireDateFlag())); |
| | | config.setReceiptDateFlag(Boolean.TRUE.equals(reqVO.getReceiptDateFlag())); |
| | | config.setVendorFlag(Boolean.TRUE.equals(reqVO.getVendorFlag())); |
| | | config.setPurchaseOrderCodeFlag(Boolean.TRUE.equals(reqVO.getPurchaseOrderCodeFlag())); |
| | | config.setLotNumberFlag(Boolean.TRUE.equals(reqVO.getLotNumberFlag())); |
| | | config.setQualityStatusFlag(Boolean.TRUE.equals(reqVO.getQualityStatusFlag())); |
| | | // 清空不需要的字段 |
| | | config.setClientFlag(false); |
| | | config.setSalesOrderCodeFlag(false); |
| | | config.setWorkOrderFlag(false); |
| | | config.setTaskFlag(false); |
| | | config.setWorkstationFlag(false); |
| | | config.setToolFlag(false); |
| | | config.setMoldFlag(false); |
| | | |
| | | if (existing != null) { |
| | | config.setId(existing.getId()); |
| | | itemBatchConfigMapper.updateById(config); |
| | | } else { |
| | | itemBatchConfigMapper.insert(config); |
| | | } |
| | | } |
| | | |
| | | } |