package cn.iocoder.yudao.module.mes.service.dv.machinery;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.StrUtil;
|
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.api.item.MdmItemApi;
|
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemCreateReqDTO;
|
import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
|
import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
|
import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum;
|
import cn.iocoder.yudao.module.mes.api.item.MesMdItemApi;
|
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dv.machinery.vo.MesDvMachineryImportExcelVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dv.machinery.vo.MesDvMachineryImportRespVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dv.machinery.vo.MesDvMachineryPageReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.dv.machinery.vo.MesDvMachinerySaveReqVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryTypeDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkshopDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.dv.machinery.MesDvMachineryMapper;
|
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
|
import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum;
|
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
|
import cn.iocoder.yudao.module.mes.service.dv.checkplan.MesDvCheckPlanMachineryService;
|
import cn.iocoder.yudao.module.mes.service.dv.checkrecord.MesDvCheckRecordService;
|
import cn.iocoder.yudao.module.mes.service.dv.maintenrecord.MesDvMaintenRecordService;
|
import cn.iocoder.yudao.module.mes.service.dv.repair.MesDvRepairService;
|
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
|
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
|
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkshopService;
|
import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService;
|
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
|
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
|
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
|
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.math.BigDecimal;
|
import java.time.LocalDateTime;
|
import java.util.*;
|
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.stream.Collectors;
|
|
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 MesDvMachineryServiceImpl implements MesDvMachineryService {
|
|
/**
|
* 设备联动物料的类型编码(mes_md_item_type / mdm_item_category 中“设备”行的 code)
|
*/
|
private static final String EQUIPMENT_ITEM_TYPE_CODE = "EQUIPMENT";
|
/**
|
* 设备联动物料的计量单位编码(mes_md_unit_measure / mdm_unit_measure 中“台”行的 code)
|
*/
|
private static final String EQUIPMENT_UNIT_CODE = "TAI";
|
|
@Resource
|
private MesDvMachineryMapper machineryMapper;
|
|
@Resource
|
@Lazy // 延迟加载,避免循环依赖
|
private MesDvMachineryTypeService machineryTypeService;
|
@Resource
|
@Lazy
|
private MesMdWorkshopService workshopService;
|
@Resource
|
@Lazy
|
private cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService workstationService;
|
@Resource
|
private MesWmBarcodeService barcodeService;
|
@Resource
|
@Lazy
|
private MesDvCheckPlanMachineryService checkPlanMachineryService;
|
@Resource
|
@Lazy
|
private MesDvCheckRecordService checkRecordService;
|
@Resource
|
@Lazy
|
private MesDvMaintenRecordService maintenRecordService;
|
@Resource
|
@Lazy
|
private MesDvRepairService repairService;
|
|
// ========== 设备物料联动依赖 ==========
|
@Resource
|
private MdmItemApi mdmItemApi;
|
@Resource
|
private MdmUnitMeasureApi mdmUnitMeasureApi;
|
@Resource
|
private MesMdItemApi mesMdItemApi;
|
@Resource
|
@Lazy
|
private MesMdItemTypeService mesMdItemTypeService;
|
@Resource
|
@Lazy
|
private MesMdUnitMeasureService mesMdUnitMeasureService;
|
@Resource
|
private MesWmTransactionService wmTransactionService;
|
@Resource
|
private MesWmWarehouseService wmWarehouseService;
|
@Resource
|
private MesWmWarehouseLocationService wmLocationService;
|
@Resource
|
private MesWmWarehouseAreaService wmAreaService;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Long createMachinery(MesDvMachinerySaveReqVO createReqVO) {
|
// 校验设备类型存在
|
machineryTypeService.getMachineryType(createReqVO.getMachineryTypeId());
|
// 校验车间存在
|
workshopService.getWorkshop(createReqVO.getWorkshopId());
|
// 校验编码唯一
|
validateMachineryCodeUnique(null, createReqVO.getCode());
|
|
// 创建对应 MDM 物料并同步 MES 物料(同一事务,失败整体回滚)
|
Long itemId = createEquipmentMaterial(createReqVO.getCode(), createReqVO.getName(),
|
createReqVO.getSpecification(), createReqVO.getRemark());
|
|
// 插入设备台账
|
MesDvMachineryDO machinery = BeanUtils.toBean(createReqVO, MesDvMachineryDO.class);
|
machinery.setItemId(itemId);
|
machineryMapper.insert(machinery);
|
|
// 自动生成条码
|
barcodeService.autoGenerateBarcode(BarcodeBizTypeEnum.MACHINERY.getValue(),
|
machinery.getId(), machinery.getCode(), machinery.getName());
|
// 自动入设备仓(库存 + 流水)
|
createEquipmentStock(itemId, machinery.getId(), machinery.getCode());
|
return machinery.getId();
|
}
|
|
@Override
|
public void updateMachinery(MesDvMachinerySaveReqVO updateReqVO) {
|
// 校验存在
|
validateMachineryExists(updateReqVO.getId());
|
MesDvMachineryDO existMachinery = machineryMapper.selectById(updateReqVO.getId());
|
// 校验设备类型存在
|
machineryTypeService.getMachineryType(updateReqVO.getMachineryTypeId());
|
// 校验车间存在
|
workshopService.getWorkshop(updateReqVO.getWorkshopId());
|
// 校验编码唯一
|
validateMachineryCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
|
|
// 更新
|
MesDvMachineryDO updateObj = BeanUtils.toBean(updateReqVO, MesDvMachineryDO.class);
|
// 物料关联由创建时自动生成,更新不允许清空(前端也不传入 itemId)
|
updateObj.setItemId(existMachinery.getItemId());
|
machineryMapper.updateById(updateObj);
|
}
|
|
@Override
|
public void deleteMachinery(Long id) {
|
// 校验存在
|
validateMachineryExists(id);
|
// 校验已生成物料并入设备仓:避免库存/流水失去业务主体,禁止删除
|
MesDvMachineryDO machinery = machineryMapper.selectById(id);
|
if (machinery.getItemId() != null) {
|
throw exception(DV_MACHINERY_HAS_ITEM);
|
}
|
// 校验关联数据
|
if (checkPlanMachineryService.getCheckPlanMachineryCountByMachineryId(id) > 0) {
|
throw exception(DV_MACHINERY_HAS_CHECK_PLAN);
|
}
|
if (checkRecordService.getCheckRecordCountByMachineryId(id) > 0) {
|
throw exception(DV_MACHINERY_HAS_CHECK_RECORD);
|
}
|
if (maintenRecordService.getMaintenRecordCountByMachineryId(id) > 0) {
|
throw exception(DV_MACHINERY_HAS_MAINTEN_RECORD);
|
}
|
if (repairService.getRepairCountByMachineryId(id) > 0) {
|
throw exception(DV_MACHINERY_HAS_REPAIR);
|
}
|
|
// 删除
|
machineryMapper.deleteById(id);
|
}
|
|
@Override
|
public void validateMachineryExists(Long id) {
|
if (machineryMapper.selectById(id) == null) {
|
throw exception(DV_MACHINERY_NOT_EXISTS);
|
}
|
}
|
|
private void validateMachineryCodeUnique(Long id, String code) {
|
if (code == null) {
|
return;
|
}
|
MesDvMachineryDO machinery = machineryMapper.selectByCode(code);
|
if (machinery == null) {
|
return;
|
}
|
if (ObjUtil.notEqual(machinery.getId(), id)) {
|
throw exception(DV_MACHINERY_CODE_DUPLICATE);
|
}
|
}
|
|
@Override
|
public MesDvMachineryDO getMachinery(Long id) {
|
return machineryMapper.selectById(id);
|
}
|
|
@Override
|
public PageResult<MesDvMachineryDO> getMachineryPage(MesDvMachineryPageReqVO pageReqVO) {
|
// 处理设备类型树形查询:选择父类型时,同时查询所有子类型下的设备
|
if (pageReqVO.getMachineryTypeId() != null) {
|
List<MesDvMachineryTypeDO> children = machineryTypeService.getMachineryTypeChildrenList(
|
pageReqVO.getMachineryTypeId());
|
Set<Long> typeIds = new HashSet<>();
|
typeIds.add(pageReqVO.getMachineryTypeId());
|
children.forEach(child -> typeIds.add(child.getId()));
|
pageReqVO.setMachineryTypeIds(typeIds);
|
}
|
return machineryMapper.selectPage(pageReqVO);
|
}
|
|
@Override
|
public Long getMachineryCountByMachineryTypeId(Long machineryTypeId) {
|
return machineryMapper.selectCountByMachineryTypeId(machineryTypeId);
|
}
|
|
@Override
|
public void updateMachineryLastCheckTime(Long machineryId, LocalDateTime lastCheckTime) {
|
machineryMapper.updateById(new MesDvMachineryDO().setId(machineryId).setLastCheckTime(lastCheckTime));
|
}
|
|
@Override
|
public void updateMachineryLastMaintenTime(Long machineryId, LocalDateTime lastMaintenTime) {
|
machineryMapper.updateById(new MesDvMachineryDO().setId(machineryId).setLastMaintenTime(lastMaintenTime));
|
}
|
|
@Override
|
public List<MesDvMachineryDO> getMachineryList() {
|
return machineryMapper.selectList();
|
}
|
|
@Override
|
public List<MesDvMachineryDO> getMachineryList(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return Collections.emptyList();
|
}
|
return machineryMapper.selectByIds(ids);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public MesDvMachineryImportRespVO importMachineryList(List<MesDvMachineryImportExcelVO> importMachineryList,
|
boolean updateSupport) {
|
// 1. 参数校验
|
if (CollUtil.isEmpty(importMachineryList)) {
|
throw exception(DV_MACHINERY_IMPORT_LIST_IS_EMPTY);
|
}
|
|
// 2. 批量加载设备类型、车间和工作站,构建编码到实体的映射
|
List<MesDvMachineryTypeDO> allTypes = machineryTypeService.getMachineryTypeList(
|
new cn.iocoder.yudao.module.mes.controller.admin.dv.machinery.vo.type.MesDvMachineryTypeListReqVO());
|
Map<String, MesDvMachineryTypeDO> typeCodeMap = allTypes.stream()
|
.collect(Collectors.toMap(MesDvMachineryTypeDO::getCode, t -> t, (a, b) -> a));
|
List<MesMdWorkshopDO> allWorkshops = workshopService.getWorkshopListByStatus(
|
cn.iocoder.yudao.framework.common.enums.CommonStatusEnum.ENABLE.getStatus());
|
Map<String, MesMdWorkshopDO> workshopCodeMap = allWorkshops.stream()
|
.collect(Collectors.toMap(MesMdWorkshopDO::getCode, w -> w, (a, b) -> a));
|
List<cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO> allWorkstations =
|
workstationService.getWorkstationListByStatus(
|
cn.iocoder.yudao.framework.common.enums.CommonStatusEnum.ENABLE.getStatus());
|
Map<String, cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO> workstationCodeMap =
|
allWorkstations.stream().collect(Collectors.toMap(
|
cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO::getCode,
|
w -> w, (a, b) -> a));
|
|
// 3. 遍历,逐个创建 or 更新
|
MesDvMachineryImportRespVO respVO = MesDvMachineryImportRespVO.builder()
|
.createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
|
.failureCodes(new LinkedHashMap<>()).build();
|
AtomicInteger index = new AtomicInteger(1);
|
importMachineryList.forEach(importItem -> {
|
int currentIndex = index.getAndIncrement();
|
// 3.1 校验必填字段
|
String key = StrUtil.blankToDefault(importItem.getCode(), "第 " + currentIndex + " 行");
|
if (StrUtil.isBlank(importItem.getCode())) {
|
respVO.getFailureCodes().put(key, "设备编码不能为空");
|
return;
|
}
|
if (StrUtil.isBlank(importItem.getName())) {
|
respVO.getFailureCodes().put(key, "设备名称不能为空");
|
return;
|
}
|
// 3.2 校验设备类型编码
|
if (StrUtil.isBlank(importItem.getMachineryTypeCode())) {
|
respVO.getFailureCodes().put(key, "设备类型编码不能为空");
|
return;
|
}
|
MesDvMachineryTypeDO machineryType = typeCodeMap.get(importItem.getMachineryTypeCode());
|
if (machineryType == null) {
|
respVO.getFailureCodes().put(key, "设备类型编码[" + importItem.getMachineryTypeCode() + "]不存在");
|
return;
|
}
|
// 3.3 校验车间编码
|
if (StrUtil.isBlank(importItem.getWorkshopCode())) {
|
respVO.getFailureCodes().put(key, "车间编码不能为空");
|
return;
|
}
|
MesMdWorkshopDO workshop = workshopCodeMap.get(importItem.getWorkshopCode());
|
if (workshop == null) {
|
respVO.getFailureCodes().put(key, "车间编码[" + importItem.getWorkshopCode() + "]不存在");
|
return;
|
}
|
|
// 3.4 校验工作站编码(可选)
|
Long workstationId = null;
|
if (StrUtil.isNotBlank(importItem.getWorkstationCode())) {
|
cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO workstation =
|
workstationCodeMap.get(importItem.getWorkstationCode());
|
if (workstation == null) {
|
respVO.getFailureCodes().put(key, "工作站编码[" + importItem.getWorkstationCode() + "]不存在");
|
return;
|
}
|
workstationId = workstation.getId();
|
}
|
|
// 3.5 判断:创建 or 更新
|
MesDvMachineryDO existMachinery = machineryMapper.selectByCode(importItem.getCode());
|
if (existMachinery == null) {
|
// 3.5.1 创建
|
MesDvMachineryDO machinery = BeanUtils.toBean(importItem, MesDvMachineryDO.class);
|
machinery.setMachineryTypeId(machineryType.getId());
|
machinery.setWorkshopId(workshop.getId());
|
machinery.setWorkstationId(workstationId);
|
// 创建对应 MDM 物料并同步 MES 物料(同一事务)
|
machinery.setItemId(createEquipmentMaterial(machinery.getCode(), machinery.getName(),
|
machinery.getSpecification(), machinery.getRemark()));
|
machineryMapper.insert(machinery);
|
// 自动生成条码
|
barcodeService.autoGenerateBarcode(BarcodeBizTypeEnum.MACHINERY.getValue(),
|
machinery.getId(), machinery.getCode(), machinery.getName());
|
// 自动入设备仓(库存 + 流水)
|
createEquipmentStock(machinery.getItemId(), machinery.getId(), machinery.getCode());
|
respVO.getCreateCodes().add(importItem.getCode());
|
} else if (updateSupport) {
|
// 3.5.2 更新
|
MesDvMachineryDO updateObj = BeanUtils.toBean(importItem, MesDvMachineryDO.class);
|
updateObj.setId(existMachinery.getId());
|
updateObj.setMachineryTypeId(machineryType.getId());
|
updateObj.setWorkshopId(workshop.getId());
|
updateObj.setWorkstationId(workstationId);
|
// 物料关联由创建时自动生成,更新不允许清空
|
updateObj.setItemId(existMachinery.getItemId());
|
machineryMapper.updateById(updateObj);
|
respVO.getUpdateCodes().add(importItem.getCode());
|
} else {
|
// 不支持更新
|
respVO.getFailureCodes().put(key, "设备编码已存在");
|
}
|
});
|
return respVO;
|
}
|
|
/**
|
* 创建设备对应物料:先在 MDM 建主数据物料,再显式同步 MES 物料(两套主数据 id 对齐)。
|
*
|
* @return MDM 物料编号(= MES 物料编号)
|
*/
|
private Long createEquipmentMaterial(String code, String name, String specification, String remark) {
|
// 1. 解析“设备”类型(MES 侧)与“台”单位(MDM 侧 + MES 侧),缺失则提示主数据未初始化
|
MesMdItemTypeDO itemType = mesMdItemTypeService.getItemTypeByCode(EQUIPMENT_ITEM_TYPE_CODE);
|
if (itemType == null) {
|
throw exception(DV_MACHINERY_EQUIP_MASTER_MISSING);
|
}
|
if (mesMdUnitMeasureService.getUnitMeasureByCode(EQUIPMENT_UNIT_CODE) == null) {
|
throw exception(DV_MACHINERY_EQUIP_MASTER_MISSING);
|
}
|
CommonResult<MdmUnitMeasureRespDTO> unitResult = mdmUnitMeasureApi.getUnitMeasureByCode(EQUIPMENT_UNIT_CODE);
|
MdmUnitMeasureRespDTO mdmUnit = unitResult.isSuccess() ? unitResult.getData() : null;
|
if (mdmUnit == null) {
|
throw exception(DV_MACHINERY_EQUIP_MASTER_MISSING);
|
}
|
|
// 2. 创建 MDM 物料;分类使用“设备”分类(其 id 与 MES 类型 id 对齐,由初始化脚本保证)
|
MdmItemCreateReqDTO createReqDTO = new MdmItemCreateReqDTO();
|
createReqDTO.setCode(code);
|
createReqDTO.setName(name);
|
createReqDTO.setSpecification(specification);
|
createReqDTO.setRemark(remark);
|
createReqDTO.setCategoryId(itemType.getId());
|
createReqDTO.setUnitMeasureId(mdmUnit.getId());
|
createReqDTO.setItemType(MdmItemTypeEnum.EQUIPMENT.getType());
|
createReqDTO.setStatus(0);
|
createReqDTO.setIsBatchManaged(false);
|
Long mdmItemId = mdmItemApi.createItem(createReqDTO).getCheckedData();
|
|
// 3. 显式同步 MES 物料(MdmItemCreateReqDTO 无 syncMes 开关,不会自动同步)
|
MesMdItemSyncReqDTO syncReqDTO = new MesMdItemSyncReqDTO();
|
syncReqDTO.setCode(code);
|
syncReqDTO.setName(name);
|
syncReqDTO.setSpecification(specification);
|
syncReqDTO.setRemark(remark);
|
syncReqDTO.setUnitCode(EQUIPMENT_UNIT_CODE);
|
syncReqDTO.setItemTypeCode(EQUIPMENT_ITEM_TYPE_CODE);
|
syncReqDTO.setBatchFlag(false);
|
syncReqDTO.setSafeStockFlag(false);
|
syncReqDTO.setStatus(0);
|
syncReqDTO.setMdmItemId(mdmItemId);
|
mesMdItemApi.createItem(syncReqDTO);
|
return mdmItemId;
|
}
|
|
/**
|
* 设备自动入设备仓:创建一条入库数量为 1 的库存事务,仓库/库区/库位取系统内置设备仓。
|
* 库存行、数量与流水统一经 {@link MesWmTransactionService},禁止直接写库存表。
|
*/
|
private void createEquipmentStock(Long itemId, Long machineryId, String machineryCode) {
|
MesWmWarehouseDO warehouse = wmWarehouseService.getWarehouseByCode(MesWmWarehouseDO.EQUIPMENT_WAREHOUSE);
|
MesWmWarehouseLocationDO location = wmLocationService.getWarehouseLocationByCode(MesWmWarehouseLocationDO.EQUIPMENT_LOCATION);
|
MesWmWarehouseAreaDO area = wmAreaService.getWarehouseAreaByCode(MesWmWarehouseAreaDO.EQUIPMENT_AREA);
|
if (warehouse == null || location == null || area == null) {
|
throw exception(DV_MACHINERY_EQUIP_MASTER_MISSING);
|
}
|
wmTransactionService.createTransaction(new MesWmTransactionSaveReqDTO()
|
.setType(MesWmTransactionTypeEnum.IN.getType())
|
.setItemId(itemId)
|
.setQuantity(BigDecimal.ONE)
|
.setWarehouseId(warehouse.getId())
|
.setLocationId(location.getId())
|
.setAreaId(area.getId())
|
.setBizType(MesBizTypeConstants.DV_MACHINERY)
|
.setBizId(machineryId)
|
.setBizCode(machineryCode)
|
.setBizLineId(machineryId));
|
}
|
|
}
|