Files
orbit/sdk/go/task_source.go

97 lines
5.4 KiB
Go
Raw Permalink 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"
"errors"
"time"
)
// TaskSource 只读的直接来源,不遍历整条重试链,也不授予来源资源管理权限。
type TaskSource struct {
// TaskID 本次已授权查询的任务标识。
TaskID string `json:"task_id"`
// Kind DIRECT、MANUAL_RETRY、SCHEDULE或WORKFLOW;分别是普通受理、人工派生、计划触发或流程步骤。
Kind string `json:"kind"`
// RetryOf 人工重试的直接原任务,限同一命名空间及队列;其他种类省略。
RetryOf *string `json:"retry_of,omitempty"`
// Schedule 计划触发的固定来源,仅SCHEDULE返回,不包含任务模板或计划配置。
Schedule *TaskScheduleSource `json:"schedule,omitempty"`
// Workflow 流程任务的固定来源,不返回其他步骤、业务输入或完整定义。
Workflow *TaskWorkflowSource `json:"workflow,omitempty"`
}
// TaskWorkflowSource 指向任务自己的历史激活;人工恢复不会把旧任务改指向当前激活。
type TaskWorkflowSource struct {
WorkflowID string `json:"workflow_id"` // WorkflowID 所属流程实例。
DefinitionID string `json:"definition_id"` // DefinitionID 稳定定义身份。
DefinitionVersionID string `json:"definition_version_id"` // DefinitionVersionID 实例固定的不可变版本记录。
Version string `json:"version"` // Version 固定发布版本名,不跟随新发布版本。
StepID string `json:"step_id"` // StepID 实例内逻辑步骤身份。
StepKey string `json:"step_key"` // StepKey 固定定义中的步骤名称。
StepKind string `json:"step_kind"` // StepKind TASK或POLL,不返回内部根或等待节点。
ActivationID string `json:"activation_id"` // ActivationID 创建此任务的激活身份,可能已经是历史代次。
ActivationSequence uint32 `json:"activation_sequence"` // ActivationSequence 此步骤的激活代次,从1开始。
PollRound uint32 `json:"poll_round,omitempty"` // PollRound 原业务查询轮编号,跨激活恢复不重置;TASK省略。
PollRoundID *string `json:"poll_round_id,omitempty"` // PollRoundID 当前任务对应的唯一轮次记录;TASK省略。
RetryOf *string `json:"retry_of,omitempty"` // RetryOf 人工恢复所关联的直接旧任务,限同一流程/步骤及业务轮次,不递归展开。
}
// TaskScheduleSource 关联已生成任务的触发记录及当时的不可变计划版本。
type TaskScheduleSource struct {
// ScheduleID 原计划标识,不因发布新版本而变化。
ScheduleID string `json:"schedule_id"`
// ScheduleVersion 触发时使用的固定版本,JSON字符串避免精度丢失。
ScheduleVersion uint64 `json:"schedule_version,string"`
// OccurrenceID 唯一关联该任务的触发记录标识。
OccurrenceID string `json:"occurrence_id"`
// ScheduledAt 原计划时间,UTC微秒精度,不等于实际受理/执行时间。
ScheduledAt time.Time `json:"scheduled_at"`
// Decision FIRED为单点触发,COALESCED为合并触发;跳过不会产生任务。
Decision string `json:"decision"`
// CoveredUntil 合并范围的排除边界,仅COALESCED返回。
CoveredUntil *time.Time `json:"covered_until,omitempty"`
}
// GetTaskSource 查询直接来源;不会授予来源计划/流程的读取、管理或执行权限。
func (c *Client) GetTaskSource(ctx context.Context, id string) (TaskSource, error) {
var out TaskSource
if !validID(id) {
return out, errors.New("invalid task id")
}
if err := c.call(ctx, "GET", "/v1/tasks/"+id+"/source", "", nil, &out); err != nil {
return TaskSource{}, err
}
if out.TaskID != id {
return TaskSource{}, errors.New("source does not match requested task")
}
valid := false
switch out.Kind {
case "DIRECT":
valid = out.RetryOf == nil && out.Schedule == nil && out.Workflow == nil
case "MANUAL_RETRY":
valid = out.RetryOf != nil && validID(*out.RetryOf) && *out.RetryOf != id && out.Schedule == nil && out.Workflow == nil
case "SCHEDULE":
s := out.Schedule
if s != nil && out.RetryOf == nil && out.Workflow == nil && validID(s.ScheduleID) && validID(s.OccurrenceID) && s.ScheduleVersion > 0 && validSourceTime(s.ScheduledAt) {
valid = s.Decision == "FIRED" && s.CoveredUntil == nil || s.Decision == "COALESCED" && s.CoveredUntil != nil && validSourceTime(*s.CoveredUntil) && s.CoveredUntil.After(s.ScheduledAt)
}
case "WORKFLOW":
w := out.Workflow
if w != nil && out.RetryOf == nil && out.Schedule == nil && validID(w.WorkflowID) && validID(w.DefinitionID) && validID(w.DefinitionVersionID) && workflowVersionName(w.Version) && validID(w.StepID) && workflowVersionName(w.StepKey) && validID(w.ActivationID) && w.ActivationSequence > 0 {
valid = w.StepKind == "TASK" && w.PollRound == 0 && w.PollRoundID == nil || w.StepKind == "POLL" && w.PollRound > 0 && w.PollRoundID != nil && validID(*w.PollRoundID)
if w.RetryOf != nil {
valid = valid && w.ActivationSequence > 1 && validID(*w.RetryOf) && *w.RetryOf != id
} else if w.StepKind == "TASK" && w.ActivationSequence > 1 {
valid = false
}
}
}
if !valid {
return TaskSource{}, errors.New("invalid or unsupported task source response")
}
return out, nil
}
func validSourceTime(t time.Time) bool {
return t.Year() >= 2000 && t.Year() <= 9998 && t.Nanosecond()%1000 == 0
}