# B4 四链路(广告/收益/分享/互动)staging 烟测登记 > 日期:2026-06-09 | 执行:测试工程师 subagent | 目标环境:staging(mini-desktop, http://100.64.0.7:48080) > 目的:给 MVP 总账一份「真实/桩」的诚实清册,**不修代码、不下结论性承诺**。 > 判据:HTTP 200 ∧ body.code==0 ∧ 结构合理 = 通;受控业务错误码精确命中 = 链路活但按设计拒绝。 > 写库纪律:全程零业务写入,唯一例外为 2 条 telemetry 测试事件(eventId 前缀 `b4smoke-`,已留清理 SQL,见 §6)。 --- ## 1. 结论速览(总表) | 链路 | 判定 | 一句话 | |---|---|---| | 广告 | **部分真实** | 后端计费链(幂等/归因/落账/合规闸门/T+1 对账锚点)真实,但计价与激励校验是 mock SPI、staging 0 个广告位、**前端完全未接后端计费 API**(宿主桩弹层自产自销) | | 收益 | **部分真实** | 账户原子账本/提现状态机/80% 分成结算代码真实且幂等,但打款是 mock 状态机、**结算 Job 在 staging 被关闭(xxl.job.enabled=false)永不运行**、前端无收益页入口、全链路 0 数据 | | 分享 | **部分真实** | 端点活:发布门禁真实(project.status==4)、utm_source 渠道归因真实、versionId 回填真实;但 **OG 标题/封面/描述=空串桩**、分享域名写死占位 `https://wanxiang.ai/s/` | | 互动 | **部分真实**(排序权重路径=**已证**,走 telemetry) | interact 幂等落库+聚合计数回显真实、前端 InteractBar 已接线;但 **interact 信号未接排序权重**(排序的 like/share 权重实际来自 telemetry 事件,B2 已证+本次复证)、举报转 compliance 是占位日志、互动态回显恒 false | staging 数据现状(只读核查,烟测前后均如此,证明探针零污染): ``` ad_slot=0 ad_revenue=0 trade_account=0 trade_income=0 trade_withdraw=0 interact_log=0 ``` 即:**四条链路在 staging 从未被真实使用过**(对比:feed/telemetry 主链路 B1/B2 已有数据)。 --- ## 2. 路径发现方法 - 模块定位:`game-cloud/game-module-ad`(广告)、`game-module-trade`(收益/分成/提现)、`game-module-feed`(分享+互动)、`game-module-telemetry`(互动遥测口径)。 - app 侧前缀:`/app-api` 由框架按包名 `controller.app.*` 自动添加(各 controller 头注释自述,如 AdController.java:28;并经本次 curl 实测证实路径可达)。 - Flyway V6(ad)/V7(trade)/V10 均无 INSERT 种子(grep 实证)→ 广告位等运营数据需人工配置,staging 没人配过。 --- ## 3. 各链路详情 ### 3.1 广告链路(game-module-ad) **端点烟测表** | # | 路径 | 方法 | HTTP | code | 判定 | 证据 | |---|---|---|---|---|---|---| | 1 | `/app-api/ad/slot/list-enabled` | GET | 200 | 0 | 真实(但空配置) | 实测 `{"code":0,"data":[]}`;DB `game_ad_slot`=0 行;AdSlotService 真查库(AdController.java:48-51) | | 2 | `/app-api/ad/report/impression` | POST(假 slotId 探针,零写) | 200 | 1111002000 | 部分真实 | 实测精确命中 `AD_REPORT_SLOT_DISABLED`(ErrorCodeConstants.java:27),证明 路由→VO 校验→Service→slot 查库 全链活;计费核心见下 | | 3 | `/app-api/ad/report/reward` | POST(同上探针) | 200 | 1111002000 | 部分真实 | 同上,同走 `bill()` 计费核心 | **真伪判读(源码亲核)** - 真实部分:计费核心 `bill()` = slot 校验→合规闸门→(traceId,eventType) 幂等先查+DuplicateKeyException 并发兜底→gameId 经 ProjectApi 反查创作者归因→落 `game_ad_revenue` 台账(settle_status=0,statDate=今日)——AdRevenueServiceImpl.java:108-171。结算回标幂等 0→1(同文件 179-188)。 - 桩部分: - **计价 mock**:唯一 provider 是 `MockAdProvider`,`calcRevenue = ecpmFloor/1000` 整除(MockAdProvider.java:29-35),`verifyReward()` 恒 true(同文件 38-41);工厂对未注册 provider(csj/gdt)一律降级 mock(AdProviderFactory.java:41-44)。 - **合规桩**:`isMinor()` 恒 false、`currentSessionCount()` 恒 0(AdComplianceChecker.java:60-75,注释自述「MVP 对接点」)。 - **前端断链(亲核)**:game-studio 无 `src/api/ad.ts`(api 目录仅 aigc/feed/project/runtime/telemetry);宿主侧广告是桩弹层——GamePlayer.vue:12「ad/pay 桩弹层:点击模拟激励视频/支付,回调 rewarded:true」、:315-336 模拟看完直接回 `rewarded:true`,**不调 `/ad/report/*` 也不拉 `/ad/slot/list-enabled`**。即后端计费 API 真实可用但当前无任何调用方。 ### 3.2 收益链路(game-module-trade) **端点烟测表** | # | 路径 | 方法 | HTTP | code | 判定 | 证据 | |---|---|---|---|---|---|---| | 4 | `/app-api/trade/income/page` | GET | 200 | 0 | 真实(0 数据) | 实测 `{"code":0,"data":{"total":0,"list":[]}}`;真实分页查 `game_trade_income`(IncomeServiceImpl.java:94-96,userId 边界强制) | | 5 | `/app-api/trade/withdraw/page` | GET | 200 | 0 | 真实(0 数据) | 实测同上结构;WithdrawServiceImpl.java:161-163 | | 6 | `/app-api/trade/withdraw/apply` | POST(amount=1 门槛探针,零写) | 200 | 1106002000 | 真实 | 精确命中 `TRADE_WITHDRAW_BELOW_MIN`;门槛校验先于一切写入(WithdrawServiceImpl.java:70-72,Nacos `trade.withdraw-min` 默认 500 分),实证未写库 | | 7 | `/app-api/trade/account/mine` | GET | 未实测 | — | 真实(代码判读) | **有写副作用故未实测**:首查即建零值账户(AccountServiceImpl.java:33-58 getOrInitAccount),为守住「不写库」纪律跳过;同 controller 其余端点已证路由与鉴权通 | **真伪判读(源码亲核)** - 真实部分: - 账户原子账本:余额增减全部下沉行级 UPDATE(`SET balance=balance±? WHERE balance>=?`),0 行=余额不足防超扣(AccountServiceImpl.java 头注释 17-21 + freeze 72-78)。 - 提现状态机:insert(uk_biz_no 幂等)+freeze 同事务、审核 CAS 流转防二次发钱/退款(WithdrawServiceImpl.java:66-106、109-158)。 - 分成结算:T+1 拉 ad 未结算台账→逐笔 `uk_source` 幂等入账(net=gross×Nacos `trade.creator-share` 默认 0.80,BigDecimal 向下取整)→先入账后回标+补偿(SettlementServiceImpl.java:54-89;IncomeServiceImpl.java:40-91)。 - 桩/休眠部分: - **打款 mock**:审核通过即 1→2「已打款」纯状态翻转,channel=`mock`,无真实支付渠道(WithdrawServiceImpl.java:44-45、126-135)。 - **结算调度休眠**:唯一触发器是 XXL-Job `tradeSettlementJob`(SettlementJob.java:39),admin 控制器无手动结算端点(TradeAdminController 仅 withdraw/audit/income/report 四端点,grep 实证);而 staging `xxl.job.enabled: false`(application-staging.yaml:90-92)且 docker 仅 mysql+redis 两容器(实测 `docker ps`)→ **即使广告计了费,钱也永远到不了创作者账户**。 - **前端无入口**:game-studio 无 `src/api/trade.ts`,无收益/提现页面。 ### 3.3 分享链路(game-module-feed · share) **端点烟测表** | # | 路径 | 方法 | HTTP | code | 判定 | 证据 | |---|---|---|---|---|---|---| | 8 | `/app-api/feed/share/9001?channel=wechat` | GET | 200 | 0 | 部分真实 | 实测 `data={gameId:9001, shareUrl:"https://wanxiang.ai/s/9001?utm_source=wechat", ogTitle:"", ogImage:"", ogDescription:"", channel:"wechat", versionId:9001}` —— OG 三字段空串是**桩的活体证据** | | 9 | `/app-api/feed/share/999999` | GET(未发布探针) | 200 | 1103002001 | 真实 | 精确命中 `FEED_SHARE_GAME_NOT_PUBLISHED`,发布门禁生效 | **真伪判读(源码亲核)** - 真实部分:发布门禁经 ProjectApi 读 status 仅 PUBLISHED(4) 放行(FeedServiceImpl.java:477-482);utm_source 渠道归因拼接(:462-466);versionId 经 ProjectApi 回填供落地页直达即玩(:261-264、278-284)。 - 桩部分:`ogTitle/ogImage/ogDescription` 写死空串,TODO 待对接 project 本体元信息(FeedServiceImpl.java:257-260);分享域名写死占位 `https://wanxiang.ai/s/`,TODO 待 Nacos 化(:463-464),该域名可达性/备案未验证。 - 前端入口:已接线(game-studio `src/api/feed.ts:73-79` getShareMeta + `src/views/share/Share.vue`),属 B2′ 已跑通的前端面。 - 与 B2 的关系:B2 实证的是「share **遥测事件** → share_count → quality_score」;本链路是「分享**落地页元数据**生成」,两者独立、互不替代。 ### 3.4 互动链路(game-module-feed · interact + telemetry 口径) **端点烟测表** | # | 路径 | 方法 | HTTP | code | 判定 | 证据 | |---|---|---|---|---|---|---| | 10 | `/app-api/feed/interact` | POST(action=99 非法探针,零写) | 200 | 1103001000 | 部分真实 | 精确命中 `FEED_INTERACT_ACTION_INVALID`(校验先于写入,FeedServiceImpl.java:195-198),链路活且实证未写库(interact_log 测后仍 0) | | 11 | `/app-api/telemetry/events/batch` | POST(b4smoke- like+share 各 1 条,任务豁免写入) | 200 | 0 | **已证** | 实测 `{"code":0,"data":{"accepted":2,"rejected":0}}`;落库核验:`game_telemetry_event` 两行 process_status=1;`game_telemetry_game_stat` gameId=9002 当日 like_count=1、share_count=1 —— B2 链路复证成功 | **真伪判读(源码亲核)** - 真实部分:interact = 动作枚举校验(1 赞 2 藏 3 享 4 报,FeedActionEnum.java:22-25)→ 幂等 upsert `game_feed_interact_log`(命中唯一键翻转 active,可取消语义正确)→ 聚合计数回显(FeedServiceImpl.java:193-241)。前端已接线:`src/api/feed.ts:60-65` + `src/components/InteractBar.vue`。 - 桩/缺失部分: - **未接排序权重**:`FeedInteractLogMapper` 仅 feed 模块内部使用(全仓 grep 实证,消费方只有 FeedServiceImpl/FeedConvert);controller 宣称「点赞/收藏/分享/举报 → 排序权重」(AppFeedController.java:64)**名不副实**——排序的 like/share 权重实际来自 telemetry 事件聚合回灌(EventIngestServiceImpl.java:185-194 计数 → :150-162 QUALITY_REFRESH 回灌 feed_rank)。 - 举报转 compliance = 占位日志(FeedServiceImpl.java:227-232,TODO 事务内禁远程调用,待 MQ/Feign)。 - 匿名互动 anon_id = 空串 TODO(:207);feed 流互动态 liked/favorited 回显恒 false(:118 TODO + FeedConvert.java:38)。 - 「已证」的边界说明:B2 已实证 + 本次 b4smoke 复证的是 **telemetry 路径**(like/share 事件 → game_stat 计数 → quality_score → feed_rank.sort_score 重排);而 **interact 路径**(按钮点击 → interact_log → 计数回显)落库真实但只到「回显」为止,不参与排序。staging `game_feed_interact_log`=0 行也佐证 B2′ 实玩未触发过 interact 写入。 --- ## 4. 缺口清单(MVP 上线前各链路还缺什么) **广告(上线即赚钱的硬前提全缺)** 1. 前后端断链:game-studio 无 ad API 接线,宿主桩弹层自回调 rewarded:true,不拉广告位、不上报计费 → 后端台账永远 0。 2. 无真实广告联盟 SPI(穿山甲/优量汇),MockAdProvider 计价=ecpmFloor/1000、激励校验直通——真实接入还有联盟审核闸门(日历闸门)。 3. 广告位运营数据为零(Flyway 无种子、staging ad_slot=0),需 admin 端配置流程跑一遍。 4. 未成年识别、单会话频控均为恒放行桩(合规风险,上线前必须接真实信号)。 **收益(钱的管道有管无水)** 5. T+1 结算调度在 staging 关闭(xxl.job.enabled=false 且无 xxl-job-admin 容器),且无手动触发端点 → 广告收入到创作者账户的链路从未端到端跑通过一次。 6. 打款是 mock 状态机,未接微信/支付宝企业付款(涉及支付资质日历闸门)。 7. 前端无收益/提现页(无 trade.ts),创作者「看见钱」的入口缺失。 8. `/trade/account/mine` 首查建账的写副作用本次未实测,待一次受控验证。 **分享(能分享但分享卡片是白板)** 9. OG 标题/封面/描述空串 → 微信/QQ 分享卡片无图无题,传播转化必伤;需对接 project 元信息。 10. 分享域名写死占位 `https://wanxiang.ai/s/`,未 Nacos 化,域名可达性/备案未验证。 **互动(信号进了库但不进排序)** 11. interact 信号未接入排序权重,与 controller 文档宣称不符;需决策:要么接通(interact→telemetry 事件或直刷 rank),要么改文档口径承认排序只认 telemetry。 12. 举报→compliance 受理链路未接(占位日志),举报功能存在合规缺口。 13. feed 流互动态回显恒 false:用户刷新后看不到自己点过赞/收藏,体验断点。 14. 匿名互动 anon_id 未透传(未登录用户互动会归到 userId=0)。 --- ## 5. 烟测覆盖与局限 - 覆盖:11 个探测中 10 个实测(4 GET + 5 POST + 1 落库核验),1 个因写副作用主动跳过(标注于 §3.2 #7)。 - 局限:错误路径只探了首个闸门(如 ad 探针停在 slot 校验,未实测归因失败/会话超限分支——已由源码判读补位);admin 侧端点(withdraw/audit、slot 配置)未实测(需 admin 会话且涉写库)。 ## 6. 测试残留物(可清理) 仅 2 条 telemetry 事件 + 1 行当日聚合增量(gameId=9002,2026-06-10 行 like_count/share_count 各 +1): ```sql -- 清理(如需): DELETE FROM `ruoyi-vue-pro`.game_telemetry_event WHERE event_id LIKE 'b4smoke-%'; -- 聚合行修正(或整行删除当日 9002 行,下次事件会重建): UPDATE `ruoyi-vue-pro`.game_telemetry_game_stat SET like_count=like_count-1, share_count=share_count-1 WHERE game_id=9002 AND stat_date='2026-06-10'; ``` 其余六张链路表(ad_slot/ad_revenue/trade_account/trade_income/trade_withdraw/interact_log)测前测后均为 0 行,零污染。