games-development-ai/docs/agent-specs/2026-06-11-W4单位经济埋点-review.md
zizi a2d10be66b docs(w4-metrics): W4 三待拍创始人拍板全采纳——回填 §8.1(裁定回填铁律)
2026-06-11 创始人拍板:①假设值设硬红线(真账单前一律【假设·待测】、不作预测口径、不对外锁价)②gross语义现定schema形状(game_ad_revenue 存G总消耗+N渠道净额+渠道扣率快照,分成从N算、对账用G),实际值留真实化波③对账归属推迟到支付真实化波。三条均不阻塞→W4埋点review spec解锁,待排期开execution(③④独立小波)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 13:54:27 +00:00

39 KiB
Raw Blame History

W4 单位经济埋点 · Review 版 SpecHJ-W4-METRICS-001

2026-06-11 起草 维护:主 agent review 版:结论先行、供人决策、降认知负荷、不写可执行代码 上游依据:docs/mvp/单位经济敏感性模型.md下称【模型】R3 拍板后唯一口径§7 测量计划 关联件newapi 计费 review v22026-06-11-newapi计费平面集成-review.md/ Wave4 community-biz executionV14 预占登记)/ events.schema.json 契约 #5


0. 结论先行(一段话)

本波 = 给 B2 数据回路补「单位经济测量measurement」能力让【模型】§1/§3/§5 的【假设·待测】数字逐项变【实测】,覆盖四类指标:① LLM token 计费 ② 素材生成成本 ③ 广告产值 ④ 创作者应得。

范围边界(已收敛,不再浮动):本波 = ③④ 后端按游戏埋点的独立小波(不是仅交付 spec、也不推迟到 M4recon 已证③④数据源就位、改动链清晰可做,见 §8.0 S1 范围裁决已收敛)。范围只到「埋点 measurement + 按游戏 curl/DB 读出」不含 M4 支付——不做真实记账/真实支付/对账重建/控成本/IP 真实分账(继承 M4 边界与 newapi review v2 推迟裁定)。

核心取舍三条(a) per-game 粒度分两档落地——③④ 走后端、game_id 在 ad 侧天然就位故做真 per-game,①成本侧 new-api logs 无 game_id 列故MVP 接受 per-batch 时间窗均摊(不强行透传 game_id 进网关),②素材无源则降级为「不建桶、只登记口径」(b) ③④落点已定向(不再二选一)——③走 ad 侧加聚合查询game_id+idx_game 已就位,不落 telemetry 列)、④走 trade 侧补 game_id 贯通ad.yaml契约+DTO+列+透传四步链 + admin group by均在收益侧自有表,零跨模块写 seam;明确否决「往 telemetry game_telemetry_game_stat 加 ad/income 聚合列」路径telemetry-api 仅暴露 3 枚举、无 Stat 写 APIinsertOrAccumulate 是冻结签名 8 列硬编码、trade pom 不依赖 telemetry「加列」实为新建跨模块 Feign 写 seam非加列且会把恰一次保证从 ad/trade 的 uk_trace/uk_source 错位到 telemetry 的 uk_event_id详见 §4.1(c) 复用优先于新建——③④复用 ad/trade 现表与现幂等键,仅④的 game_trade_income.game_id 占 V14 一次 ALTER不新建独立宽表(维度=游戏×日,现表已兼容)。

成本/单位口径说明(避免跨平面误读)收益侧ad/tradeDB 全程用BIGINT 整数),成本侧(编排器 newapi_cost.py/report.py全程用元(浮点)——两者分属两个平面、两个精度域,对账时须显式换算,禁止把「元」成本与「分」产值同列求差(详见 §6.3)。

本波不碰任何业务代码,仅交付本 review 版 spec下一步进 execution 版才落地③④后端埋点。


1. 背景与目标

1.1 B2 回路现状(已 e2e 真实,但有四类指标缺口)

B2 数据回路当前是 HTTP 请求线程内同步事务实现MQ 消费者 TelemetryEventConsumer 是空壳,真实链路全在 EventIngestServiceImpl.ingestBatch 同步 @Transactional 内),事件 → quality_score → feed 重排已端到端接通、有单测 + e2e 证据。成本侧另在编排器平面:newapi_cost.py 读 new-api logs.quota 权威成本已落(薄片①),与后端 telemetry/trade 收益侧是两个分离平面

链路形态澄清(消除契约描述与实现的张力)contracts/events.schema.json:5V5.0.0 建表头注描述的「→ MQ 异步入库 → qualityScore → feed」是目标态M5 余项),当前实现为同步事务写(见上)。本波不改契约/建表的链路描述execution 一律以代码现状(同步写)为准,勿以契约描述误判链路形态。

1.2 四类指标缺口盘点(带证据)

