feat: publish immutable workflow definitions with typed SDK

This commit is contained in:
2026-09-18 00:28:00 +08:00
parent a664ec9370
commit 520e98d9d1
33 changed files with 2603 additions and 11 deletions

View File

@@ -0,0 +1,132 @@
// workflow-definition 演示研发定义与不可变版本发布;当前不启动流程实例。
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
orbit "git.yuntuo.hk/k2safe/orbit/sdk/go"
"os"
"reflect"
"time"
)
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run() error {
token, queue := os.Getenv("ORBIT_TOKEN"), os.Getenv("ORBIT_QUEUE_ID")
if path := os.Getenv("ORBIT_CREDENTIALS_FILE"); path != "" {
raw, err := os.ReadFile(path)
if err != nil {
return err
}
var credentials struct {
Token string `json:"token"`
QueueID string `json:"queue_id"`
}
if json.Unmarshal(raw, &credentials) != nil {
return errors.New("invalid private credential file")
}
if token == "" {
token = credentials.Token
}
if queue == "" {
queue = credentials.QueueID
}
}
client, err := orbit.NewClient(os.Getenv("ORBIT_URL"), orbit.Options{Token: token})
if err != nil {
return err
}
op := os.Getenv("ORBIT_OPERATION_ID")
if op == "" {
op, err = orbit.NewWorkerMessageID()
if err != nil {
return err
}
}
// 先输出稳定操作身份;响应丢失时用同一身份、同一版本和同一源码重新运行。
fmt.Printf("operation=%s\n", op)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
created, err := client.CreateWorkflowDefinition(ctx, op+".create", orbit.CreateWorkflowDefinitionRequest{Name: "object-flow." + op, Description: "对象准备、并行生成结果与有界状态查询"})
if err != nil {
return err
}
id := created.Definition.ID
in := orbit.PublishWorkflowVersionRequest{Version: "v1", Reason: "initial developer release", Specification: specification(queue)}
published, err := client.PublishWorkflowVersion(ctx, id, op+".v1", in)
if err != nil {
return err
}
again, err := client.PublishWorkflowVersion(ctx, id, op+".v1", in)
if err != nil || !again.Deduplicated || !reflect.DeepEqual(again.Version, published.Version) {
return fmt.Errorf("original publication replay mismatch: %v", err)
}
same, err := client.PublishWorkflowVersion(ctx, id, op+".v1.same", in)
if err != nil || !same.Deduplicated || same.Version.ID != published.Version.ID {
return fmt.Errorf("same-version replay mismatch: %v", err)
}
in.Version = "v2"
in.Reason = "extend finite workflow deadline"
in.Specification.TimeoutMS = 90000
next, err := client.PublishWorkflowVersion(ctx, id, op+".v2", in)
if err != nil {
return err
}
original, err := client.GetWorkflowVersion(ctx, id, "v1")
if err != nil || !reflect.DeepEqual(original, published.Version) {
return fmt.Errorf("original immutable version changed: %v", err)
}
meta, err := client.GetWorkflowDefinition(ctx, id)
if err != nil {
return err
}
fmt.Printf("definition=%s revision=%d v1=%s v2=%s steps=%d max_concurrency=%d max_task_attempts=%d\n", id, meta.Revision, original.ID, next.Version.ID, original.Analysis.Steps, original.Analysis.MaxConcurrency, original.Analysis.MaxTaskAttempts)
fmt.Println("Both definitions are stored. No workflow instance or business task has been started.")
return nil
}
func specification(queue string) orbit.WorkflowSpecification {
str := orbit.WorkflowType{Kind: "STRING"}
boolean := orbit.WorkflowType{Kind: "BOOLEAN"}
input := orbit.WorkflowType{Kind: "OBJECT", Fields: map[string]orbit.WorkflowType{"object_ref": str, "operation": str, "approved": boolean}}
literal := func(raw string) orbit.WorkflowValue {
return orbit.WorkflowValue{Kind: "LITERAL", Literal: json.RawMessage(raw)}
}
block := func(value orbit.WorkflowValue) orbit.WorkflowBlock {
return orbit.WorkflowBlock{OutputType: str, Output: value}
}
task := func(handler string, value orbit.WorkflowValue) orbit.WorkflowTask {
return orbit.WorkflowTask{QueueID: queue, Handler: handler, HandlerVersion: "v1", PayloadVersion: "v1", InputType: str, Input: value, ResultType: str, TimeoutMS: 5000, Retry: orbit.RetryPolicy{MaxAttempts: 1, AttemptTimeoutMS: 3000}, RecoverySafety: orbit.RecoveryReconcileOnly}
}
prepare := task("object.prepare", orbit.WorkflowRef("input", "object_ref"))
branchDefault := block(literal(`"standard"`))
left, right := orbit.WorkflowRef("input", "approved"), literal(`true`)
branch := orbit.WorkflowBranch{Cases: []orbit.WorkflowBranchCase{{When: orbit.WorkflowPredicate{Op: "EQ", Left: &left, Right: &right}, Body: block(literal(`"approved"`))}}, Default: &branchDefault}
parallel := orbit.WorkflowParallel{}
for _, name := range []string{"preview", "manifest"} {
work := task("object."+name, orbit.WorkflowRef("prepare"))
body := block(orbit.WorkflowRef(name))
body.Steps = []orbit.WorkflowStep{{Key: name, Kind: "TASK", Task: &work}}
parallel.Branches = append(parallel.Branches, orbit.WorkflowParallelBranch{Name: name, Body: body})
}
status := orbit.WorkflowType{Kind: "OBJECT", Fields: map[string]orbit.WorkflowType{"status": {Kind: "STRING", Enum: []string{"PENDING", "DONE", "FAILED", "REVIEW"}}}}
query := task("job.query", orbit.WorkflowRef("input", "operation"))
query.ResultType = status
query.Retry = orbit.RetryPolicy{MaxAttempts: 2, AttemptTimeoutMS: 1000, InitialDelayMS: 100, MaxDelayMS: 200}
query.RecoverySafety = orbit.RecoveryIdempotent
poll := orbit.WorkflowPoll{Task: query, MaxRounds: 5, TimeoutMS: 20000, IntervalMS: 1000, StatusPath: []string{"status"}, Pending: []string{"PENDING"}, Succeeded: []string{"DONE"}, Failed: []string{"FAILED"}, Review: []string{"REVIEW"}, FinalSignal: &orbit.WorkflowSignal{Name: "job.final", PayloadVersion: "v1", Correlation: orbit.WorkflowRef("input", "operation"), PayloadType: status, TimeoutMS: 20000}}
return orbit.WorkflowSpecification{SchemaVersion: 1, InputType: input, TimeoutMS: 60000, FailurePolicy: "REVIEW", Body: orbit.WorkflowBlock{Steps: []orbit.WorkflowStep{
{Key: "prepare", Kind: "TASK", Task: &prepare},
{Key: "route", Kind: "BRANCH", Branch: &branch},
{Key: "outputs", Kind: "PARALLEL", Parallel: &parallel},
{Key: "approval", Kind: "SIGNAL", Signal: &orbit.WorkflowSignal{Name: "object.approval", PayloadVersion: "v1", Correlation: orbit.WorkflowRef("input", "operation"), PayloadType: boolean, TimeoutMS: 10000}},
{Key: "settle", Kind: "TIMER", Timer: &orbit.WorkflowTimer{DelayMS: 1000}},
{Key: "result", Kind: "POLL", Poll: &poll},
}, OutputType: status, Output: orbit.WorkflowRef("result")}}
}

