| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | 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.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.MesMdItemTypeApi; |
| | | import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO; |
| | | import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemTypeRespDTO; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.util.*; |
| | |
| | | @Resource |
| | | private MdmBrandService brandService; |
| | | |
| | | @Resource |
| | | private MesMdItemApi mesMdItemApi; |
| | | @Resource |
| | | private MesMdItemTypeApi mesMdItemTypeApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createItem(MdmItemSaveReqVO createReqVO) { |
| | | // 校验数据 |
| | | validateItemSaveData(createReqVO); |
| | | |
| | | // 插入 |
| | | // 插入 MDM 物料 |
| | | MdmItemDO item = BeanUtils.toBean(createReqVO, MdmItemDO.class); |
| | | itemMapper.insert(item); |
| | | |
| | | // 同步到 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); |
| | | |
| | | // 同步到 MES(勾选 syncMes 时) |
| | | if (Boolean.TRUE.equals(updateReqVO.getSyncMes())) { |
| | | syncItemToMes(updateReqVO); |
| | | } |
| | | } |
| | | |
| | | private void validateItemSaveData(MdmItemSaveReqVO reqVO) { |
| | | // 校验物料编码的唯一性 |
| | | validateItemCodeUnique(reqVO.getId(), reqVO.getCode()); |
| | | } |
| | | |
| | | /** |
| | | * 同步物料到 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(使用 MES 物料分类 API) |
| | | String itemTypeCode = null; |
| | | if (reqVO.getCategoryId() != null) { |
| | | CommonResult<MesMdItemTypeRespDTO> result = mesMdItemTypeApi.getItemType(reqVO.getCategoryId()); |
| | | if (result != null && result.isSuccess() && result.getData() != null) { |
| | | itemTypeCode = result.getData().getCode(); |
| | | } |
| | | } else if (reqVO.getItemType() != null) { |
| | | // 根据 itemType 查找对应分类编码 |
| | | // itemType: 1原料, 2半成品, 3成品, 4辅料 |
| | | String itemTypeName = convertItemTypeToName(reqVO.getItemType()); |
| | | CommonResult<List<MesMdItemTypeRespDTO>> listResult = mesMdItemTypeApi.getItemTypeList(null); |
| | | if (listResult != null && listResult.isSuccess() && listResult.getData() != null) { |
| | | itemTypeCode = listResult.getData().stream() |
| | | .filter(c -> c.getName() != null && c.getName().equals(itemTypeName)) |
| | | .findFirst() |
| | | .map(MesMdItemTypeRespDTO::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()); |
| | | // create 走 createItem,update 走 updateItem(MES 侧按 code upsert) |
| | | if (reqVO.getId() == null) { |
| | | mesMdItemApi.createItem(syncReqDTO); |
| | | } else { |
| | | mesMdItemApi.updateItem(syncReqDTO); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | @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; |
| | | } |
| | | CommonResult<MesMdItemTypeRespDTO> result = mesMdItemTypeApi.getItemType(categoryId); |
| | | if (result == null || !result.isSuccess() || result.getData() == null) { |
| | | throw exception(MDM_ITEM_CATEGORY_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; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public MdmItemDO getItem(Long id) { |
| | | return itemMapper.selectById(id); |