|
|
|
|
@@ -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<DailyPlanScheduleCandidate> 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<DailyPlanScheduleCandidate> candidates = previewSleevingCandidates();
|
|
|
|
|
return candidates.isEmpty() ? null : claimSleevingCandidate(candidates.get(0).getDailyPlanId(), operator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private DailyPlanScheduleCandidate selectCandidate(DailyPlanStrategyTypeEnum type,
|
|
|
|
|
List<DailyPlanDO> candidates, String operator) {
|
|
|
|
|
/** 预览本轮备货候选顺序。 */
|
|
|
|
|
@Override
|
|
|
|
|
public List<DailyPlanScheduleCandidate> previewStockingCandidates() {
|
|
|
|
|
return previewCandidates(DailyPlanStrategyTypeEnum.STOCKING, dailyPlanMapper.selectStockingCandidates());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 预览本轮套管候选顺序。 */
|
|
|
|
|
@Override
|
|
|
|
|
public List<DailyPlanScheduleCandidate> previewSleevingCandidates() {
|
|
|
|
|
return previewCandidates(DailyPlanStrategyTypeEnum.SLEEVING, dailyPlanMapper.selectSleevingCandidates());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 按当前策略排列候选,但不产生领取。 */
|
|
|
|
|
private List<DailyPlanScheduleCandidate> previewCandidates(DailyPlanStrategyTypeEnum type,
|
|
|
|
|
List<DailyPlanDO> 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<DailyPlanDO> 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<DailyPlanScheduleCandidate> 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);
|
|
|
|
|
/** 指定领取一张备货计划。 */
|
|
|
|
|
@Override
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public DailyPlanScheduleCandidate claimStockingCandidate(Long dailyPlanId, String operator) {
|
|
|
|
|
expireClaims(operator);
|
|
|
|
|
return claimSingleCandidate(dailyPlanId, DailyPlanStrategyTypeEnum.STOCKING, operator);
|
|
|
|
|
}
|
|
|
|
|
return candidate;
|
|
|
|
|
|
|
|
|
|
/** 指定领取一张套管计划。 */
|
|
|
|
|
@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<DailyPlanDO> 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<DailyPlanDO> 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) {
|
|
|
|
|
|