games-development-ai/docs/agent-specs/2026-07-07-内测-WU2-newapi额度接入-设计.md
lili 462946518f
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled
docs(mvp): 内测A段文档账校正——退役过时误建 plan + 07-07 三档对齐已建地面
内测 A 段(WU-1/2/3)代码上会话 Complete A 已全量实现并合入 dev/2.0.0(三 opus 子代理
读码坐实)。本次理账,不重复产设计门:

- 退役误建的 A 段 plan(39c64712):其框架把已建当缺口、§4 抄了创始人 S0 否决的旧路
  (运行时管理 client 开户,实际落地是离线预置池)。07-07 三档 + tech-decisions 才是 SoT。
- WU3 07-07 档校正实例归属:全篇共享 muse :18080 → game 专属 :28080(创始人 workspace
  分离约束),加 2026-07-08 校正横幅,凭据见内网端点档;端口 :28080/:28443/plugin :25003。
- 三档 status 草案 → 已实现,附各自实现 commit;WU2 显式记「落地=预置池、非否决的管理 client」。

docs-gate 七检绿(退役 plan 后 G7 谱系仍达 canonical——三档挂 MVP作战清单)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 03:28:38 -07:00

54 KiB
Raw Permalink Blame History

date, topic, status, sot-impact, 上级, 依赖, 关联, 图清单
date topic status sot-impact 上级 依赖 关联 图清单
2026-07-07 newapi-per-user-quota 已实现(离线预置池+注册 claim池表 c13595fbclaim 消费者 4cf0e14ejob userToken a069afb6quota_exhausted 跨契约 fe6079baworker 取 token e8f7dc83随 Complete A 合入 dev/2.0.0。落地方案=预置池,非早期设想的运行时管理 client 开户——后者 S0 实测撞 admin 不能替他人建 token 硬边界,创始人已否决,见 tech-decisions.md:254 修订「生成引擎运行时」(§6.1 job 契约新增 userToken 位、§6.5 新增 quota_exhausted 失败因);修订「数据模型」(新增额度池表 newapi_quota_pool——离线预建 FREE 条目 + 注册 claim 绑定);关联「鉴权与权限」(不改注册路,仅消费 WU1 afterCommit 发出的「注册成功」事件从池 claim 一个条目绑定,运行时不调 new-api);修订「契约#6 dify-workflow-io.json」与「aigc.yaml FailureReason」(跨契约共享枚举同批新增 quota_exhausted须与 WU3 协调)。刻意不改「变现端到端」——本设计对接 new-api 网关额度,不在 game-cloud 自建 credits 钱包。 docs/mvp/MVP作战清单.md docs/agent-specs/2026-07-07-内测-WU1-注册登录用户名密码-设计.mdWU1 §3.6 是唯一注册接缝afterCommit 发「注册成功」领域事件/RocketMQ 消息,载荷 userId/registerChannel/anonId。本设计是该事件的消费者不在注册事务内挂点WU1 迁移取 V31本设计顺延取 V32
game-cloud/huijing-module-system/.../passport/PassportServiceImpl.java:214 @ cb9f135dWU1 注册锚,本设计不改它,只消费其 afterCommit 事件)
game-cloud/game-module-aigc/.../service/task/AigcTaskServiceImpl.java:202-204 @ cb9f135d编排器/bake-off「伪装在线提交」经 submitGenerate→dispatchGenerictask_source 现全落 online
game-cloud/game-module-aigc/.../executor/AigcGenerateExecutor.java:658-681 @ cb9f135ddispatchGeneric 组 §6.1 job运行在有界消费池 consumeThreadMax=15
game-cloud/game-module-aigc/.../executor/ExecutorLlmClient.java:124-149 @ cb9f135d
game-cloud/game-module-aigc/game-module-aigc-api/.../enums/FailureReasonEnum.java @ cb9f135d跨契约共享枚举头注禁单边扩展
contracts/api-schemas/aigc.yaml:296 · contracts/dify-workflow-io.json:50 @ cb9f135d两处 FailureReason enum须同批加 quota_exhausted
game-cloud/game-module-trade/.../dataobject/GrantDO.java @ cb9f135d
wg1/gen-worker/worker/_client.py:47-57 · wg1/gen-worker/worker/cost.py:8-15 @ cb9f135d
cheap-worker/cheap_service_driver.py:28-44 · cheap-worker/worker_service.py:142-146 @ cb9f135d
图0 一图看懂(SVG)
图1 六接线点数据流(mermaid)
图2 池条目 FREE→CLAIMED claim 状态机(mermaid)
图3 生成期额度扣减与失败时序(mermaid)

内测 new-api per-user ¥100 生成额度接入 · 功能设计

0 一图看懂

内测阶段每个用户拿 ¥100 生成额度。这笔额度不在 game-cloud 里记一本自己的账,而是直接落在 new-api 网关上——权威余额闸 = 用户 token 的 tokens.remain_quota(预建时挂 ¥100 折算值、unlimited_quota=false权威消耗 = 该 user 的 users.used_quota(网关每调一次 LLM 逐笔累加);users.quota 同额设一份保持账户自洽,但触发耗尽 402 的那个字段是 token 的 remain_quota。做法分两段:离线由一个 ops 脚本在 mini-infra 一次性预建 N 个专属 (user + token + ¥100),把条目写进 game-cloud 的一张额度池表S0 实测坐实 new-api 的 admin 令牌无法替他人建 token、只能直连它的 postgres 才能 per-user 开户,故 provision 全部离线做、绝不进运行时);运行时消费 WU1 注册成功后 afterCommit 发出的事件,从池里 CAS claim 一个 FREE 条目绑给该用户,这是 game-cloud 内部一条 SQL、零 new-api 外呼。生成任务派发时把这个用户 claim 到的 token 随 job 带到 workerworker 用它去调网关,网关按这把 token 扣该用户的额度。额度花完,网关直接拒,用户看到一句「生成额度已用完」。

