生成器实现状态机自描述:每个状态为生成器,首次yield返回含name、allowed_events、guards等元信息的字典,后续yield处理逻辑或迁移目标,支持可视化建模、运行时检查与动态调试。

用生成器实现带“状态机自描述”特性的业务流程编排,核心在于让每个状态既是执行单元,又是元信息载体——它不仅知道“做什么”,还明确声明“从哪来、能去哪、受什么约束、触发后干什么”。这种设计天然支持可视化建模、运行时检查和动态调试,比硬编码 if-else 或静态配置表更轻量且更具表达力。
状态即生成器:每个状态返回自身结构
不把状态写成字符串或枚举值,而是定义为生成器函数,首次 yield 返回一个结构化字典,包含该状态的元信息:
- name:状态标识(如 "pending_payment")
- allowed_events:当前状态下合法的事件列表(如 ["pay", "cancel"])
- guards:键为事件名、值为守卫函数的映射(如 {"cancel": lambda ctx: ctx.order_age
- on_enter:进入该状态时自动执行的动作(如发短信、更新时间戳)
后续 yield 则用于实际处理逻辑或等待事件输入。这样,仅调用一次 next() 就能获取完整状态描述,无需额外反射或注解。
迁移由事件驱动,但由生成器协同控制
主流程不主动调用 state1 → state2,而是启动一个顶层生成器,它通过 send(event) 向当前状态生成器注入事件;当前状态根据守卫结果决定是否 yield 出目标状态名,或抛出拒绝异常:
- 若当前状态生成器 yield "paid",主流程就切换到 paid() 生成器
- 若守卫失败,生成器可 yield {"error": "timeout_expired", "retry_after": 60},供上层做补偿决策
- 整个流转过程可通过 inspect.getgeneratorstate() 或自定义 .describe() 方法实时查询当前状态全貌
自描述能力支撑运行时可观测性
“自描述”不是只写在文档里,而是可被程序读取的活数据:
- 前端请求 /api/order/{id}/state 时,直接调用当前状态生成器的 .describe() 方法,返回 JSON 包含:当前状态、允许操作按钮、禁用原因、预计下一步耗时等
- 测试框架遍历所有状态生成器,自动收集 allowed_events 和 guards,生成状态迁移图谱(dot 格式),无需手动画图
- 当发生非法事件(如对已发货订单调 cancel),系统捕获异常并附带该状态的完整守卫条件快照,便于回溯“为什么不允许”
示例:三步订单流程的骨架
以 pending_payment → paid → shipped 为例,每个状态生成器开头都显式声明约束:
def pending_payment(order):yield {
"name": "pending_payment",
"allowed_events": ["pay", "cancel"],
"guards": {"cancel": lambda o: o.created_at > datetime.now() - timedelta(hours=1)},
"on_enter": lambda o: notify("请尽快支付")
}
event = yield
if event == "pay": yield "paid"
elif event == "cancel": yield "canceled"
这种写法让业务规则与执行逻辑共存于同一函数,改一处即同步更新行为与描述,避免配置与代码脱节。










