package cn.iocoder.yudao.module.mdm.service.item;
|
|
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.framework.mybatis.core.mapper.BaseMapperX;
|
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.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 com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import jakarta.annotation.Resource;
|
import org.springframework.beans.factory.annotation.Autowired;
|
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;
|
import static cn.iocoder.yudao.module.mdm.enums.ErrorCodeConstants.*;
|
|
/**
|
* MDM 物料主数据 Service 实现类
|
*
|
* @author 芋道源码
|
*/
|
@Service
|
@Validated
|
public class MdmItemServiceImpl implements MdmItemService {
|
|
@Resource
|
private MdmItemMapper itemMapper;
|
|
@Resource
|
private MdmItemCategoryService itemCategoryService;
|
@Resource
|
private MdmUnitMeasureService unitMeasureService;
|
@Resource
|
private MdmBrandService brandService;
|
|
@Resource
|
@Lazy
|
private MesMdItemApi mesMdItemApi;
|
@Resource
|
private MdmItemBatchConfigMapper itemBatchConfigMapper;
|
|
@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);
|
|
// 保存批次配置(如果有)
|
saveBatchConfig(item.getId(), createReqVO);
|
|
// 同步到 MES(勾选 syncMes 时)
|
createReqVO.setId(item.getId());
|
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());
|
}
|
|
/**
|
* 同步物料到 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) {
|
CommonResult<MesMdItemTypeRespDTO> itemType = mesMdItemTypeApi.getItemType(reqVO.getCategoryId());
|
itemTypeCode = itemType.getData().getCode();
|
}
|
// 组装同步 DTO
|
MesMdItemSyncReqDTO syncReqDTO = new MesMdItemSyncReqDTO();
|
syncReqDTO.setCode(reqVO.getCode());
|
syncReqDTO.setMdmItemId(reqVO.getId());
|
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
|
public void updateItemStatus(Long id, Integer status) {
|
// 校验存在
|
validateItemExists(id);
|
// 更新状态
|
itemMapper.updateById(new MdmItemDO().setId(id).setStatus(status));
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void deleteItem(Long 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);
|
}
|
|
@Override
|
public MdmItemDO validateItemExists(Long id) {
|
MdmItemDO item = itemMapper.selectById(id);
|
if (item == null) {
|
throw exception(MDM_ITEM_NOT_EXISTS);
|
}
|
return item;
|
}
|
|
@Override
|
public MdmItemDO validateItemExistsAndEnable(Long id) {
|
MdmItemDO item = validateItemExists(id);
|
if (ObjUtil.notEqual(cn.iocoder.yudao.framework.common.enums.CommonStatusEnum.ENABLE.getStatus(), item.getStatus())) {
|
throw exception(MDM_ITEM_IS_DISABLE);
|
}
|
return item;
|
}
|
|
private void validateItemCodeUnique(Long id, String code) {
|
MdmItemDO item = itemMapper.selectByCode(code);
|
if (item == null) {
|
return;
|
}
|
if (ObjUtil.notEqual(item.getId(), id)) {
|
throw exception(MDM_ITEM_CODE_DUPLICATE);
|
}
|
}
|
|
private void validateCategoryExists(Long categoryId) {
|
if (categoryId == null) {
|
return;
|
}
|
// 使用 MDM 本地分类服务校验
|
MdmItemCategoryDO category = itemCategoryService.getItemCategory(categoryId);
|
if (category == null) {
|
throw exception(MDM_ITEM_CATEGORY_NOT_EXISTS);
|
}
|
}
|
|
private void validateUnitMeasureExists(Long unitMeasureId) {
|
if (unitMeasureService.getUnitMeasure(unitMeasureId) == null) {
|
throw exception(MDM_UNIT_MEASURE_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;
|
}
|
}
|
|
@Override
|
public MdmItemDO getItem(Long id) {
|
return itemMapper.selectById(id);
|
}
|
|
@Override
|
public PageResult<MdmItemDO> getItemPage(MdmItemPageReqVO pageReqVO) {
|
// 查找时,如果查找某个分类编号,则包含它的子分类
|
Set<Long> categoryIds = null;
|
if (pageReqVO.getCategoryId() != null) {
|
categoryIds = new HashSet<>();
|
categoryIds.add(pageReqVO.getCategoryId());
|
List<cn.iocoder.yudao.module.mdm.dal.dataobject.category.MdmItemCategoryDO> children =
|
itemCategoryService.getItemCategoryChildrenList(pageReqVO.getCategoryId());
|
categoryIds.addAll(convertSet(children, cn.iocoder.yudao.module.mdm.dal.dataobject.category.MdmItemCategoryDO::getId));
|
}
|
// 分页查询
|
return itemMapper.selectPage(pageReqVO, categoryIds);
|
}
|
|
@Override
|
public List<MdmItemDO> getItemList(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return Collections.emptyList();
|
}
|
return itemMapper.selectBatchIds(ids);
|
}
|
|
@Override
|
public Long getItemCountByCategoryId(Long categoryId) {
|
return itemMapper.selectCountByCategoryId(categoryId);
|
}
|
|
@Override
|
public Long getItemCountByUnitMeasureId(Long unitMeasureId) {
|
return itemMapper.selectCountByUnitMeasureId(unitMeasureId);
|
}
|
|
@Override
|
public Long getItemCountByBrandId(Long brandId) {
|
return itemMapper.selectCountByBrandId(brandId);
|
}
|
|
@Override
|
public MdmItemDO getItemByCode(String code) {
|
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);
|
}
|
}
|
|
}
|