|
|
|
|
@@ -0,0 +1,182 @@
|
|
|
|
|
package cn.code.nl.module.lms.service.dailyplan;
|
|
|
|
|
|
|
|
|
|
import cn.code.nl.module.lms.dal.dataobject.dailyplan.DailyPlanDO;
|
|
|
|
|
import cn.code.nl.module.lms.dal.dataobject.dailyplan.DailyPlanOperationLogDO;
|
|
|
|
|
import cn.code.nl.module.lms.dal.dataobject.dailyplan.DailyPlanStrategyDO;
|
|
|
|
|
import cn.code.nl.module.lms.dal.mysql.dailyplan.DailyPlanMapper;
|
|
|
|
|
import cn.code.nl.module.lms.dal.mysql.dailyplan.DailyPlanOperationLogMapper;
|
|
|
|
|
import cn.code.nl.module.lms.dal.mysql.dailyplan.DailyPlanStrategyMapper;
|
|
|
|
|
import cn.code.nl.module.lms.enums.DailyPlanOrderStatusEnum;
|
|
|
|
|
import cn.code.nl.module.lms.enums.DailyPlanStrategyModeEnum;
|
|
|
|
|
import cn.code.nl.module.lms.enums.DailyPlanStrategyTypeEnum;
|
|
|
|
|
import cn.code.nl.module.lms.enums.DailyPlanWorkStatusEnum;
|
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
|
|
|
|
import jakarta.annotation.Resource;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
|
|
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_COMPLETED_QTY_EXCEEDS_TOTAL;
|
|
|
|
|
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_NOT_EXISTS;
|
|
|
|
|
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_PROGRESS_QTY_NOT_POSITIVE;
|
|
|
|
|
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_STATUS_OPERATION_NOT_ALLOWED;
|
|
|
|
|
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_STRATEGY_NOT_EXISTS;
|
|
|
|
|
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_STRATEGY_VERSION_CONFLICT;
|
|
|
|
|
import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_VERSION_CONFLICT;
|
|
|
|
|
|
|
|
|
|
/** 日计划调度服务实现。 */
|
|
|
|
|
@Service
|
|
|
|
|
public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService {
|
|
|
|
|
|
|
|
|
|
private static final String TARGET_STOCKING = "STOCKING";
|
|
|
|
|
private static final String TARGET_SLEEVING = "SLEEVING";
|
|
|
|
|
private static final String TYPE_PROGRESS = "PROGRESS";
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private DailyPlanMapper dailyPlanMapper;
|
|
|
|
|
@Resource
|
|
|
|
|
private DailyPlanStrategyMapper dailyPlanStrategyMapper;
|
|
|
|
|
@Resource
|
|
|
|
|
private DailyPlanOperationLogMapper dailyPlanOperationLogMapper;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public DailyPlanScheduleCandidate getNextStockingCandidate(String operator) {
|
|
|
|
|
return selectCandidate(DailyPlanStrategyTypeEnum.STOCKING, dailyPlanMapper.selectStockingCandidates(), operator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public DailyPlanScheduleCandidate getNextSleevingCandidate(String operator) {
|
|
|
|
|
return selectCandidate(DailyPlanStrategyTypeEnum.SLEEVING, dailyPlanMapper.selectSleevingCandidates(), operator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private DailyPlanScheduleCandidate selectCandidate(DailyPlanStrategyTypeEnum type,
|
|
|
|
|
List<DailyPlanDO> candidates, String operator) {
|
|
|
|
|
DailyPlanStrategyDO strategy = dailyPlanStrategyMapper.selectByStrategyType(type.getCode());
|
|
|
|
|
if (strategy == null) {
|
|
|
|
|
throw exception(DAILY_PLAN_STRATEGY_NOT_EXISTS);
|
|
|
|
|
}
|
|
|
|
|
if (candidates.isEmpty()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
return convertCandidate(selected, remaining(selected, type), mode.getCode());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int selectedIndex = findCursorIndex(candidates, strategy.getCursorDailyPlanId());
|
|
|
|
|
DailyPlanDO selected = candidates.get(selectedIndex);
|
|
|
|
|
DailyPlanDO next = candidates.get((selectedIndex + 1) % candidates.size());
|
|
|
|
|
int affected = dailyPlanStrategyMapper.updateCursorWithVersion(strategy.getStrategyId(),
|
|
|
|
|
next.getDailyPlanId(), strategy.getVersion(), normalizedOperator(operator));
|
|
|
|
|
if (affected != 1) {
|
|
|
|
|
// 本次候选不应在游标推进失败时被执行,调用方可重新获取最新候选。
|
|
|
|
|
throw exception(DAILY_PLAN_STRATEGY_VERSION_CONFLICT);
|
|
|
|
|
}
|
|
|
|
|
int quantity = Math.min(selected.getWeight(), remaining(selected, type));
|
|
|
|
|
return convertCandidate(selected, quantity, mode.getCode());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int findCursorIndex(List<DailyPlanDO> candidates, Long cursorDailyPlanId) {
|
|
|
|
|
if (cursorDailyPlanId != null) {
|
|
|
|
|
for (int i = 0; i < candidates.size(); i++) {
|
|
|
|
|
if (cursorDailyPlanId.equals(candidates.get(i).getDailyPlanId())) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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 convertCandidate(DailyPlanDO plan, int quantity, Integer strategyMode) {
|
|
|
|
|
DailyPlanScheduleCandidate candidate = new DailyPlanScheduleCandidate();
|
|
|
|
|
candidate.setDailyPlanId(plan.getDailyPlanId());
|
|
|
|
|
candidate.setPlanCode(plan.getPlanCode());
|
|
|
|
|
candidate.setMaterialId(plan.getMaterialId());
|
|
|
|
|
candidate.setMaterialCode(plan.getMaterialCode());
|
|
|
|
|
candidate.setMaterialName(plan.getMaterialName());
|
|
|
|
|
candidate.setMaterialSpec(plan.getMaterialSpec());
|
|
|
|
|
candidate.setSuggestedQuantity(quantity);
|
|
|
|
|
candidate.setVersion(plan.getVersion());
|
|
|
|
|
candidate.setStrategyMode(strategyMode);
|
|
|
|
|
return candidate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public void increaseStockedQuantity(Long dailyPlanId, Integer quantity, Integer version, String operator) {
|
|
|
|
|
increaseQuantity(dailyPlanId, quantity, version, operator, DailyPlanStrategyTypeEnum.STOCKING);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public void increaseSleevedQuantity(Long dailyPlanId, Integer quantity, Integer version, String operator) {
|
|
|
|
|
increaseQuantity(dailyPlanId, quantity, version, operator, DailyPlanStrategyTypeEnum.SLEEVING);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void increaseQuantity(Long dailyPlanId, Integer quantity, Integer version, String operator,
|
|
|
|
|
DailyPlanStrategyTypeEnum type) {
|
|
|
|
|
if (quantity == null || quantity <= 0) {
|
|
|
|
|
throw exception(DAILY_PLAN_PROGRESS_QTY_NOT_POSITIVE);
|
|
|
|
|
}
|
|
|
|
|
DailyPlanDO plan = dailyPlanMapper.selectDailyPlanById(dailyPlanId);
|
|
|
|
|
if (plan == null) {
|
|
|
|
|
throw exception(DAILY_PLAN_NOT_EXISTS);
|
|
|
|
|
}
|
|
|
|
|
if (!plan.getVersion().equals(version)) {
|
|
|
|
|
throw exception(DAILY_PLAN_VERSION_CONFLICT);
|
|
|
|
|
}
|
|
|
|
|
Integer workStatus = type == DailyPlanStrategyTypeEnum.STOCKING
|
|
|
|
|
? plan.getStockingStatus() : plan.getSleevingStatus();
|
|
|
|
|
if (!DailyPlanOrderStatusEnum.IN_PROGRESS.getCode().equals(plan.getOrderStatus())
|
|
|
|
|
|| !DailyPlanWorkStatusEnum.IN_PROGRESS.getCode().equals(workStatus)) {
|
|
|
|
|
throw exception(DAILY_PLAN_STATUS_OPERATION_NOT_ALLOWED,
|
|
|
|
|
plan.getOrderStatus() + "/" + workStatus, "更新作业进度");
|
|
|
|
|
}
|
|
|
|
|
int before = type == DailyPlanStrategyTypeEnum.STOCKING ? plan.getStockedQty() : plan.getSleevedQty();
|
|
|
|
|
long total = (long) plan.getPlanQty() + plan.getAppendQty();
|
|
|
|
|
if ((long) before + quantity > total) {
|
|
|
|
|
throw exception(DAILY_PLAN_COMPLETED_QTY_EXCEEDS_TOTAL);
|
|
|
|
|
}
|
|
|
|
|
int affected = type == DailyPlanStrategyTypeEnum.STOCKING
|
|
|
|
|
? dailyPlanMapper.increaseStockedQuantity(dailyPlanId, quantity, version, normalizedOperator(operator))
|
|
|
|
|
: dailyPlanMapper.increaseSleevedQuantity(dailyPlanId, quantity, version, normalizedOperator(operator));
|
|
|
|
|
if (affected != 1) {
|
|
|
|
|
throw exception(DAILY_PLAN_VERSION_CONFLICT);
|
|
|
|
|
}
|
|
|
|
|
insertProgressLog(dailyPlanId, type, before, before + quantity, quantity, operator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void insertProgressLog(Long dailyPlanId, DailyPlanStrategyTypeEnum type, int before, int after,
|
|
|
|
|
int quantity, String operator) {
|
|
|
|
|
DailyPlanOperationLogDO log = new DailyPlanOperationLogDO();
|
|
|
|
|
log.setOperationLogId(IdWorker.getId());
|
|
|
|
|
log.setDailyPlanId(dailyPlanId);
|
|
|
|
|
log.setOperationTarget(type == DailyPlanStrategyTypeEnum.STOCKING ? TARGET_STOCKING : TARGET_SLEEVING);
|
|
|
|
|
log.setOperationType(TYPE_PROGRESS);
|
|
|
|
|
log.setBeforeValue(String.valueOf(before));
|
|
|
|
|
log.setAfterValue(String.valueOf(after));
|
|
|
|
|
log.setChangeQty(quantity);
|
|
|
|
|
log.setOperator(normalizedOperator(operator));
|
|
|
|
|
if (dailyPlanOperationLogMapper.insertOperationLog(log) != 1) {
|
|
|
|
|
throw exception(DAILY_PLAN_VERSION_CONFLICT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String normalizedOperator(String operator) {
|
|
|
|
|
return StrUtil.blankToDefault(StrUtil.trim(operator), "SCHEDULER");
|
|
|
|
|
}
|
|
|
|
|
}
|