games-development-ai/docs/agent-specs/2026-06-10-真实鉴权与匿名玩家-execution.md
zizi 8a59a35133 docs(audit)+docs(mvp): 三向审计修复全链入库——报告+W1回填+创始人拍板波+W2清洗+三件产出
- 审计报告 HJ-AUDIT-001 入库 + W1 文档回填波(技术决策版/Doc B/开发团队版废止横幅/AGENTS.md/两规则档/tech-decisions)
- 创始人拍板波(2026-06-10 晚,回填铁律逐项执行):
  · R4=维持 D2 按 D2 改建(M-c 建 merge→idle→tycoon,动作类降 P1 留契约)→ D2 复审注+tech-decisions #6
  · 鉴权七项全拍(§9.1:C 验证码+邀请码旁路/受限激活/开放注册/创作限白名单/一键登录/实名提现前/纯客户端 anonId)→ glossary+events.schema.json 同步
  · A1 闸门看板建账(12+1 项含短信报备+大模型登记/算法备案/分账选型,主体已确认)+ 律所合规咨询 brief
  · 奇绩 ★1/2/3 定稿 + 品牌造梦→绘境清扫(4 档正文+文件名、demo 改名 huijing-ai-demo.html、禁投/禁外发横幅、内部引用 5 处)
  · M-c 四细节(两批 merge 先行/idle 纯前端+storage/10 校准+20 正式/拖拽为主)
  · R3 收口(叠加规则=IP 从创作者份额出·净额基数·平台恒 20%,eCPM 档 15/30/60)→ D3 复审注+glossary+BP:299 勘误
- W2 对外清洗收口:BP 改造版红线清洗(20 处锁风系红线词清零/独家→非独家/绝对化清零/06-10 实证+三线排序入文,留 3★ 待创始人)
- 三件产出:鉴权 execution 版(V11/system 内扩展/14 @PermitAll 端点/NOT NULL 硬边界)+ Mc 模板波 review 版(已拍)+ 单位经济敏感性模型(基准 324 元/月/千DAU,覆盖基建需 1.33 万 DAU→实证 B 端现金线优先)
- 两本账同步:作战清单(五件拍板项清零)+ 进度总账(三行执行记录)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 08:51:30 +00:00

42 KiB
Raw Blame History

真实鉴权与匿名玩家身份 · execution 版

  • 文档编号: HJ-PASSPORT-EXEC-001
  • 日期: 2026-06-10
  • 状态: 待主 agent 验收(评审版七项拍板已全落,见 §0
  • 唯一设计依据: docs/agent-specs/2026-06-10-真实鉴权与匿名玩家-review.md(含 §9.1 创始人七项拍板)。本文不得与其矛盾;执行中发现矛盾,停工上报,以评审版为准修订本文。
  • 读者: 实现 agent 分工认领;主 agent 验收
  • 环境铁律: 构建/测试一律 mini-desktopgit push→pull 同步,本机严禁 mvn/npm buildstaging app 在 mini-desktop http://100.64.0.7:48080;批跑依赖 Bearer test1 的链路零中断§10 保障措施);密钥只走环境变量;含中文 SQL 必须 --default-character-set=utf8mb4;前端验证门=npm run buildvue-tsc --noEmit 是假门禁,记忆 frontend-spine-built)。

0. 拍板前提(评审版 §9.1,七项全拍,本文全按拍板结果起草)

# 决策 拍板结果(本文落点)
1 首发登录方式 C验证码为主 + 邀请码旁路§5 passport 端点三件套;旁路退役开关 §5.4
2 报备前激活玩家漏斗 是·受限激活邀请码注册实现§4.2 邀请码表 + §5.2
3 玩家注册 开放(受限期凭邀请码;报备后 invite-register 退役、验证码全量)
4 创作/发布 限 A2 白名单game_player.creator_flag + PlayerApi 校验§6.3
5 游客转正 互动即弹一键登录(前端 §9.4;影子账号合并不做)
6 实名收集 提现前game_player 仅预留加密字段,本件不做收集流程)
7 匿名身份 B纯客户端 anonId + 聚合侧异常剔除(剔除桩 §8glossary/events.schema.json 已修订完毕,亲核 §2.4,不需再改)

1. 目标与范围边界