内测额度口径:账本在 new-api——离线预置池运行时只 claim / 扣额 离线预置ops 脚本 · 一次性建 N 条 · 补池=重跑) ops 预置脚本 建户→DB写access_token→建token→设quota new-api 预建账户 N×(user + token + ¥100) game-cloud 额度池表 (D) N 条 status=FREE 凭据A+postgres 脏活 脚本回写 FREE 条目 用户注册 registerPlayer 建 game_playerWU1 afterCommit 发注册成功事件 WU2 消费事件 → 从池 claim CAS 绑定·幂等·渠道过滤 claim FREE→CLAIMED 生成任务派发 job 加 userToken 位(查池表 CLAIMED worker 用 job.userToken 调网关 new-api 网关(账本) tokens.remain_quota 减 users.used_quota 增 = 权威消耗 100.64.0.8:3000 该 token → 扣该用户额度 额度耗尽 → 网关 402/403 → 干净失败「生成额度已用完」

核心思想new-api per-user quota 当账本provision 离线预置池,运行时只 claim 一个条目 + 生成扣额,零运行时 admin 耦合。 边界:不建钱包 / 不自助充值续额 / 运行时不调 admin API。新失败面=池空(懒 claim + 水位告警 + 补池)。成功=真机五验。

三句话:核心思想——把 new-api 的 per-user quota 直接当额度账本(权威余额=token remain_quota、权威消耗=user used_quota),额度由离线 ops 脚本预建成池、注册时消费 WU1「注册成功」事件从池 CAS claim 一个条目绑玩家、生成时随 job 带该 token 扣额、耗尽网关直接拒。边界——只接网关额度,不在 game-cloud 建 credits 钱包、不做用户自助充值也不做运营续额闭环、运行时不调 new-api admin APIprovision 全离线)、权威 logs.quota 对账延后per-user 门只对真实 member 在线创作生效,编排器/bake-off/系统触发旁路走全局 key。怎么算成功——真机五验ops 脚本预建 N 条池new-api users/tokens 新增、每 token remain_quota=¥100 折算值);注册后从池 claim 一条 FREE→CLAIMED 绑该玩家;一次真 gen 后该 user used_quota 增;余额耗尽后 gen 被网关干净拒且回传可读拒因;池空时注册仍成功、水位告警、首次生成懒 claim 兜。

1 意图与目标

内测要给种子创作者发生成额度,防止有人无限刷生成把网关额度烧穿,同时给「花了多少、还剩多少」一个权威口径。创始人已拍定这条口径不是在后端自建一套 credits 钱包,而是复用 new-api 网关本就有的 per-user 额度体系:每个内测用户 ¥100消耗由网关权威计量。至于这批 per-user 账户怎么建S0 实测坐实了一条硬约束——new-api 的 admin 令牌能建用户、设 quota不能替他人建 token(建 token 严格自认证、access_token 也不可经 API 设),任何 per-user token 都得直连它的 postgres 才能造出来。据此创始人拍定provision 不进 game-cloud 运行时,而是离线由一个 ops 脚本一次性预建一批 (user+token+¥100) 成池,运行时注册只从池里 claim 一个绑给用户——零运行时 admin 调用、零 game-cloud↔new-api-postgres 耦合。

选这条路的理由是它把「计量」这件最容易算错、最需要和真实扣费对齐的事,交给了唯一真正扣费的那一层。网关调用一次 LLM、按 token 数和模型倍率扣一次 quotaused_quota 就是逐笔累加的真账;后端若自建钱包,就得把网关的扣费公式(含缓存折扣、模型倍率、分组倍率)在 Java 侧再实现一遍并保持同步,这正是 wg1/gen-worker/worker/cost.py 反复踩过的对账坑——它自己都在注释里写明「精确 logs.quota 仍需网关 access token 读 /api/log/selfsk- key 无管理权」。把账本收敛到网关,这条线只需做「离线预置 token、运行时把对的 token 递到对的调用上」,不碰计量。

目标锁三条,全部可真机取证:

  • 每个注册用户从预置池 claim 到一个专属 user + 专属 tokentoken remain_quota = ¥100 按网关口径折算的 quota 值¥100 由 ops 脚本预建时一次性设好)。
  • 每次生成消耗都落在该用户自己的 token 上,网关 used_quota 随之增长,账目可查。
  • 额度耗尽后生成被网关拒绝,后端把它翻译成用户可读的「生成额度已用完」,而不是一个含糊的「生成失败」。

2 边界

:一个离线 ops 预置脚本(宿主 mini-infra用 new-api 管理凭据 + 直连其 postgres建 user / DB 写 access_token / 以该用户身份建 token / 设 quota一次性预建 N 个 (user + token + ¥100) 并把条目写进 game-cloud 池表;game-cloud 维护一张额度池表(预建条目 status FREE/CLAIMED + claim 绑定字段);消费 WU1 afterCommit 发出的「注册成功」事件,在 WU2 自己的事务线上从池做一次幂等、CAS 的 claim从 FREE 抢一个绑 game_player,不进注册事务、渠道过滤);给生成 job 的 §6.1 契约加一个 userToken 位并让三个 worker 入口从「读全局 env key」改成「优先读 job 上的 token」给失败因枚举加一个 quota_exhausted,且按 contract-first 同批改 aigc.yamldify-workflow-io.json 两处共享枚举让耗尽能干净回传。

不做不在 game-cloud 运行时调 new-api admin API、不做运行时开户——provision 全部离线由 ops 脚本完成,运行时只对池表做 claim零 game-cloud↔new-api-postgres 运行时耦合;不在 game-cloud 建 credits/钱包表,不做扣费公式的二次实现,不做用户自助充值 / 支付 / 提现,也不做运营续额闭环(这些属「变现端到端」,本设计刻意不碰;每个池条目 ¥100 由 ops 脚本预建时一次性设好,运行时绝不改 quota、绝不对已 claim/已消耗条目重设额度);不做权威 logs.quota 对账(需管理 token 读 /api/log/self,明确延后到有需求时再接);不改注册路本身只挂事件消费者passport 不感知 WU2不改现有 bake-off / spike / 编排器批跑的行为——这些触发没有 game_player 成员身份dispatch 层的 per-user 门只对真实 member 生效,系统/编排触发在派发时按「无成员身份」直接旁路、回落全局 key判据见 §3.6,堵在 dispatch 层而非 worker 层)。

flowchart TB
  subgraph IN["做(本设计闭环 · 六接线点 A-F"]
    A["A 管理凭据<br/>ops 脚本用·不进运行时"]
    B["B 离线 ops 预置脚本<br/>预建 N×(user+token+¥100)"]
    C["C 注册 claim 消费者<br/>从池 CAS 绑定·渠道过滤"]
    D["D 额度池表<br/>FREE/CLAIMED 条目"]
    E["E job 加 userToken 位<br/>§6.1 契约追加字段"]
    F["F worker 取 job token<br/>缺失回落全局 key"]
  end
  subgraph OUT["不做(越界,明确挡住)"]
    X["运行时调 new-api admin API / 运行时开户"]
    W["credits 钱包 / 扣费公式二次实现"]
    R["用户自助充值 / 支付 / 提现 / 续额"]
    L["权威 logs.quota 对账(延后)"]
    S["改 spike/bake-off 全局 key 路"]
  end
  IN -.->|边界| OUT

