docs: 完善日计划管芯调度注释

This commit is contained in:
zhouz
2026-08-17 16:19:01 +08:00
parent 5e70feeb27
commit dcb5852a8f
18 changed files with 186 additions and 21 deletions

View File

@@ -10,9 +10,13 @@ import lombok.Getter;
@AllArgsConstructor
public enum DailyPlanOrderStatusEnum {
/** 尚未开始,可编辑、开始或取消。 */
NOT_STARTED(0),
/** 订单执行中,允许备货和套管作业参与调度。 */
IN_PROGRESS(1),
/** 订单已结束,不再产生新的现场任务。 */
FINISHED(2),
/** 订单已取消,不再产生新任务,但取消前已下发任务允许回写结果。 */
CANCELED(3);
/**

View File

@@ -10,7 +10,9 @@ import lombok.Getter;
@AllArgsConstructor
public enum DailyPlanSourceTypeEnum {
/** ERP 接口推送的日计划。 */
ERP(1),
/** LMS 页面人工创建的日计划。 */
MANUAL(2);
/**

View File

@@ -10,7 +10,9 @@ import lombok.Getter;
@AllArgsConstructor
public enum DailyPlanStrategyModeEnum {
/** 严格按顺序号选择最靠前的可执行日计划。 */
SEQUENCE(1),
/** 仍按顺序轮转,并按每张日计划的权重连续执行指定根数。 */
WEIGHT_ROUND_ROBIN(2);
/**

View File

@@ -10,7 +10,9 @@ import lombok.Getter;
@AllArgsConstructor
public enum DailyPlanStrategyTypeEnum {
/** 管芯从备货区进入管芯库的作业策略。 */
STOCKING(1),
/** 管芯从管芯库送往套管机构的作业策略。 */
SLEEVING(2);
/**

View File

@@ -10,9 +10,13 @@ import lombok.Getter;
@AllArgsConstructor
public enum DailyPlanWorkStatusEnum {
/** 作业尚未开始,不参与定时调度。 */
NOT_STARTED(0),
/** 作业进行中,且完成量不足时参与定时调度。 */
IN_PROGRESS(1),
/** 作业数量已达到订单总量;追加数量后会重新进入进行中。 */
COMPLETED(2),
/** 作业人工取消,不产生新的任务。 */
CANCELED(3);
/**

View File

@@ -12,19 +12,33 @@ import java.time.LocalDateTime;
@Data
public class DailyPlanClaimDO extends TenantBaseDO {
/** 领取记录ID。 */
@TableId
private Long claimId;
/** 对外使用的领取令牌,现场任务回调必须原样携带。 */
private String claimToken;
/** 被领取的日计划ID。 */
private Long dailyPlanId;
/** 作业类型:备货或套管。 */
private Integer strategyType;
/** 本次预占数量;当前现场任务固定为一根。 */
private Integer claimedQty;
/** 领取时日计划通用版本,仅用于追踪领取快照。 */
private Integer planVersion;
/** 领取时对应作业的完成数量,用于防止重复或乱序累计。 */
private Integer progressQty;
/** Claim 自身乐观锁版本。 */
private Integer version;
/** 领取状态0活动、1已确认、2已释放、3已过期。 */
private Integer claimStatus;
/** 活动唯一标记;结束后置空以释放唯一索引。 */
private Integer activeFlag;
/** 是否已经成功创建现场任务;已创建任务不按普通租约自动回收。 */
private Boolean taskCreated;
/** 未创建任务时的领取租约过期时间。 */
private LocalDateTime expireTime;
/** 任务完成并确认进度的时间。 */
private LocalDateTime confirmedTime;
/** 领取、确认或释放该记录的执行者。 */
private String operator;
}

View File

@@ -15,14 +15,14 @@ public class DailyPlanStrategyDO extends TenantBaseDO {
/** 策略 ID */
@TableId
private Long strategyId;
/** 策略类型 */
/** 策略类型;备货和套管各维护一套独立策略。 */
private Integer strategyType;
/** 策略模式 */
/** 策略模式:顺序或权重轮转。 */
private Integer strategyMode;
/** 当前游标日计划 ID */
/** 权重模式下当前应执行的日计划ID */
private Long cursorDailyPlanId;
/** 当前权重轮转已执行数量 */
/** 当前游标计划已成功创建的单根任务数量 */
private Integer cursorExecutedQty;
/** 乐观锁版本号 */
/** 乐观锁版本号,防止策略切换与定时调度互相覆盖。 */
private Integer version;
}

View File

