144 lines
6.2 KiB
Go
144 lines
6.2 KiB
Go
package orbit
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"errors"
|
||
"time"
|
||
)
|
||
|
||
// ReviewTaskRequest 人工核查未知效果;声明与证据由获授权的核查主体负责。
|
||
type ReviewTaskRequest struct {
|
||
// ExpectedRevision 当前待核查任务版本,非零;重报保留原版本。
|
||
ExpectedRevision uint64 `json:"expected_revision,string"`
|
||
// Reason 核查原因与结论说明,最多1024个Unicode字符。
|
||
Reason string `json:"reason"`
|
||
// EvidenceRef 受控证据引用,1至512个无空白/控制字符的Unicode字符;平台不请求该地址。
|
||
EvidenceRef string `json:"evidence_ref"`
|
||
// ExecutionSettled 必须显式为true,声明已核实全部历史执行已结束且不会继续产生效果。
|
||
ExecutionSettled bool `json:"execution_settled"`
|
||
// Outcome 核查结论,仅SUCCEEDED或FAILED;不能直接重开旧任务。
|
||
Outcome TaskState `json:"outcome"`
|
||
// Result 非空JSON结论或结果引用,最大256 KiB,保留精确数值。
|
||
Result json.RawMessage `json:"result"`
|
||
}
|
||
|
||
type ReviewTaskResult struct {
|
||
// Task 核查提交时的任务快照;当前状态另行查询。
|
||
Task Task `json:"task"`
|
||
// AuditID 不可变核查审计记录标识,可通过任务操作历史读取。
|
||
AuditID string `json:"audit_id"`
|
||
// Deduplicated 是否返回原核查操作回执。
|
||
Deduplicated bool `json:"deduplicated"`
|
||
}
|
||
|
||
// RetryTaskRequest 为已确认失败/取消的独立任务建立新预算,不修改原任务历史。
|
||
type RetryTaskRequest struct {
|
||
// ExpectedRevision 原任务的当前版本,重报保持原值。
|
||
ExpectedRevision uint64 `json:"expected_revision,string"`
|
||
// Reason 人工重新执行的原因,最多1024个Unicode字符。
|
||
Reason string `json:"reason"`
|
||
// EvidenceRef 重新执行的安全依据引用,最多512个字符,不包含秘密。
|
||
EvidenceRef string `json:"evidence_ref"`
|
||
// SafeToRetry 必须显式为true,声明旧执行已结束且按原业务身份重新执行安全。
|
||
SafeToRetry bool `json:"safe_to_retry"`
|
||
// TaskTimeoutMS 新任务从受理起的总时限,必须显式指定,最多30天。
|
||
TaskTimeoutMS uint64 `json:"task_timeout_ms"`
|
||
// AvailableAt 新任务最早开始时间,空表示立即;不复用旧任务的过期绝对时间。
|
||
AvailableAt *time.Time `json:"available_at,omitempty"`
|
||
// StartDeadlineAt 新任务最晚开始时间,空表示使用新的总截止时间。
|
||
StartDeadlineAt *time.Time `json:"start_deadline_at,omitempty"`
|
||
}
|
||
|
||
type RetryTaskResult struct {
|
||
// SourceTaskID 被人工重新发起的原任务标识,其终态与尝试历史保持不变。
|
||
SourceTaskID string `json:"source_task_id"`
|
||
// Task 新任务的原始受理快照,RetryOf指回原任务。
|
||
Task Task `json:"task"`
|
||
// AuditID 原任务上的人工重试审计标识,包含新任务关联。
|
||
AuditID string `json:"audit_id"`
|
||
// Deduplicated 是否返回原人工重试回执。
|
||
Deduplicated bool `json:"deduplicated"`
|
||
}
|
||
|
||
type TaskOperationSummary struct {
|
||
// ID 不可改写的审计记录标识。
|
||
ID string `json:"id"`
|
||
// TaskID 本页授权任务标识。
|
||
TaskID string `json:"task_id"`
|
||
// OperationID 服务端管理操作身份,不是调用方原始幂等键。
|
||
OperationID string `json:"operation_id"`
|
||
// ActorID 实际认证主体标识,不接受请求体自报。
|
||
ActorID string `json:"actor_id"`
|
||
// Action 管理动作,例如task.review、task.retry、task.cancel、task.result。
|
||
Action string `json:"action"`
|
||
// CreatedAt 不可变审计创建时间,UTC微秒精度。
|
||
CreatedAt time.Time `json:"created_at"`
|
||
}
|
||
|
||
type TaskOperation struct {
|
||
TaskOperationSummary
|
||
// Reason 实际提交的操作原因。
|
||
Reason string `json:"reason"`
|
||
// EvidenceRef 操作证据引用;非核查类操作可能为空。
|
||
EvidenceRef string `json:"evidence_ref,omitempty"`
|
||
// Before 操作前的受控状态摘要,不包含完整业务输入。
|
||
Before json.RawMessage `json:"before"`
|
||
// After 操作后摘要、核查声明或关联新任务,不包含凭据秘密。
|
||
After json.RawMessage `json:"after"`
|
||
}
|
||
|
||
type TaskOperationPage struct {
|
||
// Items 按创建时间及审计ID倒序排列,不返回原因与摘要正文。
|
||
Items []TaskOperationSummary `json:"items"`
|
||
// NextCursor 必须沿相同任务继续使用,空表示本次已无下一页。
|
||
NextCursor string `json:"next_cursor,omitempty"`
|
||
}
|
||
|
||
// ReviewTask 需要显式核查权限;超时重报必须保留原键、原版本、结论与证据。
|
||
func (c *Client) ReviewTask(ctx context.Context, id, key string, in ReviewTaskRequest) (ReviewTaskResult, error) {
|
||
var out ReviewTaskResult
|
||
if !validID(id) {
|
||
return out, errors.New("invalid task id")
|
||
}
|
||
err := c.call(ctx, "POST", "/v1/tasks/"+id+":review", key, in, &out)
|
||
if err == nil && (!validID(out.AuditID) || out.Task.ID != id || out.Task.State != in.Outcome || out.Task.EffectUncertain) {
|
||
return ReviewTaskResult{}, errors.New("invalid task review receipt; reconcile using original identity")
|
||
}
|
||
return out, err
|
||
}
|
||
|
||
// RetryTask 创建与原任务关联的新任务;不会修改原任务的终态或重置原尝试预算。
|
||
func (c *Client) RetryTask(ctx context.Context, id, key string, in RetryTaskRequest) (RetryTaskResult, error) {
|
||
var out RetryTaskResult
|
||
if !validID(id) {
|
||
return out, errors.New("invalid task id")
|
||
}
|
||
err := c.call(ctx, "POST", "/v1/tasks/"+id+":retry", key, in, &out)
|
||
if err == nil && (!validID(out.AuditID) || out.SourceTaskID != id || !validID(out.Task.ID) || out.Task.ID == id || out.Task.RetryOf == nil || *out.Task.RetryOf != id) {
|
||
return RetryTaskResult{}, errors.New("invalid task retry receipt; reconcile using original identity")
|
||
}
|
||
return out, err
|
||
}
|
||
|
||
func (c *Client) ListTaskOperations(ctx context.Context, id string, in HistoryPageRequest) (TaskOperationPage, error) {
|
||
var out TaskOperationPage
|
||
if !validID(id) {
|
||
return out, errors.New("invalid task id")
|
||
}
|
||
query, err := historyPageQuery(in)
|
||
if err != nil {
|
||
return out, err
|
||
}
|
||
err = c.call(ctx, "GET", "/v1/tasks/"+id+"/operations"+query, "", nil, &out)
|
||
return out, err
|
||
}
|
||
func (c *Client) GetTaskOperation(ctx context.Context, id, operation string) (TaskOperation, error) {
|
||
var out TaskOperation
|
||
if !validID(id) || !validID(operation) {
|
||
return out, errors.New("invalid task operation id")
|
||
}
|
||
err := c.call(ctx, "GET", "/v1/tasks/"+id+"/operations/"+operation, "", nil, &out)
|
||
return out, err
|
||
}
|