View File

@@ -0,0 +1,55 @@
package orbit
import (
"context"
"errors"
)
// WorkflowRef 构造输入或已完成步骤的字段引用;合法作用域及类型在发布时由服务端校验。
func WorkflowRef(source string, path ...string) WorkflowValue {
return WorkflowValue{Kind: "REFERENCE", Reference: &WorkflowReference{Source: source, Path: append([]string(nil), path...)}}
}
// CreateWorkflowDefinition 幂等创建稳定定义身份,不自动发布或启动流程。
func (c *Client) CreateWorkflowDefinition(ctx context.Context, key string, in CreateWorkflowDefinitionRequest) (WorkflowDefinitionResult, error) {
var out WorkflowDefinitionResult
err := c.call(ctx, "POST", "/v1/workflow-definitions", key, in, &out)
return out, err
}
func (c *Client) GetWorkflowDefinition(ctx context.Context, id string) (WorkflowDefinition, error) {
var out WorkflowDefinition
if !validID(id) {
return out, errors.New("invalid definition id")
}
err := c.call(ctx, "GET", "/v1/workflow-definitions/"+id, "", nil, &out)
return out, err
}
// PublishWorkflowVersion 冻结规则;网络结果不明时保留原定义ID、版本、内容、原因和幂等键重报。
func (c *Client) PublishWorkflowVersion(ctx context.Context, id, key string, in PublishWorkflowVersionRequest) (WorkflowVersionResult, error) {
var out WorkflowVersionResult
if !validID(id) {
return out, errors.New("invalid definition id")
}
err := c.call(ctx, "POST", "/v1/workflow-definitions/"+id+"/versions", key, in, &out)
return out, err
}
func (c *Client) GetWorkflowVersion(ctx context.Context, id, version string) (WorkflowVersion, error) {
var out WorkflowVersion
if !validID(id) || !workflowVersionName(version) {
return out, errors.New("invalid definition id or version")
}
err := c.call(ctx, "GET", "/v1/workflow-definitions/"+id+"/versions/"+version, "", nil, &out)
return out, err
}
func workflowVersionName(s string) bool {
if len(s) == 0 || len(s) > 64 {
return false
}
for i, r := range s {
if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || i > 0 && (r == '_' || r == '.' || r == '-')) {
return false
}
}
return true
}

