云
2026-10-25 d54fbc9d4f8981fa0f7f613b33a28a8cd4f764ea
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesServiceImpl.java
@@ -1,12 +1,17 @@
package cn.iocoder.yudao.module.mes.service.wm.returnsales;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.MesWmReturnSalesImportExcelVO;
import cn.iocoder.yudao.module.mes.api.event.MesReturnSalesProgressEvent;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.MesWmReturnSalesImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.MesWmReturnSalesPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.MesWmReturnSalesSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
@@ -24,13 +29,17 @@
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -39,7 +48,7 @@
/**
 * MES 销售退货单 Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -58,6 +67,8 @@
    private MesMdmItemService mdmItemService;
    @Resource
    private MesWmTransactionService wmTransactionService;
    @Resource
    private ApplicationEventPublisher eventPublisher;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -67,6 +78,7 @@
        // 2. 插入主表
        MesWmReturnSalesDO returnSales = BeanUtils.toBean(createReqVO, MesWmReturnSalesDO.class);
        returnSales.setAfterSaleReturnId(createReqVO.getAfterSaleReturnId());
        returnSales.setStatus(MesWmReturnSalesStatusEnum.PREPARE.getStatus());
        returnSalesMapper.insert(returnSales);
        return returnSales.getId();
@@ -76,12 +88,16 @@
    @Transactional(rollbackFor = Exception.class)
    public void updateReturnSales(MesWmReturnSalesSaveReqVO updateReqVO) {
        // 1.1 校验存在 + 准备中状态
        validateReturnSalesExistsAndPrepare(updateReqVO.getId());
        MesWmReturnSalesDO existing = validateReturnSalesExistsAndPrepare(updateReqVO.getId());
        if (existing.getAfterSaleReturnId() != null) {
            throw exception(WM_RETURN_SALES_AFTERSALE_LINKED);
        }
        // 1.2 校验关联数据
        validateSaveData(updateReqVO);
        // 2. 更新主表
        MesWmReturnSalesDO updateObj = BeanUtils.toBean(updateReqVO, MesWmReturnSalesDO.class);
        updateObj.setAfterSaleReturnId(null); // 来源申请不可通过编辑接口改绑
        returnSalesMapper.updateById(updateObj);
    }
@@ -89,7 +105,10 @@
    @Transactional(rollbackFor = Exception.class)
    public void deleteReturnSales(Long id) {
        // 1. 校验存在 + 准备中状态
        validateReturnSalesExistsAndPrepare(id);
        MesWmReturnSalesDO returnSales = validateReturnSalesExistsAndPrepare(id);
        if (returnSales.getAfterSaleReturnId() != null) {
            throw exception(WM_RETURN_SALES_AFTERSALE_LINKED);
        }
        // 2.1 级联删除明细
        returnSalesDetailService.deleteReturnSalesDetailByReturnId(id);
@@ -127,8 +146,13 @@
        Integer targetStatus = hasPendingQc ? MesWmReturnSalesStatusEnum.CONFIRMED.getStatus()
                : MesWmReturnSalesStatusEnum.APPROVING.getStatus();
        // 2.2 提交(草稿 -> 待检验/待执行)
        returnSalesMapper.updateById(new MesWmReturnSalesDO()
                .setId(id).setStatus(targetStatus));
        if (updateStatusIfCurrent(id, MesWmReturnSalesStatusEnum.PREPARE.getStatus(), targetStatus) != 1) {
            throw exception(WM_RETURN_SALES_STATUS_NOT_PREPARE);
        }
        if (!hasPendingQc && validateReturnSalesExists(id).getAfterSaleReturnId() != null) {
            eventPublisher.publishEvent(new MesReturnSalesProgressEvent(id,
                    MesReturnSalesProgressEvent.Stage.QUALITY_FINISHED, "免检"));
        }
    }
    @Override
@@ -145,8 +169,10 @@
            throw exception(WM_RETURN_SALES_NO_LINE);
        }
        // 2. 执行退货(待执行 -> 待上架)
        returnSalesMapper.updateById(new MesWmReturnSalesDO()
                .setId(id).setStatus(MesWmReturnSalesStatusEnum.APPROVED.getStatus()));
        if (updateStatusIfCurrent(id, MesWmReturnSalesStatusEnum.APPROVING.getStatus(),
                MesWmReturnSalesStatusEnum.APPROVED.getStatus()) != 1) {
            throw exception(WM_RETURN_SALES_STATUS_NOT_APPROVING);
        }
    }
    @Override
@@ -178,12 +204,16 @@
            }
        }
        // 2. 遍历所有明细,创建库存事务(增加库存 + 记录流水)
        // 抢占状态后再记库存流水,避免两个并发上架请求重复入库;事务失败会回滚状态。
        if (updateStatusIfCurrent(id, MesWmReturnSalesStatusEnum.APPROVED.getStatus(),
                MesWmReturnSalesStatusEnum.FINISHED.getStatus()) != 1) {
            throw exception(WM_RETURN_SALES_STATUS_NOT_APPROVED);
        }
        createTransactionList(returnSales);
        // 3. 更新退货单状态
        returnSalesMapper.updateById(new MesWmReturnSalesDO()
                .setId(id).setStatus(MesWmReturnSalesStatusEnum.FINISHED.getStatus()));
        if (returnSales.getAfterSaleReturnId() != null) {
            eventPublisher.publishEvent(new MesReturnSalesProgressEvent(id,
                    MesReturnSalesProgressEvent.Stage.INBOUND_FINISHED, null));
        }
    }
    private void createTransactionList(MesWmReturnSalesDO returnSales) {
@@ -210,8 +240,13 @@
        }
        // 取消
        returnSalesMapper.updateById(new MesWmReturnSalesDO()
                .setId(id).setStatus(MesWmReturnSalesStatusEnum.CANCELED.getStatus()));
        if (updateStatusIfCurrent(id, returnSales.getStatus(), MesWmReturnSalesStatusEnum.CANCELED.getStatus()) != 1) {
            throw exception(WM_RETURN_SALES_CANCEL_NOT_ALLOWED);
        }
        if (returnSales.getAfterSaleReturnId() != null) {
            eventPublisher.publishEvent(new MesReturnSalesProgressEvent(id,
                    MesReturnSalesProgressEvent.Stage.CANCELED, null));
        }
    }
    @Override
@@ -251,8 +286,16 @@
    @Override
    public void updateReturnSalesStatus(Long id, Integer status) {
        validateReturnSalesExists(id);
        returnSalesMapper.updateById(new MesWmReturnSalesDO().setId(id).setStatus(status));
        if (updateStatusIfCurrent(id, MesWmReturnSalesStatusEnum.CONFIRMED.getStatus(), status) != 1) {
            throw exception(WM_RETURN_SALES_RQC_SOURCE_INVALID);
        }
    }
    private int updateStatusIfCurrent(Long id, Integer currentStatus, Integer targetStatus) {
        return returnSalesMapper.update(null, new LambdaUpdateWrapper<MesWmReturnSalesDO>()
                .eq(MesWmReturnSalesDO::getId, id)
                .eq(MesWmReturnSalesDO::getStatus, currentStatus)
                .set(MesWmReturnSalesDO::getStatus, targetStatus));
    }
    /**
@@ -276,4 +319,50 @@
        customerApi.validateCustomer(reqVO.getClientId());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MesWmReturnSalesImportRespVO importReturnSalesList(List<MesWmReturnSalesImportExcelVO> importList, boolean updateSupport) {
        if (CollUtil.isEmpty(importList)) {
            throw exception(WM_RETURN_SALES_IMPORT_LIST_IS_EMPTY);
        }
        MesWmReturnSalesImportRespVO respVO = MesWmReturnSalesImportRespVO.builder()
                .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
                .failureCodes(new LinkedHashMap<>()).build();
        AtomicInteger index = new AtomicInteger(1);
        importList.forEach(importItem -> {
            int currentIndex = index.getAndIncrement();
            String code = importItem.getCode();
            String key = StrUtil.blankToDefault(code, "第 " + currentIndex + " 行");
            if (StrUtil.isBlank(code)) {
                respVO.getFailureCodes().put(key, "单号不能为空");
                return;
            }
            if (StrUtil.isBlank(importItem.getName())) {
                respVO.getFailureCodes().put(key, "名称不能为空");
                return;
            }
            MesWmReturnSalesDO existing = returnSalesMapper.selectByCode(code);
            if (existing != null && updateSupport) {
                if (!MesWmReturnSalesStatusEnum.PREPARE.getStatus().equals(existing.getStatus())) {
                    respVO.getFailureCodes().put(key, "单据非草稿状态,不允许覆盖更新");
                    return;
                }
                MesWmReturnSalesDO updateObj = BeanUtils.toBean(importItem, MesWmReturnSalesDO.class);
                updateObj.setId(existing.getId());
                updateObj.setStatus(existing.getStatus());
                returnSalesMapper.updateById(updateObj);
                respVO.getUpdateCodes().add(code);
            } else if (existing != null) {
                respVO.getFailureCodes().put(key, "单号已存在:" + code);
            } else {
                MesWmReturnSalesDO createObj = BeanUtils.toBean(importItem, MesWmReturnSalesDO.class);
                createObj.setId(null);
                createObj.setStatus(MesWmReturnSalesStatusEnum.PREPARE.getStatus());
                returnSalesMapper.insert(createObj);
                respVO.getCreateCodes().add(code);
            }
        });
        return respVO;
    }
}