自动化立体仓库 - WMS系统
chen.llin
2025-12-24 f7ac54f86129a2d4875c7107a84556849c3d8c2a
月结-完成字段调整,和部分sql优化
8个文件已修改
328 ■■■■ 已修改文件
src/main/java/com/zy/asrs/mapper/ManLocDetlMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/MonthlySettleMapper.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/ManLocDetlService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/MonthlySettleService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/ManLocDetlServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/MonthlySettleServiceImpl.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/ManLocDetlMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/MonthlySettleMapper.xml 121 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/ManLocDetlMapper.java
@@ -57,6 +57,12 @@
    Double queryStockAnfme(String matnr, String batch);
    /**
     * 批量查询库存数量
     * @param matnrBatchList 物料编码和批次的组合列表,格式:matnr_batch
     */
    List<com.zy.asrs.entity.result.StockQtyDTO> queryStockAnfmeBatch(@Param("matnrBatchList") List<String> matnrBatchList);
    List<StockVo> queryStockTotal();
src/main/java/com/zy/asrs/mapper/MonthlySettleMapper.java
@@ -20,14 +20,14 @@
    MonthlySettle selectLatestSettle();
    /**
     * 统计月结时间范围内的订单数量
     * 统计月结时间范围内未完成的入库订单数量
     */
    int countOrdersInRange(@Param("startDate") String startDate, @Param("endDate") String endDate);
    int countUnfinishedOrdersInRangePakin(@Param("startDate") String startDate, @Param("endDate") String endDate);
    /**
     * 统计月结时间范围内未完成的订单数量(入库和出库)
     * 统计月结时间范围内未完成的出库订单数量
     */
    int countUnfinishedOrdersInRange(@Param("startDate") String startDate, @Param("endDate") String endDate);
    int countUnfinishedOrdersInRangePakout(@Param("startDate") String startDate, @Param("endDate") String endDate);
    /**
     * 统计月结时间范围内的物料出入库数量(从入库表查询)
src/main/java/com/zy/asrs/service/ManLocDetlService.java
@@ -47,6 +47,12 @@
    Double queryStockAnfme(String matnr, String batch);
    /**
     * 批量查询库存数量
     * @param matnrBatchList 物料编码和批次的组合列表,格式:matnr_batch
     */
    List<com.zy.asrs.entity.result.StockQtyDTO> queryStockAnfmeBatch(List<String> matnrBatchList);
    List<StockVo> queryStockTotal();
src/main/java/com/zy/asrs/service/MonthlySettleService.java
@@ -49,6 +49,13 @@
     * 删除月结记录
     */
    void deleteSettle(Long settleId);
    /**
     * 检查指定日期是否在任何已月结的区间内
     * @param orderTime 订单业务时间(字符串格式:yyyy-MM-dd HH:mm:ss)
     * @return 如果日期在任何已月结区间内,返回true;否则返回false
     */
    boolean isOrderTimeInSettledRange(String orderTime);
}
src/main/java/com/zy/asrs/service/impl/ManLocDetlServiceImpl.java
@@ -105,6 +105,14 @@
    }
    @Override
    public List<com.zy.asrs.entity.result.StockQtyDTO> queryStockAnfmeBatch(List<String> matnrBatchList) {
        if (matnrBatchList == null || matnrBatchList.isEmpty()) {
            return new java.util.ArrayList<>();
        }
        return this.baseMapper.queryStockAnfmeBatch(matnrBatchList);
    }
    @Override
    public List<StockVo> queryStockTotal() {
        return this.baseMapper.queryStockTotal();
    }
src/main/java/com/zy/asrs/service/impl/MonthlySettleServiceImpl.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.core.common.Cools;
import com.core.common.SnowflakeIdWorker;
import com.core.exception.CoolException;
import com.zy.asrs.entity.MonthlySettle;
@@ -91,12 +92,21 @@
        cal.set(Calendar.MILLISECOND, 999);
        endDate = cal.getTime();
        
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        // 注意:order_time 是 varchar 类型,需要格式化为完整的日期时间字符串进行比较
        SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startDateStr = dateFormat.format(startDate);
        // 起始日期设置为当天的 00:00:00
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);
        startCal.set(Calendar.HOUR_OF_DAY, 0);
        startCal.set(Calendar.MINUTE, 0);
        startCal.set(Calendar.SECOND, 0);
        startCal.set(Calendar.MILLISECOND, 0);
        String startDateStr = dateTimeFormat.format(startCal.getTime());
        String endDateStr = dateTimeFormat.format(endDate);
        int count = this.baseMapper.countUnfinishedOrdersInRange(startDateStr, endDateStr);
        return count > 0;
        // 分两次查询:入库和出库
        int pakinCount = this.baseMapper.countUnfinishedOrdersInRangePakin(startDateStr, endDateStr);
        int pakoutCount = this.baseMapper.countUnfinishedOrdersInRangePakout(startDateStr, endDateStr);
        return (pakinCount + pakoutCount) > 0;
    }
    @Override