@@ -2,6 +2,8 @@ package cn.code.nl.module.lms.enums;
/** 管芯任务回调处理器编码。 */
public interface CoreTubeHandleCodeConstants {
/** 备货区托盘搬运及单根抓取任务回调处理器。 */
String CORE_TUBE_STOCKING_TASK = "CORE_TUBE_STOCKING_TASK";
/** 管芯库单根出库任务回调处理器。 */
String CORE_TUBE_SLEEVING_TASK = "CORE_TUBE_SLEEVING_TASK";
}

View File

@@ -10,13 +10,21 @@ import lombok.Getter;
@AllArgsConstructor
public enum CoreTubeLocationTypeEnum {
/** PDA 绑定管芯时托盘所在的绑定位。 */
STOCKING_BINDING(1),
/** 备货区缓存位。 */
STOCKING_CACHE(2),
/** 备货区与桁架交接的对接位。 */
STOCKING_DOCK(3),
/** 管芯已经完成备货并进入管芯库。 */
CORE_WAREHOUSE(4),
/** 管芯位于套管等生产设备。 */
EQUIPMENT(5),
/** 管芯或托盘正在搬运途中。 */
IN_TRANSIT(6),
/** 套管完成后的管芯缓存位。 */
SLEEVING_CACHE(7);
/** 位置类型编码,与 lms_core_tube.current_location_type 对应。 */
private final Integer code;
}

View File

@@ -10,14 +10,23 @@ import lombok.Getter;
@AllArgsConstructor
public enum CoreTubeStageEnum {
/** 已绑定到备货托盘,尚未被桁架抓取。 */
STOCKING_AREA(1),
/** 备货抓取任务已创建,等待进入管芯库。 */
STOCKING_PICKING(2),
/** 已进入管芯库,可供套管出库。 */
CORE_WAREHOUSE(3),
/** 管芯出库任务已创建,正在送往套管机构。 */
CORE_OUTBOUND(4),
/** 已到达套管机构,正在参与套管。 */
SLEEVING(5),
/** 套管完成后位于套管缓存位。 */
SLEEVING_CACHE(6),
/** 管芯已被生产消耗,生命周期结束。 */
CONSUMED(7),
/** 数据或现场状态异常,需要人工或程序恢复。 */
ABNORMAL(8);
/** 生命周期阶段编码,与 lms_core_tube.stage 对应。 */
private final Integer code;
}

View File

@@ -10,12 +10,19 @@ import lombok.Getter;
@AllArgsConstructor
public enum CoreTubeStatusEnum {
/** 可被新的日计划任务选择。 */
AVAILABLE(1),
/** 已取得日计划 Claim尚未成功绑定现场任务。 */
RESERVED(2),
/** 已绑定现场任务,禁止其他任务重复选择。 */
PROCESSING(3),
/** 已使用,不再参与库存分配。 */
USED(4),
/** 人工冻结,暂不参与调度。 */
FROZEN(5),
/** 状态异常,等待恢复处理。 */
ABNORMAL(6);
/** 占用状态编码,与 lms_core_tube.status 对应。 */
private final Integer code;
}

View File

@@ -2,8 +2,12 @@ package cn.code.nl.module.lms.enums;
/** 管芯现场任务业务类型。 */
public enum CoreTubeTaskBizTypeEnum {
/** 将目标规格托盘从缓存位搬入对接位。 */
CORE_TUBE_PALLET_IN,
/** 将对接位上非当前需要的托盘移回缓存位。 */
CORE_TUBE_PALLET_OUT,
/** 从对接位托盘抓取一根管芯并送入管芯库。 */
CORE_TUBE_STOCKING_PICK,
/** 从管芯库取出一根管芯并送往套管机构。 */
CORE_TUBE_SLEEVING_OUT
}

View File

