Files
orbit/sdk/go/recovery.go

144 lines
6.2 KiB
Go
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.
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
}