@@ -141,9 +151,16 @@
        }
        // 统计物料出入库数量(分别查询两个表,在Java代码中合并)
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        // 注意:order_time 是 varchar 类型,需要格式化为完整的日期时间字符串进行比较
        SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String startDateStr = dateFormat.format(startDate);
        // 起始日期设置为当天的 00:00:00
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);
        startCal.set(Calendar.HOUR_OF_DAY, 0);
        startCal.set(Calendar.MINUTE, 0);
        startCal.set(Calendar.SECOND, 0);
        startCal.set(Calendar.MILLISECOND, 0);
        String startDateStr = dateTimeFormat.format(startCal.getTime());
        String endDateStr = dateTimeFormat.format(endDate);
        
        // 分别查询入库表和出库表
@@ -259,6 +276,42 @@
            currentMaterialMap.put(key, stat);
        }
        // 收集所有需要查询库存的物料(本期有出入库的 + 上一期存在但本期没有出入库的)
        List<String> matnrBatchList = new java.util.ArrayList<>();
        for (MaterialInOutStatDTO stat : materialStats) {
            String matnr = stat.getMatnr();
            String batch = stat.getBatch() != null ? stat.getBatch() : "";
            matnrBatchList.add(matnr + "_" + batch);
        }
        if (previousSettle != null && !previousDetailMap.isEmpty()) {
            for (Map.Entry<String, PreviousSettleEndingQtyDTO> entry : previousDetailMap.entrySet()) {
                String key = entry.getKey();
                if (!currentMaterialMap.containsKey(key)) {
                    PreviousSettleEndingQtyDTO previousDetail = entry.getValue();
                    String matnr = previousDetail.getMatnr();
                    String batch = previousDetail.getBatch() != null ? previousDetail.getBatch() : "";
                    String matnrBatchKey = matnr + "_" + batch;
                    if (!matnrBatchList.contains(matnrBatchKey)) {
                        matnrBatchList.add(matnrBatchKey);
                    }
                }
            }
        }
        // 批量查询库存
        Map<String, BigDecimal> stockQtyMap = new HashMap<>();
        if (!matnrBatchList.isEmpty()) {
            List<com.zy.asrs.entity.result.StockQtyDTO> stockQtyList = manLocDetlService.queryStockAnfmeBatch(matnrBatchList);
            if (stockQtyList != null) {
                for (com.zy.asrs.entity.result.StockQtyDTO stockQtyDTO : stockQtyList) {
                    String key = stockQtyDTO.getMatnr() + "_" + (stockQtyDTO.getBatch() != null ? stockQtyDTO.getBatch() : "");
                    stockQtyMap.put(key, stockQtyDTO.getStockQty() != null ? stockQtyDTO.getStockQty() : BigDecimal.ZERO);
                }
            }
        }
        // 收集所有明细记录,用于批量插入
        List<MonthlySettleDetail> detailList = new java.util.ArrayList<>();
        // 1. 处理本期有出入库的物料
        for (MaterialInOutStatDTO stat : materialStats) {
            // 1. 提取基础信息
@@ -274,16 +327,18 @@
            // 3. 计算库存相关数量
            BigDecimal beginningQty = getBeginningQty(matnr, batch, brand, previousEndingQtyMap);
            BigDecimal endingQty = calculateEndingQty(beginningQty, inQty, outQty);
            BigDecimal stockQtyDecimal = getCurrentStockQty(matnr, batch);
            // 从批量查询的Map中获取库存
            String stockKey = matnr + "_" + batch;
            BigDecimal stockQtyDecimal = stockQtyMap.getOrDefault(stockKey, BigDecimal.ZERO);
            BigDecimal diffQty = calculateDiffQty(stockQtyDecimal, endingQty);
            
            // 4. 创建并保存明细记录
            // 4. 创建明细记录(暂不插入)
            MonthlySettleDetail detail = buildMonthlySettleDetail(
                monthlySettle.getId(), settleNo, matnr, batch, maktx, brand,
                beginningQty, inQty, outQty, endingQty, stockQtyDecimal, diffQty
            );
            detail.setIsDeleted(0); // 未删除
            monthlySettleDetailMapper.insert(detail);
            detailList.add(detail);
            // 5. 累计统计
            totalInQty = totalInQty.add(inQty);
@@ -315,21 +370,36 @@
                    BigDecimal beginningQty = previousEndingQty;
                    // 期末库存 = 期初 + 入库 - 出库 = 期初(因为本期没有出入库)
                    BigDecimal endingQty = beginningQty;
                    // 获取当前实际库存
                    BigDecimal stockQtyDecimal = getCurrentStockQty(matnr, batch);
                    // 从批量查询的Map中获取库存
                    String stockKey = matnr + "_" + batch;
                    BigDecimal stockQtyDecimal = stockQtyMap.getOrDefault(stockKey, BigDecimal.ZERO);
                    // 计算差异
                    BigDecimal diffQty = calculateDiffQty(stockQtyDecimal, endingQty);
                    
                    // 创建并保存明细记录
                    // 创建明细记录(暂不插入)
                    MonthlySettleDetail detail = buildMonthlySettleDetail(
                        monthlySettle.getId(), settleNo, matnr, batch, maktx, brand,
                        beginningQty, inQty, outQty, endingQty, stockQtyDecimal, diffQty
                    );
                    detail.setIsDeleted(0); // 未删除
                    monthlySettleDetailMapper.insert(detail);
                    detailList.add(detail);
                    
                    // 累计统计(虽然入库和出库为0,但也要计入物料种类数)
                    materialCount++;
                }
            }
        }
        // 分批插入,每批1000条,避免一次性插入过多数据
        if (!detailList.isEmpty()) {
            int batchSize = 1000;
            for (int i = 0; i < detailList.size(); i += batchSize) {
                int end = Math.min(i + batchSize, detailList.size());
                List<MonthlySettleDetail> batch = detailList.subList(i, end);
                for (MonthlySettleDetail detail : batch) {
                    if (monthlySettleDetailMapper.insert(detail) <= 0) {
                        throw new CoolException("插入月结明细失败");
                    }
                }
            }
        }
