package com.vincent.rsf.server.manager.schedules; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.manager.entity.AsnOrderItemLog; import com.vincent.rsf.server.manager.entity.AsnOrderLog; import com.vincent.rsf.server.manager.entity.Matnr; import com.vincent.rsf.server.manager.service.AsnOrderItemLogService; import com.vincent.rsf.server.manager.service.AsnOrderLogService; import com.vincent.rsf.server.manager.service.MatnrService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; /** * ASN 单据压测数据定时生成器。 */ @Slf4j @Component public class AsnOrderPressureSchedules { private static final DateTimeFormatter ORDER_CODE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); private static final String ORDER_TYPE = "in"; private static final String ORDER_WORK_TYPE = "71"; private static final Integer TENANT_ID = 1; private static final Long USER_ID = 51L; private static final String MEMO = "ASN_PRESSURE_TEST"; private final AtomicBoolean running = new AtomicBoolean(false); @Autowired private AsnOrderLogService asnOrderLogService; @Autowired private AsnOrderItemLogService asnOrderItemLogService; @Autowired private MatnrService matnrService; @Value("${pressure.asn-order.enabled:false}") private boolean enabled; @Value("${pressure.asn-order.order-count-per-run:20}") private int orderCountPerRun; @Value("${pressure.asn-order.item-count-per-order:5}") private int itemCountPerOrder; @Value("${pressure.asn-order.item-qty:10}") private double itemQty; @Scheduled(cron = "${pressure.asn-order.cron:0/10 * * * * ?}") @Transactional(rollbackFor = Exception.class) public void insertPressureOrders() { if (!enabled) { return; } if (!running.compareAndSet(false, true)) { log.warn("ASN压测任务仍在执行中,本轮跳过"); return; } try { if (orderCountPerRun <= 0 || itemCountPerOrder <= 0 || itemQty <= 0) { log.warn("ASN压测任务配置无效,跳过执行: orderCountPerRun={}, itemCountPerOrder={}, itemQty={}", orderCountPerRun, itemCountPerOrder, itemQty); return; } List matnrs = loadMatnrs(); if (matnrs.isEmpty()) { log.warn("ASN压测任务未获取到可用物料,跳过执行"); return; } Collections.shuffle(matnrs); Date now = new Date(); LocalDateTime nowTime = LocalDateTime.now(); double totalQty = itemCountPerOrder * itemQty; List orders = new ArrayList<>(orderCountPerRun); for (int i = 0; i < orderCountPerRun; i++) { orders.add(buildOrder(now, nowTime, totalQty, i)); } if (!asnOrderLogService.saveBatch(orders, 200)) { throw new CoolException("ASN压测主单插入失败"); } List persistedOrders = asnOrderLogService.list(new LambdaQueryWrapper() .in(AsnOrderLog::getCode, extractOrderCodes(orders))); Map orderMap = new HashMap<>(persistedOrders.size()); for (AsnOrderLog order : persistedOrders) { orderMap.put(order.getCode(), order); } List items = new ArrayList<>(orderCountPerRun * itemCountPerOrder); for (int orderIndex = 0; orderIndex < orders.size(); orderIndex++) { AsnOrderLog order = orderMap.get(orders.get(orderIndex).getCode()); if (order == null) { throw new CoolException("ASN压测主单回查失败"); } for (int itemIndex = 0; itemIndex < itemCountPerOrder; itemIndex++) { Matnr matnr = matnrs.get((orderIndex * itemCountPerOrder + itemIndex) % matnrs.size()); items.add(buildOrderItem(order, matnr, now, orderIndex, itemIndex)); } } if (!asnOrderItemLogService.saveBatch(items, 500)) { throw new CoolException("ASN压测明细插入失败"); } log.info("ASN压测任务执行完成,本次插入主单 {} 条,明细 {} 条", orders.size(), items.size()); } finally { running.set(false); } } private List loadMatnrs() { int needCount = Math.min(Math.max(orderCountPerRun * itemCountPerOrder, 200), 2000); return matnrService.list(new LambdaQueryWrapper() .eq(Matnr::getDeleted, 0) .eq(Matnr::getStatus, 1) .select(Matnr::getId, Matnr::getCode, Matnr::getName, Matnr::getFieldsIndex, Matnr::getSpec, Matnr::getModel, Matnr::getUnit, Matnr::getPurUnit, Matnr::getStockUnit, Matnr::getBaseUnit, Matnr::getUseOrgId, Matnr::getUseOrgName, Matnr::getErpClsId) .orderByDesc(Matnr::getId) .last("limit " + needCount)); } private List extractOrderCodes(List orders) { List codes = new ArrayList<>(orders.size()); for (AsnOrderLog order : orders) { codes.add(order.getCode()); } return codes; } private AsnOrderLog buildOrder(Date now, LocalDateTime nowTime, double totalQty, int sequence) { String suffix = String.format("%04d", sequence + 1); String code = "erp" + nowTime.format(ORDER_CODE_FORMATTER) + suffix; long serialNo = System.currentTimeMillis() * 1000 + sequence; AsnOrderLog order = new AsnOrderLog(); order.setCode(code); order.setPoCode(code); order.setPoId(serialNo); order.setType(ORDER_TYPE); order.setWkType(ORDER_WORK_TYPE); order.setAnfme(totalQty); order.setQty(totalQty); order.setRleStatus((short) 0); order.setNtyStatus((short) 0); order.setExceStatus((short) 4); order.setStatus(1); order.setDeleted(0); order.setTenantId(TENANT_ID); order.setCreateBy(USER_ID); order.setCreateTime(now); order.setUpdateBy(USER_ID); order.setUpdateTime(now); order.setMemo(MEMO); return order; } private AsnOrderItemLog buildOrderItem(AsnOrderLog order, Matnr matnr, Date now, int orderIndex, int itemIndex) { String stockUnit = StringUtils.firstNonBlank(matnr.getStockUnit(), matnr.getPurUnit(), matnr.getUnit(), matnr.getBaseUnit()); String purUnit = StringUtils.firstNonBlank(matnr.getPurUnit(), matnr.getUnit(), matnr.getStockUnit(), matnr.getBaseUnit()); String batchCode = "B" + new SimpleDateFormat("yyyyMMddHHmmss").format(now) + String.format("%02d%02d", orderIndex + 1, itemIndex + 1); String trackCode = "T" + System.currentTimeMillis() + String.format("%02d%02d", orderIndex + 1, itemIndex + 1); return new AsnOrderItemLog() .setLogId(order.getId()) .setAsnId(order.getAsnId()) .setAsnCode(order.getCode()) .setPlatItemId("M" + (itemIndex + 1)) .setPoCode(order.getPoCode()) .setFieldsIndex(matnr.getFieldsIndex()) .setMatnrId(matnr.getId()) .setMatnrCode(matnr.getCode()) .setMaktx(matnr.getName()) .setAnfme(itemQty) .setPurQty(itemQty) .setQty(itemQty) .setStockUnit(stockUnit) .setPurUnit(purUnit) .setSplrBatch(batchCode) .setSplrCode("gongys1") .setSplrName("供应商1") .setTrackCode(trackCode) .setBarcode(trackCode) .setNtyStatus((short) 0) .setStatus(1) .setDeleted(0) .setTenantId(TENANT_ID) .setCreateBy(USER_ID) .setCreateTime(now) .setUpdateBy(USER_ID) .setUpdateTime(now) .setMemo(MEMO); } }