news 2026/10/5 2:11:01

Elsa User Tasks 数据模型深度解析:身份中立的人类任务聚合、生命周期与持久化设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Elsa User Tasks 数据模型深度解析:身份中立的人类任务聚合、生命周期与持久化设计
  • 后端
  • 工作流自动化
  • 流程编排
  • 低代码

【免费下载链接】elsa-core

The Workflow Engine for .NET

项目地址:https://gitcode.com/gh_mirrors/el/elsa-core
点击查看免费下载

User Tasks(013-user-tasks)是 Elsa Workflow Engine 中面向"人机协作"的模块:工作流在人类决策处挂起,通过安全的任务队列暴露工作项,并以类型化结果恢复执行。本文以>ParticipantReference ├── TenantId required, storage scope ├── Provider required namespace/issuer ├── Type user | group ├── Id required external identifier └── DisplayName optional non-authoritative snapshot

默认的 claims 适配器(DefaultClaimsIdentityResolver,Services/DefaultClaimsIdentityResolver.cs)把已认证主体映射为 user 引用和 group 引用;宿主可以替换该适配器,也可使用任意外部目录或授权策略。Elsa.Identity 不是必需依赖,User Tasks 没有任何表与 Elsa.Identity 表建立外键。单元测试ClaimsResolver_PreservesExternalGroupClaimValues(test/unit/Elsa.UserTasks.UnitTests/UserTaskTests.cs)验证了带分隔符的外部组声明值(如group,with;delimiters)会被原样保留。

Live 成员关系

Live 任务持久化组引用,在列表/操作时评估调用者当前的带命名空间 claims。目录查找可用于丰富名称或报告健康,但目录不可用不会使精确的权威 claim 匹配失效——这与 spec 中"authorization must work with claims alone"的决策一致。

Snapshot 成员关系

激活时,参与者目录枚举每个配置的组,并把生成的用户引用与原始组引用一并持久化。之后组织结构变化不影响资格。若枚举不可用或失败,任务仍然持久化,但变为manager-only并产生一个阻塞性健康问题。源码中对应的 health code 有snapshot-resolution-failed与snapshot-directory-missing(DefaultUserTaskManager.cs)。

排除(Exclusions)

排除项按规范化引用在认领、分配和访客验证时检查。被排除的参与者不能认领或接收任务,除非活动允许经理覆盖且经理提供写入审计事件的理由。源码中AssignAsync会先检查ExcludedUsers.Any(x => x.Matches(request.Assignee)),若被排除且(不允许覆盖或缺少理由)则返回excluded-assignee冲突。

Actions 与 Forms

Actions

每个 action 具有:

  • Key:字面量、不可变的工作流结果键;
  • Label:物化的显示值,其来源可支持表达式;
  • 可选的安全展示元数据。

Action key 在任务内唯一。Timeout与Cancelled是保留键,设计器不能配置。这一点由UserTaskDefinitionSnapshot.Normalize()强制(见 UserTaskModels.cs);同时活动 UserTask.cs 中的ActionDefinitions输入将DefaultSyntax与SupportedSyntaxes限定为Literal,防止表达式改变已激活任务的契约。没有启用表单的任务只接受所选配置 action,不接受任意完成 JSON——CompleteAsync中RequestedForm == null && Data != null会返回form-required失败。

Forms

表单引用包含ProviderName、Key以及请求的绑定/版本信息。激活时解析并钉住具体的 provider 版本;开放中的任务绝不跟随之后的"latest"表单版本。安装的 provider 针对钉住的版本和所选 action 校验并归一化提交数据。

若解析或钉住失败,任务保持持久化但变为 manager-only 并带阻塞性健康问题(health code 包括form-provider-missing、form-resolution-failed)。Repair(RetryResolutionAsync)重试原始引用;经理不能临时替换活跃任务上的表单引用。表单 provider 契约定义于 UserTaskContracts.cs 的IUserTaskFormProvider(ResolveAsync+ValidateAndNormalizeAsync)。

生命周期

状态机