指标 权威源现状 缺口 证据
① LLM token 计费 已落地(编排器计量 + new-api logs.quota 权威 + 报告 §8 渲染;实测权威 ¥0.031/款 来自模型 §5 merge-prod-20 批实测) 批级总量、真实数据待下次批跑;per-game 仅「批级总量÷acceptCount」近似 llm_client.py:166-197/newapi_cost.py:136-141/report.py:316;模型:48,63
② 素材生成成本 零数据 ComfyUI 未部署,当前生成管线 assets 恒空集合,无文生图 AigcGenerateExecutor.java:396setAssets(emptyList()));模型:46,61
③ 广告产值 数据源已落、按游戏可聚合ad 侧 game_ad_revenue 有 game_id + idx_game 缺按游戏粒度的聚合查询/报表落点;反作弊仅幂等键+IP 限流桩 V6.0.0__create_game_ad.sql:54-77;模型:25,61
④ 创作者应得 分账流水已落game_trade_income 有 net/share_rate 快照) trade 侧零 game_id 引用seam DTO 丢弃 game_id→ 按游戏归集断裂IP 叠加分账未实现(代码恒 80% 平铺) V7.0.0__create_game_trade.sql:59-79AdRevenueRespDTO.java:17-40SettlementServiceImpl.java:43-44

1.3 本波补什么

