Files
orbit/docs/api/task-source.md

188 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 任务直接来源查询
状态:已实现普通任务、人工重试、计划和流程任务来源的服务端、独立SDK及示例。接口只读,使用已有模型和索引,不增加数据库外键或迁移。证据见[P3报告](../reports/phase-3-task-source.md)和[流程来源报告](../reports/phase-5-task-source.md)。
## 1. 请求与权限
`GET /v1/tasks/{task_id}/source`
请求头:`Authorization: Bearer <token>`。不需要请求体、Content-Type或Idempotency-Key,不接受任何查询参数。task_id为32位小写十六进制ID;认证决定命名空间,URL不能覆盖它。
权限为`tasks:read`及任务所属队列范围。每次请求验证当前凭据;不存在、跨命名空间或不在队列范围的任务均返回404。只获得来源引用,不因此获得计划/流程定义、其他步骤、管理或执行权限。读取完整计划及固定版本仍需`schedules:read`;读取流程需`workflows:read`及全部固定队列范围,读取流程定义需`workflows:definitions:read`。只拥有一个队列任务权限时,仍能读取该任务的有限流程来源。
```sh
curl "$ORBIT_URL/v1/tasks/$TASK_ID/source" \
-H "Authorization: Bearer $ORBIT_TOKEN"
```
## 2. 响应字段
HTTP 200,使用`request_id`和`data`信封;X-Request-ID与正文一致。下表字段均位于data。
| 字段 | 类型 / 必填 | 含义 |
| --- | --- | --- |
| task_id | string / 是 | 已授权查询的任务ID |
| kind | string / 是 | DIRECT、MANUAL_RETRY、SCHEDULE或WORKFLOW |
| retry_of | string / 仅MANUAL_RETRY | 同命名空间、同队列的直接原任务ID |
| schedule | object / 仅SCHEDULE | 触发该任务的固定关联,字段如下 |
| schedule.schedule_id | string / 是 | 来源计划ID |
| schedule.schedule_version | 十进制字符串 / 是 | 实际触发时的非零uint64版本,发布新版本不改写它 |
| schedule.occurrence_id | string / 是 | 唯一关联该任务的触发记录ID |
| schedule.scheduled_at | UTC RFC3339 / 是 | 原计划时间,最多微秒精度,与实际受理/执行时间分开 |
| schedule.decision | string / 是 | FIRED单点触发或COALESCED合并触发 |
| schedule.covered_until | UTC RFC3339 / 仅COALESCED | 合并范围的排除上界,严格晚于scheduled_at |
| workflow | object / 仅WORKFLOW | 此任务创建时的流程来源,字段见第5节 |
DIRECT表示普通任务受理,包括单项和批量受理;任务内部的自动安全重试仍属于同一个任务,不改变来源。MANUAL_RETRY表示[人工重试](task-recovery.md)创建的新任务,只返回直接父任务,不递归遍历整条重试链。父任务继续保留原历史。
SCHEDULE中的版本来自触发记录,不能用计划最新版本替换。COALESCED对应区间`[scheduled_at, covered_until)`;合并范围内由计划定义确定原始时间点,不承诺每个自然时间单位都有一次触发。SKIPPED未创建任务,因此不会成为任务来源。
普通受理的完整响应:
```json
{
"request_id":"44444444444444444444444444444444",
"data":{
"task_id":"11111111111111111111111111111111",
"kind":"DIRECT"
}
}
```
人工重试的完整响应:
```json
{
"request_id":"44444444444444444444444444444444",
"data":{
"task_id":"11111111111111111111111111111111",
"kind":"MANUAL_RETRY",
"retry_of":"22222222222222222222222222222222"
}
}
```
计划合并触发的完整响应:
```json
{
"request_id":"44444444444444444444444444444444",
"data":{
"task_id":"11111111111111111111111111111111",
"kind":"SCHEDULE",
"schedule":{
"schedule_id":"22222222222222222222222222222222",
"schedule_version":"1",
"occurrence_id":"33333333333333333333333333333333",
"scheduled_at":"2026-09-17T00:00:00Z",
"decision":"COALESCED",
"covered_until":"2026-09-17T00:03:00.000001Z"
}
}
}
```
单点触发返回decision=FIRED并省略covered_until。所有不适用的可选字段省略,不用null占位。来源不包含任务payload/result、计划模板、策略、凭据或执行租约秘密。任务当前状态另用GetTask读取。
## 3. 关联校验与错误
平台采用无外键设计,本接口在认证事务内显式核对命名空间、任务、队列、计划或流程、不可变版本和创建记录。人工重试父任务须为同队列中已失败/取消的独立任务。流程来源核验实例门控、版本摘要与冻结策略、固定步骤处理器契约、任务与激活或轮次的双向关系;恢复父任务还须属于同一步骤的前一激活,POLL须为同一业务轮。
无所有者但存在触发/激活/轮次反向记录、同时关联不同来源种类、悬空父任务、缺失版本、错误队列或未知所有者均拒绝,不能把关联损坏降级成DIRECT。查询沿现有唯一索引读取当前任务及至多一个直接父任务,不递归遍历恢复历史,不锁运行器父资源,也不进入派发路径。
| HTTP / code | 处理 |
| --- | --- |
| 400 INVALID_ARGUMENT | 检查task_id;删除不受支持、重复或格式错误的查询参数 |
| 401 UNAUTHENTICATED | 检查凭据、过期、撤销及应用/命名空间状态 |
| 403 PERMISSION_DENIED | 需要tasks:read动作权限 |
| 404 NOT_FOUND | 任务不存在或不在当前命名空间/队列范围;旧版本节点也可能没有本路由 |
| 503 UNAVAILABLE | 数据库或来源关联不能可靠确认;不返回部分来源,不推测DIRECT |
读取不改变任务状态、版本、额度、尝试或审计。网络错误可以在有限预算内重新读取;SDK不自动重试。持续503应排查数据关联和服务日志,不能通过删除来源字段“修复”,也不能根据来源查询失败重新提交任务。
## 4. SDK调用与场景
```go
func (c *Client) GetTaskSource(ctx context.Context, taskID string) (TaskSource, error)
```
下方为已有客户端与taskID的调用片段;完整初始化、错误处理和可运行程序见后面的示例。
```go
source, err := client.GetTaskSource(ctx, taskID)
if err != nil { return err }
switch source.Kind {
case "DIRECT":
fmt.Println("普通受理", source.TaskID)
case "MANUAL_RETRY":
fmt.Println("直接原任务", *source.RetryOf)
case "SCHEDULE":
fmt.Println("来源计划", source.Schedule.ScheduleID,
"固定版本", source.Schedule.ScheduleVersion,
"触发记录", source.Schedule.OccurrenceID)
case "WORKFLOW":
fmt.Println("所属流程", source.Workflow.WorkflowID,
"步骤", source.Workflow.StepKey,
"激活代次", source.Workflow.ActivationSequence,
"业务查询轮次", source.Workflow.PollRound)
}
```
SDK的ScheduleVersion为uint64,JSON仍为字符串;时间使用time.Time。SDK验证响应task_id一致性、来源种类与字段组合、ID、版本及时间范围;未知kind或矛盾数据返回错误,不返回可误用的部分来源。协议新增来源种类时,接入方需升级SDK再使用。
| 场景 | 完整源码 | 运行说明与新增输出 |
| --- | --- | --- |
| 单项受理、取消后读取来源 | [tasks](../../sdk/go/examples/tasks/main.go) | [SDK初始化](../sdk/README.md),source_kind=DIRECT |
| 按计划找到触发任务,再反查来源 | [schedules](../../sdk/go/examples/schedules/main.go) | [计划场景](schedules.md#6-sdk-与完整示例),source_kind=SCHEDULE、fixed_version=1 |
| 人工核查后创建关联新任务 | [recovery](../../sdk/go/examples/recovery/main.go) | [恢复场景](../examples/recovery.md),source_kind=MANUAL_RETRY、parent=原任务ID |
| 并行流程完成后核对每项任务来源 | [workflow](../../sdk/go/examples/workflow/main.go) | [流程场景](../examples/workflow.md),workflow_sources_verified=3、fixed_version=v1 |
上述示例均运行在隔离回归中;新发布计划/流程版本后老任务仍指向原版本,人工恢复后旧激活仍可追溯。WORKFLOW是新增来源种类,旧SDK可能按未知种类返回错误;应升级SDK,不能降级为DIRECT。不更改Task/TaskSummary或Worker消息结构,不增加权限配置;新客户端调用旧节点的404/503同样不能理解为DIRECT。
## 5. 流程固定来源
以下字段位于`data.workflow`,只适用于TASK和POLL产生的业务任务:
| 字段 | 类型 / 必填 | 含义 |
| --- | --- | --- |
| workflow_id | string / 是 | 所属流程实例 |
| definition_id | string / 是 | 稳定定义身份 |
| definition_version_id | string / 是 | 实例固定的版本记录身份 |
| version | string / 是 | 固定发布版本名,不解析最新版本 |
| step_id / step_key | string / 是 | 逻辑步骤身份及固定名称 |
| step_kind | string / 是 | TASK或POLL |
| activation_id | string / 是 | 创建此任务的历史激活身份 |
| activation_sequence | uint32 / 是 | 步骤激活代次,从1开始;并非当前激活代次 |
| poll_round | uint32 / 仅POLL | 首次查询开始累计的业务轮编号;人工恢复同轮不重置 |
| poll_round_id | string / 仅POLL | 唯一关联该任务的轮次记录身份 |
| retry_of | string / 恢复任务可有 | 同步骤、前一激活的直接旧任务;POLL还要求同一业务轮 |
TASK第二代及之后必有workflow.retry_of;POLL只在恢复激活的首轮任务上带此关联,后续正常业务轮没有retry_of。顶层retry_of仍只用于MANUAL_RETRY;流程恢复任务的kind一直是WORKFLOW。自动尝试重试不创建新任务,也不改变来源。
第二代激活重查原第2业务轮的完整响应:
```json
{
"request_id":"44444444444444444444444444444444",
"data":{
"task_id":"11111111111111111111111111111111",
"kind":"WORKFLOW",
"workflow":{
"workflow_id":"22222222222222222222222222222222",
"definition_id":"33333333333333333333333333333333",
"definition_version_id":"55555555555555555555555555555555",
"version":"v1",
"step_id":"66666666666666666666666666666666",
"step_key":"query","step_kind":"POLL",
"activation_id":"77777777777777777777777777777777",
"activation_sequence":2,
"poll_round":2,
"poll_round_id":"88888888888888888888888888888888",
"retry_of":"99999999999999999999999999999999"
}
}
}
```
流程暂停、结束、发布新版本或后续人工恢复,都不会把旧任务的来源改指向新激活。要取得当前步骤需另调[GetWorkflowStep](workflows.md),不要用来源中的历史代次向当前等待发送信号。完整激活/轮次历史分页仍按开发计划推进,本接口一次只查一个任务的直接来源。