3 方案

3.1 六个接线点如何串成一条数据流

额度供给分两段:离线预置ops 脚本预建池,接线点 A·B·D运行时(注册 claim、生成扣额、耗尽拒接线点 C·E·F。下图是事实源逐点的字段契约在其后展开。

flowchart LR
  subgraph OFFLINE["离线预置ops 脚本 · A·B·D"]
    OPS["ops 预置脚本<br/>建户 API→DB 写 access_token<br/>→以该用户建 token API→设 quota API"]
    OPS -->|凭据 A + postgres 脏活| NAP["new-api预建 N×(user+token+¥100)"]
    NAP -->|脚本回写条目 FREE| POOL["额度池表 newapi_quota_pool<br/>N 条 FREE (D)"]
  end

  U[用户] -->|注册| REG["registerPlayerWU1<br/>注册事务提交"]
  REG -->|同事务| PL["game_player 落库"]
  REG -.->|afterCommit 发事件| EVT["注册成功事件/RocketMQ<br/>userId·registerChannel·anonId"]
  EVT -->|WU2 消费C· 渠道过滤| CLAIM["从池 CAS claim 一个 FREE<br/>绑 game_player·写 biz_no"]
  CLAIM -->|抢占 FREE→CLAIMED| POOL
  CLAIM -.->|池空| ALERT["不抛·水位告警<br/>懒 claim 兜(§3.6)"]

  U -->|创作生成| GEN["dispatchGeneric<br/>组 §6.1 job (E)"]
  GEN -->|creatorUserId 是否为 member?| MEMB{有 game_player 行}
  MEMB -->|否 系统/编排/bake-off| GLOB["旁路:回落全局 key<br/>不懒 claim"]
  MEMB -->|是 真实 member| LOOK["查池表该玩家 CLAIMED 条目"]
  LOOK -->|命中| TOK["job.userToken=token_key"]
  LOOK -->|无 CLAIMED 条目| LZ["懒 claim 单 CAS 抢一次<br/>池空→干净失败+水位告警"]
  LZ -->|抢到| TOK
  TOK -->|job.userToken| WK["worker (F)"]
  WK -->|用 job.userToken 调网关| NA["new-api 网关"]
  NA -->|扣该 user 额度| LEDGER["token remain_quota 减<br/>user used_quota 增"]
  NA -.->|余额耗尽 402/403| FAIL["quota_exhausted 干净回传"]

3.2 A · 管理凭据S0 已实测坐实

现有那把共享 NEWAPI_KEYsk-84GA...,见 docs/内网凭据与端点.md new-api 段)是一把普通调用 token没有管理权——它能调 /v1/chat/completions,但建不了 user、改不了 quota。ops 预置脚本要建 user、设 quota必须用 new-api 的管理凭据。S02026-07-07真机亲验、测试用户已清理把这套管理面全部坐实不再是假设

  • root 与令牌root 用户 = id=1 qingserole=100status=1),持 32 字符 system access_token存 new-api postgres users.access_token)。
  • 鉴权头Authorization: Bearer <access_token> New-Api-User: 1(缺 New-Api-User 头即 401。这是 New API 分支build fd3e95ac2902-20260527,非 one-api
  • 端点(尾斜杠必须gin RedirectTrailingSlash,否则 307GET /api/user/?p=0&page_size=N 列用户、POST /api/user/ {username,password,display_name} 建用户、PUT /api/user/ {id,username,display_name,quota,group} 设 quota、GET /api/token/?p=0 列 tokentokens 表有 name 列,可按 user+name 建/查 token
  • 折算quota_per_unit=500000usd_exchange_rate 用默认 7.3DB options 未覆盖¥100 grant_quota = round(100/7.3×500000) ≈ 6,849,315(回读落值确认)。

这套管理凭据只给离线 ops 预置脚本用,不进 game-cloud 运行时(运行时零 admin API 调用);令牌存进 docs/内网凭据与端点.md「new-api 管理令牌」节),不进 env-only遵守协议 §9 内网密钥进项目文档)。

关键约束S0 坐实,正是它决定了 provision 走离线预置池)admin 令牌不能替他人建 token——POST /api/token/ 严格自认证(New-Api-User 必须等于令牌属主,否则 401「与登录用户不匹配」body.user_id 被忽略、token 落到 root 名下),且 access_token 不可经 PUT /api/user/ 设(回读 empty。因此任何 per-user token 都得直连 new-api 的 postgresinfra-postgres:5432/new-api)才能造出来,纯 HTTP API 做不到。这把「碰 postgres 的脏活」收进离线 ops 脚本,运行时便彻底不必接触 new-api 的内部 schema。

3.3 B · 离线 ops 预置脚本:一次性预建额度池

provision 不进 game-cloud 运行时,而是一个离线脚本,宿主 mini-infraSSH 直连、绕系统代理,遵守内网直连纪律),建议 Python(可同时用 HTTP API 与 psycopg 直连 postgres把干净活和 §3.2 那一步碰 postgres 的脏活一体承载)。脚本一次预建 N 个额度账户(内测 ≤200 规模N 取略高于当前种子用户数、留补池余量)。

每个池条目四步建成S0 坐实的唯一可行路径)

  1. POST /api/user/APIroot 令牌建用户username 用确定性命名 neice_<序号>neice_001neice_200),拿 newapi_user_id
  2. DB 直写 new-api postgresinfra-postgres:5432/new-apiUPDATE users SET access_token=<确定值> WHERE id=<uid>——这一步是 S0 坐实的关键,access_token 无法经 API 设、又是下一步「以该用户身份建 token」的前提。
  3. POST /api/token/API用该用户的 access_token + New-Api-User:<uid>,即以该用户身份)建 tokenname 用 neice_<序号>unlimited_quota=falseexpired_time=不过期,拿 48 位 token_key
  4. PUT /api/user/APIroot 令牌)设 quota=¥100 折算grant_quota§3.2 口径token 侧 remain_quota 挂同额。

幂等(脚本可重跑补池):按确定性 username neice_<序号> 去重——重跑前先 GET /api/user/ 列已建用户,跳过已存在的序号,只补齐缺口到目标 N补池 = 提高目标 N 重跑脚本。每条落一行审计日志(序号 / user_id / token_id / remain_quota / 结果),单条失败可单独重试、不影响已建条目。