一句话:把③④从「全平台日期聚合」升到「按游戏game_id × 日)可 curl/DB 取到把①的口径锚定为「per-batch 均摊MVP+ 待网关账单替换假设单价」,把②降级为「登记口径、接文生图时再建桶」。产出 = 用实测列替换【模型】§1/§3/§5 的【假设·待测】标注(模型:61


2. 非目标(明确不做)

继承 M4 变现真实化边界 + newapi review v2 推迟裁定,本波显式不做

  1. 真实记账 / 真实支付 / 真实打款trade 结算链当前是 mock providerMockAdProvider 单次收入=ecpmFloor/1000本波不接真实广告联盟、不接微信支付、不做提现打款真实化。
  2. per-creator 用户开通 + 支付→配额同步 + 订阅/充值 + UI 购买流:按 newapi review v2 推迟到支付通道真实化后(阻塞 ICP 7-20 工作日 + 微信进件 1-3 周3-5 周内无法 e2e订阅定价未定
  3. 对账重建:渠道结算单 T+1/月结对账、三方对账(微信 trade_no ↔ 平台订单 ↔ new-api top_ups.trade_no属支付真实化波本波不做。
  4. 素材 GPU 成本桶(无源则降级)ComfyUI 未部署、assets 恒空 = 零生成零成本,本波不建素材成本桶,仅登记「接文生图/GPU 渲染时再补独立桶」口径new-api logs 不覆盖 GPU除非图片模型也走 new-api 通道)。
  5. 控成本 / 生成定价锁价售卖:生成成本可忽略——实测权威 ¥0.031/款【模型】§5 merge-prod-20 批实测20 创意/57 调用/19 accept¥0.59 总/¥0.0103 次newapi review §0 另给 ¥0.009/款(单次 602 quota 估算,非初稿 ¥0.02-0.04,评审 F 已否定高估 3-5×——两数来源不同0.009=单次 quota 推算0.031=批实测),非连续区间两端execution 引数以模型 §5 实测 ¥0.031/款为准。计量的 MVP 价值是「成本归属/基建预演」非「控成本」new-api QUOTA_PER_UNIT/FX 是假设值,只供成本台账,不得用于对外锁价(硬边界,见 §6/§8.1)。
  6. MQ 异步化M5 余项):当前同步写已 e2e 闭环MQ 化前置依赖 mini-infra 补 RocketMQ/Nacos当前缺默认不纳入本波(见 §5 权衡,留待裁决)。
  7. IP 叠加分账真实落账IP 素材标记字段在 ad/trade/project 现表全缺,「该游戏是否用 IP + IP 方是谁」数据源完全缺失,本波不建 IP 分账流水,仅在 share_rate 快照位为口径演进留位(见 §4.4)。

3. 推荐方案(四类指标:数据源 → 挂点 → per-game 落库)

3.1 数据血缘总图(四类指标如何汇到 per-game 载体)

flowchart TB
  subgraph GEN["生成平面(编排器 / aigc成本侧"]
    LLM["LLM 调用<br/>设计1+对抗1+修复≤1+裁判1 ≈4±1次"]
    NAPI[("new-api PG logs<br/>type=2 消费日志<br/>sum(quota) 含真实倍率")]
    CMFY["ComfyUI 文生图<br/>未部署·assets 恒空)"]
    LLM -->|model×token_name 聚合| NAPI
    CMFY -.->|零数据·本波不建桶| X1["②素材成本=降级登记"]
  end

  subgraph PLAY["消费平面telemetry / ad / trade收益侧"]
    IMP["ad_impression / ad_reward<br/>@PermitAll + 幂等键 + IP限流"]
    ADREV[("game_ad_revenue<br/>★有 game_id + idx_game<br/>revenue_amount(分)")]
    SETTLE["SettlementJob T+1<br/>recordIncome 逐笔入账"]
    TRDINC[("game_trade_income<br/>net=gross×share_rate<br/>⚠无 game_id 列")]
    IMP -->|bill 计费| ADREV
    ADREV -->|getUnsettledRevenue| SETTLE
    SETTLE -->|分账后流水| TRDINC
  end

  subgraph SINK["per-game 读出口(本波裁决:收益侧自有表·零跨模块写)"]
    Q3["③ ad 侧 group by game_id<br/>(零建表·idx_game 已就位)"]
    Q4["④ game_trade_income 加 game_id 列<br/>(V14 ALTER) + admin group by"]
  end

  NAPI -.->|①per-batch 均摊<br/>无 game_id 列·MVP 不透传| REPORT["报告 §8 / 成本台账<br/>estCostYuan÷acceptCount"]
  ADREV ==>|③广告产值<br/>ad 侧天然 per-game| Q3
  TRDINC ==>|④创作者应得<br/>补 game_id 贯通| Q4

  style ADREV stroke:#2a2,stroke-width:2px
  style Q3 stroke:#22a,stroke-width:2px
  style Q4 stroke:#22a,stroke-width:2px
  style X1 stroke:#a22,stroke-dasharray:4

读图要点:双线箭头(==>= 本波要打通的 per-game 真链路(③④,均在收益侧自有表);虚线(-.->= 本波接受降级/不建桶(①均摊、②无源)。绿框 game_ad_revenue = 唯一天然 per-game 就位的源;③只读聚合不建表、④补 game_id 列;否决往 telemetry game_telemetry_game_stat 加 ad/income 列(理由见 §4.1)。

3.2 指标① LLM token 计费 —— 维持现状 + 锚定 per-batch 均摊口径

  • 数据源new-api PG logstype=2sum(quota) 含真实模型倍率)为权威;llm_client.py 累加 usage 为 fallback/交叉校验。
  • 挂点:已就位,无需新挂点(newapi_cost.py 只读、零生成路径依赖、零回归)。
  • per-game 落库裁决MVP 接受 per-batch 时间窗均摊report.py:316 estCostYuan÷acceptCount不强行把 game_id/trace_id 透传进 new-api 网关new-api logs 无 game_id/version_id/trace_id 列,要 per-game 需在生成调用编码 token_name 或时间戳夹逼单 task复杂度高、价值低——成本本身可忽略。本波仅显式声明此为 MVP 近似口径,真 per-game 归属留作「后续可细化」(模型:63
  • 次薄片(可选):按 --platform-token 标签分「平台铺量 vs 创作者自产」成本桶(机制已在 newapi_cost.py:111-172 预埋,需给 agent-loop 批跑挂独立 token_name 标签才生效);按创作者 token-name 归属拿「每创作者成本」,绕开 per-creator 建号阻塞。

3.3 指标② 素材生成成本 —— 降级登记,不建桶

  • 数据源现状零。Tier1 自研 Canvas 用代码渲染、无需文生图,AigcGenerateExecutor.java:396 assets 恒空。
  • 裁决本波不建素材成本桶。仅在 spec/模型登记口径:「接文生图/GPU 渲染时再建独立成本桶;若图片模型走 new-api 通道则可纳入同一 logs.quotatype=2否则需独立 GPU 计量源」。避免后补的同时不引入空表。

3.4 指标③ 广告产值 —— ad 侧天然 per-game加聚合 + 看板维度

  • 数据源game_ad_revenue(计费级台账,game_id/creator_user_id/event_type/revenue_amount(分)/stat_date/trace_id,已建 idx_game(game_id,stat_date))。收入口径=reward 的 impression 不计收入防双算、eCPM 取广告位 ecpm_floor、归因 projectApi.getCreatorUserId(gameId)、幂等键 (traceId,event_type,tenant_id) 命中 uk_trace。
  • 挂点ad 侧已是按游戏可聚合的天然落点,无需改 ad 表结构,只需加「按 game_id × stat_date 聚合查询 + 看板维度」。
  • per-game 落库裁决(已定向·非二选一)③直接在 ad 侧加聚合查询 + admin 报表 group by game_id(零冗余、读在 ad 模块、改动面最小),不往 game_telemetry_game_stat 加列(避开新建 telemetry 写 seam理由见 §4.1)。归因 creator_user_id 在计费时已反查定格落库快照V6 注释「由 game_id 反查后落库定格」),聚合直读已定格的 game_id/creator_user_id与运行时 project 状态解耦——被删/下架游戏的历史收入仍按定格快照可聚合、不漏账、不在聚合期重新反查 projectApi

3.5 指标④ 创作者应得 —— 补 game_id 贯通 trade 侧(最小改动链)

  • 数据源game_trade_income(分账后流水,net=gross×share_rate、share_rate 快照定格、source_ref=game_ad_revenue.id 对账锚、trace_id 全链路)。
  • 断裂点:跨模块 seam DTO AdRevenueRespDTO 只有 5 字段id/creatorUserId/revenueAmount/statDate/traceIdgame_id 不在其中 → trade 整个模块零 game_id 引用、game_trade_income 无 game_id 列。
  • 裁决:要让④按游戏归集,走最小改动链四步(缺一即断):
    1. 契约 ad.yamlx-feign-contracts.operations[getUnsettledRevenue].output.itemFieldsgameId: Longad.yaml:389-394 是该 DTO 字段的授权源DTO 注释「字段严格对齐契约·不多不少」,先改契约再改 DTO,否则契约与代码漂移)。
    2. AdRevenueRespDTOgameId(与上一步契约同步;新增字段不算 breaking但触碰已合入契约须通知 ad/trade stakeholder
    3. game_trade_incomegame_idV14 ALTER裸 BIGINT 业务列无 FKV14 两副本字节一致同 §4.4禁第三副本入 trade 单模块——V7 残留三副本是反面模板不可仿照,详见 §4.4)。
    4. recordIncome 透传 gameId注意现签名为 7 参,加 gameId 后须同步改调用方 settle
  • 挂点:照搬 Wave4 notifyIncomeChanged 范式——聚合/透传一律只在 firstRecord 真实入账分支内执行(命中 uk_source 的幂等回查分支 firstRecord=false 不得累加/重计,否则同 source_ref 重放会虚高)、失败不中断结算循环仅 log.error、系统身份 withSystemIdentity 注入 LoginUser(id=0) + finally clearContext 防 NOT NULL 拒绝。④落 trade 自有表 = 同事务安全、零跨模块写,否决任何「telemetry 回写卷入分账事务」

3.6 指标①③④ 与看板的关系

A1 看板(平台产值/创作者应得)落点 = TradeAdminController.getRevenueReport(现仅按 settle_date 区间聚合 Σgross/Σnet加 game_id/creator group by 维度(③读 ad 侧聚合、④读 trade game_trade_income.game_id)。因③④已定向落在 ad/trade 自有表(不落 telemetry见 §4.1),看板读口走 ad/trade 的 admin 端而非 telemetry game-stat 读口。本波只到「按游戏可 curl/DB 取到」,看板 UI 渲染属 A1 看板波


4. 数据模型取舍

4.1 ★核心裁决③④落点为何选「收益侧自有表」而非「telemetry 加列」

现状V14 当前未占用(实测 ls V14* exit=2V12/V13community/biz已双副本落盘、Wave4 已收口contracts/db-schemas/ + yudao-server/.../db/migration/ 各两副本),故 V14 是已收口后的下一空位、无并行撞号风险。Wave4 execution §0.2:409 登记「V14 预留给 W4 埋点加列(本波不写,仅占号)」。

flowchart LR
  Q{"③④聚合落点?"}
  Q -->|★选定·收益侧自有表| A["③ ad 侧加聚合查询(零建表)<br/>④ game_trade_income 加 game_id 列(V14 ALTER)<br/>+ admin group by game_id"]
  Q -->|❌否决·telemetry 加列| B["往 game_telemetry_game_stat<br/>加 ad/income 列"]
  A --> A1["优点:复用 ad/trade 现幂等键<br/>(uk_trace / uk_source)<br/>同事务安全·零跨模块写 seam<br/>仅④占 V14 一次 ALTER"]
  B --> B1["❌真实成本=新建跨模块 Feign 写 seam:<br/>telemetry-api 仅 3 枚举无 Stat 写 API<br/>insertOrAccumulate 冻结签名 8 列硬编码<br/>trade pom 不依赖 telemetry<br/>且恰一次保证从 uk_trace/uk_source<br/>错位到 telemetry uk_event_id"]

  style A stroke:#2a2,stroke-width:2px
  style B stroke:#a22,stroke-width:2px,stroke-dasharray:4

裁决依据(为何否决 telemetry 加列、为何不新建宽表)

  • 否决「加列到 game_telemetry_game_stat:该路径表面是「加列」、实为新建跨模块写 seam——game-module-telemetry-api 仅暴露 3 个枚举文件TelemetryEventEnum/ErrorCodeConstants/EventProcessStatusEnum无任何 Stat 写 APIgame_telemetry_game_stat 的唯一写通道 insertOrAccumulate冻结签名的 8 列硬编码 INSERT...ON DUPLICATE KEY UPDATE数据回路小波·修2只含 6 计数器);game-module-trade-server pom 仅依赖 ad-api + community-api、不依赖 telemetry。要让 ad/trade 金额写进 telemetry 表,须「新增 telemetry-api 写 seam新跨模块 Feign 契约 + @Primary 实现)+ 扩冻结的 insertOrAccumulate」或「trade 直写 telemetry 表破模块边界」,均非『加列』。更关键telemetry 聚合的恰一次保证来自入库层 uk_event_idGameStatMapper:72 注释明证「聚合层纯加法、恰一次由 uk_event_id 挡重放」),而 ad/trade 的去重轨道是 uk_trace/uk_source——把金额累加进 telemetry 表时,uk_game_date 只保证「同游戏同日一行」、绝不保证「同一笔不重复累加」,防线只能来自上游 firstBilling/firstRecord 判定(见 §6.1)。故 telemetry 加列既增跨模块成本、又埋幂等错位雷,裁定否决
  • ③ = ad 侧加聚合查询game_ad_revenue 已有 game_id+idx_game(game_id,stat_date),直接 group by 即可,零建表、零 V14
  • ④ = trade 侧补 game_id 贯通 + 占 V14 一次 ALTERgame_trade_incomegame_id 列(裸 BIGINT、无 FK落在 trade 自有表 = 同事务安全、零跨模块写,配合 §3.5 四步链贯通 game_id。
  • 不新建独立宽表W4 维度 = 游戏×ad/trade 现表已兼容;新建宽表只引冗余 + 独立一致性维护,无收益。

4.2 ④ game_id 加列ALTER落地铁律

④ 的 game_trade_income.game_id 加列复用 V10本仓唯一 ALTER ADD COLUMN 先例)两条铁律:

  1. 新增列若进唯一键必须 NOT NULLV10 event_id VARCHAR(64) NOT NULL DEFAULT ''头注守门①「MySQL 唯一键允许多 NULL → 可空则幂等失效」)。本波 game_id 不进唯一键uk_source 不含 game_id但仍给 NOT NULL DEFAULT 0 兼容存量行。
  2. 加列须给 DEFAULT 兼容存量行(存量 trade_income 行 game_id 回填 0 或 later 反查补execution 定)。
  3. ★ALTER 放宽业务列凡涉及写路径必须复核审计列/系统身份engineering-conventions §1.2:42 红线V11 漏 creator/updater 即此雷)。④ game_id 由 recordIncome/settle 现有写路径透传填充、写路径已在 withSystemIdentity 范式内,不引新写点。因③④已否决「telemetry 加列」(见 §4.1),本波无 telemetry 表回写故不存在「trade 结算任务跨模块回写 telemetry」的系统身份/事务复核问题。

