From 7a215e51f9f4066f2024d8476e5b5db2c06358a1 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期二, 07 四月 2026 13:51:01 +0800
Subject: [PATCH] #lua锁示例
---
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WmsRedisLuaService.java | 18 ++
rsf-server/src/main/resources/wms-lua/location-claim.lua | 19 --
rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/LocManageUtil.java | 57 +++++----
rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderPressureSchedules.java | 216 ++++++++++++++++++++++++++++++++++++
rsf-server/src/main/resources/wms-lua/station-claim.lua | 9 +
rsf-server/src/main/resources/application-dev.yml | 8 +
6 files changed, 284 insertions(+), 43 deletions(-)
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderPressureSchedules.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderPressureSchedules.java
new file mode 100644
index 0000000..8c187c6
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderPressureSchedules.java
@@ -0,0 +1,216 @@
+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.Matnr;
+import com.vincent.rsf.server.manager.entity.WkOrder;
+import com.vincent.rsf.server.manager.entity.WkOrderItem;
+import com.vincent.rsf.server.manager.service.AsnOrderItemService;
+import com.vincent.rsf.server.manager.service.AsnOrderService;
+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.List;
+
+/**
+ * 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";
+
+ @Autowired
+ private AsnOrderService asnOrderService;
+ @Autowired
+ private AsnOrderItemService asnOrderItemService;
+ @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 synchronized void insertPressureOrders() {
+ if (!enabled) {
+ return;
+ }
+ if (orderCountPerRun <= 0 || itemCountPerOrder <= 0 || itemQty <= 0) {
+ log.warn("ASN鍘嬫祴浠诲姟閰嶇疆鏃犳晥锛岃烦杩囨墽琛�: orderCountPerRun={}, itemCountPerOrder={}, itemQty={}",
+ orderCountPerRun, itemCountPerOrder, itemQty);
+ return;
+ }
+
+ List<Matnr> 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<WkOrder> orders = new ArrayList<>(orderCountPerRun);
+ for (int i = 0; i < orderCountPerRun; i++) {
+ orders.add(buildOrder(now, nowTime, totalQty, i));
+ }
+ if (!asnOrderService.saveBatch(orders, 200)) {
+ throw new CoolException("ASN鍘嬫祴涓诲崟鎻掑叆澶辫触");
+ }
+
+ List<WkOrderItem> items = new ArrayList<>(orderCountPerRun * itemCountPerOrder);
+ for (int orderIndex = 0; orderIndex < orders.size(); orderIndex++) {
+ WkOrder order = orders.get(orderIndex);
+ 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 (!asnOrderItemService.saveBatch(items, 500)) {
+ throw new CoolException("ASN鍘嬫祴鏄庣粏鎻掑叆澶辫触");
+ }
+
+ log.info("ASN鍘嬫祴浠诲姟鎵ц瀹屾垚锛屾湰娆℃彃鍏ヤ富鍗� {} 鏉★紝鏄庣粏 {} 鏉�", orders.size(), items.size());
+ }
+
+ private List<Matnr> loadMatnrs() {
+ int needCount = Math.min(Math.max(orderCountPerRun * itemCountPerOrder, 200), 2000);
+ return matnrService.list(new LambdaQueryWrapper<Matnr>()
+ .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 WkOrder 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;
+
+ return new WkOrder()
+ .setCode(code)
+ .setPoCode(code)
+ .setPoId(serialNo)
+ .setType(ORDER_TYPE)
+ .setWkType(ORDER_WORK_TYPE)
+ .setAnfme(totalQty)
+ .setQty(totalQty)
+ .setWorkQty(0.0)
+ .setCheckType(0)
+ .setRleStatus((short) 0)
+ .setNtyStatus(0)
+ .setExceStatus((short) 4)
+ .setStatus(1)
+ .setDeleted(0)
+ .setTenantId(TENANT_ID)
+ .setCreateBy(USER_ID)
+ .setCreateTime(now)
+ .setUpdateBy(USER_ID)
+ .setUpdateTime(now)
+ .setMemo(MEMO)
+ .setReportOnce(4)
+ .setBusinessTime(now)
+ .setStationId("1215")
+ .setOrderInternalCode(String.valueOf(serialNo))
+ .setStockDirect("stockDirect")
+ .setCustomerId("custom1")
+ .setCustomerName("瀹㈡埛1")
+ .setSupplierId("gongys1")
+ .setSupplierName("渚涘簲鍟�1")
+ .setStockOrgId("stockYH")
+ .setStockOrgName("娴欐睙閾舵箹绠卞寘鏈夐檺鍏徃浠撳簱")
+ .setPurchaseOrgId("yhcaigou")
+ .setPurchaseOrgName("娴欐睙閾舵箹绠卞寘鏈夐檺鍏徃閲囪喘")
+ .setPurchaseUserId("caigouyuan1")
+ .setPurchaseUserName("閲囪喘鍛�1")
+ .setPrdOrgId("prdYH")
+ .setPrdOrgName("娴欐睙閾舵箹绠卞寘鏈夐檺鍏徃")
+ .setSaleOrgId("sale1")
+ .setSaleOrgName("鐢熶骇缁�1")
+ .setSaleUserId("shengchanyuan1")
+ .setSaleUserName("鐢熶骇鍛�1")
+ .setVersion(0);
+ }
+
+ private WkOrderItem buildOrderItem(WkOrder 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 baseUnit = StringUtils.firstNonBlank(matnr.getBaseUnit(), matnr.getUnit(), matnr.getStockUnit(), matnr.getPurUnit());
+ 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 WkOrderItem()
+ .setOrderId(order.getId())
+ .setOrderCode(order.getCode())
+ .setPlatItemId("M" + (itemIndex + 1))
+ .setPoCode(order.getPoCode())
+ .setFieldsIndex(matnr.getFieldsIndex())
+ .setMatnrId(matnr.getId())
+ .setMatnrCode(matnr.getCode())
+ .setMaktx(matnr.getName())
+ .setSpec(matnr.getSpec())
+ .setModel(matnr.getModel())
+ .setAnfme(itemQty)
+ .setWorkQty(0.0)
+ .setPurQty(itemQty)
+ .setQty(itemQty)
+ .setStockUnit(stockUnit)
+ .setPurUnit(purUnit)
+ .setBatch(batchCode)
+ .setSplrBatch(batchCode)
+ .setSplrCode("gongys1")
+ .setSplrName("渚涘簲鍟�1")
+ .setTrackCode(trackCode)
+ .setBarcode(trackCode)
+ .setProdTime(new SimpleDateFormat("yyyy-MM-dd").format(now))
+ .setNtyStatus(0)
+ .setStatus(1)
+ .setDeleted(0)
+ .setTenantId(TENANT_ID)
+ .setCreateBy(USER_ID)
+ .setCreateTime(now)
+ .setUpdateBy(USER_ID)
+ .setUpdateTime(now)
+ .setMemo(MEMO)
+ .setBaseUnit(baseUnit)
+ .setUseOrgId(matnr.getUseOrgId())
+ .setUseOrgName(matnr.getUseOrgName())
+ .setErpClsId(matnr.getErpClsId())
+ .setPriceUnitId(baseUnit);
+ }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WmsRedisLuaService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WmsRedisLuaService.java
index 9b01001..01fed7a 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WmsRedisLuaService.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WmsRedisLuaService.java
@@ -10,6 +10,7 @@
import java.math.BigDecimal;
import java.time.Duration;
import java.util.Arrays;
+import java.util.List;
@Slf4j
@Service
@@ -17,16 +18,25 @@
public class WmsRedisLuaService {
private static final DefaultRedisScript<Long> LOCATION_CLAIM_SCRIPT = createScript("wms-lua/location-claim.lua");
+ private static final DefaultRedisScript<Long> STATION_CLAIM_SCRIPT = createScript("wms-lua/station-claim.lua");
private static final DefaultRedisScript<Long> INVENTORY_RESERVE_SCRIPT = createScript("wms-lua/inventory-reserve.lua");
private final StringRedisTemplate redisTemplate;
- public boolean claimLocation(String occupyKey, String taskKey, String mode, String occupyValue, String taskValue, Duration ttl) {
+ public boolean claimLocation(String occupyKey, String occupyValue, Duration ttl) {
Long result = redisTemplate.execute(
LOCATION_CLAIM_SCRIPT,
- Arrays.asList(occupyKey, taskKey),
- mode,
+ List.of(occupyKey),
occupyValue,
+ String.valueOf(ttl.toMillis())
+ );
+ return result != null && result > 0;
+ }
+
+ public boolean claimStation(String stationKey, String taskValue, Duration ttl) {
+ Long result = redisTemplate.execute(
+ STATION_CLAIM_SCRIPT,
+ List.of(stationKey),
taskValue,
String.valueOf(ttl.toMillis())
);
@@ -36,7 +46,7 @@
public boolean reserveInventory(String inventoryKey, String orderKey, BigDecimal initialAvailable, BigDecimal reserveQuantity, Duration ttl) {
Long result = redisTemplate.execute(
INVENTORY_RESERVE_SCRIPT,
- Arrays.asList(inventoryKey, orderKey),
+ List.of(inventoryKey, orderKey),
initialAvailable.toPlainString(),
reserveQuantity.toPlainString(),
String.valueOf(ttl.toMillis())
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/LocManageUtil.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/LocManageUtil.java
index d999743..1814a3e 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/LocManageUtil.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/LocManageUtil.java
@@ -12,14 +12,20 @@
import com.vincent.rsf.server.manager.enums.TaskType;
import com.vincent.rsf.server.manager.enums.WaveRuleType;
import com.vincent.rsf.server.manager.service.*;
+import com.vincent.rsf.server.manager.service.impl.WmsRedisLuaService;
import com.vincent.rsf.server.manager.enums.LocStsType;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
+import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
public class LocManageUtil {
+
+ private static final int TARGET_LOC_QUERY_LIMIT = 10;
+ private static final String TARGET_LOC_LOCK_KEY_PREFIX = "wms:loc:claim:";
+ private static final Duration TARGET_LOC_LOCK_TTL = Duration.ofMinutes(1);
/**
* @param
@@ -39,7 +45,7 @@
Long locType = containerType;
//TODO 搴撲綅绛栫暐鍚庣画鎺掓湡
LocService locService = SpringUtils.getBean(LocService.class);
- Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>()
+ List<Loc> locList = locService.list(new LambdaQueryWrapper<Loc>()
.eq(!Objects.isNull(locType), Loc::getType, locType)
.eq(Loc::getAreaId, areaId)
.eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
@@ -47,45 +53,48 @@
.orderByAsc(Loc::getLev)
.orderByAsc(Loc::getCol)
.orderByAsc(Loc::getRow)
- .last("LIMIT 1")
+ .last("LIMIT " + TARGET_LOC_QUERY_LIMIT)
);
- return !Objects.isNull(loc) ? loc.getCode() : null;
+ return claimTargetLoc(locList);
}
public static String getTargetLoc(Long areaId, Long containerType) {
Long locType = containerType;
-// if (!Objects.isNull(containerType)) {
-// LocTypeService locService = SpringUtils.getBean(LocTypeService.class);
-// if (containerType.equals(ContainerType.CONTAINER_TYPE_NORMAL.val)) {
-// LocType low = locService.getOne(new LambdaQueryWrapper<LocType>()
-// .eq(LocType::getCode, "L"));
-// if (Objects.isNull(low)) {
-// throw new CoolException("搴綅椤炲瀷涓嶅瓨鍦紒锛�");
-// }
-// locType = low.getId();
-// } else {
-// LocType low = locService.getOne(new LambdaQueryWrapper<LocType>()
-// .eq(LocType::getCode, "H"));
-// if (Objects.isNull(low)) {
-// throw new CoolException("搴綅椤炲瀷涓嶅瓨鍦紒锛�");
-// }
-// locType = low.getId();
-// }
-// }
//TODO 搴撲綅绛栫暐鍚庣画鎺掓湡
LocService locService = SpringUtils.getBean(LocService.class);
- Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>()
+ List<Loc> locList = locService.list(new LambdaQueryWrapper<Loc>()
.eq(!Objects.isNull(locType), Loc::getType, locType)
.eq(Loc::getAreaId, areaId)
.eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
.orderByAsc(Loc::getLev)
.orderByAsc(Loc::getCol)
.orderByAsc(Loc::getRow)
- .last("LIMIT 1")
+ .last("LIMIT " + TARGET_LOC_QUERY_LIMIT)
);
- return !Objects.isNull(loc) ? loc.getCode() : null;
+ return claimTargetLoc(locList);
+ }
+
+ private static String claimTargetLoc(List<Loc> locList) {
+ if (Cools.isEmpty(locList)) {
+ return null;
+ }
+ WmsRedisLuaService wmsRedisLuaService = SpringUtils.getBean(WmsRedisLuaService.class);
+ for (Loc loc : locList) {
+ if (Objects.isNull(loc) || Cools.isEmpty(loc.getCode())) {
+ continue;
+ }
+ boolean claimed = wmsRedisLuaService.claimLocation(buildTargetLocLockKey(loc.getCode()), loc.getCode(), TARGET_LOC_LOCK_TTL);
+ if (claimed) {
+ return loc.getCode();
+ }
+ }
+ return null;
+ }
+
+ private static String buildTargetLocLockKey(String locCode) {
+ return TARGET_LOC_LOCK_KEY_PREFIX + locCode;
}
/**
diff --git a/rsf-server/src/main/resources/application-dev.yml b/rsf-server/src/main/resources/application-dev.yml
index b0df7ab..c0b446f 100644
--- a/rsf-server/src/main/resources/application-dev.yml
+++ b/rsf-server/src/main/resources/application-dev.yml
@@ -111,3 +111,11 @@
flagAvailable: true
#鍒ゆ柇鏄惁鏍¢獙鍚堟牸鍚庯紝鎵嶅厑璁告敹璐�
flagReceiving: false
+
+pressure:
+ asn-order:
+ enabled: true
+ cron: "0/10 * * * * ?"
+ order-count-per-run: 2000
+ item-count-per-order: 10
+ item-qty: 10.0
diff --git a/rsf-server/src/main/resources/wms-lua/location-claim.lua b/rsf-server/src/main/resources/wms-lua/location-claim.lua
index b6e6896..27a983a 100644
--- a/rsf-server/src/main/resources/wms-lua/location-claim.lua
+++ b/rsf-server/src/main/resources/wms-lua/location-claim.lua
@@ -1,19 +1,8 @@
-local mode = ARGV[1]
-local occupyValue = ARGV[2]
-local taskValue = ARGV[3]
-local ttl = tonumber(ARGV[4])
+local occupyValue = ARGV[1]
+local ttl = tonumber(ARGV[2])
-if mode == 'PUTAWAY' then
- if redis.call('exists', KEYS[1]) == 1 or redis.call('exists', KEYS[2]) == 1 then
- return 0
- end
- redis.call('psetex', KEYS[1], ttl, occupyValue)
- redis.call('psetex', KEYS[2], ttl, taskValue)
- return 1
-end
-
-if redis.call('exists', KEYS[2]) == 1 then
+if redis.call('exists', KEYS[1]) == 1 then
return 0
end
-redis.call('psetex', KEYS[2], ttl, taskValue)
+redis.call('psetex', KEYS[1], ttl, occupyValue)
return 1
diff --git a/rsf-server/src/main/resources/wms-lua/station-claim.lua b/rsf-server/src/main/resources/wms-lua/station-claim.lua
new file mode 100644
index 0000000..c13956e
--- /dev/null
+++ b/rsf-server/src/main/resources/wms-lua/station-claim.lua
@@ -0,0 +1,9 @@
+local taskValue = ARGV[1]
+local ttl = tonumber(ARGV[2])
+
+if redis.call('exists', KEYS[1]) == 1 then
+ return 0
+end
+
+redis.call('psetex', KEYS[1], ttl, taskValue)
+return 1
--
Gitblit v1.9.1