From 1bb22e9930a97f241e5cac2b1ebf23d308ddd78e Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Mon, 17 Aug 2026 10:38:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=97=A5=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E9=80=90=E6=A0=B9=E6=9D=83=E9=87=8D=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=EF=BC=88=E4=BB=BB=E5=8A=A14=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dailyplan/DailyPlanStrategyDO.java | 2 + .../dailyplan/DailyPlanStrategyMapper.java | 9 ++ .../dailyplan/DailyPlanScheduleService.java | 17 +++ .../DailyPlanScheduleServiceImpl.java | 144 ++++++++++++++---- .../dailyplan/DailyPlanStrategyMapper.xml | 15 +- 5 files changed, 156 insertions(+), 31 deletions(-) diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/dataobject/dailyplan/DailyPlanStrategyDO.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/dataobject/dailyplan/DailyPlanStrategyDO.java index 2dd86b94..666ddff2 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/dataobject/dailyplan/DailyPlanStrategyDO.java +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/dataobject/dailyplan/DailyPlanStrategyDO.java @@ -21,6 +21,8 @@ public class DailyPlanStrategyDO extends TenantBaseDO { private Integer strategyMode; /** 当前游标日计划 ID */ private Long cursorDailyPlanId; + /** 当前权重轮转已执行数量 */ + private Integer cursorExecutedQty; /** 乐观锁版本号 */ private Integer version; } diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/mysql/dailyplan/DailyPlanStrategyMapper.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/mysql/dailyplan/DailyPlanStrategyMapper.java index a0a9ca14..06382ba9 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/mysql/dailyplan/DailyPlanStrategyMapper.java +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/dal/mysql/dailyplan/DailyPlanStrategyMapper.java @@ -34,4 +34,13 @@ public interface DailyPlanStrategyMapper extends BaseMapperX previewStockingCandidates(); + + /** 预览本轮套管候选顺序,不领取计划。 */ + List previewSleevingCandidates(); + + /** 指定领取一张备货计划,本次固定一根。 */ + DailyPlanScheduleCandidate claimStockingCandidate(Long dailyPlanId, String operator); + + /** 指定领取一张套管计划,本次固定一根。 */ + DailyPlanScheduleCandidate claimSleevingCandidate(Long dailyPlanId, String operator); + + /** 确认单根现场任务已经创建,成功后才推进权重轮转。 */ + void confirmTaskCreated(String claimToken, String operator); + /** 原子增加已备货数量。 */ void increaseStockedQuantity(String claimToken, Long dailyPlanId, Integer quantity, String operator); diff --git a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanScheduleServiceImpl.java b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanScheduleServiceImpl.java index 08a363ea..ab52d4fb 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanScheduleServiceImpl.java +++ b/nl-module-lms/nl-module-lms-server/src/main/java/cn/code/nl/module/lms/service/dailyplan/DailyPlanScheduleServiceImpl.java @@ -20,6 +20,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -62,45 +64,76 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService { @Transactional(rollbackFor = Exception.class) public DailyPlanScheduleCandidate getNextStockingCandidate(String operator) { expireClaims(operator); - return selectCandidate(DailyPlanStrategyTypeEnum.STOCKING, dailyPlanMapper.selectStockingCandidates(), operator); + List candidates = previewStockingCandidates(); + return candidates.isEmpty() ? null : claimStockingCandidate(candidates.get(0).getDailyPlanId(), operator); } @Override @Transactional(rollbackFor = Exception.class) public DailyPlanScheduleCandidate getNextSleevingCandidate(String operator) { expireClaims(operator); - return selectCandidate(DailyPlanStrategyTypeEnum.SLEEVING, dailyPlanMapper.selectSleevingCandidates(), operator); + List candidates = previewSleevingCandidates(); + return candidates.isEmpty() ? null : claimSleevingCandidate(candidates.get(0).getDailyPlanId(), operator); } - private DailyPlanScheduleCandidate selectCandidate(DailyPlanStrategyTypeEnum type, - List candidates, String operator) { + /** 预览本轮备货候选顺序。 */ + @Override + public List previewStockingCandidates() { + return previewCandidates(DailyPlanStrategyTypeEnum.STOCKING, dailyPlanMapper.selectStockingCandidates()); + } + + /** 预览本轮套管候选顺序。 */ + @Override + public List previewSleevingCandidates() { + return previewCandidates(DailyPlanStrategyTypeEnum.SLEEVING, dailyPlanMapper.selectSleevingCandidates()); + } + + /** 按当前策略排列候选,但不产生领取。 */ + private List previewCandidates(DailyPlanStrategyTypeEnum type, + List candidates) { DailyPlanStrategyDO strategy = strategyInitializer.getOrCreate(type); if (candidates.isEmpty()) { - return null; + return Collections.emptyList(); } DailyPlanStrategyModeEnum mode = DailyPlanStrategyModeEnum.getByCode(strategy.getStrategyMode()); if (mode == null) { throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS); } - if (mode == DailyPlanStrategyModeEnum.SEQUENCE) { - DailyPlanDO selected = candidates.get(0); - DailyPlanDO locked = lockSchedulablePlan(selected.getDailyPlanId(), type); - return claimCandidate(locked, remaining(locked, type), mode.getCode(), type, operator); + List ordered = new ArrayList<>(candidates.size()); + int start = mode == DailyPlanStrategyModeEnum.WEIGHT_ROUND_ROBIN + ? findCursorIndex(candidates, strategy.getCursorDailyPlanId()) : 0; + for (int index = 0; index < candidates.size(); index++) { + ordered.add(candidates.get((start + index) % candidates.size())); } + List result = new ArrayList<>(ordered.size()); + for (DailyPlanDO plan : ordered) { + result.add(convertCandidate(plan, mode.getCode())); + } + return result; + } - int selectedIndex = findCursorIndex(candidates, strategy.getCursorDailyPlanId()); - DailyPlanDO selected = candidates.get(selectedIndex); - DailyPlanDO next = candidates.get((selectedIndex + 1) % candidates.size()); - DailyPlanDO locked = lockSchedulablePlan(selected.getDailyPlanId(), type); - int quantity = Math.min(locked.getWeight(), remaining(locked, type)); - DailyPlanScheduleCandidate candidate = claimCandidate(locked, quantity, mode.getCode(), type, operator); - int affected = dailyPlanStrategyMapper.updateCursorWithVersion(strategy.getStrategyId(), next.getDailyPlanId(), - strategy.getVersion(), normalizedOperator(operator)); - if (affected != 1) { - // 本次候选不应在游标推进失败时被执行,调用方可重新获取最新候选。 - throw exception(DAILY_PLAN_STRATEGY_VERSION_CONFLICT); - } - return candidate; + /** 指定领取一张备货计划。 */ + @Override + @Transactional(rollbackFor = Exception.class) + public DailyPlanScheduleCandidate claimStockingCandidate(Long dailyPlanId, String operator) { + expireClaims(operator); + return claimSingleCandidate(dailyPlanId, DailyPlanStrategyTypeEnum.STOCKING, operator); + } + + /** 指定领取一张套管计划。 */ + @Override + @Transactional(rollbackFor = Exception.class) + public DailyPlanScheduleCandidate claimSleevingCandidate(Long dailyPlanId, String operator) { + expireClaims(operator); + return claimSingleCandidate(dailyPlanId, DailyPlanStrategyTypeEnum.SLEEVING, operator); + } + + /** 锁定指定计划并领取一根任务额度。 */ + private DailyPlanScheduleCandidate claimSingleCandidate(Long dailyPlanId, DailyPlanStrategyTypeEnum type, + String operator) { + DailyPlanStrategyDO strategy = strategyInitializer.getOrCreate(type); + DailyPlanDO locked = lockSchedulablePlan(dailyPlanId, type); + return claimCandidate(locked, 1, strategy.getStrategyMode(), type, operator); } private DailyPlanDO lockSchedulablePlan(Long dailyPlanId, DailyPlanStrategyTypeEnum type) { @@ -122,11 +155,7 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService { return 0; } - private int remaining(DailyPlanDO plan, DailyPlanStrategyTypeEnum type) { - int completed = type == DailyPlanStrategyTypeEnum.STOCKING ? plan.getStockedQty() : plan.getSleevedQty(); - return plan.getPlanQty() + plan.getAppendQty() - completed; - } - + /** 创建单根排他领取。 */ private DailyPlanScheduleCandidate claimCandidate(DailyPlanDO plan, int quantity, Integer strategyMode, DailyPlanStrategyTypeEnum type, String operator) { LocalDateTime expireTime = nextClaimExpireTime(); @@ -147,6 +176,15 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService { } catch (DuplicateKeyException ex) { throw exception(DAILY_PLAN_CLAIM_CONFLICT); } + DailyPlanScheduleCandidate candidate = convertCandidate(plan, strategyMode); + candidate.setSuggestedQuantity(quantity); + candidate.setClaimToken(claim.getClaimToken()); + candidate.setClaimExpireTime(expireTime); + return candidate; + } + + /** 将日计划转换为调度候选。 */ + private DailyPlanScheduleCandidate convertCandidate(DailyPlanDO plan, Integer strategyMode) { DailyPlanScheduleCandidate candidate = new DailyPlanScheduleCandidate(); candidate.setDailyPlanId(plan.getDailyPlanId()); candidate.setPlanCode(plan.getPlanCode()); @@ -154,14 +192,60 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService { candidate.setMaterialCode(plan.getMaterialCode()); candidate.setMaterialName(plan.getMaterialName()); candidate.setMaterialSpec(plan.getMaterialSpec()); - candidate.setSuggestedQuantity(quantity); + candidate.setSuggestedQuantity(1); candidate.setVersion(plan.getVersion()); candidate.setStrategyMode(strategyMode); - candidate.setClaimToken(claim.getClaimToken()); - candidate.setClaimExpireTime(expireTime); return candidate; } + /** 现场单根任务创建成功后推进权重轮转。 */ + @Override + @Transactional(rollbackFor = Exception.class) + public void confirmTaskCreated(String claimToken, String operator) { + DailyPlanClaimDO claim = getClaimForUpdate(claimToken); + validateActiveClaim(claim); + DailyPlanStrategyTypeEnum type = DailyPlanStrategyTypeEnum.getByCode(claim.getStrategyType()); + if (type == null) { + throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS); + } + DailyPlanStrategyDO strategy = strategyInitializer.getOrCreate(type); + if (!DailyPlanStrategyModeEnum.WEIGHT_ROUND_ROBIN.getCode().equals(strategy.getStrategyMode())) { + return; + } + if (strategy.getCursorDailyPlanId() != null + && !strategy.getCursorDailyPlanId().equals(claim.getDailyPlanId())) { + return; + } + DailyPlanDO current = dailyPlanMapper.selectDailyPlanById(claim.getDailyPlanId()); + if (current == null) { + throw exception(DAILY_PLAN_NOT_EXISTS); + } + int executedQty = strategy.getCursorExecutedQty() + 1; + Long nextDailyPlanId = current.getDailyPlanId(); + if (executedQty >= current.getWeight()) { + executedQty = 0; + List candidates = type == DailyPlanStrategyTypeEnum.STOCKING + ? dailyPlanMapper.selectStockingCandidates() : dailyPlanMapper.selectSleevingCandidates(); + nextDailyPlanId = findNextDailyPlanId(current, candidates); + } + if (dailyPlanStrategyMapper.updateSingleProgressWithVersion(strategy.getStrategyId(), nextDailyPlanId, + executedQty, strategy.getVersion(), normalizedOperator(operator)) != 1) { + throw exception(DAILY_PLAN_STRATEGY_VERSION_CONFLICT); + } + } + + /** 查找当前计划之后的下一张可执行计划。 */ + private Long findNextDailyPlanId(DailyPlanDO current, List candidates) { + for (DailyPlanDO candidate : candidates) { + if (candidate.getSortSeq() > current.getSortSeq() + || candidate.getSortSeq().equals(current.getSortSeq()) + && candidate.getDailyPlanId() > current.getDailyPlanId()) { + return candidate.getDailyPlanId(); + } + } + return candidates.isEmpty() ? current.getDailyPlanId() : candidates.get(0).getDailyPlanId(); + } + @Override @Transactional(rollbackFor = Exception.class) public void increaseStockedQuantity(String claimToken, Long dailyPlanId, Integer quantity, String operator) { diff --git a/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanStrategyMapper.xml b/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanStrategyMapper.xml index 5010fab7..713a6d25 100644 --- a/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanStrategyMapper.xml +++ b/nl-module-lms/nl-module-lms-server/src/main/resources/mapper/dailyplan/DailyPlanStrategyMapper.xml @@ -3,7 +3,7 @@ - strategy_id, strategy_type, strategy_mode, cursor_daily_plan_id, version, + strategy_id, strategy_type, strategy_mode, cursor_daily_plan_id, cursor_executed_qty, version, creator, create_time, updater, update_time, deleted, tenant_id @@ -27,6 +27,7 @@ UPDATE lms_daily_plan_strategy SET strategy_mode = #{strategyMode}, cursor_daily_plan_id = NULL, + cursor_executed_qty = 0, updater = #{updater}, version = version + 1 WHERE strategy_id = #{strategyId} @@ -37,6 +38,18 @@ UPDATE lms_daily_plan_strategy SET cursor_daily_plan_id = #{cursorDailyPlanId}, + cursor_executed_qty = 0, + updater = #{updater}, + version = version + 1 + WHERE strategy_id = #{strategyId} + AND version = #{version} + AND deleted = b'0' + + + + UPDATE lms_daily_plan_strategy + SET cursor_daily_plan_id = #{cursorDailyPlanId}, + cursor_executed_qty = #{cursorExecutedQty}, updater = #{updater}, version = version + 1 WHERE strategy_id = #{strategyId}