状态含义工作可见性
Unassigned无直接 assignee、候选者或邀请可用Manager-only;绝不成为开放的"everyone"队列
Available至少一个候选者或邀请可认领合格候选者可见安全摘要
Assigned一个参与者负责Assignee 可见受保护内容;经理在租户范围内可见
Completing有效的完成操作赢得竞争,bookmark 恢复挂起中不再接受第二个终结 action
TimingOut超时操作赢得竞争,保留Timeout恢复挂起中不接受任何工作操作
Cancelling经理取消操作赢得竞争,保留Cancelled恢复挂起中不接受任何工作操作
Completedbookmark 恢复以配置 action 提交终结
TimedOutbookmark 恢复以保留Timeout提交终结
Cancelled任务被其工作流/活动取消,或经启用的经理操作取消终结

这九个状态与枚举UserTaskStatus(UserTaskModels.cs)完全一致,IsTerminal帮助方法仅把后三者视为终结。

转换规则

触发转换与规则
带直接 assignee 的活动激活创建Assigned
带候选者/邀请的活动激活创建Available
无任何分配的激活创建Unassigned并产生警告/健康问题
合格认领原子Available → Assigned;仅一个赢家
释放(release)Assigned → Available,或当无合格候选者/邀请时 →Unassigned;受保护访问立即终止
经理分配/再分配任意开放任务 →Assigned;经理完成仍需先分配给责任人
认证完成Assigned → Completing;入队专用 User Task bookmark 恢复
启用超时的到期处理开放状态 →TimingOut;经保留Timeout恢复;终结为TimedOut
未启用超时的到期处理保持开放、置IsOverdue、发布一条幂等逾期通知
启用的经理取消开放状态 →Cancelling;经保留Cancelled恢复;终结为Cancelled。必须提供理由
工作流/活动 bookmark 移除存在匹配的挂起完成时终结为Completed;否则终结为Cancelled。绝不独立恢复工作流

完成、超时和取消共用一条乐观转换路径:第一个提交的转换获胜,后续请求收到冲突。访客验证是特殊的认领操作:第一个有效邀请原子认领任务并撤销兄弟邀请;访客不能释放任务。

源码佐证:DefaultUserTaskManager中ClaimAsync仅接受Available/Unassigned,ReleaseAsync清空 assignee 并按剩余候选者决定回到Available或Unassigned;TimeoutAsync明确注释"reserved outcomes are system transitions and must not be callable as worker-selected actions"。bookmark 移除的终结逻辑在DefaultUserTaskProjectionService.FinalizeBookmarkRemovalAsync(Services/DefaultUserTaskProjectionService.cs)中:Completing → Completed、TimingOut → TimedOut、Cancelling → Cancelled、其余开放状态 →Cancelled,并且任务终结时会调用guestSessions.RevokeForTaskAsync立即吊销所有访客会话。

激活后可变性

只有Priority 和 DueAt可通过受治理的运行时更新路径修改。分配变更使用专门的 claim/release/assign 操作。Title、Summary、Instructions、受保护任务数据、Actions、Exclusions、成员模式、钉住的表单在活跃任务上不可变。对应UpdateSchedulingAsync只处理Priority与DueAt的更新请求(UserTaskSchedulingUpdate)。

Operations 与幂等性

一个 operation 以(TenantId, TaskId, OperationId)为作用域,存储规范化请求哈希、期望修订号、操作种类、状态与时间戳(见UserTaskOperationrecord)。

  • 每个需要竞态安全结果的变更都提供ExpectedRevision;
  • 重复的 operation ID 与请求哈希匹配时,返回既有结果/状态,不重复执行命令;
  • 复用 operation ID 但内容不同 →冲突(409);
  • 完成操作只把规范化完成数据存储在受保护的任务/操作存储中,绝不放进事件或通知载荷;
  • 陈旧的Completing/TimingOut/Cancelling操作由 reconciliation 按其持久化状态重试或完成,绝不静默丢弃。

请求哈希由SHA256(kind + ":" + JsonSerializer.Serialize(value))计算(DefaultUserTaskManager.Hash)。在CompleteAsync中,哈希的是 provider 归一化后的规范数据,因此网络重试比较的是同一请求,而非归一化前的原始形状。单元测试Repository_CursorCoversSupportedSortsAndDirections与Repository_CursorIsStableAndTotalCountIgnoresCursor(UserTaskTests.cs)印证了列表查询在多种排序下的游标稳定性。

