应使用go-workflow库实现审批状态机,通过显式transition函数触发状态迁移、避免轮询,将路由逻辑封装为动态解析函数,并用timer+signal处理超时与撤回,确保worker注册与task queue严格匹配。

审批流程状态机怎么用 go-workflow 实现
Go 里没有官方工作流库,go-workflow(如 github.com/antonmedv/workflow 或更成熟的 github.com/temporalio/sdk-go)是实际项目中最常被选中的方案。但直接套模板容易卡在「状态跳转不触发」或「回调没执行」上——根本原因是没把审批节点和状态迁移绑定到具体业务逻辑里。
实操建议:
- 每个审批节点(如
"submit"→"review"→"approve")必须对应一个明确的Transition函数,不能只靠配置文件定义; - 状态变更要通过
workflow.ExecuteTransition(ctx, "approve", data)主动触发,而不是监听数据库字段轮询; -
data参数需包含当前操作人 ID、审批意见、附件路径等上下文,否则下游节点拿不到决策依据; - 避免在 Transition 中做耗时操作(如发邮件、调外部 API),应拆成异步任务或用 Temporal 的
Activity隔离。
如何让审批人自动路由到正确节点
常见错误是硬编码 nextAssignee := "admin@company.com",导致无法适配组织架构变动或角色继承。真实系统里,审批人往往来自岗位、部门、角色树或动态规则(比如「采购金额 >10w 时加签财务总监」)。
实操建议:
- 把路由逻辑封装成独立函数,例如
func ResolveApprover(node string, payload map[string]interface{}) ([]string, error); - 优先查缓存(如 Redis)里的
role:procurement:approver,再 fallback 到数据库查询; - 支持表达式引擎(如
github.com/Knetic/govaluate)解析条件:"amount > 100000 && department == 'finance'"; - 务必记录路由结果到流程实例元数据(
instance.Metadata["resolved_approvers"]),方便审计和重试时复用。
temporal-go 中如何处理审批超时与撤回
审批挂起太久没人点「同意」,或者申请人突然想撤回,这两类需求在 temporal-go 里不是靠定时器轮询实现的,而是靠 Timer + Signal 组合。
实操建议:
- 启动审批节点时,调用
workflow.NewTimer(ctx, 72*time.Hour)设置超时,到期后自动触发"timeout"transition; - 撤回不是删掉 workflow,而是发送 Signal:
client.SignalWorkflow(ctx, workflowID, runID, "cancel", nil),并在 workflow 中注册 handler 处理; - Signal handler 必须检查当前状态是否允许撤回(比如只允许
"pending"状态撤回),否则返回workflow.ErrAlreadyCompleted; - 超时或撤回后,记得用
workflow.UpsertWorkflowState更新业务表状态,避免 DB 和 workflow 状态不一致。
为什么本地调试时审批节点总卡住不动
最常被忽略的是 worker 注册缺失或 task queue 名称不匹配。你写了 workflow.Register(ReviewWorkflow),但没注册对应的 activity.Register(ReviewActivity),或者启动 worker 时用了 "default" 而 workflow 启动时指定的是 "approval-queue"。
实操建议:
- 启动 worker 前打印所有已注册的 workflow 和 activity 名称,确认无拼写错误;
- 用
temporal webUI 查看 workflow 实例详情页的Task Queue字段,和 worker 启动参数严格比对; - 本地调试时关掉 TLS(
--tls-disable),否则 gRPC 连接失败但日志只显示"connection refused"; - 别依赖 IDE 断点——workflow 执行在另一个 goroutine,要用
workflow.GetLogger(ctx).Info("here")打日志并查 temporal server 的 history event。
真正麻烦的从来不是写完第一个审批节点,而是当「多级会签」「加签驳回重走」「跨系统回调更新状态」这些边界场景叠加时,workflow 的 state machine 定义和 activity 分界是否足够清晰。这时候看一眼 workflow.GetInfo(ctx).GetHistoryLength(),比反复重启 worker 更管用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











