package cn.iocoder.yudao.module.mes.service.pro.route;
|
|
import cn.hutool.core.util.ObjUtil;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductMapper;
|
import cn.iocoder.yudao.module.mes.enums.md.MesMdItemTypeEnum;
|
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
|
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
|
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
|
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
|
import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
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.List;
|
import java.util.HashSet;
|
import java.util.Set;
|
import java.util.Objects;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
|
|
/**
|
* MES 工艺路线产品 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
public class MesProRouteProductServiceImpl implements MesProRouteProductService {
|
|
@Resource
|
private MesProRouteProductMapper routeProductMapper;
|
|
@Resource
|
@Lazy
|
private MesProRouteService routeService;
|
@Resource
|
@Lazy
|
private MesProRouteProductBomService routeProductBomService;
|
@Resource
|
private MesMdItemService itemService;
|
@Resource
|
private MesMdItemTypeService itemTypeService;
|
@Resource
|
private MesWmWarehouseAreaService warehouseAreaService;
|
@Resource
|
private MdmItemApi mdmItemApi;
|
|
@Override
|
public Long createRouteProduct(MesProRouteProductSaveReqVO createReqVO) {
|
// 1.0 已启用的工艺路线,不允许操作
|
routeService.validateRouteNotEnable(createReqVO.getRouteId());
|
// 1.1 校验产品存在、启用且类型正确
|
validateProductItem(createReqVO.getItemId());
|
// 1.2 校验产品唯一性(一个产品只能关联一条工艺路线)
|
validateItemUnique(null, createReqVO.getItemId());
|
warehouseAreaService.validateWarehouseAreaExists(createReqVO.getWarehouseId(),
|
createReqVO.getLocationId(), createReqVO.getAreaId());
|
|
// 2. 插入
|
MesProRouteProductDO routeProduct = BeanUtils.toBean(createReqVO, MesProRouteProductDO.class);
|
routeProductMapper.insert(routeProduct);
|
return routeProduct.getId();
|
}
|
|
@Override
|
public void updateRouteProduct(MesProRouteProductSaveReqVO updateReqVO) {
|
// 1.0 已启用的工艺路线,不允许操作
|
routeService.validateRouteNotEnable(updateReqVO.getRouteId());
|
// 1.1 校验存在
|
MesProRouteProductDO existing = routeProductMapper.selectById(updateReqVO.getId());
|
validateRouteProductExists(existing);
|
// 历史路线产品可能采用旧分类;只修改仓位等配置时不重新拦截原有物料。
|
if (ObjUtil.notEqual(existing.getItemId(), updateReqVO.getItemId())) {
|
validateProductItem(updateReqVO.getItemId());
|
}
|
// 1.3 校验产品唯一性
|
validateItemUnique(updateReqVO.getId(), updateReqVO.getItemId());
|
warehouseAreaService.validateWarehouseAreaExists(updateReqVO.getWarehouseId(),
|
updateReqVO.getLocationId(), updateReqVO.getAreaId());
|
|
// 2. 更新
|
MesProRouteProductDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProductDO.class);
|
routeProductMapper.updateById(updateObj);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void deleteRouteProduct(Long id) {
|
// 1.1 校验存在
|
MesProRouteProductDO routeProduct = routeProductMapper.selectById(id);
|
validateRouteProductExists(routeProduct);
|
// 1.2 已启用的工艺路线,不允许操作
|
routeService.validateRouteNotEnable(routeProduct.getRouteId());
|
|
// 2.1 级联删除关联的 BOM
|
routeProductBomService.deleteRouteProductBomByRouteIdAndProductId(routeProduct.getRouteId(), routeProduct.getItemId());
|
// 2.2 删除产品关联
|
routeProductMapper.deleteById(id);
|
}
|
|
private void validateRouteProductExists(Long id) {
|
if (routeProductMapper.selectById(id) == null) {
|
throw exception(PRO_ROUTE_PRODUCT_NOT_EXISTS);
|
}
|
}
|
|
private void validateRouteProductExists(MesProRouteProductDO routeProduct) {
|
if (routeProduct == null) {
|
throw exception(PRO_ROUTE_PRODUCT_NOT_EXISTS);
|
}
|
}
|
|
private void validateProductItem(Long itemId) {
|
MesMdItemDO item = itemService.validateItemExistsAndEnable(itemId);
|
Long typeId = item.getItemTypeId();
|
Set<Long> visited = new HashSet<>();
|
while (typeId != null && !MesMdItemTypeDO.PARENT_ID_ROOT.equals(typeId) && visited.add(typeId)) {
|
MesMdItemTypeDO itemType = itemTypeService.getItemType(typeId);
|
if (itemType == null) {
|
break;
|
}
|
// 兼容旧分类:可能未设置 PRODUCT 标识,也未采用 FG/SF 编码。
|
if (MesMdItemTypeEnum.isProduct(itemType.getItemOrProduct())
|
|| "FG".equalsIgnoreCase(itemType.getCode())
|
|| "SF".equalsIgnoreCase(itemType.getCode())
|
|| isProductCategoryName(itemType.getName())) {
|
return;
|
}
|
typeId = itemType.getParentId();
|
}
|
// MES 兼容分类可能仍标记为 ITEM;以 MDM 物料主数据的成品/半成品类型兜底。
|
CommonResult<MdmItemRespDTO> mdmResult = mdmItemApi.getItemByCode(item.getCode());
|
if (mdmResult != null && mdmResult.isSuccess() && mdmResult.getData() != null) {
|
MdmItemRespDTO mdmItem = mdmResult.getData();
|
Integer mdmType = mdmItem.getItemType();
|
// 历史同步曾将分类 ID 写入 itemType;与 categoryId 相同的值不能当作类型枚举。
|
if (!Objects.equals(mdmItem.getCategoryId(), mdmType == null ? null : mdmType.longValue())
|
&& (MdmItemTypeEnum.PRODUCT.getType().equals(mdmType)
|
|| MdmItemTypeEnum.SEMI_FINISHED.getType().equals(mdmType))) {
|
return;
|
}
|
}
|
throw exception(PRO_ROUTE_PRODUCT_ITEM_TYPE_INVALID, item.getName());
|
}
|
|
private boolean isProductCategoryName(String name) {
|
return name != null && (name.contains("半成品") || name.contains("产成品")
|
|| (name.contains("成品") && !name.contains("非成品")));
|
}
|
|
private void validateItemUnique(Long id, Long itemId) {
|
MesProRouteProductDO existing = routeProductMapper.selectByItemId(itemId);
|
if (existing == null) {
|
return;
|
}
|
if (ObjUtil.notEqual(existing.getId(), id)) {
|
throw exception(PRO_ROUTE_PRODUCT_ITEM_DUPLICATE);
|
}
|
}
|
|
@Override
|
public MesProRouteProductDO getRouteProduct(Long id) {
|
return routeProductMapper.selectById(id);
|
}
|
|
@Override
|
public MesProRouteProductDO getRouteProductByItemId(Long itemId) {
|
return routeProductMapper.selectByItemId(itemId);
|
}
|
|
@Override
|
public List<MesProRouteProductDO> getRouteProductListByRouteId(Long routeId) {
|
return routeProductMapper.selectListByRouteId(routeId);
|
}
|
|
@Override
|
public void deleteRouteProductByRouteId(Long routeId) {
|
routeProductMapper.deleteByRouteId(routeId);
|
}
|
|
}
|