4.3 按游戏粒度的聚合策略:同步写 or MQ 异步

  • 现状B2 回路是同步写(EventIngestServiceImpl.ingestBatch 在 HTTP 事务内完成聚合 + 远程回灌 feedMQ 消费者空壳。
  • 裁决倾向本波沿用同步写(与现有 B2 一致、零基建依赖)。③广告产值=ad 侧只读聚合查询group by game_id无写累加、无幂等风险④创作者应得=recordIncome 透传 gameId 后写入 game_trade_income.game_id透传一律只在 firstRecord=true 真实入账分支(命中 uk_source 的回查分支不写),按游戏归集由 admin 侧 group by game_id 读出。MQ 异步化留待 §5 权衡裁决(前置缺 RocketMQ/Nacos

4.4 契约扩展(三处,缺一即断)

flowchart LR
  subgraph C["W4 埋点契约扩展面"]
    DB["DB #2 Flyway<br/>V14 两副本字节一致<br/>(contracts/db-schemas/ + yudao-server/.../db/migration/)<br/>禁第三副本入单模块"]
    EC["错误码段<br/>挂 telemetry→复用 104<br/>挂 ad→复用 111<br/>挂 trade→复用 106<br/>★无独立新段"]
    EV["events.schema.json<br/>★仅当新增遥测事件才动<br/>+ 同步 TelemetryEventEnum 枚举"]
  end
  DB -.常规收口.-> SEQ["V14 单序列资源<br/>(V12/V13 已落·无并行撞号)<br/>两副本字节一致 + 先 push 再 pull"]
  EV -.本波不动.-> RED["③④不新增事件名<br/>→ events.schema.json/枚举零改动<br/>(红线本波不适用)"]

  style RED stroke:#2a2,stroke-width:2px
  • DB #2 Flyway V14:命名 V{x.y.z}__{desc}.sql两副本字节一致contracts/db-schemas/ 授权源 + yudao-server/.../db/migration/ 唯一执行副本md5 全等);绝不放单模块 -server/db/migration/(同版本多 classpath jar 触发 Flyway 重复校验失败V11 守门①根因);已合入禁改、回滚写新补偿迁移 V14.0.1 DROP。V12/V13community/biz已双副本落盘、Wave4 已收口V14 为无争议的下一空位、无并行撞号——只需常规单序列收口纪律(两副本字节一致 + 先 push 再 pull★防呆V14 ALTER 的是 game_trade_income,但 V14 文件禁止放 game-module-trade-server/src/main/resources/db/migration/——本仓 V7 残留第三副本trade 单模块 src与两授权副本 md5 全等 dd5bb43…反面模板、不可仿照,照它布局会正好踩中 Flyway 重复校验雷。
  • 错误码段:③在 ad 复用 111 / ④在 trade 复用 106W4 埋点无独立新段;新增子码在对应 -apiErrorCodeConstants.javanew ErrorCode(1_106_00X_***,"中文") 登记(③只读聚合通常无需新错误码)。
  • events.schema.json本波结论不动:③④仅在 ad/trade 侧加聚合查询/加 game_id 列、不新增遥测事件名,故 events.schema.json 与 TelemetryEventEnum 本波零改动。补确证review 阶段已结案,非 execution 待办):枚举已含 AD_IMPRESSION/AD_REWARD/INCOME_SETTLEDTelemetryEventEnum.java:45-47events.schema.json eventRegistry 已登记 ad_impression/ad_reward/income_settled:66-68契约↔枚举两处已双向就位。仅当未来确需新增事件名才回到双处同步红线conventions §1.2:43只改契约不改枚举 = 事件静默 rejectedaccepted:0 但信封 code:0鉴权波 user_login e2e 实证)。
  • tenant_id 单租户口径统一表述(避免实现 agent 混淆DB 列 tenant_id BIGINT DEFAULT 0V5/V12/V13 列默认);运行期系统身份/聚合任务注入的 LoginUser tenantId=1L(随 execution §8.2 范本tenant.enable=false。两者分属 DB 列默认与运行期注入两层、不矛盾execution 须显式声明。

4.5 全仓零 FOREIGN KEY 硬范式

所有跨表关联靠业务列 + 应用层维护DB 层不建外键yudao 一贯范式14 个迁移文件零 FK 命中)。④若加 game_trade_income.game_id = 裸 BIGINT 业务列,逻辑关联 game_project.id,无 FK。归属隔离靠 Mapper 显式 creator_user_id/user_id WHERE 谓词 + getLoginUserId()token 解析非前端入参),非 @DataPermission 框架黄金模块零此注解——W4 客户侧端点同此口径,勿假定框架已隔离。


