fix: 锁定日计划后创建调度领取
This commit is contained in:
@@ -66,4 +66,5 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode DAILY_PLAN_CLAIM_INVALID = new ErrorCode(41, "日计划领取标识无效、已结束或已过期");
|
||||
ErrorCode DAILY_PLAN_CLAIM_MISMATCH = new ErrorCode(42, "日计划领取信息与进度确认不一致");
|
||||
ErrorCode DAILY_PLAN_CLAIM_PROGRESS_CONFLICT = new ErrorCode(43, "日计划作业进度已变化,该领取无法确认");
|
||||
ErrorCode DAILY_PLAN_CLAIM_CANDIDATE_CHANGED = new ErrorCode(44, "日计划候选状态已变化,请重新获取");
|
||||
}
|
||||
|
||||
@@ -51,6 +51,12 @@ public interface DailyPlanMapper extends BaseMapperX<DailyPlanDO> {
|
||||
*/
|
||||
DailyPlanDO selectDailyPlanById(@Param("dailyPlanId") Long dailyPlanId);
|
||||
|
||||
/**
|
||||
* 锁定并重新校验可调度计划,Claim 必须使用该锁内快照创建。
|
||||
*/
|
||||
DailyPlanDO selectSchedulableForUpdate(@Param("dailyPlanId") Long dailyPlanId,
|
||||
@Param("strategyType") Integer strategyType);
|
||||
|
||||
/**
|
||||
* 根据 ERP 订单号查询日计划。
|
||||
*/
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.List;
|
||||
|
||||
import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_CLAIM_CONFLICT;
|
||||
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_CLAIM_CANDIDATE_CHANGED;
|
||||
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_CLAIM_INVALID;
|
||||
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_CLAIM_MISMATCH;
|
||||
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_CLAIM_PROGRESS_CONFLICT;
|
||||
@@ -84,14 +85,16 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService {
|
||||
}
|
||||
if (mode == DailyPlanStrategyModeEnum.SEQUENCE) {
|
||||
DailyPlanDO selected = candidates.get(0);
|
||||
return claimCandidate(selected, remaining(selected, type), mode.getCode(), type, operator);
|
||||
DailyPlanDO locked = lockSchedulablePlan(selected.getDailyPlanId(), type);
|
||||
return claimCandidate(locked, remaining(locked, type), mode.getCode(), type, operator);
|
||||
}
|
||||
|
||||
int selectedIndex = findCursorIndex(candidates, strategy.getCursorDailyPlanId());
|
||||
DailyPlanDO selected = candidates.get(selectedIndex);
|
||||
DailyPlanDO next = candidates.get((selectedIndex + 1) % candidates.size());
|
||||
int quantity = Math.min(selected.getWeight(), remaining(selected, type));
|
||||
DailyPlanScheduleCandidate candidate = claimCandidate(selected, quantity, mode.getCode(), type, operator);
|
||||
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) {
|
||||
@@ -101,6 +104,14 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private DailyPlanDO lockSchedulablePlan(Long dailyPlanId, DailyPlanStrategyTypeEnum type) {
|
||||
DailyPlanDO locked = dailyPlanMapper.selectSchedulableForUpdate(dailyPlanId, type.getCode());
|
||||
if (locked == null) {
|
||||
throw exception(DAILY_PLAN_CLAIM_CANDIDATE_CHANGED);
|
||||
}
|
||||
return locked;
|
||||
}
|
||||
|
||||
private int findCursorIndex(List<DailyPlanDO> candidates, Long cursorDailyPlanId) {
|
||||
if (cursorDailyPlanId != null) {
|
||||
for (int i = 0; i < candidates.size(); i++) {
|
||||
|
||||
@@ -51,6 +51,28 @@
|
||||
ORDER BY plan_date DESC, sort_seq ASC, daily_plan_id DESC
|
||||
</select>
|
||||
|
||||
<select id="selectSchedulableForUpdate"
|
||||
resultType="cn.code.nl.module.lms.dal.dataobject.dailyplan.DailyPlanDO">
|
||||
SELECT <include refid="dailyPlanColumns"/>
|
||||
FROM lms_daily_plan
|
||||
WHERE daily_plan_id = #{dailyPlanId}
|
||||
AND order_status = 1
|
||||
AND deleted = b'0'
|
||||
AND (
|
||||
(#{strategyType} = 1 AND stocking_status = 1 AND stocked_qty < plan_qty + append_qty)
|
||||
OR (#{strategyType} = 2 AND sleeving_status = 1 AND sleeved_qty < plan_qty + append_qty)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM lms_daily_plan_claim claim
|
||||
WHERE claim.daily_plan_id = lms_daily_plan.daily_plan_id
|
||||
AND claim.tenant_id = lms_daily_plan.tenant_id
|
||||
AND claim.strategy_type = #{strategyType}
|
||||
AND claim.claim_status = 0 AND claim.active_flag = 1
|
||||
AND claim.expire_time > NOW() AND claim.deleted = b'0'
|
||||
)
|
||||
FOR UPDATE
|
||||
</select>
|
||||
|
||||
<select id="selectWorkPage"
|
||||
resultType="cn.code.nl.module.lms.dal.dataobject.dailyplan.DailyPlanDO">
|
||||
SELECT <include refid="dailyPlanColumns"/>
|
||||
|
||||
@@ -102,6 +102,9 @@ CREATE TABLE IF NOT EXISTS `lms_daily_plan_operation_log` (
|
||||
) COMMENT='LMS日计划操作日志';
|
||||
|
||||
-- 兼容已执行过旧版脚本的数据库,幂等补充候选组合索引。
|
||||
SET @lms_claim_progress_upgrade = NOT EXISTS(
|
||||
SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE()
|
||||
AND table_name = 'lms_daily_plan_claim' AND column_name = 'progress_qty');
|
||||
SET @lms_daily_plan_ddl = IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE()
|
||||
AND table_name = 'lms_daily_plan' AND index_name = 'idx_stocking_candidate'),
|
||||
@@ -118,6 +121,14 @@ SET @lms_daily_plan_ddl = IF(
|
||||
PREPARE lms_daily_plan_stmt FROM @lms_daily_plan_ddl;
|
||||
EXECUTE lms_daily_plan_stmt;
|
||||
DEALLOCATE PREPARE lms_daily_plan_stmt;
|
||||
-- 旧版活跃 Claim 没有可靠的领取时进度快照,首次升级时统一释放,避免按默认 0 错误确认。
|
||||
SET @lms_daily_plan_ddl = IF(
|
||||
@lms_claim_progress_upgrade,
|
||||
'UPDATE lms_daily_plan_claim SET claim_status = 2, active_flag = NULL, updater = ''SQL_UPGRADE'' WHERE claim_status = 0 AND active_flag = 1 AND deleted = b''0''',
|
||||
'SELECT 1');
|
||||
PREPARE lms_daily_plan_stmt FROM @lms_daily_plan_ddl;
|
||||
EXECUTE lms_daily_plan_stmt;
|
||||
DEALLOCATE PREPARE lms_daily_plan_stmt;
|
||||
SET @lms_daily_plan_ddl = IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE()
|
||||
AND table_name = 'lms_daily_plan_claim' AND column_name = 'version'),
|
||||
|
||||
Reference in New Issue
Block a user