计费迁移本质是状态机

两个看似无关的Bug
客户打开邮箱,发现同一订阅被开了两张发票;另一个客户出现在流失报告里,可对方从未取消过订阅。这两个问题都来自一次真实的计费系统迁移。
当时,一家大型文件共享公司正把自研的报价和计费系统替换为 Salesforce CPQ + Salesforce Billing,并与 Oracle ERP 集成,涉及数十万客户账户。数据迁移本身很顺利——记录都搬过去了,但状态出了问题。
订阅状态分散在四个地方
一个订阅并不只存在于一个系统里。
- 报价系统知道卖了什么;
- 计费系统知道开票和收款情况;
- 配置系统知道客户实际能访问什么;
- ERP 知道收入如何确认。
每个系统都握着一份“部分真相”,而且各自按自己的节奏更新。当这些副本不一致时,Bug 就会落到客户头上:计费系统还在持续开票,但配置系统早已关闭了访问权限;或者反过来,权限提前开通了,但款项根本没到账。
这些都不是数据搬运的问题——记录确实到达了目标系统,只是到达时携带的状态,被接收系统按不同的方式解读了。
先画状态机,再配系统
修复方案是从一块白板开始的。团队为每个订阅定义了显式的状态机:命名状态、允许的转换、以及每个转换由哪个系统拥有。
状态包括:Draft(草稿)、Quoted(已报价)、Ordered(已下单)、Activated(已激活)、Suspended(已暂停)、Amended(已修改)、Expired(已过期)、Cancelled(已取消)。
规则也很明确:订阅可以从 Ordered 进入 Activated,但绝不能从 Quoted 直接跳到 Invoiced。任何试图执行非法转换的流程都会被拒绝,而不是悄悄写入一个坏状态。
这听起来很基础,但大多数计费系统恰恰没这么做。状态往往只是某个状态字段、某个复选框、某个可能为空或不为空的日期——状态被隐式表达。一旦状态是隐式的,每个集成都会发明自己的解读方式,然后这些分歧就被直接“发货”给客户。
对开发者意味着什么
如果你正在做类似的迁移,或者要构建依赖业务状态的集成,下面几条经验值得记下来:
- 迁移前先定义状态机。别等到配置完系统再补,那样只会让每个集成各自为政。
- 让非法转换显式失败。与其静默写入错误状态,不如让流程报错,这样问题能早发现。
- 明确状态所有权。每个转换只允许一个系统负责,避免多个系统同时改状态。
- 把状态机当作可测试的代码。状态转换表可以做成配置或代码,用单元测试覆盖所有合法和非法路径。
这套做法不仅对计费迁移有效,对任何涉及多系统状态同步的场景都适用——比如订单履约、权限管理、订阅生命周期。
对AI代理尤其重要
作者在后续工作中把 AI 引入销售运营,发现同样的教训反复出现:AI 代理的可靠性,取决于它们读取的状态是否可靠。如果代理读到的是隐式的、被不同系统不同解读的状态,它就会基于错误信息做出错误决策——重复开票、误判流失、错误授权。
所以,当你准备让 AI 代理去操作业务系统时,先确保底层状态是显式的、可验证的。否则,AI 只会更快地把不一致的状态放大成客户可见的故障。
小结
计费迁移失败往往不是数据搬错了,而是状态解读不一致。把状态机画出来、写下来、强制校验,是成本最低也最有效的防护。这个道理不只适用于计费系统——任何由多个系统共同维护业务状态的架构,都值得先把状态机定义清楚。