事件与健康

UserTaskEvent是追加式(append-only)、租户作用域、按任务修订排序的审计流。事件包含事件种类、已知时的参与者 actor 引用、时间戳、operation ID、安全理由与安全元数据;绝不包含受保护的 instructions、任务数据、表单数据、完成数据、邀请密钥或 provider 私有载荷。UserTaskEventSummary作为安全审计投影,不披露 actor 标识符与含输入的 reason。

至少记录以下事件:Created、Claimed、Released、Assigned、Reassigned、CompletionRequested、Completed、TimedOut、Cancelled、OverdueNotified、InvitationIssued、InvitationVerified、InvitationRevoked,以及失败的受特权操作。源码中UserTaskEvent的EventType字段对应这些种类,DefaultUserTaskManager的各变更路径都会追加对应事件。

健康问题与生命周期状态分离:它们标识阻塞性或建议性问题,如表单解析失败、快照枚举失败、未知参与者显示数据、邀请投递失败。健康数据对经理与诊断安全,不能泄露令牌或受保护载荷。对应UserTaskHealthSeverity(Advisory/Blocking)、HealthCode、HealthMessage字段与UserTaskHealthChanged通知。

可见性与保留不变量

  • 候选者只收到安全摘要;受保护内容只披露给 assignee 或经理;
  • 释放任务立即撤销受保护访问;
  • 已完成任务受保护数据对完成者与经理可见;前 assignee 只保留摘要与安全审计可见性;
  • Requester 仅凭 requester 身份不获得任何访问;
  • 不可访问的任务 ID 与不存在的 ID 无法区分(API 边界返回404);
  • 所有读写都是租户作用域,且必须同时通过模块权限与任务关系/策略检查;
  • 终结任务默认无限期保留;可选的 purge 只删除终结任务及其事件。开放任务及其操作记录永不 purge;
  • 过期邀请、已吊销访客会话与已投递的邀请 outbox 条目可使用独立的短保留设置,但清理不得影响任务聚合或审计历史。

这些不变量在 UserTaskContracts.cs 的IUserTaskAccessPolicy与IUserTaskRepository中有完整的实现契约:CreateScopeAsync返回null即拒绝("null is a denial, never a no-filter"),SaveAsync以乐观并发修订冲突抛UserTaskRevisionConflictException,AppendEventAsync不消耗聚合修订号以免把读取变成伪冲突。REST 端点层(Endpoints/UserTasksEndpoints.cs)将领域冲突码统一映射为 HTTP 语义:终结命令先返回202 Accepted(工作流在带外恢复),409表示竞态或分歧的 operation 复用,422表示领域校验失败。

实战速览:从活动到 REST 的完整闭环

UserTask活动(Activities/UserTask.cs)是工作流侧的入口:它声明所有输入(Title、Summary、Reference、Tags、TaskType、Requester、Assignee、CandidateUsers/CandidateGroups、ExcludedUsers、MembershipResolutionMode、AllowManagerExclusionOverride、Priority、DueAt、Instructions、TaskData、Form、ActionDefinitions、Invitations、EnableTimeoutOutcome、EnableCancellationOutcome),在ExecuteAsync中创建 bookmark,把UserTaskMaterialization作为 stimulus,并把生成的TaskId输出;ResumeAsync从工作流输入读取UserTaskStimulus,把UserTaskResult(ActionKey, Data, CompletedBy, CompletedAt)写入活动结果并完成活动。

权限侧,模块声明两个资源(Permissions/UserTasksResourcePermissions.cs):user-tasks(verbs:view、update、claim、complete、assign、cancel、invite、supervise)与user-tasks/participants(view)。supervise是独立的高阶动词,不隐含其他任何动词。

