接一个按秒计费的视频生成接口,前端状态机要比你想的多三个状态

3 阅读5分钟

大部分人第一次接生成式视频接口,状态机是这么画的:idle → loading → success | error

三个状态,四条边,看起来很干净。上线之后你会发现它撑不住,而且撑不住的地方不在技术上——在客服工单里。

下面这三个状态是我在给 Wan 3.0 做前端的时候一个个补进去的,每一个都是被真实反馈逼出来的。

一、queuedrunning 必须分开

异步接口返回一个 task id,你开始轮询。轮询到的状态里,"排队中"和"生成中"通常是两个不同的值,而前端最省事的做法是把它们都映射成 loading,转个圈。

这在两秒的接口上无所谓。在一个可能要跑一两分钟的接口上,它是最主要的用户流失点:用户分不清"系统卡住了"和"前面还有人",而这两件事他该做的反应完全相反。前者应该刷新,后者应该等着。

代价很具体:用户在排队阶段刷新页面、重新提交,于是同一个需求排了两次队,两次都要计费

type JobState =
  | { kind: 'queued';  position?: number }   // 不是 loading
  | { kind: 'running'; progress?: number }
  | { kind: 'succeeded'; url: string; modelId: string; taskId: string }
  | { kind: 'failed';  code: string; refunded: boolean }
  | { kind: 'expired'; taskId: string }

文案上也要分开写。"正在排队"和"正在生成"是两句话,不要合并成"处理中"。

二、failed 要带一个 refunded 字段

这一条是我觉得最容易被漏掉的。

按秒计费的生成接口,失败通常是自动退额度的——Wan 3.0 就是这样,生成失败不扣费,退回是自动的,不用提工单。但如果你的 UI 只显示一句"生成失败,请重试",用户脑子里第一个念头不是重试,是**"那我这次的钱是不是白花了"**。

于是他不重试,他去找客服。

这一句话的成本差异很夸张:

UI 文案用户下一步
生成失败,请重试开工单问扣没扣钱
生成失败,本次消耗的额度已自动退回直接点重试

失败态里退款信息比错误码重要。 错误码是给你看的,退款状态是给用户看的。

顺带一句:错误码也别原样透传。上游返回的 InvalidParameter.MutuallyExclusive 对用户没有任何意义,它该被翻译成"参考素材和首尾帧不能同时用",而且理想情况下这个组合在提交前就该被前端拦掉,根本不该走到失败态。

三、expired 不是 failed

生成成功之后,结果不是永久的。任务记录和产物文件通常各有各的有效期,而且这两个时钟不一定同时开始走

这意味着有一个状态是"这个任务确实成功过,但你现在拿不到文件了"。它长得像失败,但处理方式完全不同:

  • failed → 可以重试,而且大概率退过钱
  • expired → 重试就是重新生成,要重新计费,而且原来那条结果永远回不来了

expired 折叠进 failed,用户点"重试"的时候以为自己在恢复一个东西,实际上在下一笔新订单。这个误解一旦发生在企业客户身上,就是账单争议。

正确的做法是:成功之后立刻把文件转存到你自己的存储,然后 expired 这个状态在你的产品里就只对"没转存成功的历史任务"生效,用户基本碰不到。但状态机里必须留着它,因为总有转存失败的时候。

四、顺带说一个记账字段

结果态里我建议强制带上 modelIdtaskId 并且展示在 UI 上,不要只存在数据库里。

理由不是调试方便(虽然确实方便)。理由是这个市场里,界面上写着某个模型、后面跑的是另一个模型的情况是真实存在的——有排在搜索前列的站点在自己的定价页 FAQ 里写着"当前运行在上一代模型引擎上"。把模型 ID 印在结果下面,是你能给用户的最便宜的一条可验证承诺。

taskId 的价值则在几天之后才显现:用户回来说"上周那条能不能改长一点",没有 taskId 你连是哪条都对不上。

小结

三个状态,一句话概括:

  • 拆开 queued / running,省的是重复计费和流失
  • failedrefunded,省的是工单
  • expired 独立于 failed,省的是账单争议

这三条都不是技术难点,是产品和计费模型漏进前端的部分。按秒计费的接口和按次调用的接口,前端复杂度差异几乎全在这里。


我在做 wan-3.run,一个跑 Wan 3.0 的第三方浏览器界面,上面这些状态都是那边踩出来的。模型本身的参数、时长上限、各分辨率档位这些数字,我整理了一份每一行都链到阿里云官方文档并标了核对日期的规格表——顺便说,这个模型没有 4K 档,帧率是 30 不是 24,也没有开放权重,这三条全网抄错的比例高得离谱,接之前值得自己核一遍。

利益披露:wan-3.run 是我做的独立第三方界面,与阿里巴巴、阿里云、通义万相均无关联。