3 天以前 b852254d90e7d1955db31db154193ec8ee84d8b3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/sn/MesWmSnServiceImpl.java
@@ -2,33 +2,53 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnDetailRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnGenerateReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnGroupRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnInStockPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnInStockReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnOutStockReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.sn.vo.MesWmSnPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.sn.MesWmSnDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction.MesWmTransactionDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.sn.MesWmSnMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.transaction.MesWmTransactionMapper;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmSnStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
/**
 * MES SN 码 Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -37,6 +57,8 @@
    @Resource
    private MesWmSnMapper snMapper;
    @Resource
    private MesWmTransactionMapper transactionMapper;
    @Resource
    private MesMdAutoCodeRecordService autoCodeRecordService;
@@ -46,6 +68,14 @@
    private MesProWorkOrderService workOrderService;
    @Resource
    private MesWmBarcodeService barcodeService;
    @Resource
    private MesWmTransactionService transactionService;
    @Resource
    private MesWmWarehouseService warehouseService;
    @Resource
    private MesWmWarehouseLocationService locationService;
    @Resource
    private MesWmWarehouseAreaService areaService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -99,4 +129,100 @@
        snMapper.deleteByUuid(uuid);
    }
}
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void registerSnInStock(MesWmSnInStockReqVO reqVO) {
        // 1. 校验 SN 存在且物料匹配
        MesWmSnDO sn = validateSnExists(reqVO.getCode());
        validateSnItemMatch(sn, reqVO.getItemId());
        // 2. 校验物料启用序列号管理
        MesMdmItemDO item = mdmItemService.validateItemExists(reqVO.getItemId());
        if (!Boolean.TRUE.equals(item.getIsSerialManaged())) {
            throw exception(WM_SN_ITEM_NOT_SERIAL);
        }
        // 3. 状态校验:已在库则拒绝重复登记
        if (ObjUtil.equal(sn.getStatus(), MesWmSnStatusEnum.IN_STOCK.getStatus())) {
            throw exception(WM_SN_ALREADY_IN_STOCK);
        }
        // 4. 校验库存位置存在(冻结与混放规则由事务内部兜底)
        warehouseService.validateWarehouseExists(reqVO.getWarehouseId());
        locationService.validateWarehouseLocationExists(reqVO.getLocationId());
        areaService.validateWarehouseAreaExists(reqVO.getAreaId());
        // 5. 创建单件入库事务(+1)
        transactionService.createTransaction(new MesWmTransactionSaveReqDTO()
                .setType(MesWmTransactionTypeEnum.IN.getType())
                .setBizType(MesBizTypeConstants.WM_SN)
                .setBizId(sn.getId()).setBizCode(sn.getCode()).setBizLineId(sn.getId())
                .setItemId(sn.getItemId()).setQuantity(BigDecimal.ONE)
                .setBatchId(sn.getBatchId()).setBatchCode(sn.getBatchCode())
                .setSnCode(sn.getCode())
                .setWarehouseId(reqVO.getWarehouseId()).setLocationId(reqVO.getLocationId()).setAreaId(reqVO.getAreaId()));
        // 6. 更新 SN:在库 + 记录库位
        snMapper.updateById(new MesWmSnDO().setId(sn.getId())
                .setStatus(MesWmSnStatusEnum.IN_STOCK.getStatus())
                .setWarehouseId(reqVO.getWarehouseId()).setLocationId(reqVO.getLocationId()).setAreaId(reqVO.getAreaId())
                .setRemark(reqVO.getRemark()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void outboundSn(MesWmSnOutStockReqVO reqVO) {
        // 1. 校验 SN 存在且物料匹配
        MesWmSnDO sn = validateSnExists(reqVO.getCode());
        validateSnItemMatch(sn, reqVO.getItemId());
        // 2. 状态校验:必须在库且记录过库位
        if (!ObjUtil.equal(sn.getStatus(), MesWmSnStatusEnum.IN_STOCK.getStatus())
                || sn.getWarehouseId() == null || sn.getLocationId() == null || sn.getAreaId() == null) {
            throw exception(WM_SN_NOT_IN_STOCK);
        }
        // 3. 创建单件出库事务(-1),按 SN 在库位置精确定位库存记录
        transactionService.createTransaction(new MesWmTransactionSaveReqDTO()
                .setType(MesWmTransactionTypeEnum.OUT.getType())
                .setBizType(MesBizTypeConstants.WM_SN)
                .setBizId(sn.getId()).setBizCode(sn.getCode()).setBizLineId(sn.getId())
                .setItemId(sn.getItemId()).setQuantity(BigDecimal.ONE.negate())
                .setBatchId(sn.getBatchId()).setBatchCode(sn.getBatchCode())
                .setSnCode(sn.getCode())
                .setWarehouseId(sn.getWarehouseId()).setLocationId(sn.getLocationId()).setAreaId(sn.getAreaId()));
        // 4. 更新 SN:已出库 + 清空在库位置
        snMapper.markOutStockAndClearLocation(sn.getId());
        snMapper.updateById(new MesWmSnDO().setId(sn.getId()).setRemark(reqVO.getRemark()));
    }
    @Override
    public MesWmSnDetailRespVO getSnDetail(String snCode) {
        MesWmSnDO sn = validateSnExists(snCode);
        MesWmSnDetailRespVO resp = BeanUtils.toBean(sn, MesWmSnDetailRespVO.class);
        MesWmTransactionDO transaction = transactionMapper.selectLatestBySnCode(snCode);
        if (transaction != null) {
            MesWmTransactionTypeEnum typeEnum = MesWmTransactionTypeEnum.valueOf(transaction.getType());
            resp.setLastTransactionType(transaction.getType());
            resp.setLastTransactionTypeName(typeEnum != null ? typeEnum.getName() : null);
            resp.setLastTransactionQuantity(transaction.getQuantity());
            resp.setLastTransactionBizType(transaction.getBizType());
            resp.setLastTransactionBizCode(transaction.getBizCode());
            resp.setLastTransactionTime(transaction.getTransactionTime());
        }
        return resp;
    }
    @Override
    public PageResult<MesWmSnDO> getSnInStockPage(MesWmSnInStockPageReqVO reqVO) {
        return snMapper.selectStatusPage(reqVO);
    }
    private MesWmSnDO validateSnExists(String code) {
        MesWmSnDO sn = snMapper.selectByCode(code);
        if (sn == null) {
            throw exception(WM_SN_NOT_EXISTS);
        }
        return sn;
    }
    private void validateSnItemMatch(MesWmSnDO sn, Long itemId) {
        if (ObjUtil.notEqual(sn.getItemId(), itemId)) {
            throw exception(WM_SN_ITEM_MISMATCH);
        }
    }
}