一次完整闭环(详见 quickstart.md):

  1. 宿主启用模块:services.AddElsa(elsa => elsa.UseUserTasks());,默认使用内存仓库;生产环境选择 SQLite / SQL Server / PostgreSQL / MySQL / Oracle 的 EF Core 持久化包(沿用Elsa.Secrets的拆分模式);
  2. 执行工作流后,以 worker token 查询GET /user-tasks?scope=available&taskType=invoice-approval&limit=20&includeTotalCount=true,得到仅含安全字段的摘要行(dataAccess: "summary"、allowedActions: ["claim"]);
  3. POST /user-tasks/{id}/claim携带{"expectedRevision": 1}原子认领;
  4. 认领后GET /user-tasks/{id}返回钉住的表单引用、Instructions、TaskData 与审计时间线;
  5. POST /user-tasks/{id}/complete携带expectedRevision、operationId、actionKey与表单数据,返回202;轮询直至Completed;重复同一operationId幂等,换内容则409;
  6. 经理可POST /user-tasks/{id}/assign(含 reason)、POST /user-tasks/{id}/release、查询scope=all与GET /user-tasks/{id}/events。

持久化与测试验证

模块的持久化契约(IUserTaskRepository)覆盖:按租户/任务 ID 获取、游标分页查询、按MaterializationKey与BookmarkId查找、按邀请令牌哈希查找(匿名调用方只出示密钥,绝不信任调用方自报的租户/任务 ID)、乐观并发保存与追加审计事件。测试侧,test/unit/Elsa.UserTasks.Persistence.ConformanceTests 提供仓库一致性、故障注入、访客会话与邀请 outbox 的一致性测试基类;test/unit/Elsa.UserTasks.UnitTests/UserTaskTests.cs 验证 claims 解析、游标稳定性、全排序方向覆盖等关键行为;test/unit/Elsa.UserTasks.Persistence.EFCore.UnitTests 验证 EF Core 仓库实现与内存实现对等。

至此,从领域词汇、聚合边界、九态生命周期、幂等操作、追加式审计到安全披露规则,User Tasks 的完整数据模型已经与源码一一对应。无论是嵌入宿主现有身份系统、构建候选队列,还是通过访客邀请打通外部审批场景,这套模型都提供了明确、可验证的实现边界。

  • 后端
  • 工作流自动化
  • 流程编排
  • 低代码

【免费下载链接】elsa-core

The Workflow Engine for .NET

项目地址:https://gitcode.com/gh_mirrors/el/elsa-core
点击查看免费下载

相关推荐

上一篇:使用 fq 的 pg_heap 解码器深入分析 PostgreSQL 堆表文件(页面、页头与元组)
下一篇:Agenda 6.x 完整指南:可插拔存储后端的 Node.js 轻量级任务调度框架

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 2:05:35

AI Agent 面试题 155:LoRA和QLoRA微调技术在Agent场景中的应用实践

🔥 AI Agent 面试题 155:LoRA和QLoRA微调技术在Agent场景中的应用实践摘要:本文深入解析了「LoRA和QLoRA微调技术在Agent场景中的应用实践」这一 AI Agent 领域的核心面试题。文章从 模型微调与适配 的基本概念出发,系统性地剖析了…

作者头像 李华
网站建设 2026/10/5 2:05:21

AI Agent 面试题 140:Agent架构中的消息队列应用场景和选型

🔥 AI Agent 面试题 140:Agent架构中的消息队列应用场景和选型摘要:本文深入解析了「Agent架构中的消息队列应用场景和选型」这一 AI Agent 领域的核心面试题。文章从 混合架构模式 的基本概念出发,系统性地剖析了 消息队列、选型…

作者头像 李华
网站建设 2026/10/5 2:05:17

青少年软编等考七级题解目录

这个专栏发布中国电子学会主办的青少年软件编程等级考试 C 语言七级题目解析,每篇文章包含一次考试完整题目的思路解析。由于考级允许使用 C/C 语言,因此解析中给出的参考代码均为 C 代码。为了方便大家查找,特此发布一篇文章作为目录。 所有…

作者头像 李华
网站建设 2026/10/5 1:57:41

终极指南:如何使用Ludusavi轻松备份和恢复你的PC游戏存档

终极指南:如何使用Ludusavi轻松备份和恢复你的PC游戏存档 【免费下载链接】ludusavi Backup tool for PC game saves 项目地址: https://gitcode.com/GitHub_Trending/lu/ludusavi 想要保护你的游戏进度不丢失吗?Ludusavi是你的完美解决方案&…

作者头像 李华