产出交接给 game-cloud:脚本把每个建成条目的 (newapi_user_id, newapi_token_id, newapi_token_key, grant_quota, quota_per_unit_snapshot, usd_rate_snapshot) 写入 game-cloud 池表§3.5status=FREE。两种落法二选一——(a) 脚本直连 game-cloud MySQL INSERT ... ON DUPLICATE KEY UPDATE(按 newapi_user_id 去重);(b) 脚本导出 JSON、game-cloud 侧一次性导入脚本读入。内测取 (a) 最短路(脚本本就在做 DB 脏活,多一个 MySQL 连接可接受),去重键 = newapi_user_id

¥100 ↔ quota 折算:口径与 cost.py 同源、读网关而非硬编码。GET /api/status(无需鉴权,cost.py:51-55 已这么用)拿 quota_per_unit(默认 500000/usd_exchange_rate(默认 7.3grant_quota = round(yuan / usd_exchange_rate × quota_per_unit)¥100 ≈ 6,849,315S0 实测参考值,供验收对表)。折算时把 (quota_per_unit, usd_exchange_rate, grant_quota) 三元快照随条目写进池表(quota_per_unit_snapshot/usd_rate_snapshot/grant_quota),日后网关调率不影响已发额度的审计追溯。

外部交互处置:每次 API 外呼连接超时 5s、读超时 10s5xx/超时重试 2 次(指数退避 0.5s→1s4xx 不重试postgres 直写用事务、失败回滚该条。脚本是离线一次性运行,不承载运行时 SLA但仍全程可追溯日志脱敏 token/access_token,见 3.8)。它把「三步建 token + 一次碰 postgres」这组无内建幂等的原子副作用一次性做完并落进池表运行时的幂等语义则完全由 §3.4 的 claim 状态机承载——两者解耦脚本负责「池里有货」claim 负责「一人分一个、不重分」。

3.4 C · 注册 claim消费 WU1「注册成功」事件从池 CAS 抢一个 FREE 条目绑玩家

接缝同前不变。WU1 §3.6 对外只暴露一条接缝:注册事务提交后(afterCommit)投递一个「注册成功」领域事件 / RocketMQ 消息,载荷含 userIdregisterChannelanonId不在事务内提供任何挂点且明确要求「hook 消费的是『注册成功』事件而非『password 注册成功』事件」。因此 WU2 不能在注册事务里 co-commit那是 WU1 用事件刻意解耦要避开的耦合),而是作为该事件的消费者,在 WU2 自己的线上从池 claim。宿主放 game-module-aigc新子包co-locate 派发期的池查询、passport 不感知 WU2经 RocketMQ 订阅注册成功 topic。

provision 已离线完成,运行时不开户、不调 new-api——claim 是一次纯 game-cloud 内部 DB 操作,没有任何 HTTP 外呼、没有跨 HTTP 开事务的原子性隐患,比原「运行时三步开户 + 状态机 + 三补偿腿」的方案简单一个量级。

渠道过滤(同前):只对真实种子渠道 claimregisterChannel ∈ {password, invite}sms 自动注册与 sms-mock 8888 后门跳过——避免一次性手机号每次首登都占掉一个 ¥100 池条目(见 §6 攻击面),这类若真去生成再由懒 claim 兜。

claim 的原子绑定(单条 SQL CAS幂等

  • 幂等前置:该 game_player 是否已 claim 由 uk(claimed_by_game_player_id) 保证一玩家至多一条 CLAIMED 条目——已有则 no-op 复用(重投事件 / 并发天然收敛)。
  • 抢占 FREE 条目UPDATE newapi_quota_pool SET status='CLAIMED', claimed_by_game_player_id=?, claimed_at=now(), biz_no='claim_<gamePlayerId>' WHERE id=(SELECT id FROM (SELECT id FROM newapi_quota_pool WHERE status='FREE' ORDER BY id LIMIT 1) t) AND status='FREE'(行级 CAS只有把某条 FREE→CLAIMED 翻转成功的那一路生效)。并发多消费者 / 重投由 status='FREE' 条件 + uk(claimed_by_game_player_id) + uk(biz_no) 三重收敛成一条,任何路径都不会给同一玩家占第二个条目、也不会两玩家抢到同一条
  • 抢到affected=1→ 该玩家已绑好 token_key,结束。

注册不阻断WU1 已保证事件发出即返回claim 与注册解耦,池抖动不拖垮注册。

新失败模式——池空(无 FREE 条目):这是 provision 从运行时移到离线后新引入的失败面,必须显式兜。处置:

  • claim 消费者遇池空不抛异常:池空是非瞬时故障(补池是人工 / 离线动作,不在 MQ 重试窗口内可恢复),抛异常只会把消息灌进 DLQ 空转。改为记 WARN + 触发池水位告警(提示 ops 补池),该玩家暂不绑定,注册照常成功。
  • 持久兜底 = 首次生成懒 claim§3.6 E该玩家真正要生成时派发线做一次单 CAS 懒 claim——池已补ops 重跑脚本)则绑上继续;仍空则当次干净失败给可读拒因(「额度账户准备中,请稍后重试」),玩家补池后重试即得。懒 claim 是一条 DB CAS不是慢外呼放在派发热路是安全的与原方案的三步慢开户根本不同见 §3.6)。
  • 可选的定时补绑腿best-effort默认不做补池后由 @Scheduled 扫「password/invite 渠道、注册已成功、但无 CLAIMED 池条目」的玩家批量补 claim让额度在首次生成前就绪。它需按 game_player 反查未绑玩家join 查询);因懒 claim 已覆盖真正需要额度的那一刻(生成时),此腿仅作体验前置增强、延后。

池水位告警FREE 条目数低于阈值(如 <20 或 <种子用户日增量)即告警,提示 ops 重跑脚本补池。这是池模型的核心运维信号,进观测。

stateDiagram-v2
  [*] --> FREE: ops 脚本离线预建<br/>(user+token+¥100) 写池表
  FREE --> CLAIMED: 注册/懒 claim CAS 抢占<br/>绑 game_player·写 biz_no
  CLAIMED --> INVALID: 运行时发现 token 失效(401)<br/>隔离·告警 ops(§3.9)
  INVALID --> [*]: ops 排查/补池替换
  CLAIMED --> [*]: 玩家生命周期内长期持有
  FREE --> FREE: 池空→水位告警·懒 claim 兜(§3.6)

3.5 D · 额度池表:新表 newapi_quota_poolV32.0.0

