// Package flowdemo 仅为可运行流程示例提供初始化和有界等待,不属于公开SDK或业务恢复策略。 package flowdemo import ( "context" "encoding/json" "errors" "fmt" "os" "time" orbit "git.yuntuo.hk/k2safe/orbit/sdk/go" ) func Client() (*orbit.Client, string, string, error) { raw, err := os.ReadFile(os.Getenv("ORBIT_CREDENTIALS_FILE")) if err != nil { return nil, "", "", err } var credentials struct { Token string `json:"token"` QueueID string `json:"queue_id"` } if json.Unmarshal(raw, &credentials) != nil || credentials.Token == "" || credentials.QueueID == "" { return nil, "", "", errors.New("invalid private credentials file") } client, err := orbit.NewClient(os.Getenv("ORBIT_URL"), orbit.Options{Token: credentials.Token}) if err != nil { return nil, "", "", err } operation, err := orbit.NewWorkerMessageID() return client, credentials.QueueID, operation, err } // PublishStart 每次示例运行创建独立身份;不将重新运行命令当作未知响应的原键恢复。 func PublishStart(ctx context.Context, api *orbit.Client, operation, name string, spec orbit.WorkflowSpecification, input json.RawMessage) (orbit.WorkflowInstance, error) { var empty orbit.WorkflowInstance fmt.Printf("scenario=%s operation=%s\n", name, operation) created, err := api.CreateWorkflowDefinition(ctx, operation, orbit.CreateWorkflowDefinitionRequest{Name: name + "." + operation, Description: "controlled standalone workflow example"}) if err != nil { return empty, err } if _, err = api.PublishWorkflowVersion(ctx, created.Definition.ID, operation, orbit.PublishWorkflowVersionRequest{Version: "v1", Specification: spec, Reason: "publish controlled SDK example"}); err != nil { return empty, err } result, err := api.StartWorkflow(ctx, operation, orbit.StartWorkflowRequest{DefinitionID: created.Definition.ID, Version: "v1", BusinessKey: operation, Input: input}) if err != nil { return empty, err } fmt.Printf("workflow=%s version=%s\n", result.Instance.ID, result.Instance.Version) return result.Instance, nil } // Runner 持有一个真实Worker及其结束通知,等待时同时检测Worker异常退出。 type Runner struct { Worker *orbit.Worker stop context.CancelFunc done chan struct{} err error // err 仅在done关闭后读取,关闭通道建立同步关系。 } func StartWorker(ctx context.Context, api *orbit.Client, queue string, handlers ...orbit.WorkerHandler) (*Runner, error) { w, err := orbit.NewWorker(api, orbit.WorkerOptions{QueueID: queue, Capacity: 1, ShutdownTimeout: 5 * time.Second, Handlers: handlers}) if err != nil { return nil, err } workerCtx, stop := context.WithCancel(ctx) r := &Runner{Worker: w, stop: stop, done: make(chan struct{})} go func() { r.err = w.Run(workerCtx); close(r.done) }() return r, nil } func (r *Runner) Close() error { r.stop() <-r.done return r.err } func (r *Runner) Wait(ctx context.Context, check func() (bool, error)) error { for { ready, err := check() if err != nil || ready { return err } select { case <-ctx.Done(): return ctx.Err() case <-r.done: return fmt.Errorf("example Worker stopped before expected state: %v", r.err) case <-time.After(100 * time.Millisecond): } } } func (r *Runner) Step(ctx context.Context, api *orbit.Client, workflow, key string, predicate func(orbit.WorkflowStepDetail) bool) (orbit.WorkflowStepDetail, error) { var out orbit.WorkflowStepDetail err := r.Wait(ctx, func() (bool, error) { var err error out, err = api.GetWorkflowStep(ctx, workflow, key) return err == nil && predicate(out), err }) return out, err } func (r *Runner) Workflow(ctx context.Context, api *orbit.Client, workflow, state string) (orbit.WorkflowInstance, error) { var out orbit.WorkflowInstance err := r.Wait(ctx, func() (bool, error) { var err error out, err = api.GetWorkflow(ctx, workflow) if err != nil || out.State == state { return err == nil, err } if out.State == "NEEDS_REVIEW" || out.State == "FAILED" || out.State == "CANCELLED" || out.State == "SUCCEEDED" { return false, fmt.Errorf("unexpected workflow state %s; wanted %s", out.State, state) } return false, nil }) return out, err }