5. 关键权衡

权衡维度 选项 A 选项 B 本 spec 倾向
同步埋点 vs 异步聚合 同步写(现状 B2零基建依赖事务内完成 MQ 异步(快速 ACK + 削峰,前置缺 RocketMQ/Nacos A 同步写与现状一致、本波不引基建MQ 化作 M5 余项留裁)
埋点精度 vs 性能 每条 engagement 事件同步触发 feed rank UPDATE现状高频写放大 按 gameId 节流/批量回灌 维持现状(同步写每 play_end 打一次 upsertRank性能优化留作观察项,热门游戏 rank 写放大若实测成瓶颈再做
成本源权威性 new-api logs.quota 实测(含真实倍率) 客户端估算 fallback假设单价 2.0 A 权威 + B fallback 交叉校验已落权威为准client 降 fallback
per-game 粒度(①成本) per-batch 时间窗均摊(已落,无 game_id 列) 真 per-game需透传 game_id 进网关) A 均摊成本可忽略、透传复杂度高价值低MVP 接受近似,显式标注)
per-game 粒度(③④收益) 收益侧自有表(③ ad 侧只读聚合 + ④ trade 加 game_id 列) 往 telemetry game_telemetry_game_stat 加列 / 新建独立宽表 A 收益侧自有表(零跨模块写、复用 uk_trace/uk_source 幂等telemetry 加列实为新建跨模块写 seam + 幂等错位,宽表引冗余——均否决,见 §4.1
eCPM/人均展示档 锁单值 保留三档敏感性扫描15/30/60 B 三档扫描(外部核查仅从业者区间 20-80 低置信,须保留敏感性 + 显式声明「非收入预测」,模型:4 性质声明)

6. Blast Radius / 风险 / 兼容

6.1 挂点触及模块 vs Wave4 已挂 community notify

flowchart TB
  W4["W4 埋点挂点"]
  W4 --> AD["ad: 加 game_id×stat_date 聚合查询<br/>(只读·已有 game_id·零写累加)"]
  W4 --> TRD["trade: recordIncome 透传 gameId<br/>+ game_trade_income 加 game_id 列(V14)<br/>+ admin group by"]
  W4 --> ORC["编排器: newapi_cost.py<br/>已落·零改动"]

  WAVE4["Wave4 已挂: settle firstRecord<br/>→ communityNotifyApi.notifyIncomeChanged"]
  TRD -.同一挂点区.-> WAVE4

  WAVE4 --> CHK{"撞点检查<br/>(execution 阶段必验)"}
  CHK --> OK["同进程 @Primary 本地调用<br/>同 withSystemIdentity 范式<br/>失败不中断结算循环<br/>→ 可并存·照搬范式"]

  style WAVE4 stroke:#a82,stroke-width:2px
  • 关键风险:④的 trade 挂点与 Wave4 已挂的 communityNotifyApi.notifyIncomeChanged同一 SettlementServiceImpl.settle firstRecord 分支区。两者均为「结算任务触发的写库/通知埋点」,范式相同可并存(同进程 @Primary、withSystemIdentity 注入 LoginUser(id=0)、失败仅 log.error 不中断循环。execution 阶段须验:加 gameId 透传不破坏 recordIncome 既有 uk_source 幂等、不破坏 notifyIncomeChanged 触发条件。
  • ★firstRecord/firstBilling 守门(防重复计数硬约束):③④的写/透传一律只在首次真实入账分支执行——③若在 ad 侧累加(本波③走只读聚合查询、无累加,此约束主要针对未来;若改累加须落 firstBilling=true④的 game_id 透传只在 firstRecord=true 分支(命中 uk_source 的回查分支 firstRecord=false 不得写/累加)。AdRevenueServiceImpl.bill 命中 uk_trace 走幂等回查 return AdBillingResult(existing, false)——任何挂在 bill 主路径而非 firstBilling 分支内的累加,会在重复上报时虚高 sum(gross)。验收项6须含「同 trace_id/source_ref 重放后按游戏 sum 不变」的 staging 实测(非仅单测 mock
  • 事务边界(已定向·无跨模块写)recordIncome 是 @Transactional写流水 + 累加账户同事务),④的 game_id 落在 trade 自有表 game_trade_income(同事务、同进程、无跨模块远程调用),天然事务安全。因③④已否决 telemetry 加列§4.1),本波不存在「telemetry 表回写卷入分账事务」的两难——明确否决任何把 telemetry 写操作放进 trade 事务的方案;④若未来需异步外写,照 Wave4 notifyIncomeChanged 事务内 try-catch 吞异常范式(提交后补写失败仅 log、不回滚已发的钱
  • 幂等(三轨道不可混淆):③复用 ad uk_trace(trace_id,event_type,tenant_id);④复用 trade uk_source(source,source_ref,tenant_id)均含 tenant_id 三列,单租户 tenant_id=1L 恒定下等价,但实现 agent 写去重谓词须按三列、勿按二列假设);①成本侧由 new-api logs 去重。注意telemetry 的 uk_event_id聚合纯加法的恰一次来源GameStatMapper:72与 ad/trade 的 uk_trace/uk_source 是两条不相干的去重轨道——③④的恰一次只能靠上游 firstBilling/firstRecord 守门 + ad/trade 自身幂等键,与 telemetry uk_event_id/uk_game_date 无关(这也是否决 telemetry 加列的幂等理由,见 §4.1)。

6.2 兼容性红线

  • DTO + 契约改动(成对)AdRevenueRespDTO 加 gameId = 新增字段不算 breakingREADME §三),但必须同步改契约授权源 contracts/api-schemas/ad.yamlx-feign-contracts.operations[getUnsettledRevenue].output.itemFieldsDTO 注释「严格对齐契约·不多不少」,只改 DTO 不改 ad.yaml = 契约与代码漂移——故④的契约面是「ad.yaml itemFields + DTO + V14 列 + 透传」四点,缺一即断;触碰已合入契约须通知 ad/trade 双方 stakeholder。
  • Flyway V14:已合入禁改;V12/V13 已落盘、Wave4 已收口V14 为无并行撞号的下一空位,常规单序列收口(两副本字节一致 + 先 push 再 pull且禁第三副本入 trade 单模块V7 残留三副本是反例,见 §4.4)。
  • 量纲红线telemetry quality_score0-100 运营质量分)≠ aigc 生成质量分0-1③④收益维度落在 ad/trade 自有表、不与 quality_score 同表混算(这也是③④不落 telemetry 的附带好处)。
  • 三空列与本波无关(不强绑)game_telemetry_game_stat 现有三空列favorite_count/report_count/load_fail_count列+DO+VO 全就位但 insertOrAccumulate 不累加)是 B2 既有缺口——与本波单位经济埋点完全无关,明确排除在本波 DoD 之外(本波③④不碰 telemetry 表,更不会「反正要 ALTER 这张表」顺手补这三列;三空列累加若做属独立改动,留作 open question §8.2#9

6.3 对账口径硬约束(单位 / 取整 / 日期键 / mock 不变式)

execution 落③④对账时必须钉死以下四条,否则 per-game 聚合会误判失败或埋真实化漂移雷:

  1. 单位双平面(禁同列求差)收益侧ad/tradeDB 全程BIGINT 整数);成本侧(编排器 report.py/newapi_cost.py)全程浮点estCostYuan/yuan_per_quota。两者两种单位、两个精度域——跨平面对照须显式换算并标注,禁止把「元」成本与「分」产值直接同列求差;若未来成本入后端台账须先定「成本也落分」口径再迁。
  2. 逐笔向下取整,禁用聚合反推:链路有两次独立 floor——MockAdProvider.calcRevenue 整除 ecpmFloor/10002999 分→2 分)、SettlementServiceImpl 算 net 又 setScale(0,RoundingMode.DOWN)。逐笔 floor 之和 < 总额 floorΣnet ≠ (Σgross)×0.80,偏差随该游戏笔数线性放大。验收项3 的对账以「逐笔」为权威(单笔 net==floor(gross×rate)、单笔 gross==floor(ecpm/1000)禁用「总额×0.80」反推;若必须聚合对账须显式声明容差 = 该游戏笔数 ×(0,1) 分量级。
  3. 日期键统一上游 stat_date:③④按游戏×日聚合统一以 game_ad_revenue.stat_date= trade settle_date 透传V7 注释二者相等)为日期键,非 create_time。验收项3 须含「③ Σgross[game,stat_date] 与 ④ Σgross[game,settle_date] 同游戏同日相等」断言,锁死 T+1/SettlementJob 重跑补结算下的日期一致性。
  4. mock 不变式(真实化阻塞前置):登记硬不变式——「game_ad_revenue.revenue_amount 在 MVP/mock 下 ≡ 实收净额 N(渠道扣率=0故 G==Nnet=gross×0.80 暂自洽」。真实接联盟波必须先确定 revenue_amount 存 G 还是 N:若存 G总消耗未扣渠道 40%),则 trade 分账须×(1-渠道扣率) 再 ×creator-share(先 ×0.6 再 ×0.8否则违反【模型】R3N=0.6G、平台恒 0.12G),且已落 game_trade_income 历史快照不可回算 → 静默口径漂移。此条为支付真实化波的阻塞前置(见 §8.2#5/#6

7. 验收标准

每类指标须可由 curl/DB 实证「按游戏取到」,且不破坏 B2 现有回路:

# 验收项 验证方法
1 ①成本 per-batch 口径显式标注 报告 §8 渲染含「单游戏成本估算 ≈ ¥Xper-batch 均摊·MVP 近似·未计素材 GPU」+ new-api 权威成本可 newapi_cost.py --batch 跑出(已落,回归不破)
2 ③广告产值按游戏取到 curl /admin-api/... 或 DB select game_id, sum(revenue_amount) from game_ad_revenue where stat_date=? group by game_id 返非空、与单游戏 impression/reward 台账对账一致;直读已定格 game_id/creator_user_id被删/下架游戏的历史收入仍可聚合不漏账(不在聚合期重新反查 projectApi
3 ④创作者应得按游戏取到 game_id 贯通后 DB 可 select game_id, sum(net_amount) from game_trade_income where settle_date=? group by game_id对账以逐笔为权威(单笔 net==floor(gross×share_rate)、单笔 gross==floor(ecpm/1000)trace_id/source_ref 双锚),禁用「Σnet==Σgross×0.80」反推(两次 floor 致聚合不等§6.3);日期键统一上游 stat_date③Σgross[game,statDate] 与 ④Σgross[game,settleDate] 同游戏同日相等
4 ②素材成本口径登记 spec/模型登记「素材桶降级、接文生图再建」,无空表引入
5 B2 回路不破坏 既有单测全绿EventIngestServiceImplTest 等staging e2eplay_end → quality_score → feed 重排链路不变曝光中性裁决不破impression 零增量不建全零行);telemetry 表零改动(③④不落 telemetry
6 幂等(重放不重计) ③④staging 重放同 trace_id/event/source_ref 后按游戏 sum 不变(非仅单测 mock④ game_id 透传只在 firstRecord=true 分支、命中 uk_source 回查分支不写uk_trace/uk_source均含 tenant_id 三列)守门
7 系统身份/审计列 ④ game_id 经现有 recordIncome/settle 写路径透传、写路径已在 withSystemIdentity 范式内creator/updater 落 '0'系统身份staging 实测不撞 NOT NULL单测 mock 测不到,须 staging 实证);本波无 telemetry 跨模块回写故无新写点系统身份雷
8 敏感性声明 eCPM 三档15/30/60保留扫描 + 对外/对内数字保留【假设·待测】标注,不作预测口径

8. 待确认项(已收敛裁决 + 仍待创始人/排期拍的口径)

8.0 本 review 版已收敛的裁决(不再浮动,进 execution 不需再问)

定稿时把原「★待裁」中纯设计/范围裁决按 plan 倾向固化,使 execution 拿到闭合范围。

# 原待裁项 收敛结论 落点
S1 ③④埋点归属(并入 M4 / 独立小波) 本波 = ③④后端按游戏埋点的独立小波(数据源已落、改动链清晰可做,不推迟 M4 §0 范围边界
S2 ③④落库形态telemetry 加列 vs 收益侧自有表 vs 宽表) ③ ad 侧只读聚合 + ④ trade 加 game_id 列否决 telemetry 加列(新建跨模块写 seam + 幂等错位)、否决新建宽表(引冗余) §4.1
S3 events.schema.json / 枚举是否动 本波不动(③④不新增事件名,枚举+契约已双处就位) §4.4
S4 V14 收口动作 常规单序列收口(两副本字节一致 + 先 push 再 pullV12/V13 已落、无并行撞号;禁第三副本入 trade 单模块 §4.4/§6.2
S5 ①成本 per-game 关联键 接受 per-batch 均摊(成本可忽略、透传复杂度高价值低,显式标注 MVP 近似) §3.2/§5
S6 quality_score 分母口径 与本波解耦B2 既有缺口,单独裁决,本波不碰)
S7 三空聚合列是否顺带补 与本波解耦、排除在 DoD 外(本波不碰 telemetry 表) §6.2
S8 MQ 异步化是否纳入本波 不纳入(基建 RocketMQ/Nacos 未就位,留 M5 §5
S9 IP 叠加分账 本波不建IP 标识数据源 ad/trade/project 全缺,仅 share_rate 快照位留演进位) §2#7

8.1 仍须创始人/排期拍的口径(仅 3 条,均为口径/真实化路线,非本波实现裁决)

创始人拍板 2026-06-11全采纳:① 设硬红线——PRICE_PER_MTOKEN_YUAN/QUOTA_PER_UNIT/USD_CNY 三假设值拿到 new-api 真实账单/实配前一律保留【假设·待测】、不作预测口径、不对外锁价;② 现定 schema 形状——game_ad_revenue 真实化后存 G总消耗+ N渠道实收净额+ 渠道扣率快照,分成从 N 算、对账用 G实际值留真实化波防历史不可回算对账归属推迟到支付真实化波。三条均不阻塞 execution——W4 埋点 review spec 自此解锁,待排期开 execution③④独立小波

  1. 【★假设值红线】token 单价/换算三个假设值替换路径绑 A1 LLM 实名充值闸门PRICE_PER_MTOKEN_YUAN=2.0llm_client.py:43 假设占位)、QUOTA_PER_UNIT=500000newapi_cost.py:34 编译默认非库内实配)、USD_CNY=7.2(假设 FX。需确认拿到 new-api 网关真实账单/QUOTA_PER_UNIT 实配前,对外/对内数字是否一律保留【假设·待测】标注、不得作为预测口径、不得用于对外锁价(建议写成硬边界)。

  2. 【★真实化路线·gross 语义 + 分成深度】合并裁决:代码现状 net=gross×0.80 平铺(无渠道扣 40%、无 IP 叠加、无平台恒 20% 显式建模与【模型】R3渠道扣40%→净额N→创作者80%N→IP15-25%N 从创作者份额出→平台恒20%N有分叉。本波裁定仅埋点观测mock 链 revenue_amount 既是 G 也是 N、渠道扣率=0 暂自洽,见 §6.3 mock 不变式);待创始人/排期拍的是真实接联盟波的前置game_ad_revenue.revenue_amount 真实化后存 G总消耗还是 N渠道实收净额——决定 trade 的 ×0.8 是否需先 ×0.6(若存 G 则 trade 须先 ×(1-渠道扣率) 再 ×creator-share否则违反 R3 且历史快照不可回算)。字段命名/口径须在真实化波开工前定义防漂移。

  3. 【★对账归属·排期】③④与渠道结算单对账归属:本波③④到「平台内按游戏可 curl/DB 取到」即止;与渠道结算单 T+1/月结对账(外部账单 ↔ 平台台账)属支付真实化波,待排期定何时做、谁做。

注:原 §8.1#1/#2落点二选一 / 范围归属)与原 §8.2#4/#8/#9/#10/#11成本键 / 分母 / 三空列 / MQ / V14 收口)已在 §8.0 收敛不再作为开放裁决§8.2#5/#6分成深度 / gross 语义)合并为本节第 2 条。