池表存的是预建条目不再是「一玩家一映射行」——ops 脚本预建时按条目 INSERTstatus=FREEclaimed_by 空),注册时某条被 claim 才翻转成 CLAIMED 并绑到玩家。它带自己的生命周期FREE→CLAIMED→可选 INVALID与审计快照折算三元、幂等单号独立成表既方便脚本批量灌数、也让 claim 有清晰的抢占边界不污染玩家主表。DO 继承 TenantBaseDO(沿用 creator/时间/deleted/tenant_id 审计列;池是系统级资源,tenant_id 落系统租户即可)。

版本号仲裁(与 WU1 并行落地必须协调):执行副本当前连续到 V30.0.0,而 WU2 依赖的 WU1game_player 增列 + mobile 放宽)已取 V31.0.0 并先落——WU2 骑在 WU1 的 schema 之上,故顺延取 V32.0.0__create_newapi_quota_pool.sql若两单同取 V31 会 Flyway 撞号、校验失败无法应用。落地前把「ls .../db/migration | sort -V | tail 复核执行副本当时最大号、若已有更高号再顺延」当硬前置(与 WU1 §3.1 同口径),不能只当脚注;且同 WU1 一样同时落契约源 contracts/db-schemas/ 与执行副本两处,守门①在本单做实。

字段级契约草案:

类型 语义
id bigint PK 池条目主键(自增)
newapi_user_id bigint, uk 预建的 new-api users.id(脚本灌数去重键)
newapi_token_id bigint 预建的 new-api tokens.id
newapi_token_key varchar(64) tokens.key48 位 sk-…敏感,随 job 下发)
grant_quota bigint 该条目预置的 ¥100 折算 quota审计
quota_per_unit_snapshot bigint 折算时网关 quota_per_unit
usd_rate_snapshot decimal(10,4) 折算时 usd_exchange_rate
status varchar(16) FREE(可 claim/ CLAIMED(已绑玩家)/ INVALIDtoken 失效隔离§3.9claim CAS 抢占的锚§3.4
claimed_by_game_player_id bigint, null, uk claim 后 ↔ game_player.idFREE 时为 null唯一键允许多 null故多条 FREE 可并存,CLAIMED 保证一玩家至多一条)
claimed_at datetime, null claim 时间
biz_no varchar(64), null, uk claim 幂等键 claim_<gamePlayerId>FREE 时为 null
remark varchar(255) 隔离原因等

newapi_token_key 是密钥落库内测阶段明文存但须视为敏感3.8 讲传输与日志脱敏,生产化时应加密列——本阶段不做,记为红线待办)。前后兼容:纯新增表,不改任何既有表结构,无迁移风险;回滚 = drop 表 + 摘事件消费者。

额度落点钉死(避免摇摆)¥100 grant 由 ops 脚本预建时写 new-api 两处,但权威口径唯一——token 的 remain_quota 是权威余额闸(预建 token 时 unlimited_quota=false、挂 grant_quotausers.quota 设同额只为账户自洽、不作限额裁决;users.used_quota 是权威消耗(网关逐笔累加)。触发耗尽 402 的是 token remain_quota→0(网关按这把 token 扣该 user。据此三处验收对同一口径预建后查 token remain_quota=grant_quota§5.1)、一次 gen 后查 user used_quota 增量§5.3)、把 token remain_quota 设近 0 触发耗尽§5.4)。池表只存 grant_quota 审计快照,不在 game-cloud 侧记余额(余额始终以网关为准)。

3.6 E · job 加 userToken 位与 per-user 门的适用边界§6.1 契约变更)

AigcGenerateExecutor.dispatchGeneric658-681组 §6.1 job 时追加一个 userToken 字段。但per-user 额度门只对「真实 member 在线创作」生效,这是一条必须钉死的边界,否则会误伤既有验证线。

为什么必须分身份:编排器批跑与 bake-off 是「伪装在线提交」——经 submitGenerate → dispatchGenericAigcTaskServiceImpl 202-204 注释确认),同样流经此处;task_sourceV17当前无 ReqVO 信号、全落 online,据 source 辨别不了。若在此一律按 member 查池、无 CLAIMED 条目即懒 claim 或干净失败n=5 收敛环 / bake-off / 脚本路会被这道闸挡死;更糟的是 admin/system 触发身份属另一套 id 空间、没有 game_player,懒 claim 会替一个 admin/system id 占掉一个 ¥100 池条目(白白耗池,这些 id 也不是真实玩家)。

据此的判据(今天就成立)——门只认 creatorUserId 是否命中一行 game_player

  • game_player(系统 / 编排器 / bake-off / admin 触发)→ 旁路:不查池、不懒 claimjob.userToken 留空worker 回落全局 key§3.7)。这条兜住越界回归。
  • 命中 game_player(真实 member 在线创作)→ 走 per-user 门:来源链 creatorUserIdAigcTaskDO 38 行既有字段)→ 查 newapi_quota_pool 取该玩家 CLAIMED 条目的 newapi_token_key → 放进 job.userToken。将来编排器据契约显式声明 source=orchestrator 后,可把 source 叠加进旁路判据、更精确。

该玩家无 CLAIMED 条目(存量 null / 注册时池空未绑)时——派发线做一次单 CAS 懒 claim。这里有个相对原「运行时开户」方案的关键收益claim 是纯 game-cloud 内部一条 FREE→CLAIMED 的 DB CAS不是三步慢外呼——原方案那条「懒开户在有界消费池(consumeThreadMax=15)里串 createUser→createToken→setUserQuota 三次分钟级外呼、首次生成高峰叠加 new-api 抖动拖垮共享池」的稳定性红线,随 provision 离线化一并消失,派发热路里放一条 DB CAS 是安全的。处置:命中 CLAIMED 条目取 newapi_token_key 放进 job.userToken;无 CLAIMED 条目则懒 claim 抢一个 FREE——抢到就用;池空抢不到当次干净失败给可读拒因(quota_exhausted,文案「额度账户准备中,请稍后重试」),同时触发池水位告警提示 ops 补池玩家待补池后重试即得§3.4 的池空处置)。无论如何绝不派一个没 token 的 create 路 job 下去

契约兼容声明(须随契约通知相关方):userToken追加字段,老 worker 收到多余字段不炸——三个 worker 入口解析 job 均走 job.get(...) 逐键取值(worker_service.py:142-146 即此模式),未知键被忽略,不做严格 schema 校验。所以这是一次向后兼容的加法:新后端 + 老 worker = worker 忽略 userToken 走全局 key行为同今天新后端 + 新 worker = 走 per-user token。无需 worker 与后端同步上线。