View File

@@ -0,0 +1,109 @@
package orbit
import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"reflect"
"strings"
"sync/atomic"
"testing"
"time"
)
func TestIntegrationWorkflowDefinitionSDK(t *testing.T) {
api, queue, _ := integrationWorkerClient(t)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
key := workerTestID(t)
created, err := api.CreateWorkflowDefinition(ctx, key, CreateWorkflowDefinitionRequest{Name: "workflow." + key, Description: "精确数字发布回归"})
if err != nil {
t.Fatal(err)
}
id := created.Definition.ID
typ := WorkflowType{Kind: "OBJECT", Fields: map[string]WorkflowType{"n": {Kind: "NUMBER"}}}
task := WorkflowTask{QueueID: queue, Handler: "object.prepare", HandlerVersion: "v1", PayloadVersion: "v1", InputType: typ, Input: WorkflowValue{Kind: "LITERAL", Literal: json.RawMessage(`{"n":9007199254740993}`)}, ResultType: typ, TimeoutMS: 5000, Retry: RetryPolicy{MaxAttempts: 1, AttemptTimeoutMS: 3000}, RecoverySafety: RecoveryReconcileOnly}
in := PublishWorkflowVersionRequest{Version: "v1", Reason: "SDK publication", Specification: WorkflowSpecification{SchemaVersion: 1, TimeoutMS: 30000, FailurePolicy: "REVIEW", InputType: typ, Body: WorkflowBlock{Steps: []WorkflowStep{{Key: "prepare", Kind: "TASK", Task: &task}, {Key: "wait", Kind: "TIMER", Timer: &WorkflowTimer{DelayMS: 1000}}}, OutputType: typ, Output: WorkflowRef("prepare")}}}
// 真服务确认发布提交后,代理关闭客户端连接;SDK不得自动换键或默默重试。
target, _ := url.Parse(api.baseURL)
proxy := httputil.NewSingleHostReverseProxy(target)
var dropped atomic.Bool
var calls atomic.Int32
proxy.ModifyResponse = func(r *http.Response) error {
calls.Add(1)
if dropped.CompareAndSwap(false, true) {
raw, err := io.ReadAll(r.Body)
r.Body.Close()
if err != nil {
return err
}
if r.StatusCode != 201 || !strings.Contains(string(raw), `"deduplicated":false`) {
t.Error("publication was not committed before response loss")
}
return io.ErrUnexpectedEOF
}
return nil
}
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
conn, _, e := w.(http.Hijacker).Hijack()
if e == nil {
conn.Close()
}
}
gateway := httptest.NewServer(proxy)
defer gateway.Close()
client, err := NewClient(gateway.URL, Options{Token: api.token})
if err != nil {
t.Fatal(err)
}
if _, err := client.PublishWorkflowVersion(ctx, id, key, in); err == nil || calls.Load() != 1 || !dropped.Load() {
t.Fatal("response loss not surfaced", err)
}
published, err := client.PublishWorkflowVersion(ctx, id, key, in)
if err != nil || !published.Deduplicated {
t.Fatal("lost publication not recovered", err)
}
original := published.Version
if string(original.Specification.Body.Steps[0].Task.Input.Literal) != `{"n":9007199254740993}` || len(original.ContentDigest) != 64 || original.Analysis.Steps != 2 || original.Analysis.MaxTaskAttempts != 1 {
t.Fatal("SDK result changed precision or analysis")
}
same, err := api.PublishWorkflowVersion(ctx, id, workerTestID(t), in)
if err != nil || !same.Deduplicated || !reflect.DeepEqual(same.Version, original) {
t.Fatal("same version", err)
}
in.Specification.Body.Steps[0].Task.Input.Literal = json.RawMessage(`{"n":9007199254740992}`)
_, err = api.PublishWorkflowVersion(ctx, id, workerTestID(t), in)
var apiErr *APIError
if !errors.As(err, &apiErr) || apiErr.Code != "STATE_CONFLICT" {
t.Fatal("overwrite accepted", err)
}
in.Version = "v2"
next, err := api.PublishWorkflowVersion(ctx, id, workerTestID(t), in)
if err != nil || next.Version.ContentDigest == original.ContentDigest {
t.Fatal("v2", err)
}
stored, err := api.GetWorkflowVersion(ctx, id, "v1")
if err != nil || !reflect.DeepEqual(stored, original) {
t.Fatal("immutable v1", err)
}
metadata, err := api.GetWorkflowDefinition(ctx, id)
if err != nil || metadata.Revision != 3 {
t.Fatal("unexpected version count", err)
}
// 类型的空块输出支持显式null,不放宽普通任务的null参数约束。
in.Version = "v3"
in.Specification.Body = WorkflowBlock{OutputType: WorkflowType{Kind: "NULL"}, Output: WorkflowValue{Kind: "LITERAL", Literal: json.RawMessage(`null`)}}
v3, err := api.PublishWorkflowVersion(ctx, id, workerTestID(t), in)
if err != nil || v3.Version.Analysis.Queues == nil || len(v3.Version.Analysis.Queues) != 0 {
t.Fatal("null block", err)
}
if _, err := api.GetWorkflowVersion(ctx, id, "../other"); err == nil {
t.Fatal("unsafe path accepted")
}
t.Log("real HTTP publication response dropped after commit; original identity restored precise immutable version, no second revision; all SDK definition routes exercised")
}

