9 小时以前 7bf754a7835d06f26240c4ca15bc5f83650de377
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -20,9 +20,11 @@
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.procurementrecord.bean.vo.ShippingProductVo;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.sales.dto.SalesLedgerArrivalDto;
import com.ruoyi.sales.dto.ShippingApproveDto;
import com.ruoyi.sales.dto.ShippingInfoDto;
import com.ruoyi.sales.dto.ShippingProductDetailDto;
import com.ruoyi.sales.mapper.SalesLedgerArrivalMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.mapper.ShippingProductDetailMapper;
@@ -36,9 +38,12 @@
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * @author :yys
@@ -56,6 +61,7 @@
    private final ShippingInfoMapper shippingInfoMapper;
    private final SalesLedgerArrivalMapper salesLedgerArrivalMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
@@ -73,13 +79,44 @@
    public IPage<ShippingInfoDto> listPage(Page page, ShippingInfo req) {
        IPage<ShippingInfoDto> listPage = shippingInfoMapper.listPage(page, req);
        listPage.getRecords().forEach(item -> {
            item.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.IMAGE, RecordTypeEnum.SHIPPING_INFO, item.getId()));
            // 油品出库行没有 shipping_product_detail,批号明细只能由出库记录自身字段构造
            if (SOURCE_OIL_OUT.equals(item.getSource())) {
                // 油品出库行的 id 是 stock_out_record.id,与 shipping_info.id 同域会撞号,
                // 用它去查附件只会捞出别的发货单的图,索性不查;附件挂在到货记录子行上
                // 油品出库行没有 shipping_product_detail,批号明细只能由出库记录自身字段构造
                item.setBatchNoList(buildOilOutBatchNoList(item));
                return;
            }
            item.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.IMAGE, RecordTypeEnum.SHIPPING_INFO, item.getId()));
        });
        assembleArrivals(listPage.getRecords());
        return listPage;
    }
    /**
     * 发货台账层级化:父行 = 一次发货(发货单 / 油品出库单),子行 = 该出库单下的到货记录。
     * <p>
     * 刻意不把到货记录 UNION 进分页 SQL:现有 UNION 的每一行天然就是一个父行,父行维度的分页与
     * total 已经正确;混入子行会撑大条数,且 {@code getDateilByShippingNo} 复用同一 listPage 取
     * 第一行时会静默取错。改成从已分页的父行里收集 id,一次批量查子行,避免 N+1。
     */
    private void assembleArrivals(List<ShippingInfoDto> records) {
        List<Long> stockOutRecordIds = records.stream()
                .filter(item -> SOURCE_OIL_OUT.equals(item.getSource()) && item.getStockOutRecordId() != null)
                .map(ShippingInfoDto::getStockOutRecordId)
                .toList();
        if (stockOutRecordIds.isEmpty()) {
            return;
        }
        Map<Long, List<SalesLedgerArrivalDto>> arrivalMap = salesLedgerArrivalMapper
                .listByStockOutRecordIds(stockOutRecordIds).stream()
                .collect(Collectors.groupingBy(SalesLedgerArrivalDto::getStockOutRecordId));
        records.forEach(item -> {
            if (!SOURCE_OIL_OUT.equals(item.getSource()) || item.getStockOutRecordId() == null) {
                return;
            }
            List<SalesLedgerArrivalDto> arrivals = arrivalMap.get(item.getStockOutRecordId());
            item.setChildren(arrivals == null ? new ArrayList<>() : new ArrayList<>(arrivals));
        });
    }
    /**
@@ -255,7 +292,9 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelByOutbound(Long shippingInfoId) {
        if (shippingInfoId == null) {
        // <=0 必须挡住:油品出库的 record_id 是 0 而 record_type 同样是「13」,
        // 传 0 进来会把同类型的油品出库记录当成本发货单的配对出库整批删掉
        if (shippingInfoId == null || shippingInfoId <= 0) {
            return;
        }
        // 回滚该发货单占用的库存