3.7 F · worker 取 job token 与回落策略

三个入口从「读全局 env NEWAPI_KEY」改成「优先读 job.userToken」:cheap-worker/cheap_service_driver.py:28-44_resolve_key 现从 client.get_api_key() 读 envcheap-worker/worker_service.py 的 job 消费边界、wg1/gen-worker/worker/_client.py:47get_api_key 现读 env NEWAPI_KEY)。

回落策略(须给判断)job.userToken 存在则用它;缺失则回落全局 env key。判断依据在 dispatch 层已划清§3.6)——真实 member 的 create 路由 E 保证必带 token拿不到就在后端 fail-fast 干净失败worker 见不到无 token 的 member create job而系统 / 编排器 / bake-off / spike / 脚本路本就没有 game_player 成员身份E 已按「无成员身份」旁路、不给它们塞 token它们缺 userToken正常且预期的,回落全局 key 才能让这些既有验证路继续跑。若在 worker 这层对缺失一律硬拒,会误伤这些路。所以额度口子不在 worker 层堵,而在后端 dispatch 层按身份堵死member create 路无 token 即失败、系统路旁路——worker 只做「有则用、无则回落」,并在回落时打一条 WARN便于发现本该带 token 的 member create job 异常走了共享计量)。这样既不破坏既有验证路,又不给 member create 路留静默烧共享额度的洞。

3.8 per-user token 在内网传输的安全面

userToken 是一把 new-api 调用凭据,现在要随 job 从后端流到 worker内网 HTTP dispatch。它的暴露面job 载荷若被完整打进日志、token 出现在 worker 结果回传里、dispatch 通道本身是否鉴权。处置:

  • 日志脱敏:后端与 worker 两侧都不整条打 userToken,仿 DesensitizedUtil 只留前后各 4 位;池表与 job 载荷不进 INFO 级全量日志。
  • 传输面同现状job dispatch 与 worker 回调都在内网Tailscale 100.64.x / localhost与现有全局 NEWAPI_KEY 已经躺在 worker env 里是同一信任边界——本变更没有把凭据带出内网,只是从「一把共享」变成「每人一把」,粒度更细、单把泄漏的爆炸半径反而更小(只影响单个用户 ¥100
  • 与回调 HMAC 收口同源回调方向worker→后端已有 aigc.executor.callback-secret 的 HMAC-SHA256 验签(AdminAigcTaskController 内网回调 3b-B。派发方向后端→worker当前是内网可达即信任userToken 进入载荷把「保护 dispatch 通道」的优先级抬高了一档——内测阶段维持内网可达 + 日志脱敏可接受,生产化时应给 dispatch 通道同等的服务间鉴权(记为红线待办,随 facade 回调 HMAC 收口一并规划,不在本内测闭环内实现)。

3.9 额度耗尽如何干净失败§6.5 失败因)

用户 token remain_quota 耗尽时,网关对 /v1/chat/completions 返非 2xxone-api 系为 402/403错误体含「用户额度不足 / insufficient user quota」类 message。今天 worker 把任何非 2xx 都归 llm_errorExecutorLlmClient 134-138 的 HTTP 错分支同理),用户只会看到含糊的「生成失败」。要干净失败:

  • contract-first不可只加 Java 枚举)FailureReasonEnum 的头注明写「取值与 Dify output.failureReason 严格一致,不可单边扩展(跨契约共享枚举)」。故 quota_exhausted 必须同一改动里落三处FailureReasonEnumgame-module-aigc-api)新增 QUOTA_EXHAUSTED("quota_exhausted", "生成额度已用完")contracts/api-schemas/aigc.yaml:296FailureReason.enumcontracts/dify-workflow-io.json:50output.failureReason.enum。该枚举与 Dify 输出共享、dify-workflow-io.json 归 WU3 面,须与 WU3 同批协调落quota_exhausted 由 worker/网关侧产出、Dify 工作流本身不产出,但共享枚举须登记一致,否则 docs/contract 门拦)。落库列 game_aigc_task.failure_reasonvarchar(32)quota_exhausted 15 字符,容得下)。
  • 消费方对未知值的容错须落地核实、非断言:前端 failureReason→文案 映射须有默认回落(在 game-studio 对应映射处核未知值回落到通用「生成失败」不炸DB 列无 enum 约束天然容错;这三处逐个确认后再落,别把「不炸」当断言。
  • worker 在调网关拿到非 2xx 时,分辨两类失效(确切 message/code 待 S4 真机耗尽 / 凭据失效测试确认——S0 只坐实了管理面,未测 /v1/chat/completions 的耗尽错误体):① 402/403 额度耗尽 → 回调 failure_reason=quota_exhausted;② 401/凭据失效token 被 admin 删/改、或 CLAIMED 条目存的 token_key 已陈旧)→ 不塌成 quota_exhausted,触发一次 re-claim:把该 CLAIMED 池条目隔离为 INVALID(供 ops 排查 / 补池替换、不再派发),经 §3.4 的 CAS 给该玩家 re-claim 另一个 FREE 条目,并按可重试失败回传(维持 llm_error,不为此再摊一次跨契约枚举同步成本);池空则告警 ops。内测把池条目 token expired_time 由 ops 脚本设不过期unlimited/null从源头避免自伤式过期401 应属罕见。
  • 后端 handleCallback 落终态时把该拒因透传给前端,用户看到「生成额度已用完」。

¥100 花完后的用户体验生成入口被挡3.10 的可选轻门或耗尽时的干净失败),用户看到明确的「额度已用完」文案。内测不提供自助充值,也不做运营续额闭环:每个池条目 ¥100 由 ops 脚本预建时一次性设好,运行时绝不改 quota无运行时 setUserQuota),更严禁对已 claim/已消耗条目重设额度(绝对赋值会把已花额度刷回满额=免费续杯)。若未来确需续额,那是对 token remain_quotadelta-aware 增量(读现值再加)、或引入独立 grant 流水表承载多笔——池表 uk(claimed_by_game_player_id) 一人一条、biz_no 一单,本就不为多笔续额设计。续额属「变现端到端」范畴,不在本内测闭环。

3.10 可选加项:入队前轻余额门