115
sdk/go/workflow_types.go Normal file
View File

@@ -0,0 +1,115 @@
package orbit
import "encoding/json"
// WorkflowSpecification 是结构版本1的完整定义。版本发布后禁止原地修改。
type WorkflowSpecification struct {
SchemaVersion uint32 `json:"schema_version"` // SchemaVersion 当前必须为1,和业务发布版本分开。
InputType WorkflowType `json:"input_type"` // InputType 实例输入契约,OBJECT字段全部必填且不允许额外字段。
TimeoutMS uint64 `json:"timeout_ms"` // TimeoutMS 从实例受理起计的有限总时限,1毫秒至30天,暂停不续期。
FailurePolicy string `json:"failure_policy"` // FailurePolicy REVIEW或FAIL_AFTER_SETTLED;未知效果始终先核查。
Body WorkflowBlock `json:"body"` // Body 顶层顺序块;组合节点内部同样使用有限顺序块。
}
// WorkflowType 是有限的类型描述;JSON表示只能整体传递的任意JSON,不能据此访问未知字段。
type WorkflowType struct {
Kind string `json:"kind"` // Kind STRING、NUMBER、BOOLEAN、NULL、OBJECT、ARRAY或JSON。
Fields map[string]WorkflowType `json:"fields,omitempty"` // Fields 仅OBJECT使用,最多64项;字段名称为ASCII标识。
Items *WorkflowType `json:"items,omitempty"` // Items 仅ARRAY使用,数组元素采用同一契约。
Enum []string `json:"enum,omitempty"` // Enum 仅STRING使用,可选的1至32个互异值,按精确文本比较。
}
// WorkflowValue 明确区分常量、引用和构造对象/数组,禁止字符串插值和任意脚本。
type WorkflowValue struct {
Kind string `json:"kind"` // Kind LITERAL、REFERENCE、OBJECT或ARRAY。
Literal json.RawMessage `json:"literal,omitempty"` // Literal 仅LITERAL使用,保留精确数字,可以显式为null。
Reference *WorkflowReference `json:"reference,omitempty"` // WorkflowReference 仅REFERENCE使用,必须指向当前路径可用的输入或结果。
Fields map[string]WorkflowValue `json:"fields,omitempty"` // Fields 仅OBJECT使用,字段值递归解析,最多64项。
Items []WorkflowValue `json:"items,omitempty"` // Items 仅ARRAY使用,最多256项;业务大数组可整体引用。
}
type WorkflowReference struct {
Source string `json:"source"` // Source input、workflow或在当前词法作用域已完成的步骤key。
Path []string `json:"path,omitempty"` // Path 最多8层已声明OBJECT字段;空路径引用整个值,不支持动态路径或数组下标。
}
// WorkflowBlock 的输出是组合节点向外暴露的唯一数据出口;内部步骤不能被外部直接引用。
type WorkflowBlock struct {
Steps []WorkflowStep `json:"steps,omitempty"` // Steps 按顺序执行;空块可直接映射输入,所有分支仍需显式输出。
OutputType WorkflowType `json:"output_type"` // OutputType 本块输出契约。
Output WorkflowValue `json:"output"` // Output 全部步骤完成后解析的输出表达式。
}
type WorkflowStep struct {
Key string `json:"key"` // Key 全定义唯一ASCII标识,1至64字符,不能为input或workflow。
Kind string `json:"kind"` // Kind TASK、BRANCH、PARALLEL、SIGNAL、TIMER或POLL;必须只提供对应配置。
Task *WorkflowTask `json:"task,omitempty"` // WorkflowTask 普通业务工作,交由独立Worker处理。
Branch *WorkflowBranch `json:"branch,omitempty"` // WorkflowBranch 按顺序选择第一个匹配路径,必须有默认路径。
Parallel *WorkflowParallel `json:"parallel,omitempty"` // WorkflowParallel 静态有限分支,全部成功后汇合。
Signal *WorkflowSignal `json:"signal,omitempty"` // WorkflowSignal 等待一次按步骤和业务操作匹配的外部输入。
Timer *WorkflowTimer `json:"timer,omitempty"` // WorkflowTimer 持久化时间等待,不占业务Worker槽位。
Poll *WorkflowPoll `json:"poll,omitempty"` // WorkflowPoll 有界查询,轮次预算与每轮Pulse尝试预算独立。
}
type WorkflowTask struct {
QueueID string `json:"queue_id"` // QueueID 发布及启动时都需按命名空间和当前权限校验。
Handler string `json:"handler"` // Handler 已命名的业务处理器;定义不携带可执行源码。
HandlerVersion string `json:"handler_version"` // HandlerVersion 固定的处理器版本。
PayloadVersion string `json:"payload_version"` // PayloadVersion 固定的输入契约版本。
InputType WorkflowType `json:"input_type"` // InputType 输入映射必须满足的处理器参数契约。
Input WorkflowValue `json:"input"` // Input 在激活时根据已保存数据解析,不使用当前时间或随机数。
ResultType WorkflowType `json:"result_type"` // ResultType 权威成功结果契约,不匹配时进入核查而不推进。
TimeoutMS uint64 `json:"timeout_ms"` // TimeoutMS 单任务总时限,正值且不超过流程时限,运行时还受实例剩余时限限制。
Retry RetryPolicy `json:"retry"` // Retry 显式冻结有限策略,包含首次尝试,最多100次。
RecoverySafety RecoverySafety `json:"recovery_safety"` // RecoverySafety 显式RECONCILE_ONLY或IDEMPOTENT。
Priority int16 `json:"priority,omitempty"` // Priority 冻结优先级0至9。
BusinessKey *WorkflowValue `json:"business_key,omitempty"` // BusinessKey 可选字符串映射,只用于业务追踪,不代表幂等。
ConcurrencyKey *WorkflowValue `json:"concurrency_key,omitempty"` // ConcurrencyKey 可选字符串映射,采用Pulse共享资源许可规则。
}
type WorkflowPredicate struct {
Op string `json:"op"` // Op EQ/NE/LT/LE/GT/GE或AND/OR/NOT;只比较已声明的标量。
Left *WorkflowValue `json:"left,omitempty"` // Left 比较操作左值,逻辑操作不得携带。
Right *WorkflowValue `json:"right,omitempty"` // Right 比较操作右值,必须与左值同种标量。
Arguments []WorkflowPredicate `json:"arguments,omitempty"` // Arguments AND/OR为2至16项,NOT为1项,深度最多8层。
}
type WorkflowBranch struct {
Cases []WorkflowBranchCase `json:"cases"` // Cases 1至16个按顺序匹配的条件路径。
Default *WorkflowBlock `json:"default"` // Default 必填,穷尽未匹配情况,不能静默跳过。
}
type WorkflowBranchCase struct {
When WorkflowPredicate `json:"when"` // When 在分支激活时根据前置持久化数据计算。
Body WorkflowBlock `json:"body"` // Body 选中路径;所有路径输出类型必须一致。
}
type WorkflowParallel struct {
Branches []WorkflowParallelBranch `json:"branches"` // Branches 2至8个静态分支;结果为各分支名称对应输出的OBJECT。
}
type WorkflowParallelBranch struct {
Name string `json:"name"` // Name 本并行节点内唯一ASCII字段标识。
Body WorkflowBlock `json:"body"` // Body 分支内部独立顺序块;不能读取兄弟分支尚未汇合的数据。
}
type WorkflowSignal struct {
Name string `json:"name"` // Name 外部信号类型,需同时匹配实例、步骤激活和业务关联。
PayloadVersion string `json:"payload_version"` // PayloadVersion 外部输入契约版本。
Correlation WorkflowValue `json:"correlation"` // Correlation 必须解析为非空业务操作字符串,运行时冻结在等待记录。
PayloadType WorkflowType `json:"payload_type"` // PayloadType 信号内容及该节点成功输出的类型。
TimeoutMS uint64 `json:"timeout_ms"` // TimeoutMS 激活起的有限等待时限,不超过流程时限;暂停不续期。
}
type WorkflowTimer struct {
DelayMS uint64 `json:"delay_ms"` // DelayMS 激活起的正等待时长,不超过流程时限;输出为NULL。
}
type WorkflowPoll struct {
Task WorkflowTask `json:"task"` // WorkflowTask 同一查询处理器及固定输入;查询不得再次调用原业务提交接口。
MaxRounds uint32 `json:"max_rounds"` // MaxRounds 1至1000,轮次含首次;每轮一个任务,单轮内部重试不新增轮次。
TimeoutMS uint64 `json:"timeout_ms"` // TimeoutMS 激活起的总查询时限,不超过流程时限。
IntervalMS uint64 `json:"interval_ms"` // IntervalMS 从上一轮处理中结果受理后等待,至少1000毫秒且小于查询总时限;不补发错过的轮次。
StatusPath []string `json:"status_path,omitempty"` // StatusPath 成功查询结果内的STRING枚举字段路径,空表示整个结果就是枚举。
Pending []string `json:"pending"` // Pending 表示仍在处理的枚举值,至少1项。
Succeeded []string `json:"succeeded"` // Succeeded 表示业务确认成功的枚举值,至少1项。
Failed []string `json:"failed,omitempty"` // Failed 表示业务确认失败的枚举值。
Review []string `json:"review,omitempty"` // Review 表示需要核查的枚举值;四组必须无交集并覆盖整个状态枚举。
FinalSignal *WorkflowSignal `json:"final_signal,omitempty"` // FinalSignal 可选最终回执,其内容类型与查询结果一致,只接受非Pending状态。
}