@@ -13,10 +13,15 @@ import org.springframework.stereotype.Component;
@Component
public class DailyPlanCoreTubeJob {
/** 管芯现场任务调度服务。 */
@Resource
private CoreTubeTaskDispatchService dispatchService;
/** 每次每租户最多创建一条备货现场任务。 */
/**
* 执行备货调度。
*
* <p>{@link TenantJob} 会按租户分别执行;每个租户每次最多创建一条管芯抓取或托盘搬运任务。</p>
*/
@XxlJob("dailyPlanStockingJob")
@TenantJob
public void dailyPlanStockingJob() {
@@ -26,7 +31,11 @@ public class DailyPlanCoreTubeJob {
XxlJobHelper.handleSuccess(result);
}
/** 每次每租户最多创建一条单根管芯出库任务。 */
/**
* 执行套管调度。
*
* <p>{@link TenantJob} 会按租户分别执行;每个租户每次最多创建一条单根管芯出库任务。</p>
*/
@XxlJob("dailyPlanSleevingJob")
@TenantJob
public void dailyPlanSleevingJob() {

View File

@@ -5,14 +5,24 @@ import lombok.Data;
/** 管芯现场任务回调上下文。 */
@Data
public class CoreTubeTaskContext {
/** 现场任务业务类型,用于在统一回调中区分处理分支。 */
private String bizType;
/** 任务服务的日计划。托盘移出任务可为空。 */
private Long dailyPlanId;
/** 单根任务对应的排他领取令牌。托盘任务为空。 */
private String claimToken;
/** 单根管芯实例ID。托盘任务为空。 */
private Long coreTubeId;
/** 虚拟管芯编号。 */
private String coreTubeCode;
/** 托盘号;单根任务中用于追踪管芯原托盘。 */
private String vehicleCode;
/** 起始点位记录ID。 */
private Long sourcePointId;
/** 起始点位编码。 */
private String sourcePointCode;
/** 目标点位记录ID设备逻辑点位可能为空。 */
private Long targetPointId;
/** 目标点位编码。 */
private String targetPointCode;
}

View File

@@ -3,15 +3,19 @@ package cn.code.nl.module.lms.service.coretube;
/** 管芯日计划现场任务调度服务。 */
public interface CoreTubeTaskDispatchService {
/** 执行一轮备货调度。 */
/**
* 执行一轮备货调度。
*
* <p>优先从对接位抓取一根目标管芯;全部候选都不能直接抓取时,本轮最多创建一条托盘任务。</p>
*/
String dispatchStockingOnce();
/** 执行一轮套管调度。 */
/** 从管芯库选择一根目标管芯并创建出库任务;本轮最多创建一条。 */
String dispatchSleevingOnce();
/** 处理任务完成。 */
/** 幂等处理现场任务完成,仅单根任务完成时累计对应日计划进度。 */
void handleFinished(Long taskId, boolean sleeving);
/** 处理任务取消。 */
/** 幂等处理现场任务取消,释放管芯占用及未确认的日计划 Claim。 */
void handleCancelled(Long taskId, boolean sleeving);
}

View File

@@ -32,34 +32,52 @@ import java.util.Set;
@Service
public class CoreTubeTaskDispatchServiceImpl implements CoreTubeTaskDispatchService {
/** 定时调度写入数据库的操作人标识。 */
private static final String OPERATOR = "SCHEDULER";
/** Task 模块业务防重使用的归属服务。 */
private static final String OWNER_SERVICE = "LMS";
/** 日计划候选、排他领取及进度确认服务。 */
@Resource
private DailyPlanScheduleService dailyPlanScheduleService;
/** 管芯实例数据库访问对象。 */
@Resource
private CoreTubeMapper coreTubeMapper;
/** 备货区点位及托盘格位数据库访问对象。 */
@Resource
private StockingIvtMapper stockingIvtMapper;
/** 现场运输任务远程服务。 */
@Resource
private TransportTaskApi transportTaskApi;
/** AGV 系统类型,由现场 Task/ACS 配置约定。 */
@Value("${lms.core-tube.agv-system-type:AGV}")
private String agvSystemType;
/** 缓存位到对接位的托盘移入 ACS 任务类型。 */
@Value("${lms.core-tube.pallet-in-acs-type:CORE_PALLET_IN}")
private String palletInAcsType;
/** 对接位到缓存位的托盘移出 ACS 任务类型。 */
@Value("${lms.core-tube.pallet-out-acs-type:CORE_PALLET_OUT}")
private String palletOutAcsType;
/** 对接位单根管芯抓取 ACS 任务类型。 */
@Value("${lms.core-tube.stocking-pick-acs-type:CORE_STOCKING_PICK}")
private String stockingPickAcsType;
/** 管芯库单根出库 ACS 任务类型。 */
@Value("${lms.core-tube.sleeving-out-acs-type:CORE_SLEEVING_OUT}")
private String sleevingOutAcsType;
/** 备货抓取任务的管芯库入口逻辑点位。 */
@Value("${lms.core-tube.stocking-target-point:CORE_WAREHOUSE_IN}")
private String stockingTargetPoint;
/** 管芯出库任务的套管机构入口逻辑点位。 */
@Value("${lms.core-tube.sleeving-target-point:SLEEVING_IN}")
private String sleevingTargetPoint;
/** 执行一轮备货调度。 */
/**
* 执行一轮备货调度。
*
* <p>按当前顺序/权重策略扫描不同规格,任一对接位存在目标管芯时立即创建一条单根抓取任务。
* 全部规格都不能直接抓取时,才允许创建一条托盘移出或移入任务,避免一轮产生多条托盘任务。</p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String dispatchStockingOnce() {
@@ -81,7 +99,12 @@ public class CoreTubeTaskDispatchServiceImpl implements CoreTubeTaskDispatchServ
return createPalletTask(candidates, checkedMaterials);
}
/** 执行一轮套管调度。 */
/**
* 执行一轮套管调度。
*
* <p>按策略扫描不同规格,从管芯库选择一根可用管芯创建出库任务。缺货时只记录
* “正在备货”或“备货区库存不足”,继续尝试下一规格。</p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String dispatchSleevingOnce() {
@@ -108,7 +131,12 @@ public class CoreTubeTaskDispatchServiceImpl implements CoreTubeTaskDispatchServ
return "全部待套管规格当前均无可出库管芯";
}
/** 创建单根抓取或出库任务。 */
/**
* 创建单根抓取或出库任务。
*
* <p>先领取日计划的一根额度,再原子预占管芯并创建 Task。只有 Task 创建及本地绑定都成功,
* 才标记 Claim 已创建任务并推进权重游标;任一步失败由外层事务回滚本地占用。</p>
*/
protected Long createSingleTubeTask(DailyPlanScheduleCandidate preview, CoreTubeDO tube, boolean sleeving) {
DailyPlanScheduleCandidate claim = sleeving
? dailyPlanScheduleService.claimSleevingCandidate(preview.getDailyPlanId(), OPERATOR)
@@ -141,7 +169,12 @@ public class CoreTubeTaskDispatchServiceImpl implements CoreTubeTaskDispatchServ
return taskId;
}
/** 所有规格不能直接抓取时创建一条托盘任务。 */
/**
* 所有规格都不能直接抓取时创建一条托盘任务。
*
* <p>对接位有托盘时优先移出;对接位为空时,按“可一次满足剩余量的最小托盘,
* 否则库存最多托盘”选择缓存位托盘。业务键保证同规格未完成移入任务唯一。</p>
*/
private String createPalletTask(List<DailyPlanScheduleCandidate> candidates, Set<Long> checkedMaterials) {
StockingIvtDO occupiedDock = stockingIvtMapper.selectOccupiedDockPoint();
if (occupiedDock != null) {
@@ -229,7 +262,12 @@ public class CoreTubeTaskDispatchServiceImpl implements CoreTubeTaskDispatchServ
return request;
}
/** 创建任务并处理响应不确定场景。 */
/**
* 创建任务并处理远程响应不确定场景。
*
* <p>Task 模块按 ownerService、bizType、bizId 防重。远程响应丢失时回查未完成任务,
* 避免调用方重试再创建一条相同现场任务。</p>
*/
private Long createTask(TransportTaskCreateReqDTO request, String bizType, String bizId) {
request.setBizType(bizType);
request.setBizId(bizId);
@@ -262,7 +300,12 @@ public class CoreTubeTaskDispatchServiceImpl implements CoreTubeTaskDispatchServ
|| CoreTubeTaskBizTypeEnum.CORE_TUBE_STOCKING_PICK.name().equals(task.getBizType()));
}
/** 处理任务完成。 */
/**
* 幂等处理任务完成。
*
* <p>托盘任务只迁移点位和整托盘管芯位置,不累计日计划数量;单根抓取/出库任务
* 完成后才清理管芯占用、确认 Claim并分别累计已备货或已套管数量。</p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void handleFinished(Long taskId, boolean sleeving) {
@@ -302,7 +345,11 @@ public class CoreTubeTaskDispatchServiceImpl implements CoreTubeTaskDispatchServ
}
}
/** 处理任务取消。 */
/**
* 幂等处理任务取消。
*
* <p>只有仍绑定当前 taskId 的管芯会恢复为可用,同时释放对应 Claim重复取消不会重复释放。</p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void handleCancelled(Long taskId, boolean sleeving) {

View File

@@ -41,25 +41,37 @@ import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_VERSION_
@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";
/** Claim 活动状态。 */
private static final int CLAIM_ACTIVE = 0;
/** Claim 已确认状态。 */
private static final int CLAIM_CONFIRMED = 1;
/** 日计划及进度数据库访问对象。 */
@Resource
private DailyPlanMapper dailyPlanMapper;
/** 日计划排他领取数据库访问对象。 */
@Resource
private DailyPlanClaimMapper dailyPlanClaimMapper;
/** 顺序/权重调度策略数据库访问对象。 */
@Resource
private DailyPlanStrategyMapper dailyPlanStrategyMapper;
/** 日计划操作日志数据库访问对象。 */
@Resource
private DailyPlanOperationLogMapper dailyPlanOperationLogMapper;
/** 当前租户默认策略初始化器。 */
@Resource
private DailyPlanStrategyInitializer strategyInitializer;
/** 未创建现场任务的 Claim 默认租期,单位秒。 */
@Value("${lms.daily-plan.claim-timeout-seconds:300}")
private long claimTimeoutSeconds;
/** 兼容通用调度调用:领取当前策略下第一张备货候选计划。 */
@Override
@Transactional(rollbackFor = Exception.class)
public DailyPlanScheduleCandidate getNextStockingCandidate(String operator) {
@@ -68,6 +80,7 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService {
return candidates.isEmpty() ? null : claimStockingCandidate(candidates.get(0).getDailyPlanId(), operator);
}
/** 兼容通用调度调用:领取当前策略下第一张套管候选计划。 */
@Override
@Transactional(rollbackFor = Exception.class)
public DailyPlanScheduleCandidate getNextSleevingCandidate(String operator) {
@@ -88,7 +101,12 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService {
return previewCandidates(DailyPlanStrategyTypeEnum.SLEEVING, dailyPlanMapper.selectSleevingCandidates());
}
/** 按当前策略排列候选,但不产生领取。 */
/**
* 按当前策略排列候选,但不产生领取。
*
* <p>顺序模式从排序最小的计划开始;权重模式从策略游标开始轮转。预览阶段只决定尝试顺序,
* 不提前分配库存,也不会消耗权重。</p>
*/
private List<DailyPlanScheduleCandidate> previewCandidates(DailyPlanStrategyTypeEnum type,
List<DailyPlanDO> candidates) {
DailyPlanStrategyDO strategy = strategyInitializer.getOrCreate(type);
@@ -155,7 +173,12 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService {
return 0;
}
/** 创建单根排他领取。 */
/**
* 创建单根排他领取。
*
* <p>同一日计划的备货与套管使用不同 strategyType可并行各自领取同一种作业通过
* 活动唯一键保证同一时刻最多一个 Claim。</p>
*/
private DailyPlanScheduleCandidate claimCandidate(DailyPlanDO plan, int quantity, Integer strategyMode,
DailyPlanStrategyTypeEnum type, String operator) {
LocalDateTime expireTime = nextClaimExpireTime();
@@ -202,7 +225,11 @@ public class DailyPlanScheduleServiceImpl implements DailyPlanScheduleService {
return candidate;
}
/** 现场单根任务创建成功后推进权重轮转。 */
/**
* 现场单根任务创建成功后推进权重轮转。
*
* <p>AGV 托盘任务和候选跳过都不会调用本方法,因此不会提前消耗某张计划的权重。</p>
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void confirmTaskCreated(String claimToken, String operator) {

View File

@@ -19,13 +19,22 @@ import static cn.code.nl.module.lms.enums.ErrorCodeConstants.DAILY_PLAN_STRATEGY
@Component
public class DailyPlanStrategyInitializer {
/** 系统调度操作人标识。 */
private static final String SYSTEM_OPERATOR = "SCHEDULER";
/** 日计划策略数据库访问对象。 */
@Resource
private DailyPlanStrategyMapper strategyMapper;
/** 用于建立独立事务,隔离并发初始化产生的唯一键冲突。 */
@Resource
private PlatformTransactionManager transactionManager;
/**
* 获取当前租户、指定作业类型的策略;不存在时初始化为顺序模式。
*
* <p>初始化始终使用独立事务。并发插入冲突后,必须等冲突事务回滚,再使用全新的事务读取
* 另一线程已经提交的策略,避免 MySQL 可重复读快照导致误判策略仍不存在。</p>
*/
public DailyPlanStrategyDO getOrCreate(DailyPlanStrategyTypeEnum type) {
TransactionTemplate transaction = new TransactionTemplate(transactionManager);
transaction.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
@@ -42,6 +51,7 @@ public class DailyPlanStrategyInitializer {
}
}
/** 在独立事务内查询或创建默认策略。 */
private DailyPlanStrategyDO loadOrInsert(DailyPlanStrategyTypeEnum type) {
DailyPlanStrategyDO current = strategyMapper.selectByStrategyType(type.getCode());
if (current != null) {