@@ -429,8 +499,10 @@
    }
    /**
     * 获取当前实际库存数量
     * 获取当前实际库存数量(已废弃,改用批量查询)
     * @deprecated 使用批量查询方法替代,避免N+1查询问题
     */
    @Deprecated
    private BigDecimal getCurrentStockQty(String matnr, String batch) {
        Double stockQty = manLocDetlService.queryStockAnfme(matnr, batch);
        return stockQty != null ? BigDecimal.valueOf(stockQty) : BigDecimal.ZERO;
@@ -499,5 +571,59 @@
        settle.setUpdateTime(new Date());
        this.updateById(settle);
    }
    @Override
    public boolean isOrderTimeInSettledRange(String orderTime) {
        if (Cools.isEmpty(orderTime)) {
            return false;
        }
        try {
            // 解析订单业务时间
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date orderDate = sdf.parse(orderTime);
            // 查询所有已月结的记录(status=1且未删除)
            EntityWrapper<MonthlySettle> wrapper = new EntityWrapper<>();
            wrapper.eq("status", 1);
            wrapper.eq("is_deleted", 0);
            List<MonthlySettle> settledList = this.selectList(wrapper);
            if (settledList == null || settledList.isEmpty()) {
                return false;
            }
            // 检查订单时间是否在任何已月结的区间内
            for (MonthlySettle settle : settledList) {
                Date startDate = settle.getStartDate();
                Date endDate = settle.getEndDate();
                // 确保startDate是当天的00:00:00
                Calendar startCal = Calendar.getInstance();
                startCal.setTime(startDate);
                startCal.set(Calendar.HOUR_OF_DAY, 0);
                startCal.set(Calendar.MINUTE, 0);
                startCal.set(Calendar.SECOND, 0);
                startCal.set(Calendar.MILLISECOND, 0);
                // 确保endDate是当天的23:59:59.999
                Calendar endCal = Calendar.getInstance();
                endCal.setTime(endDate);
                endCal.set(Calendar.HOUR_OF_DAY, 23);
                endCal.set(Calendar.MINUTE, 59);
                endCal.set(Calendar.SECOND, 59);
                endCal.set(Calendar.MILLISECOND, 999);
                // 判断订单时间是否在区间内:[startDate的00:00:00, endDate的23:59:59.999]
                if (!orderDate.before(startCal.getTime()) && !orderDate.after(endCal.getTime())) {
                    return true;
                }
            }
            return false;
        } catch (Exception e) {
            log.error("检查订单时间是否在已月结区间内失败", e);
            return false;
        }
    }
}
src/main/resources/mapper/ManLocDetlMapper.xml
@@ -313,4 +313,22 @@
        where node_id = #{nodeId} ;
    </update>
    <!-- 批量查询库存数量 -->
    <select id="queryStockAnfmeBatch" resultType="com.zy.asrs.entity.result.StockQtyDTO">
        SELECT
            matnr,
            ISNULL(batch, '') as batch,
            SUM(anfme) as stockQty
        FROM man_loc_detl
        WHERE 1=1
        <if test="matnrBatchList != null and matnrBatchList.size > 0">
            AND (
            <foreach collection="matnrBatchList" item="item" separator=" OR ">
                (matnr + '_' + ISNULL(batch, '') = #{item})
            </foreach>
            )
        </if>
        GROUP BY matnr, ISNULL(batch, '')
    </select>
</mapper>
src/main/resources/mapper/MonthlySettleMapper.xml
@@ -27,75 +27,74 @@
        ORDER BY end_date DESC
    </select>
    <!-- 统计月结时间范围内的订单数量 -->
    <select id="countOrdersInRange" resultType="int">
        SELECT COUNT(*) FROM man_order
        WHERE status = 1
        AND order_time >= #{startDate}
        AND order_time &lt;= #{endDate}
    <!-- 统计月结时间范围内未完成的入库订单数量 -->
    <select id="countUnfinishedOrdersInRangePakin" resultType="int">
        SELECT COUNT(1) FROM man_order_log_pakin molpi
        WHERE molpi.status = 1
        AND molpi.order_time >= #{startDate}
        AND molpi.order_time &lt;= #{endDate}
        AND (molpi.settle IS NULL OR molpi.settle != 6)
        AND (molpi.monthly_settle_id IS NULL OR molpi.monthly_settle_id = 0)
        AND EXISTS (
            SELECT 1 FROM man_order_detl_log_pakin modlpi
            WHERE modlpi.order_id = molpi.id
            AND (modlpi.anfme > modlpi.qty OR modlpi.qty IS NULL)
        )
    </select>
    <!-- 统计月结时间范围内未完成的订单数量(入库和出库) -->
    <select id="countUnfinishedOrdersInRange" resultType="int">
        SELECT COUNT(*) FROM (
            SELECT DISTINCT o.id FROM man_order_log_pakin o
            INNER JOIN man_order_detl_log_pakin od ON o.id = od.order_id
            WHERE o.status = 1
            AND CONVERT(date, o.order_time) >= #{startDate}
            AND o.order_time &lt;= #{endDate}
            AND o.move_status != 2
            AND (od.anfme > od.qty OR od.qty IS NULL)
            AND (o.monthly_settle_id IS NULL OR o.monthly_settle_id = 0)
            UNION
            SELECT DISTINCT o.id FROM man_order_log_pakout o
            INNER JOIN man_order_detl_log_pakout od ON o.id = od.order_id
            WHERE o.status = 1
            AND CONVERT(date, o.order_time) >= #{startDate}
            AND o.order_time &lt;= #{endDate}
            AND o.move_status != 2
            AND (od.anfme > od.qty OR od.qty IS NULL)
            AND (o.monthly_settle_id IS NULL OR o.monthly_settle_id = 0)
        ) t
    <!-- 统计月结时间范围内未完成的出库订单数量 -->
    <select id="countUnfinishedOrdersInRangePakout" resultType="int">
        SELECT COUNT(1) FROM man_order_log_pakout molpo
        WHERE molpo.status = 1
        AND molpo.order_time >= #{startDate}
        AND molpo.order_time &lt;= #{endDate}
        AND (molpo.settle IS NULL OR molpo.settle != 6)
        AND (molpo.monthly_settle_id IS NULL OR molpo.monthly_settle_id = 0)
        AND EXISTS (
            SELECT 1 FROM man_order_detl_log_pakout modlpo
            WHERE modlpo.order_id = molpo.id
            AND (modlpo.anfme > modlpo.qty OR modlpo.qty IS NULL)
        )
    </select>
    <!-- 统计月结时间范围内的物料出入库数量(从入库表查询) -->
    <select id="statisticsMaterialInOutFromPakin" resultType="com.zy.asrs.entity.result.MaterialInOutRawDTO">
        SELECT 
            od.matnr,
            od.maktx,
            od.batch,
            od.brand,
            SUM(od.qty) as qty,
            COALESCE(od.pakin_pakout_status, o.pakin_pakout_status) as pakinPakoutStatus
        FROM man_order_log_pakin o
        INNER JOIN man_order_detl_log_pakin od ON o.id = od.order_id
        WHERE o.status = 1
        AND CONVERT(date, o.order_time) >= #{startDate}
        AND o.order_time &lt;= #{endDate}
        AND o.move_status = 2
        AND (o.monthly_settle_id IS NULL OR o.monthly_settle_id = 0)
        AND COALESCE(od.pakin_pakout_status, o.pakin_pakout_status) IN (1, 2)
        GROUP BY od.matnr, od.maktx, od.batch, od.brand, COALESCE(od.pakin_pakout_status, o.pakin_pakout_status)
            modlpi.matnr,
            modlpi.maktx,
            modlpi.batch,
            modlpi.brand,
            SUM(modlpi.qty) as qty,
            COALESCE(modlpi.pakin_pakout_status, molpi.pakin_pakout_status) as pakinPakoutStatus
        FROM man_order_log_pakin molpi
        INNER JOIN man_order_detl_log_pakin modlpi ON molpi.id = modlpi.order_id
        WHERE molpi.status = 1
        AND molpi.order_time >= #{startDate}
        AND molpi.order_time &lt;= #{endDate}
        AND molpi.settle = 6
        AND (molpi.monthly_settle_id IS NULL OR molpi.monthly_settle_id = 0)
        AND COALESCE(modlpi.pakin_pakout_status, molpi.pakin_pakout_status) IN (1, 2)
        GROUP BY modlpi.matnr, modlpi.maktx, modlpi.batch, modlpi.brand, COALESCE(modlpi.pakin_pakout_status, molpi.pakin_pakout_status)
    </select>
    <!-- 统计月结时间范围内的物料出入库数量(从出库表查询) -->
    <select id="statisticsMaterialInOutFromPakout" resultType="com.zy.asrs.entity.result.MaterialInOutRawDTO">
        SELECT 
            od.matnr,
            od.maktx,
            od.batch,
            od.brand,
            SUM(od.qty) as qty,
            COALESCE(od.pakin_pakout_status, o.pakin_pakout_status) as pakinPakoutStatus
        FROM man_order_log_pakout o
        INNER JOIN man_order_detl_log_pakout od ON o.id = od.order_id
        WHERE o.status = 1
        AND CONVERT(date, o.order_time) >= #{startDate}
        AND o.order_time &lt;= #{endDate}
        AND o.move_status = 2
        AND (o.monthly_settle_id IS NULL OR o.monthly_settle_id = 0)
        AND COALESCE(od.pakin_pakout_status, o.pakin_pakout_status) IN (1, 2)
        GROUP BY od.matnr, od.maktx, od.batch, od.brand, COALESCE(od.pakin_pakout_status, o.pakin_pakout_status)
            modlpo.matnr,
            modlpo.maktx,
            modlpo.batch,
            modlpo.brand,
            SUM(modlpo.qty) as qty,
            COALESCE(modlpo.pakin_pakout_status, molpo.pakin_pakout_status) as pakinPakoutStatus
        FROM man_order_log_pakout molpo
        INNER JOIN man_order_detl_log_pakout modlpo ON molpo.id = modlpo.order_id
        WHERE molpo.status = 1
        AND molpo.order_time >= #{startDate}
        AND molpo.order_time &lt;= #{endDate}
        AND molpo.settle = 6
        AND (molpo.monthly_settle_id IS NULL OR molpo.monthly_settle_id = 0)
        AND COALESCE(modlpo.pakin_pakout_status, molpo.pakin_pakout_status) IN (1, 2)
        GROUP BY modlpo.matnr, modlpo.maktx, modlpo.batch, modlpo.brand, COALESCE(modlpo.pakin_pakout_status, molpo.pakin_pakout_status)
    </select>
    <!-- 获取上一个月结的物料期末库存 -->
@@ -117,9 +116,9 @@
        SET monthly_settle_id = #{settleId},
            monthly_settle_no = #{settleNo}
        WHERE status = 1
        AND CONVERT(date, order_time) >= #{startDate}
        AND order_time >= #{startDate}
        AND order_time &lt;= #{endDate}
        AND move_status = 2
        AND settle = 6
        AND (monthly_settle_id IS NULL OR monthly_settle_id = 0)
    </update>
@@ -129,9 +128,9 @@
        SET monthly_settle_id = #{settleId},
            monthly_settle_no = #{settleNo}
        WHERE status = 1
        AND CONVERT(date, order_time) >= #{startDate}
        AND order_time >= #{startDate}
        AND order_time &lt;= #{endDate}
        AND move_status = 2
        AND settle = 6
        AND (monthly_settle_id IS NULL OR monthly_settle_id = 0)
    </update>