release: FL 2.0 候选(C01 记账边界+P2-2 认领协议+D10 变量契约+D11 资格终审+D09 激活代次+T01/T03/T04/T05)

按《FL最终代码审计与上线阻断清单》194 项已审计工作树形成候选,基座 = origin/main 11e0778。

- 源码/测试/资源:沿用已审计工作树内容(96 项相对远端为新增或更新),含未跟踪实现
  CallbackFactStore、CallbackSourcePolicy、VariableContract、FormalApplyCommandService、
  FlowActorContextGate 等;C01 续验夹具修正一并入内。
- CI:保留远端 11e0778 的门禁强化(CI_SOURCE_SHA 核对、容器回收验证、空 schema 反向门禁、
  main ref 才推镜像);ci/flow-test-schema.sql 按当前实体重新生成(14→20 表,补 6 新表与
  6 新列,回调日志复合唯一键 uk_flw_log_event_reg),workflow 表数断言同步 14→20。
- 依据:docs/tasks/evidence/FL-final-audit-20261027(194 项哈希/版本核对)。
This commit is contained in:
chenxing
2026-10-27 12:05:08 +08:00
parent 11e077867b
commit 500a9a2fcb
99 changed files with 7973 additions and 633 deletions
@@ -0,0 +1,107 @@
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,重启即丢)。
*
* <p>D09:每个激活批次记录精确任务成员,未办理时以 DONE 等待;办理同事务串行刷新
* 本批次的末笔任务及 PENDING 工作。worker 按 id+revision 锁定当前读,推进与结算同事务;
* NULL 代次只允许明确接棒或留待有证据的人工回填,不作为本代放行。</p>
*/
@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;
}