- 后端
- 工作流自动化
- 流程编排
- 低代码
【免费下载链接】elsa-core
The Workflow Engine for .NET
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恢复挂起中 | 不接受任何工作操作 |
Completed | bookmark 恢复以配置 action 提交 | 终结 |
TimedOut | bookmark 恢复以保留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):
- 宿主启用模块:
services.AddElsa(elsa => elsa.UseUserTasks());,默认使用内存仓库;生产环境选择 SQLite / SQL Server / PostgreSQL / MySQL / Oracle 的 EF Core 持久化包(沿用Elsa.Secrets的拆分模式); - 执行工作流后,以 worker token 查询
GET /user-tasks?scope=available&taskType=invoice-approval&limit=20&includeTotalCount=true,得到仅含安全字段的摘要行(dataAccess: "summary"、allowedActions: ["claim"]); POST /user-tasks/{id}/claim携带{"expectedRevision": 1}原子认领;- 认领后
GET /user-tasks/{id}返回钉住的表单引用、Instructions、TaskData 与审计时间线; POST /user-tasks/{id}/complete携带expectedRevision、operationId、actionKey与表单数据,返回202;轮询直至Completed;重复同一operationId幂等,换内容则409;- 经理可
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
相关推荐
猫抓 Cat-Catch:一键安装 3 种方式,网页视频下载 + M3U8 合并保姆级教程
猫抓 Cat Catch:一键安装 3 种方式,网页视频下载 + M3U8 合并保姆级教程 猫抓(cat catch)是一款免费的浏览器视频嗅探工具,专门做网页
后端工作流自动化流程编排低代码Elsa User Tasks:构建身份无关的持久化人工任务收件箱
Elsa User Tasks:构建身份无关的持久化人工任务收件箱 导读 本文深入解析 Elsa(The Workflow Engine for .NET)的
后端工作流自动化流程编排低代码Elsa Core User Tasks:身份中立的人工任务模块交付全景与源码级验证指南
Elsa Core User Tasks:身份中立的人工任务模块交付全景与源码级验证指南 Elsa Core 的 User Tasks(用户任务)特性为 .NE
后端工作流自动化流程编排低代码
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考