package com.ruoyi.stock.service.impl; import com.ruoyi.production.mapper.ProductionOrderMapper; import com.ruoyi.production.mapper.ProductionOrderPickMapper; import com.ruoyi.production.pojo.ProductionOrder; import com.ruoyi.production.pojo.ProductionOrderPick; import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper; import com.ruoyi.purchase.pojo.PurchaseReturnOrders; import com.ruoyi.sales.mapper.SalesLedgerMapper; import com.ruoyi.sales.mapper.ShippingInfoMapper; import com.ruoyi.sales.pojo.SalesLedger; import com.ruoyi.sales.pojo.ShippingInfo; import com.ruoyi.stock.mapper.StockOutRecordMapper; import com.ruoyi.stock.pojo.StockOutRecord; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @SpringBootTest public class StockOutRecordBatchUpdateTest { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private StockOutRecordMapper stockOutRecordMapper; @Autowired private PurchaseReturnOrdersMapper purchaseReturnOrdersMapper; @Autowired private ShippingInfoMapper shippingInfoMapper; @Autowired private SalesLedgerMapper salesLedgerMapper; @Autowired private ProductionOrderPickMapper productionOrderPickMapper; @Autowired private ProductionOrderMapper productionOrderMapper; /** * 更新出库单时间 */ @Test void testUpdateStockOutRecords() { List allRecords = stockOutRecordMapper.selectList(null); System.out.println("总记录数: " + allRecords.size()); List recordWithDates = new ArrayList<>(); for (StockOutRecord record : allRecords) { String type = record.getRecordType(); if (type == null) continue; switch (type) { case "1": case "10": recordWithDates.add(resolveType1or10(record, type)); break; case "3": System.out.println("类型3(生产报工-出库)预留分支,当前无数据,跳过"); break; case "8": System.out.println("类型8(销售-出库)预留分支,当前无数据,跳过"); break; case "9": recordWithDates.add(resolveType9(record)); break; case "13": recordWithDates.add(resolveType13(record)); break; case "14": recordWithDates.add(resolveType14or15(record)); break; case "15": recordWithDates.add(resolveType14or15(record)); break; default: System.out.println("未知类型 " + type + ",跳过 ID=" + record.getId()); } } Map> byDate = recordWithDates.stream() .collect(Collectors.groupingBy( rwd -> rwd.dateTime.toLocalDate(), LinkedHashMap::new, Collectors.toList() )); int totalUpdated = 0; for (Map.Entry> dateEntry : byDate.entrySet()) { LocalDate date = dateEntry.getKey(); List records = dateEntry.getValue(); records.sort(Comparator.comparing(rwd -> rwd.dateTime)); assignRandomTimes(records, date); records.sort(Comparator.comparing(rwd -> rwd.dateTime)); int seq = 1; for (RecordWithDate rwd : records) { StockOutRecord record = rwd.record; String type = record.getRecordType(); String batchNo = "CK" + date.format(DateTimeFormatter.ofPattern("yyyyMMdd")) + String.format("%03d", seq++); LocalDateTime createTime = rwd.dateTime; jdbcTemplate.update( "UPDATE stock_out_record SET outbound_batches = ?, create_time = ?, update_time = ? WHERE id = ?", batchNo, createTime, createTime, record.getId()); System.out.println("ID=" + record.getId() + " type=" + type + " batch=" + batchNo + " time=" + createTime); totalUpdated++; } } System.out.println("全部完成!共更新 " + totalUpdated + " 条记录"); } private void assignRandomTimes(List records, LocalDate date) { List individual = new ArrayList<>(); Map> grouped = new LinkedHashMap<>(); for (RecordWithDate rwd : records) { String type = rwd.record.getRecordType(); if (!"1".equals(type) && !"9".equals(type) && !"10".equals(type) && !"13".equals(type) && !"14".equals(type) && !"15".equals(type)) { continue; } if (("14".equals(type) || "15".equals(type)) && rwd.record.getRecordId() != null) { grouped.computeIfAbsent(rwd.record.getRecordId(), k -> new ArrayList<>()).add(rwd); } else { individual.add(rwd); } } List slots = new ArrayList<>(); for (RecordWithDate rwd : individual) { slots.add(new TimeSlot(rwd.record.getId(), List.of(rwd))); } for (Map.Entry> entry : grouped.entrySet()) { long minId = entry.getValue().stream() .mapToLong(rwd -> rwd.record.getId()) .min().orElse(0); slots.add(new TimeSlot(minId, entry.getValue())); } if (slots.isEmpty()) return; slots.sort(Comparator.comparingLong(s -> s.sortKey)); int totalMinutes = 480; for (int i = 0; i < slots.size(); i++) { int offsetMinutes = (int) ((long) i * totalMinutes / slots.size()); int jitter = (int) (Math.random() * 6 - 3); offsetMinutes = Math.max(0, Math.min(479, offsetMinutes + jitter)); LocalTime time; if (offsetMinutes < 240) { time = LocalTime.of(8, 0).plusMinutes(offsetMinutes); } else { time = LocalTime.of(14, 0).plusMinutes(offsetMinutes - 240); } LocalDateTime dt = LocalDateTime.of(date, time); for (RecordWithDate rwd : slots.get(i).members) { rwd.dateTime = dt; } } } private static class TimeSlot { final long sortKey; final List members; TimeSlot(long sortKey, List members) { this.sortKey = sortKey; this.members = members; } } private RecordWithDate resolveType1or10(StockOutRecord record, String type) { LocalDate date = record.getCreateTime() != null ? record.getCreateTime().toLocalDate() : LocalDate.now(); return new RecordWithDate(record, LocalDateTime.of(date, LocalTime.of(8, 0))); } private RecordWithDate resolveType9(StockOutRecord record) { Long recordId = record.getRecordId(); if (recordId != null) { PurchaseReturnOrders pro = purchaseReturnOrdersMapper.selectById(recordId); if (pro != null && pro.getPreparedAt() != null) { return new RecordWithDate(record, LocalDateTime.of(pro.getPreparedAt(), LocalTime.of(8, 0))); } } return fallbackDateTime(record); } private RecordWithDate resolveType13(StockOutRecord record) { Long recordId = record.getRecordId(); if (recordId != null) { ShippingInfo shippingInfo = shippingInfoMapper.selectById(recordId); if (shippingInfo != null && shippingInfo.getSalesLedgerId() != null) { SalesLedger salesLedger = salesLedgerMapper.selectById(shippingInfo.getSalesLedgerId()); if (salesLedger != null && salesLedger.getDeliveryDate() != null) { return new RecordWithDate(record, LocalDateTime.of(salesLedger.getDeliveryDate(), LocalTime.of(8, 0))); } } } return fallbackDateTime(record); } private RecordWithDate resolveType14or15(StockOutRecord record) { Long recordId = record.getRecordId(); if (recordId != null) { ProductionOrderPick pick = productionOrderPickMapper.selectById(recordId); if (pick != null && pick.getProductionOrderId() != null) { ProductionOrder order = productionOrderMapper.selectById(pick.getProductionOrderId()); if (order != null && order.getStartTime() != null) { return new RecordWithDate(record, LocalDateTime.of(order.getStartTime().toLocalDate(), LocalTime.of(8, 0))); } } } return fallbackDateTime(record); } private RecordWithDate fallbackDateTime(StockOutRecord record) { return record.getCreateTime() != null ? new RecordWithDate(record, record.getCreateTime()) : new RecordWithDate(record, LocalDateTime.now()); } private static class RecordWithDate { final StockOutRecord record; LocalDateTime dateTime; RecordWithDate(StockOutRecord record, LocalDateTime dateTime) { this.record = record; this.dateTime = dateTime; } } }