3 天以前 b852254d90e7d1955db31db154193ec8ee84d8b3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/machinery/MesDvMachineryServiceImpl.java
@@ -3,30 +3,51 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
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;
@@ -38,11 +59,20 @@
/**
 * MES 设备台账 Service 实现类
 *
 * @author 芋道源码
 * @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;
@@ -53,6 +83,9 @@
    @Resource
    @Lazy
    private MesMdWorkshopService workshopService;
    @Resource
    @Lazy
    private cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService workstationService;
    @Resource
    private MesWmBarcodeService barcodeService;
    @Resource
@@ -68,7 +101,30 @@
    @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());
@@ -77,13 +133,20 @@
        // 校验编码唯一
        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();
    }
@@ -91,6 +154,7 @@
    public void updateMachinery(MesDvMachinerySaveReqVO updateReqVO) {
        // 校验存在
        validateMachineryExists(updateReqVO.getId());
        MesDvMachineryDO existMachinery = machineryMapper.selectById(updateReqVO.getId());
        // 校验设备类型存在
        machineryTypeService.getMachineryType(updateReqVO.getMachineryTypeId());
        // 校验车间存在
@@ -100,6 +164,8 @@
        // 更新
        MesDvMachineryDO updateObj = BeanUtils.toBean(updateReqVO, MesDvMachineryDO.class);
        // 物料关联由创建时自动生成,更新不允许清空(前端也不传入 itemId)
        updateObj.setItemId(existMachinery.getItemId());
        machineryMapper.updateById(updateObj);
    }
@@ -107,6 +173,11 @@
    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);
@@ -201,7 +272,7 @@
            throw exception(DV_MACHINERY_IMPORT_LIST_IS_EMPTY);
        }
        // 2. 批量加载设备类型和车间,构建编码到实体的映射
        // 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()
@@ -210,6 +281,13 @@
                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()
@@ -249,24 +327,45 @@
                return;
            }
            // 3.4 判断:创建 or 更新
            // 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.4.1 创建
                // 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.4.2 更新
                // 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 {
@@ -277,4 +376,77 @@
        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));
    }
}