1.1 目标

  1. 种子创作者真实登录手机号验证码Debug 渠道后台查码)→ 发 userType=MEMBER 真 OAuth2 token → 创作/发布全链真身份A2 名单(~5 人creator_flag 置位即可用。
  2. 匿名玩家零门槛:免登刷 feed / 试玩 / 遥测(读端点 @PermitAll + anonId 透传),互动才弹一键登录。
  3. 邀请码旁路:陌生玩家凭 手机号+邀请码 注册(零短信依赖),报备后开关退役。
  4. 身份衔接:登录请求携带 anonId 落 game_player.first_anon_id + 前端登录后补发 user_login 遥测事件anonId+userId 双带1001/1 错位消除。
  5. staging 批跑零中断mock(test1) 与真 token 并存;新增真 token 黄金闭环 e2e。

1.2 明确不做(继承评审版 §3实现 agent 不得擅自扩界)

  • 三方社交登录(微信/抖音/小程序);多租户开启(维持 tenant.enable=false,新表带 tenant_id=1生产短信供应商采购与签名报备A 轨闸门);防沉迷/适龄展示v2.0member 模块复活及其运营包袱(积分/等级/找回密码/换绑/注销)。
  • 实名收集流程不做(仅表字段预留);匿名影子账号+转正合并不做(拍板 5=A
  • 服务端匿名票据不做(拍板 7=B聚合侧剔除只做最小桩§8阈值可粗暴
  • 生产 profile 不在本件建立yudao-server/src/main/resources/ 现仅 application-{dev,local,staging}.yaml(实查 2026-06-10——"生产关死 mock + test1→401 负路径"挂生产部署轨,本件只在 spec 留验收注记§11 用例 E10
  • game-admin 前端零改动(含邀请码管理页,裁决见 §5.4)。
  • 不做 anonId 服务端校验/签名(拍板 7=B 的既定代价);不做 P-ACC-02 全功能(隐私政策仅占位文案,评审版 §3 口径声明)。

2. 代码现状亲核账2026-06-10 逐文件实查;与评审版不符处 ⚠ 标出)

行号以 dev/2.0.0 工作区为准,漂移时以语义定位。

2.1 鉴权框架与配置

文件 亲核事实 与评审版比对
game-cloud/yudao-framework/yudao-spring-boot-starter-security/.../filter/TokenAuthenticationFilter.java :60-65 先 buildLoginUserByToken 查真 token、查不到才 mockLoginUser:92-95 userType 不匹配抛 AccessDeniedADMIN token 调 app-api 被拒);:110 注释"线上一定要关":117-129 mock 实现(:118 mockEnable 开关、:122 mockSecret 前缀、:126 截后缀为 userId 一致
.../config/SecurityProperties.java :38-40 mockEnable 默认 false:44 mockSecret 默认 "test":45 permitAllUrls 配置位 一致
.../config/YudaoWebSecurityConfigurerAdapter.java :125-141 收集 @PermitAll 注解 URL 按 HttpMethod 放行 + :141 合并 permit-all-urls 配置——加注解即免登,无需改安全配置类 评审版未展开,机制实证
game-cloud/yudao-server/src/main/resources/application-staging.yaml :153 tenant.enable: false:154-155 captcha.enable: false:157 mock-enable: true:158-159 permit-all-urls: [/actuator/**] 一致(评审版引 :152-157
yudao-server/src/main/resources/ 仅 application-{dev,local,staging}.yaml 三份 profile生产 profile 未建立 一致

2.2 system 基座能力(落点依据)

文件 亲核事实
yudao-module-system-server/.../service/sms/SmsCodeServiceImpl.java :53-67 发码频控仅手机号维度(:56-59 send-frequency、:61-63 当日上限);:65-66 按 IP 每日/每小时限制是上游 TODO:93-110 验码(不存在/过期/已用三态)
yudao-module-system-api/.../enums/sms/SmsSceneEnum.java :19 MEMBER_LOGIN(1, "user-sms-login", "会员用户 - 手机号登陆") 场景已内置
yudao-module-system-server/.../controller/admin/auth/AuthController.java :129-135 POST /sms-login + :138-139 POST /send-sms-code@PermitAll、:120-123 POST /register——admin 侧验证码登录范式完整passport 照此克隆
yudao-module-system-server/.../api/oauth2/OAuth2TokenApiImpl.java @RestController + @PrimaryM1 修法注释"同 PermissionApiImpl 范式"createAccessToken(userId, userType, clientId, scopes) user-type 无关,可直发 MEMBER token
yudao-module-system-server/.../controller/app/ 已有 app/dictapp/ipapp/tenant 三个 app 端控制器目录——system 内建 app 端控制器有既成先例
yudao-module-system-api/.../enums/ErrorCodeConstants.java system 段 1_002_***_***,现用业务子段最大 1_002_028notify send
yudao-module-system-server/pom.xml :100-104 protection starter 依赖被注释"TODO 芋艿:暂时去掉"

2.3 存量 app 端控制器(@PermitAll 改造面)

  • @PermitAll 在 game-module-* 全部 server 代码 0 命中grep 实证)——评审版 §1.3"app-api 无一处 @PermitAll"属实。
  • app 控制器 getLoginUserId 28 处 / 7 文件grep -c 实证aigc 5 / feed 3 / project 5 / runtime 4 / studio 4 / telemetry 3 / trade 4admin 侧 3 处 3 文件——评审版 §1.3 计数口径完全属实
  • 各端点逐行清单见 §6.1/§6.2。
  • 评审版未提的硬边界contracts/db-schemas/V3.0.0__create_game_runtime.sql:94 game_runtime_session.player_user_id BIGINT NOT NULL——匿名开试玩会话session/start userId=null会撞 NOT NULL 约束(RuntimeSessionServiceImpl.java:50 setPlayerUserId(userId) 直落)。V11 必须 ALTER§4.3),否则"匿名试玩"只是半截。
  • telemetry 侧匿名已留位:V5.0.0__create_game_telemetry.sql:23-24 user_id NULL + anon_id NOT NULL DEFAULT '',零改动。
  • RuntimePackageServiceImpl.java:194-203 预览归属校验 :198 userId == null 即拒——取包端点放开后 play 场景免登、preview 场景天然兜底,无需新代码。

2.4 拍板 7=B 的文档同步现状(亲核,不需本件再改)

文件 现状
.agents/knowledge/glossary.md"DataPermission 之外的隔离"词条,约 :40 已修订:免登读路径 + 纯客户端 anonId + 聚合侧剔除 + ⚠2026-06-10 显式变更声明
contracts/events.schema.json:28 已修订"纯客户端生成的稳定匿名 ID…身份不可信防伪造/刷量由聚合侧…剔除兜底——2026-06-10 鉴权拍板 §9-7=B…"
game-module-telemetry/.../vo/EnvelopeReqVO.java:64 未修订,仍写"匿名 token 派生的稳定匿名 ID"——本件回写§15.1 给修改稿)。评审版引 :61-66实为 UserVO :59-66、anonId 在 :64微差如实记

2.5 与评审版不符处汇总(修正声明)

  1. protection starter 引入面:评审版 §5A 称"仅 yudao-server/system/bpm 引入"——实查 system pom :100-104 与 bpm pom :85 均为注释状态,真正生效的只有 yudao-server/pom.xml:207(外加 yudao-dependencies :356 的版本管理。结论不变game-module-* 均未依赖),但引入面表述修正为"仅 yudao-server 实际引入"。
  2. 免登清单规模:评审版估"约 7-10 个"——实查放行清单为 14 个端点§6.1:普通读/上报 12 + ad 计费 2评审版漏列 compliance rating 与 ad slot 拉取、session/end。
  3. game_runtime_session.player_user_id NOT NULL:评审版未提(见 §2.3V11 新增 ALTER。
  4. TelemetryEventConsumer 注释口径过时TelemetryEventConsumer.java:12,:17-18 仍写幂等键 uk_dedup(traceId,event,ts),但 V10 已切 uk_event_id 且同步聚合实际在 EventIngestServiceImpl(消费者是骨架占位)。评审版 §5A 引该文件作幂等出处时口径偏旧;幂等真身=eventIdEnvelopeReqVO.java:23-25)。不影响结论(对变造刷量无约束依然成立),如实记录。
  5. 其余抽查mock 过滤器顺序、staging yaml 行号、28 处/7 文件计数、SmsCodeServiceImpl :65-66、AppFeedController.interact :66 anon_id 预埋注释)与评审版一致

3. 落点定稿system 内扩展(评审版倾向项,正式采纳)

结论:不建新模块 game-module-passportyudao-module-system-server 内以独立子包 passport 扩展。

3.1 依据(实证四条)

  1. 零 RPCSmsCodeService/SmsSendService/OAuth2TokenService 均为 system-server 内部 bean——只有在 system 内才能直接 @Resource 注入;新模块只能走 system-api Feign + @Primary 本地化,重走 M1 装配教训(记忆 m1-runtime-bringup-stateCommonApi 4 处 @Primary 补丁)。
  2. 口径一致:账号 owner=system 基座是三文档套件既定(需求模块映射.md:237,258,评审版已核)。
  3. 包结构先例system-server 已有 controller/app/{dict,ip,tenant}§2.2 实证app 端控制器落 system 不破坏既有约定。
  4. 体量不配新模块:两张表 + 4 个 app 端点 + 3 个 admin 端点,走 game-module 黄金模板全套(三 pom/契约/错误码段/AGENTS 登记)为过度工程。

3.2 包路径fork 侵入隔离:全部新文件收敛在 passport 子包上游合并冲突面≈0呼应评审版 R5

yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/
├── controller/app/passport/            # 新建AppPassportController + vo/
├── service/passport/                   # 新建PassportService(Impl)、InviteCodeService(Impl)
├── dal/dataobject/passport/            # 新建PlayerDO、InviteCodeDO
└── dal/mysql/passport/                 # 新建PlayerMapper、InviteCodeMapper
yudao-module-system-server/.../controller/admin/passport/   # 新建:邀请码管理 3 端点§5.4
yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/passport/
└── PlayerApi + PlayerRespDTO           # 新建跨模块创作者白名单校验§6.3

3.3 错误码段裁决

落 system 段 1_002_***_***(模块归属决定,不占 game 业务 1-1xx 段);为避开上游 yudao 增长区(现最大 1_002_028取远段

  • 1_002_090_*** = 玩家身份(手机号已注册/未注册/被禁用/非白名单创作被拒…)
  • 1_002_091_*** = 邀请码(无效/已停用/已过期/已耗尽/旁路已退役…)

登记处:yudao-module-system-api/.../enums/ErrorCodeConstants.java 文件尾追加(带"造梦AI passport 扩展段"中文注释,与上游段隔离)。

3.4 OAuth2 客户端与 refresh 轮换

  • 发 token 用 OAuth2TokenService.createAccessToken(userId, UserTypeEnum.MEMBER(1), clientId, scopes)clientId 取 yudao 默认客户端(推断="default"执行时核 system_oauth2_client 初始数据)。
  • refresh 7d/30d 轮换(验收 5由 client 的 access-token-validity/refresh-token-validity 配置承载——执行时核对该 client 配置并按安全基线调整.agents/rules/security-and-reliability.md:15)。

4. 数据契约Flyway V11.0.0(定稿)

4.1 版本号定稿与让位关系

  • 实查 contracts/db-schemas/ 与执行副本 game-cloud/yudao-server/src/main/resources/db/migration/V1.0.0V10.0.0 两处一致diff 实证无差异V10 已被黄金闭环占用
  • 本件占 V11.0.0V11.0.0__create_passport_player_invite.sql,双落点同步(契约源 + yudao-server 唯一执行副本;遵守 V10 守门②——跨模块迁移不放单模块 classpath
  • 让位声明docs/mvp/MVP进度总账.md:94 Wave4 community/biz 曾规划 V10/V11——V10 已被 golden loop 占、V11 被本件占,community/biz 顺延 V12/V13总账该行回写§15.3)。

4.2 DDL契约级列注释中文、带 tenant_id=1 兼容、唯一键含 deleted 与既有表同范式)

-- 玩家/创作者最小身份(拍板 6实名字段仅预留提现前才收集加密=应用层 AES 落密文)
CREATE TABLE `game_player` (
  `id`               BIGINT       NOT NULL AUTO_INCREMENT COMMENT '玩家编号(即 OAuth2 token 的 userIduserType=MEMBER',
  `mobile`           VARCHAR(11)  NOT NULL                COMMENT '手机号(登录主键;日志/响应必须脱敏,库内 MVP 明文+唯一键)',
  `nickname`         VARCHAR(30)  NOT NULL DEFAULT ''     COMMENT '昵称(注册默认生成,可改)',
  `avatar`           VARCHAR(512) NOT NULL DEFAULT ''     COMMENT '头像 URL',
  `status`           TINYINT      NOT NULL DEFAULT 0      COMMENT '状态0正常 1禁用CommonStatusEnum',
  `creator_flag`     TINYINT      NOT NULL DEFAULT 0      COMMENT '创作者白名单位0玩家 1创作者A2 种子名单拍板4置位走 admin 端点)',
  `real_name`        VARCHAR(256) NULL                    COMMENT '真实姓名AES 密文预留提现前收集拍板6本件不写入',
  `id_card_no`       VARCHAR(256) NULL                    COMMENT '身份证号AES 密文预留;同上)',
  `register_channel` VARCHAR(16)  NOT NULL DEFAULT 'sms'  COMMENT '注册通道sms验证码 / invite邀请码旁路漏斗归因',
  `register_ip`      VARCHAR(64)  NOT NULL DEFAULT ''     COMMENT '注册 IP风控/审计)',
  `invite_code_id`   BIGINT       NULL                    COMMENT '核销的邀请码编号invite 通道必填,关联 game_invite_code.id',
  `first_anon_id`    VARCHAR(64)  NOT NULL DEFAULT ''     COMMENT '注册时携带的客户端 anonId匿名↔登录归因衔接评审版目标3',
  `login_date`       DATETIME     NULL                    COMMENT '最近登录时间',
  -- 标准审计列creator/create_time/updater/update_time/deleted/tenant_id与既有 game_* 表同范式)
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_mobile` (`mobile`, `deleted`, `tenant_id`) COMMENT '手机号唯一(同 V3 uk_version 含 deleted/tenant_id 范式)'
) COMMENT '玩家/创作者最小身份system passport 扩展,方案乙)';

-- 邀请码拍板1=C/拍板2 受限激活:生成/核销/限量/有效期/退役全生命周期)
CREATE TABLE `game_invite_code` (
  `id`          BIGINT       NOT NULL AUTO_INCREMENT COMMENT '编号',
  `code`        VARCHAR(16)  NOT NULL                COMMENT '邀请码/口令(生成时随机大写字母数字,避易混淆字符)',
  `batch_no`    VARCHAR(32)  NOT NULL DEFAULT ''     COMMENT '批次号(运营按渠道发放归因,如 batch-xhs-01',
  `max_uses`    INT          NOT NULL DEFAULT 1      COMMENT '限量:最大核销次数',
  `used_count`  INT          NOT NULL DEFAULT 0      COMMENT '已核销次数(条件更新原子累加,见核销语义)',
  `expire_time` DATETIME     NULL                    COMMENT '有效期NULL=不过期)',
  `status`      TINYINT      NOT NULL DEFAULT 0      COMMENT '状态0启用 1停用停用=单码退役;全量退役走配置开关 §5.4',
  `remark`      VARCHAR(255) NOT NULL DEFAULT ''     COMMENT '备注(发放对象/用途)',
  -- 标准审计列同上
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_code` (`code`, `deleted`, `tenant_id`) COMMENT '码值唯一'
) COMMENT '邀请码报备前玩家受限激活旁路报备后退役拍板1C/2';

-- 匿名试玩会话兼容§2.3 ⚠ 硬边界player_user_id 放空 + 补 anon_id
ALTER TABLE `game_runtime_session`
  MODIFY COLUMN `player_user_id` BIGINT NULL COMMENT '试玩玩家用户 ID匿名试玩为空2026-06-10 鉴权件放开;预览场景=创作者本人)',
  ADD COLUMN `anon_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '客户端 anonId匿名会话归属与 telemetry anon_id 同口径)';

核销语义(防并发超发,写明给实现 agent:核销与建号同一本地事务;UPDATE game_invite_code SET used_count = used_count + 1 WHERE id = ? AND status = 0 AND used_count < max_uses AND (expire_time IS NULL OR expire_time > NOW())affected=0 即核销失败(耗尽/停用/过期分支由前置查询给精确错误码,条件更新做最终一致兜底)。

4.3 V3 既有数据兼容

game_runtime_session 存量行 player_user_id 均有值mock 喂 1MODIFY 放宽 NULL 不破坏存量anon_id DEFAULT '' 兼容。Flyway 纪律V11 合入后禁改,回滚写补偿迁移 V11.0.1§13


5. 契约先行:新增 contracts/api-schemas/passport.yaml

风格对齐 feed.yamlopenapi 3.0.3 + 头部中文契约注释 + CommonResult 信封 + 错误码段声明 1-002-090/091契约先合入,再动代码(工作协议 §6

5.1 app 端(产品端 game-studio全部 @PermitAll 或登录态,见处置列)

端点 方法 鉴权 请求 → 响应(要点)
/app-api/passport/send-sms-code POST @PermitAllR6 安全前置绑定 §7 {mobile}Boolean。场景固定 SmsSceneEnum.MEMBER_LOGIN(1);种子期走 Debug 渠道(码落 system_sms_code 表,运营后台查码);响应不区分手机号是否已注册(防枚举)
/app-api/passport/sms-login POST @PermitAll {mobile, code, anonId?}{userId, accessToken, refreshToken, expiresTime, nickname, creatorFlag}。已注册→登录;未注册→自动注册register_channel=sms+ 登录一体验证码即占有证明anonId 落 first_anon_id仅注册时写一次
/app-api/passport/invite-register POST @PermitAll受退役开关§5.4 {mobile, inviteCode, anonId?, nickname?} → 同上响应。零短信依赖(拍板 1C核销语义 §4.2;手机号已注册→提示直接验证码登录(错误码 1-002-090-xxx开关关闭→旁路已退役错误码
/app-api/passport/me GET 登录态(不加 @PermitAll {userId, mobile(脱敏 138****0000), nickname, avatar, creatorFlag}。前端守卫/白名单 UI 依据

响应安全基线日志与响应手机号一律脱敏token 不落日志(验收 5

5.2 admin 端邀请码管理RBAC 权限位 system:passport-invite:*

端点 方法 说明
/admin-api/passport/invite-code/generate POST {count≤100, maxUses, expireTime?, batchNo?, remark?} → 码值列表随机生成uk_code 冲突重试)
/admin-api/passport/invite-code/page GET 分页查batchNo/status 过滤;含 used_count 核销进度)
/admin-api/passport/invite-code/disable PUT 单码停用status=1
/admin-api/passport/player/set-creator PUT {userId, creatorFlag}——A2 白名单置位入口(拍板 4 的运营动作落点)

5.3 邀请码管理端裁决:给 admin-api 端点,不建 game-admin 页面

理由:①邀请码是报备前临时旁路(拍板 1C "报备后旁路自然退役"),为退役机制建 Vue 页面game-admin views/wanxiang/ 现仅 adSlot/dashboard/featured/review/withdraw 五目录属一次性投入②运营动作低频生成几批、查核销、停用staging/生产经 Knife4j(/doc.html)或脚本调用 admin-api 即可RBAC 权限位照常管控(信任边界在后端,不依赖前端);③若报备拖期超 1 个月再评是否补页面(显式 backlog不在本件

5.4 旁路退役开关

yudao-server yaml 新增 wanxiang.passport.invite-register-enabled: truestaging——关闭后 invite-register 返回"旁路已退役"错误码。退役是一次性事件,配置发布可接受,不做动态配置(最小变更)。生产 profile 建立时默认按报备状态配置(部署轨注记)。


6. 存量后端改动清单

6.1 @PermitAll 放行清单(14 个端点,逐一实读核验;改法=方法上加 @jakarta.annotation.security.PermitAll + 类 javadoc 鉴权口径同步改,每处 1-2 行)

# 端点 file:line控制器实读 处置与备注
1 GET /app-api/feed/stream game-module-feed/.../AppFeedController.java:42-48 放行。:46 getLoginUserId 已可空(互动态恒 false零逻辑改动
2 GET /app-api/feed/zones 同上 :50-54 放行。无 userId 依赖
3 GET /app-api/feed/zone/stream 同上 :56-61 放行。同 #1
4 GET /app-api/feed/share/{gameId} 同上 :71-77 放行。分享落地页 OG玩家漏斗入口
5 GET /app-api/runtime/package/{versionId} game-module-runtime/.../AppRuntimeController.java:57-69 放行。play 场景门禁在 Servicestatus 权威判定preview 场景 RuntimePackageServiceImpl.java:198 userId=null 即拒,匿名不可越权预览,零新代码
6 GET /app-api/runtime/package/{versionId}/manifest 同上 :71-91 放行。与 #5 同源门禁(:82 复用 getPackageManifest
7 POST /app-api/runtime/session/start 同上 :93-106 放行 + 依赖 V11 ALTER§4.2SessionStartReqVO(现 5 字段 :22-38anonId 可选字段;RuntimeSessionServiceImpl.java:50 兼容 userId=null 落 anon_id
8 POST /app-api/runtime/session/end 同上 :108-114 放行。幂等收口,匿名会话同链路
9 POST /app-api/telemetry/events/batch game-module-telemetry/.../AppTelemetryController.java:39-45 放行。:42 注释已declare匿名合法信封 anonId 已留位V5:24
10 POST /app-api/telemetry/perf/beacon 同上 :47-52 放行。sendBeacon 兜底通道
11 GET /app-api/compliance/rating/get game-module-compliance/.../ComplianceRatingController.java:37-45 放行。只读分级,无 userId评审版清单未列补入
12 GET /app-api/ad/slot/list-enabled game-module-ad/.../AdController.java:45-51 放行。拉配置非计费
13 POST /app-api/ad/report/impression 同上 :53-57 计费链裁决:放行(裁决与条件见 §6.2
14 POST /app-api/ad/report/reward 同上 :59-63 计费链裁决:放行(同上)

不放行(保持强制登录,零改动)feed interactAppFeedController.java:63-69,转化点,拍板 5studio 全部(AppStudioController.java:44-79aigc 全部(AppAigcTaskController.java:44-88project 全部(AppProjectController.java:42-81trade 全部(TradeController.java:55-82。admin 侧 3 文件AdminProjectController/ComplianceBanController/TradeAdminController自 M1 有真实登录,不在面内。

6.2 广告曝光端点裁决(评审版 §5A 留给本执行版的单独评审项)

裁决:#13/#14 进免登清单,绑定三个并行条件,缺一不得合入。

理由:①匿名玩家是种子期主要流量,曝光端点不免登 = 匿名场景广告曝光全丢 = 变现链路与分成数据在最大流量面上断(与目标 2 冲突);②当前计费为 mock eCPMAdController.java:54 "mock 算 eCPM 收入落台账"实证),伪造曝光污染的是 mock 台账与 quality 信号,尚不直接动真金——真接广告联盟前另有对账闸门;③拍板 7=B 已确认"聚合侧剔除"为既定兜底路径。

绑定条件:

  1. 限频:两端点加 @RateLimiterIP 维度 keyResolver依赖 §7.2 的 ad pom 补齐;
  2. 幂等已有reportImpression/reward 幂等键=traceIdAdController.java:54,:60 注释实证),保持;
  3. 剔除桩覆盖§8 telemetry 最小桩的异常名单输出必须同时标注 ad 计费事件ad 反作弊正式项 owner=compliance技术架构与模块.md 技术项 backlog§15.2——本件只挂桩不建正式反作弊)。

6.3 创作/发布 A2 白名单校验(拍板 4 的可信边界落点)

前端守卫挡不住直连 API必须后端校验。复用本仓既有跨模块范式telemetry→FeedApiEventIngestServiceImpl.java:86,:195-201 Feign+@Primary 本地化实证):

  • 新增 system-apiPlayerApi#validateCreator(userId)@FeignClient primary=false + system-server 内 PlayerApiImpl @RestController @Primary照 OAuth2TokenApiImpl 范式)——非 creator_flag=1 抛 1_002_090_***"创作功能限内测白名单"。
  • 校验挂点5 个创作写入口的 Service 入层)studio 建草稿/提交生成(AppStudioController.java:50,:58 对应 Service、aigc 直发生成(AppAigcTaskController.java:50、project 创建/提交发布(AppProjectController.java:50,:76)。读自己任务/项目的端点不拦(已有归属过滤)。
  • pom 改动game-module-{studio,aigc,project}-server 增 yudao-module-system-api 依赖(实查三者现均未依赖 system-api
  • mock 兼容staging test1→userId=1 无 game_player 行——白名单校验在 mock 身份下放行(按"game_player 无此行 + mock-enable=true → 放行"或 staging 配置白名单豁免 userId=1执行时择一实现并在中文注释写明保证批跑生成 QA 闭环走 studio/aigc 链零中断§10

6.4 anonId 入参原则

仅"数据归属需要"才加session/start§6.1#7 VO 加字段)+ passport 登录注册§5.1。feed 流/分享/取包/分级不加 anonId 入参服务端无归属诉求MVP 无个性化telemetry 信封已带——避免无消费方的橱窗参数(反孤儿设计)。


7. R6 切渠道安全前置(与"切真实短信渠道"绑定排期,不可省)

7.1 现状(亲核)

  • 发码频控仅手机号维度;按 IP 每日/每小时为上游 TODOSmsCodeServiceImpl.java:65-66)。
  • staging captcha.enable: falseapplication-staging.yaml:154-155)。
  • @RateLimiter 所在 yudao-spring-boot-starter-protectionyudao-server/pom.xml:207 实际引入system/bpm 为注释§2.5 修正game-module-* 全无。

7.2 交付项

落点 时序
pom 补依赖 yudao-module-system-server/pom.xml(取消 :100-104 注释或重写依赖块passport 发码端点用)+ game-module-ad-server/pom.xml§6.2 条件 1 本件内
发码端点限频 /app-api/passport/send-sms-code@RateLimiterIP keyResolver阈值建议 10 次/小时/IP推断值,执行时与运营确认);手机号维度频控复用 SmsCodeProperties 既有 本件内(种子期 Debug 渠道+白名单下风险≈0提前落不伤
图形验证码开关 确认 yudao.captcha.enable 一键启用路径对 passport 发码生效admin AuthController 已有 captcha 集成范式app 端发码是否接同一 captcha 校验执行时核,不通则在 passport 发码前置 captcha 校验补齐) 切真实渠道前必须验证可用
IP 每日/每小时上限 在 passport 发码 Service 入层补(不改上游 SmsCodeServiceImpl避免 fork 侵入;以 Redis 计数实现,复用既有 redis starter 切渠道前

纪律:切渠道 = 配置切换 + 上表后两项验证通过,二者绑定,不得只切配置。


8. §5A 聚合侧剔除最小桩owner=telemetry

边界先立、桩可粗暴(拍板 7=B 的配套义务):

  • 落点game-module-telemetry-server 新增 service/quality/AnomalyFilterService(名称执行时可调,包归 telemetry挂在 EventIngestServiceImpl 聚合链(:153 ingestOne 的 engagement 闸门处,:183-205 quality 重算前)。
  • 最小实现边界(本件只做这么多):按 anonIdregister_ip/请求 IP 维度做滑动窗口计数Redis单 anonId/单 IP 对同一 gameId 的 engagement 事件超阈值(粗暴初值100 次/小时,推断,可配置)→ 该批事件照常落库(原始数据保真)但不进聚合/不触发 quality 重算,中文日志记剔除原因(可审计)。
  • 接口先立AnomalyFilterService#shouldExclude(envelope, ip): boolean + 剔除计数指标日志——ad 反作弊owner=compliance后续接同一判定输出§6.2 条件 3
  • 不做离线回溯重算、设备指纹、模型识别v2.0技术架构与模块.md 补技术项后排期)。

9. 前端改动game-studio

9.1 登录页(新增 src/views/login/Login.vueVant

  • 两 Tab①手机号+验证码(主路径,调 send-sms-code/sms-login②邀请码注册mobile+inviteCode调 invite-register受限激活期的陌生玩家入口,页内文案注明"内测邀请")。
  • 隐私政策/用户协议占位文案勾选(评审版 §3 口径声明P-ACC-02 占位提前,正式文案挂 A 轨法务)。
  • 携带 useUserStore().anonId 入参(身份衔接);支持 ?redirect= 回跳。

9.2 store/user.ts实读挂点确认

  • setLoginstore/user.ts:44-49 已预留):登录成功调用,新增 token/userId 落 localStorage 持久化(现纯内存,刷新即丢);logout:52-57同步清。
  • token 初值优先级改造(:27-28localStorage 真 token > VITE_STUDIO_TOKEN(env) > 'mock-studio-token'——env=test1 的批跑路径原样保留§10
  • 新增 isRealLogin():仅 localStorage 真 token 视为真实登录(现 isLogin() :38-41 因 token 永有值恒 true不可用作守卫——实读发现,评审版未提);守卫与互动弹窗一律用 isRealLogin()。staging 批跑/agent 不受影响(不走守卫的 UI 路径或 env token 直连 API
  • userId 固定 '1001':30真实登录后由 setLogin 覆盖为登录响应 userId1001/1 错位在真实路径消除mock 路径维持现状,评审版 R4

9.3 路由守卫(src/router/index.ts,现无 beforeEach:95-102 实证)

  • 新增 route /login
  • meta 增 requiresAuth: true/create/create/task/:taskId/create/preview/:versionId:46-66/project* 三条(:68-88
  • router.beforeEachrequiresAuth 且 !isRealLogin()/login?redirect=feed/play/share 与 /login 放行:17-44 玩家区零门槛)。

9.4 401 统一拦截与互动转正

  • src/api/request.ts 响应错误分支(:101-109 现无 401 处理,实证):补 error.response?.status === 401 与业务包络 code === 401 → 清登录态 + Toast + 跳 /login?redirect=当前路由
  • 互动即弹一键登录(拍板 5views/feed/Feed.vue 互动入口(:139-167 onInteract/onShare/onReportfeedStore.interact)前置 isRealLogin() 判定,未登录弹 Vant Dialog 引导跳登录(不直接发请求吃 401体验优先401 拦截作兜底)。
  • 新增 src/api/passport.ts4 端点封装,复用 request 信封解包)。

9.5 遥测衔接

登录成功补发 user_login 事件buildEnvelope 双带 userId+anonIdtelemetry/index.ts:137-169 机制现成)——前置contracts/events.schema.json eventRegistry 登记 user_login(契约先行;未登记会被入口轻量校验计 rejected——EnvelopeReqVO.java:27 实证)。


10. staging/mock 并存与批跑零中断保障

机制 出处/措施
真 token 优先、mock 兜底 TokenAuthenticationFilter.java:60-65(先查 OAuth2 token:63-65 查不到才 mock——框架原生并存,无需任何开关切换
staging 保持 mock-enable: true application-staging.yaml:157 不动;.env.staging:5 VITE_STUDIO_TOKEN=test1 不动
批跑路径回归 黄金闭环 e2e/生成 QA 闭环全依赖 Bearer test1(记忆 golden-loop-b1-done):①@PermitAll 只放宽不收紧test1 请求行为不变;②白名单校验 mock 豁免§6.3);③合入后先跑一轮既有批跑冒烟再宣并存成立(验收 §11 E8
新增真 token 黄金闭环 e2e §11 E1与 test1 批跑在同一 staging 并行各跑一遍,互不干扰即证并存
生产关死 mock mock-enable=false + mockSecret 随机化(SecurityProperties.java:44 默认 "test" 必须改——挂生产部署轨§1.2),本件在部署清单文档留注记

11. e2e 用例与验收(评审版 §8 六条展开staging=mini-desktop浏览器用例走既有 CDP 配方)

# 用例 步骤要点 通过判据
E1 真 token 创作者全链(验收 1 A2 名单手机号 → send-sms-codeDebug 渠道admin 查 system_sms_code 取码)→ sms-login 得真 token → 创作 → 发布 → admin 审核 → feed 可见 全程请求头无 test1抓包/日志证据game_project.creator_user_id=真实 userId
E2 匿名零门槛(验收 2 无任何 tokenGET feed/stream、zone、share、runtime package+manifest、POST session/start+end、telemetry batch、ad slot/list-enabled、compliance rating 全部 200game_telemetry_event.anon_id 落库非空;game_runtime_session 行 player_user_id=NULL+anon_id 非空
E3 互动触发登录(验收 2/拍板 5 匿名点赞 → 前端弹登录引导;绕前端直 POST feed/interact 无 token 前端弹窗;直连返回 401信任边界在后端
E4 邀请码注册(拍板 1C/2/3 admin generate 邀请码 → 陌生手机号+码 invite-register → 得 token → 点赞成功 game_player 行 register_channel=invite、invite_code_id 关联、used_count+1
E5 负路径·无邀请码陌生玩家 未注册手机号直接 sms-login→ 走验证码自动注册(开放注册,拍板 3invite-register 用无效/耗尽/过期/停用码 四种码各返回对应 1-002-091 错误码;耗尽并发双发只成功 max_uses 次§4.2 条件更新)
E6 负路径·非白名单创作被拒(拍板 4 E4 注册的玩家真 token POST studio 草稿 / aigc generate / project 创建+发布 均返回 1-002-090 白名单错误码A2 创作者set-creator 置位后)同端点成功
E7 身份衔接(验收 3 同浏览器:匿名刷 feedanonId=X→ 登录携带 X → 互动 game_player.first_anon_id=Xuser_login 事件 user 双带;互动落库 userId=真实 ID1001/1 错位消除)
E8 mock 并存(验收 4 前半) 改动合入后原样重跑既有批跑冒烟test1 既有黄金闭环/生成 QA 链零失败
E9 旁路退役开关 staging 临时置 invite-register-enabled=false 重验 invite-register 返回"旁路已退役"错误码;验后恢复 true
E10 生产负路径(验收 4 后半) 挂生产部署轨:生产 profile mock-enable=false+随机 mockSecret 后实测 Bearer test1 →401。生产 profile 未建立§2.1 实证),本件不验,部署落地后补测,不阻塞其余验收
E11 安全基线抽查(验收 5 日志 grep 手机号/tokenrefresh 配置核对;登录页占位协议展示 日志脱敏refresh 7d/30d 生效§3.4 执行时核);协议占位可见
E12 运营不回归(验收 6 game-admin 原生登录 + 审核链冒烟 通过admin 零改动,冒烟即可)
E13 剔除桩冒烟§8 脚本以单 anonId 高频灌 engagement 事件超阈值 原始事件照落、聚合不动、quality 不被刷动、剔除日志可见

12. 边界失败路径(实现 agent 必须显式处理 + 中文日志)

场景 处置
短信通道失败Debug 渠道落库失败 / 将来真实渠道超时) sendSingleSms 异常 → 发码端点返回明确错误码(不吞);验证码未落库不计发送频次;切真实渠道后通道超时按既有 SmsSendService 异步链路兜底(执行时核对其重试语义,不另造)
邀请码耗尽/并发核销 §4.2 条件更新affected=0 → 核销失败错误码;建号事务整体回滚(无半成品 game_player 行)
转正时 anonId 缺失 anonId 为可选入参缺失→first_anon_id 落 '',登录正常完成(归因降级不阻断登录);前端 ensureAnonId 已有 localStorage 不可用退化路径(store/user.ts:20-23
同手机号重复注册竞态 uk_mobile 唯一键兜底:插入冲突 → 转登录语义或返回"已注册"错误码(与 §5.1 防枚举口径协调sms-login 路径静默转登录invite-register 路径明确报已注册)
token 校验链异常 框架既有:buildLoginUserByToken ServiceException → nullTokenAuthenticationFilter.java:101-104),免登端点照常匿名通过、登录端点 401——不改框架
匿名 session 收口失败 session/end 幂等(AppRuntimeController.java:109 注释实证sendBeacon 兜底不变
剔除桩 Redis 不可用 降级放行(聚合照常)+ WARN 日志——桩失效不可阻断遥测主链

13. 回滚策略

  • 代码回滚passport 全部新文件收敛在独立子包§3.2revert 提交即净退;存量改动仅"@PermitAll 注解 + VO 字段 + Service 白名单挂点",逐文件 revert 无连带。
  • DB 回滚Flyway 纪律 V11 合入后禁改——回滚写补偿迁移 V11.0.1DROP 两新表 + game_runtime_session 还原 NOT NULL 前须确认无 NULL 行anon_id 列可留存不删,无害)。
  • 行为回滚(不动代码):邀请码旁路=配置开关关§5.4mock 并存本身零切换风险staging 配置未动);@PermitAll 若需紧急收口,可单独 revert 注解提交前端匿名路径同步降级为弹登录feed 浏览回到 test1 形态)。
  • 批跑兜底E8 失败 → 立即回滚白名单挂点提交(最可能破坏点),其余改动可保留排查。

14. 完成条件(全部满足才可宣称收口)

  1. contracts/api-schemas/passport.yaml + contracts/db-schemas/V11.0.0__*.sql + events.schema.json 登记 user_login 合入(契约先行);
  2. 后端passport 4+4 端点上线 staging14 端点 @PermitAll 生效;白名单校验 5 挂点生效V11 迁移绿Flyway 历史无校验失败);
  3. 前端:登录页/守卫/401 拦截/邀请码入口合入,npm run build 绿;
  4. §11 用例 E1E9、E11E13 全过且留证据E10 留部署轨注记);
  5. 单测:核销并发/白名单校验/发码频控 三类最小单测绿mini-desktop mvn test
  6. 文档回写§15完成 + docs/mvp/MVP进度总账.md 登记本件收口;
  7. R6 安全前置中"本件内"两项落地§7.2"切渠道前"两项列入 A 轨闸门看板备注。

15. 文档回写清单(收口时执行)

15.1 EnvelopeReqVO.java:64 anonId javadoc修改稿照抄

现文案:@Schema(description = "匿名 token 派生的稳定匿名 ID", example = "anon-xyz") 改为:@Schema(description = "纯客户端生成的稳定匿名 IDlocalStorage 持久化;身份不可信,防伪造/刷量由聚合侧按 anonId/IP 异常剔除兜底——2026-06-10 鉴权拍板 §9-7=B取代旧'匿名 token 派生'机制)", example = "anon-xyz") (与 contracts/events.schema.json:28 已修订文案逐字对齐,#5 为单一事实源。)

15.2 docs/architecture/技术架构与模块.md:补"广告曝光反作弊"技术项owner=compliance只列清单不代改正文设计)——内容=曝光/激励计费事件的异常剔除与对账闸门MVP 形态=消费 telemetry 剔除桩判定输出§6.2/§8真接广告联盟前升级为正式项。

15.3 docs/mvp/MVP进度总账.md:94Wave4 community/biz Flyway 规划 V10/V11 → V12/V13(让位声明 §4.1)。

15.4 .agents/ distillation鉴权落点/邀请码机制/mock 并存配方写回 knowledge/tech-decisions.md 或新条目(收口时按 §7 协议判重)。

15.5 无需回写(亲核已完成):glossary.md 匿名条目、contracts/events.schema.json anonId 描述§2.4)。


16. 推断/待执行时定项(主 agent 知悉)

# 性质
1 OAuth2 clientId="default" 及 refresh 7d/30d 的 client 配置值 推断,执行时核 system_oauth2_client 初始数据§3.4
2 发码 IP 限频阈值10 次/小时/IP与剔除桩阈值100 次/小时) 推断初值可配置执行时与运营确认§7.2/§8
3 yudao.captcha.enable 对 app 端 passport 发码是否原生生效 待执行时核admin 端集成已实证app 端路径未核不通则补前置校验§7.2
4 白名单校验的 mock 豁免实现形态(无行放行 vs 配置豁免 userId=1 执行时择一,铁律=批跑零中断§6.3
5 Debug 短信渠道在 staging 的 system_sms_channel 配置现状 待执行时核 DB渠道为 DB 配置非 yaml5 渠道客户端代码已实证存在)
6 SmsSendService 真实渠道超时/重试语义 切渠道前核对§12本件不动
7 体量估算 3-5 个有效工作日 沿用评审版推断,以实际排期为准

所有 file:line 均为 2026-06-10 本仓实读核验;与评审版不符处已在 §2.5 如实修正。下游:实现 agent 按 §4→§5→§6/§7/§8→§9 顺序认领(契约先行),主 agent 按 §11/§14 验收。