AigcTaskServiceImpl.enqueueWithControlPlane335 起,现有降级门①/配额并发门②/背压门③;submitGenerateretryTask 共走)可加一个可选的轻余额预检:在既有门旁查该玩家 claim 到(CLAIMED 条目)的 token 的 remain_quota,若低于一次生成的估算成本(如 ¥0.15 折算 quota则提前拒新增错误码「额度不足」省掉一次注定被网关拒的空跑。同 §3.6 的身份边界:此预检也只对真实 membercreatorUserId 命中 game_player)生效,编排器/bake-off 经此共享入队门时须旁路(无成员身份即跳过预检),否则同样会误伤批跑。

这门是 best-effort 加项,不是闭环必需:查网关失败(超时/网关抖)不阻断——直接放行让网关在真扣费点做权威判定(网关才是额度的最终裁决者,预检只是体验优化)。默认可先不做,留作接线点跑通后的体验增强。

4 步骤计划

代码物随阶段一落地,本设计只定架构级 HOW。

阶段 交付物 验证 依赖 风险
S0 实测管理面A 已完成 docs/内网凭据与端点.md 管理令牌节 + 管理面三件事实已坐实root=id1 qingse + 鉴权头 Bearer access_token+New-Api-User:1 + 端点尾斜杠 + 折算 500000/7.3/¥100≈6,849,315关键约束坐实admin 不能替他人建 token、access_token 不可经 API 设 → per-user token 必须碰 postgres 真机手工建户/建token(以该用户身份)/setQuota 各成功一次、测试用户已清理 0 残留S0 findings 已亲验) mini-infra new-api 可达 无——已实测锁定,本档 3.2/3.3/3.9 假设项已消解
S1 ops 预置脚本 + 池表B·D Python 预置脚本(建户 API → DB 写 access_token → 以该用户身份建 token API → 设 quota APIneice_<序号> 幂等补池;产出写 game-cloud 池表)+ V32.0.0 池表迁移(契约源+执行副本两处)+ DO/Mapper staging 跑脚本预建 N 条new-api users/tokens 新增 N 行、每 token remain_quota=¥100 折算、unlimited=false;池表 N 条 FREEtoken_key/快照落齐;脚本重跑不重建(按序号去重) S0 事实 postgres 直写耦合 new-api schema——限离线脚本承担、不进运行时;ls|sort -V|tail 硬复核版本号
S2 事件消费 claim + 池空处置C RocketMQ 消费 WU1 注册成功事件(渠道过滤)+ 单 CAS FREE→CLAIMED claimuk_claimed_by/uk_biz_no 幂等)+ 池空不抛/水位告警 + 懒 claim 挂点§3.6 兜) 注册后池一条 FREE→CLAIMEDclaimed_by=该 game_player.id;重投事件不二次 claimuk+CAS 拦);把池 FREE 清零后注册仍成功、水位告警触发、补池后首次生成懒 claim 绑上 S1 池 + WU1 事件接缝已落 池空是新失败面(懒 claim + 告警兜);幂等靠 uk + 单 CAS
S3 job 加位 + 身份门 + worker 取 tokenE·F dispatchGeneric 追加 userToken + member/系统身份分流(无 game_player 行旁路回落)+ 单 CAS 懒 claim(池空当次干净失败、不阻塞派发线)+ 三 worker 入口改读 job token + 回落 新后端+老 worker 回归不炸member create 路用 per-user token编排器/bake-off/n=5 收敛环仍能跑(旁路全局 key 不被误挡);无 CLAIMED 条目时懒 claim 绑上或干净失败、派发池不阻塞 S2 池 契约兼容——追加字段、worker .get 忽略未知;懒 claim 是 DB CAS非慢外呼派发热路安全
S4 耗尽干净失败§6.5contract-first 同批改三处枚举(FailureReasonEnum + aigc.yaml:296 + dify-workflow-io.json:50,与 WU3 协调)+ worker 分辨 402/403 额度耗尽 vs 401 凭据失效(后者 re-claim 池条目)+ 前端拒因回落核实 真机把 token remain_quota 设近 0 → gen → 后端落 quota_exhausteddocs/contract 门绿;前端未知值不炸 S3 网关额度错/凭据错的确切 message——S4 真机耗尽 / 凭据失效测试确认S0 只坐实管理面、未测耗尽错误体);跨契约共享枚举须与 WU3 同批
S5可选 入队前轻余额门member-only 余额不足入队即拒;查网关失败不阻断;编排器旁路不被误挡 S3 best-effort不做也闭环

5 验证方式

验收门全部可真机取证内网直连绕系统代理SSH mini-infra 走真 IP

