package com.yunhai.flowcenter.entity; import lombok.Getter; import lombok.Setter; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Index; import javax.persistence.Table; import javax.persistence.Version; import javax.persistence.UniqueConstraint; import java.time.LocalDateTime; /** * 待推进意图(2026-10-25 T05):任务办理事务内持久化"这个节点该继续推进了"的事实, * 事务外 worker 领取执行——办理事务提交成功 ⇔ 推进意图已持久化,进程崩溃/重启后 * 扫描续跑(旧链路 AFTER_COMMIT 三次内存重试,耗尽只 log.error,重启即丢)。 * *
D09:每个激活批次记录精确任务成员,未办理时以 DONE 等待;办理同事务串行刷新 * 本批次的末笔任务及 PENDING 工作。worker 按 id+revision 锁定当前读,推进与结算同事务; * NULL 代次只允许明确接棒或留待有证据的人工回填,不作为本代放行。
*/ @Getter @Setter @Entity @Table(name = "flw_advance_intent", uniqueConstraints = @UniqueConstraint(name = "uk_flw_advance_activation", columnNames = "activation_task_id"), indexes = { @Index(name = "idx_flw_advance_intent_due", columnList = "status, next_retry_at"), @Index(name = "idx_flw_advance_intent_instance", columnList = "instance_id")}) public class FlwAdvanceIntent extends BaseEntity { public static final String STATE_PENDING = "PENDING"; public static final String STATE_DONE = "DONE"; public static final String STATE_EXHAUSTED = "EXHAUSTED"; @Column(name = "instance_id", nullable = false) private Long instanceId; /** 触发推进的节点(TASK_HANDLED 的 nodeId) */ @Column(name = "node_id", nullable = false, length = 64) private String nodeId; /** First task CREATED in this activation batch; never the last task to finish. NULL = unanchored legacy work. */ @Column(name = "activation_task_id") private Long activationTaskId; /** Exact membership, including transfers; comma-delimited positive task IDs, e.g. ,12,14,. */ @Column(name = "activation_task_ids", columnDefinition = "LONGTEXT") private String activationTaskIds; @Column(name = "handled_task_ids", columnDefinition = "LONGTEXT") private String handledTaskIds; @Column(name = "trigger_task_id") private Long triggerTaskId; @Column(name = "activation_def_id") private Long activationDefId; @Column(name = "activation_def_version") private Integer activationDefVersion; @Column(name = "activation_apply_round") private Integer activationApplyRound; /** A detached worker may act/defer only the revision it actually observed. */ @Version @Column(name = "revision", nullable = false) private Long revision = 0L; public boolean containsTask(Long id) { return contains(activationTaskIds, id); } public boolean handledTask(Long id) { return contains(handledTaskIds, id); } public void addTask(Long id) { if (!containsTask(id)) activationTaskIds = append(activationTaskIds, id); } public void addHandledTask(Long id) { if (!handledTask(id)) handledTaskIds = append(handledTaskIds, id); } private static boolean contains(String ids, Long id) { return ids != null && id != null && ids.contains("," + id + ","); } private static String append(String ids, Long id) { if (id == null || id <= 0) throw new IllegalArgumentException("Missing activation task identity"); return (ids == null ? "," : ids) + id + ","; } /** 出边名(可空=引擎默认路由) */ @Column(name = "edge_name", length = 128) private String edgeName; /** 办理人随行(推进链操作人信封,可空) */ @Column(name = "assignee_id") private Integer assigneeId; @Column(name = "assignee_name", length = 64) private String assigneeName; /** PENDING=待执行;DONE=已执行(含推进判定为无需推进的幂等结论);EXHAUSTED=重试耗尽留人工 */ @Column(name = "status", nullable = false, length = 16) private String status = STATE_PENDING; @Column(name = "attempts", nullable = false) private Integer attempts = 0; /** 退避到期时间(耗尽前按指数退避续扫) */ @Column(name = "next_retry_at") private LocalDateTime nextRetryAt; @Column(name = "last_error", length = 255) private String lastError; }