56
sdk/go/workflow_views.go Normal file
View File

@@ -0,0 +1,56 @@
package orbit
import "time"
// CreateWorkflowDefinitionRequest 创建命名空间内的稳定定义身份,不发布版本或启动实例。
type CreateWorkflowDefinitionRequest struct {
Name string `json:"name"` // Name 命名空间内唯一ASCII名称,最长128字符。
Description string `json:"description"` // Description 用途说明,最多1024个Unicode字符。
}
// PublishWorkflowVersionRequest 发布不可变版本;同版本内容冲突不能通过更换幂等键覆盖。
type PublishWorkflowVersionRequest struct {
Version string `json:"version"` // Version 研发指定的ASCII版本名,最长64字符,不包含路径分隔符。
Specification WorkflowSpecification `json:"specification"` // WorkflowSpecification 完整且显式的冻结规则。
Reason string `json:"reason"` // Reason 非空发布原因,最多1024个字符,写入审计。
}
// WorkflowDefinition 仅含命名空间级元数据;读取完整版本还须授权所有引用队列。
type WorkflowDefinition struct {
ID string `json:"id"` // ID 定义稳定标识。
NamespaceID string `json:"namespace_id"` // NamespaceID 定义隔离范围。
Name string `json:"name"` // Name 创建时冻结的唯一名称。
Description string `json:"description"` // Description 创建时冻结的用途说明。
Revision uint64 `json:"revision,string"` // Revision 从1开始,每个新发布版本增加1,不能替代业务版本名。
CreatedAt time.Time `json:"created_at"` // CreatedAt UTC数据库创建时间。
UpdatedAt time.Time `json:"updated_at"` // UpdatedAt 最近新版本发布的UTC数据库时间。
}
// WorkflowAnalysis 是静态预算上界;不表示业务任务已执行或已分配配额。
type WorkflowAnalysis struct {
Steps uint32 `json:"steps"` // Steps 全定义静态步骤数。
MaxConcurrency uint32 `json:"max_concurrency"` // MaxConcurrency 并行业务任务上界。
MaxTaskAttempts uint32 `json:"max_task_attempts"` // MaxTaskAttempts 所有路径的保守尝试预算总和。
Queues []string `json:"queues"` // Queues 排序去重队列,始终为数组。
}
// WorkflowVersion 返回原始发布内容和身份,不使用可变的latest别名。
type WorkflowVersion struct {
ID string `json:"id"` // ID 不可变版本记录标识。
NamespaceID string `json:"namespace_id"` // NamespaceID 隔离范围。
DefinitionID string `json:"definition_id"` // DefinitionID 父定义标识,由应用事务校验。
Version string `json:"version"` // Version 发布版本名。
Specification WorkflowSpecification `json:"specification"` // WorkflowSpecification 已冻结规则,数字精度不经float64转换。
ContentDigest string `json:"content_digest"` // ContentDigest 带域分隔的SHA-256,64位小写十六进制。
PublishedBy string `json:"published_by"` // PublishedBy 原发布凭据标识,不包含凭据秘密。
PublishedAt time.Time `json:"published_at"` // PublishedAt 原发布时间,重复发布不更新。
Analysis WorkflowAnalysis `json:"analysis"` // WorkflowAnalysis 根据结构版本1规则计算的静态检查结果。
}
type WorkflowDefinitionResult struct {
Definition WorkflowDefinition `json:"definition"` // Definition 创建事务的原始快照。
Deduplicated bool `json:"deduplicated"` // Deduplicated 是否返回原回执。
}
type WorkflowVersionResult struct {
Version WorkflowVersion `json:"version"` // Version 原版本快照,不包含当前父定义状态。
Deduplicated bool `json:"deduplicated"` // Deduplicated 原键或同版本同内容重复请求。
}