口径钉死(对齐 §3.5权威余额 = token remain_quota、权威消耗 = user used_quota,验收对同一口径。

  1. 预置池ops 脚本跑一次,SELECT count(*) FROM users WHERE username LIKE 'neice_%'tokens 各新增 N 行、每 token remain_quota = ¥100 折算值(unlimited_quota=false,默认约 6,849,315game-cloud SELECT count(*) FROM newapi_quota_pool WHERE status='FREE' = Ntoken_key/grant_quota/快照三元落齐。
  2. 注册 claim:注册一个内测用户(走 WU1 password 路)后,池里恰有一条 FREE→CLAIMEDclaimed_by_game_player_id=该 game_player.idbiz_no='claim_<id>'claimed_at 非空;池 FREE 计数 -1。
  3. 生成扣减:记录一次真 gen 前后该 claim 到的 user SELECT used_quota FROM users WHERE id=<newapi_user_id>Δused_quota > 0;且该 token remain_quota 相应减少(权威余额闸)。
  4. 耗尽干净拒PUT / SQL 把该 token remain_quota 设近 0 → 触发一次 gen → 观察网关返 402/403 且后端任务终态 failure_reason=quota_exhausted(而非 llm_error),前端拿到可读拒因。
  5. claim 幂等:对同一 game_player_id 重放注册成功事件(或手工重投消费),池不新增第二条 CLAIMED、该玩家仍绑同一条(uk_claimed_by + 单 CAS 拦住);并发重投时验只有一条路径把某条 FREE→CLAIMED
  6. 池空降级:把池 FREE 清零后注册一个用户注册WU1成功可登录、该玩家无 CLAIMED 条目、水位告警触发ops 补池(重跑脚本)后对该玩家首次生成触发懒 claim 绑上(或定时补绑腿)。
  7. 越界不误伤:驱动一次 bake-off / n=5 收敛环批跑(系统/编排触发,无 game_player 成员身份),验其 job 不被 per-user 门挡、userToken 留空、worker 回落全局 key 正常出图(证 §3.6 身份分流成立)。
  8. 存量 null 懒 claim:对一个无 CLAIMED 条目的存量 member 触发生成,验单 CAS 懒 claim 就地绑上(池有货)或干净失败给可读拒因(池空),派发线不被阻塞。

可观测信号:池水位(FREE 计数)与 claim 成功/池空计数、池条目 status 流转(FREE→CLAIMEDCLAIMED→INVALID 隔离)、任务终态 failure_reason 分布(quota_exhaustedllm_error 可区分、worker→网关外呼日志脱敏 token运行时不存在 admin 外呼日志,佐证零运行时 admin 调用)。

6 风险与回滚

  • provision 已离线,运行时零 new-api admin 耦合(本方案相对 on-demand 的关键收益):管理凭据与 postgres 直写只活在离线 ops 脚本里game-cloud 运行时零 admin API 调用、零 new-api-postgres 连接。原「运行时开户外呼进 DB 事务 / 三步慢外呼拖垮派发池 / CAS+PROVISIONING 串行三补偿腿」那一整套原子性与稳定性风险,随 provision 离线化一并消失。
  • 新失败面:池空provision 离线化换来的代价是池会耗尽。三层兜——注册时池空不抛异常(记 WARN + 水位告警、不绑、注册照常成功)、首次生成单 CAS 懒 claim池已补则绑上、仍空则当次干净失败给可读拒因、ops 重跑脚本补池(按 neice_<序号> 幂等去重、提高目标 N池水位告警(FREE <阈值)是核心运维信号,进观测;缺了它池会静默见底、所有新用户拿不到额度。
  • claim 幂等:一玩家至多一条 CLAIMEDuk_claimed_by+ biz_no 唯一键 + 单条 FREE→CLAIMED CAS重投 / 并发收敛成一条——不会给同一玩家占第二个条目、也不会两玩家抢到同一条。这比原方案简单claim 是纯 DB 操作没有「HTTP 副作用无法回滚」的问题。
  • 越界不误伤既有验证线per-user 门只对真实 membercreatorUserId 命中 game_player)生效;编排器批跑/bake-off/n=5 收敛环「伪装在线提交」经 submitGenerate→dispatchGenericAigcTaskServiceImpl 202-204同样流经派发点无成员身份即旁路回落全局 key、绝不懒 claim否则替一个 admin/system id 白占一个 ¥100 池条目)。堵点在 dispatch 层按身份分流,不在 worker 层。
  • 账本口径唯一:权威余额 = token remain_quota、权威消耗 = user used_quotausers.quota 只作账户自洽不作限额裁决。¥100 由 ops 脚本预建时一次性设好,运行时绝不改 quota无运行时 setUserQuota,更严禁对已 claim/已消耗条目重设额度(绝对赋值会刷回满额=免费续杯);不做运营续额闭环,续额若需为 delta-aware 增量或独立 grant 流水表,属变现端到端、不在本闭环。
  • 契约一致性contract-first会被门拦quota_exhausted 是跨契约共享枚举(FailureReasonEnum 头注禁单边扩展),必须同批落 Java 枚举 + aigc.yaml:296 + dify-workflow-io.json:50 三处、与 WU3 协调前端未知值容错须逐处核实非断言。worker 须分辨 402/403 额度耗尽(→quota_exhausted)与 401 凭据失效(→隔离该条目 INVALID + re-claim 另一 FREE + 可重试失败),池条目 token expired_time 由 ops 脚本设不过期避免自伤。
  • 凭据面newapi_token_key 明文落池表、随 job 内网传输内测可接受但须日志脱敏claim 按 registerChannel 过滤——sms-mock 8888 后门与一次性手机号自动注册不 claim避免白占 ¥100 池条目。管理令牌system access token是皇冠明珠(泄漏可建任意用户/设任意额度/读全部 channel key爆炸半径远大于任何 per-user token现只被离线 ops 脚本使用,只存 docs/内网凭据与端点.md、最小权限、可轮换、标注泄漏应急,单列一条红线;new-api postgres 直写凭据同属敏感ops 脚本用,泄漏可乱改 new-api 账本),一并列红线。生产化的列加密与 dispatch 通道服务间鉴权随 facade HMAC 收口规划,不在本闭环。
  • 脏活耦合 new-api schemapostgres 直写 users.access_token 耦合 new-api 内部 schema升级可能改列名——但仅限离线 ops 脚本承担New API 升级时只需改脚本一处、不波及 game-cloud 运行时。这正是选池模型(相对让 game-cloud 加 new-api postgres JDBC 的 on-demand 混合方案)的关键收益。
  • 版本号并行仲裁WU1 与 WU2 同批新增迁移——WU1 取 V31、WU2 顺延 V32,落地前 ls .../db/migration | sort -V | tail 硬复核,避免 Flyway 撞号。
  • 对 WU1 的依赖面WU2 骑在 WU1 的 mobile 放宽可空之上;mobile 可空的跨模块回归trade/aigc/遥测等假定非空的消费方、脱敏空安全)与防枚举验证码闸门归 WU1 验收(主线 WU 归属WU2 不重复挑仅登记此依赖WU1 未证前 WU2 的 member 身份查询不假定 mobile 非空。
  • 回滚:本设计全为加法(离线 ops 脚本 / 新池表 / 事件消费者 claim / job 追加字段 / 新枚举值),回滚 = 摘事件消费者 + worker 恢复读全局 key + drop newapi_quota_pool + 撤 userToken 字段 + 回撤三处枚举ops 脚本是离线工具,不用则不跑、删除即可;老全局 NEWAPI_KEY 路径始终保留,回滚即退回今天的共享计量行为,零数据损伤。

附 图清单与状态

媒介 位置 状态
图0 一图看懂 inline SVG §0 随文house style浅底/实线现行/虚线待办/红线拒绝路)
图1 六接线点数据流 mermaid §3.1 随文(事实源)
图2 池条目 FREE→CLAIMED claim 状态机 mermaid §3.4 随文(事实源)
图3 边界 in/out mermaid §2 随文(事实源)

关联 commitcb9f135d。收口时按 frontmatter sot-impact 把可存留结论折进「生成引擎运行时」(§6.1/§6.5) 与「数据模型」两个 canonical SoT并把 quota_exhausted 同步进 contracts/api-schemas/aigc.yamlcontracts/dify-workflow-io.json 两处共享枚举(与 WU3 协调本设计档降为留痕。S0 已实测坐实(管理端点/鉴权头/折算/「admin 不能替他人建 token」均已验证本档 3.2/3.3 的原「待实测」管理面假设项已消解provision 据此定为离线预置池 + 注册 claim§3.9 的耗尽 / 凭据失效错误码 S0 未覆盖,仍待 S4 真机确认。