- 审计报告 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>
448 lines
42 KiB
Markdown
448 lines
42 KiB
Markdown
# 真实鉴权与匿名玩家身份 · execution 版
|
||
|
||
- **文档编号**: HJ-PASSPORT-EXEC-001
|
||
- **日期**: 2026-06-10
|
||
- **状态**: 待主 agent 验收(评审版七项拍板已全落,见 §0)
|
||
- **唯一设计依据**: `docs/agent-specs/2026-06-10-真实鉴权与匿名玩家-review.md`(含 §9.1 创始人七项拍板)。本文不得与其矛盾;执行中发现矛盾,停工上报,以评审版为准修订本文。
|
||
- **读者**: 实现 agent 分工认领;主 agent 验收
|
||
- **环境铁律**: 构建/测试一律 mini-desktop(git push→pull 同步,本机严禁 mvn/npm build);staging app 在 mini-desktop `http://100.64.0.7:48080`;批跑依赖 `Bearer test1` 的链路**零中断**(§10 保障措施);密钥只走环境变量;含中文 SQL 必须 `--default-character-set=utf8mb4`;前端验证门=`npm run build`(vue-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 + 聚合侧异常剔除**(剔除桩 §8;glossary/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.0);member 模块复活及其运营包袱(积分/等级/找回密码/换绑/注销)。
|
||
- **实名收集流程不做**(仅表字段预留);**匿名影子账号+转正合并不做**(拍板 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 不匹配抛 AccessDenied(ADMIN 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 + @Primary`(M1 修法注释"同 PermissionApiImpl 范式");`createAccessToken(userId, userType, clientId, scopes)` user-type 无关,可直发 MEMBER token |
|
||
| `yudao-module-system-server/.../controller/app/` | 已有 `app/dict`、`app/ip`、`app/tenant` 三个 app 端控制器目录——**system 内建 app 端控制器有既成先例** |
|
||
| `yudao-module-system-api/.../enums/ErrorCodeConstants.java` | system 段 `1_002_***_***`,现用业务子段最大 **1_002_028**(notify 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 4),admin 侧 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.3),V11 新增 ALTER。
|
||
4. ⚠ **TelemetryEventConsumer 注释口径过时**:`TelemetryEventConsumer.java:12,:17-18` 仍写幂等键 `uk_dedup(traceId,event,ts)`,但 V10 已切 `uk_event_id` 且同步聚合实际在 `EventIngestServiceImpl`(消费者是骨架占位)。评审版 §5A 引该文件作幂等出处时口径偏旧;幂等真身=eventId(`EnvelopeReqVO.java:23-25`)。不影响结论(对变造刷量无约束依然成立),如实记录。
|
||
5. 其余抽查(mock 过滤器顺序、staging yaml 行号、28 处/7 文件计数、SmsCodeServiceImpl :65-66、AppFeedController.interact :66 anon_id 预埋注释)**与评审版一致**。
|
||
|
||
---
|
||
|
||
## 3. 落点定稿:system 内扩展(评审版倾向项,正式采纳)
|
||
|
||
**结论:不建新模块 game-module-passport,在 `yudao-module-system-server` 内以独立子包 `passport` 扩展。**
|
||
|
||
### 3.1 依据(实证四条)
|
||
|
||
1. **零 RPC**:`SmsCodeService`/`SmsSendService`/`OAuth2TokenService` 均为 system-server 内部 bean——只有在 system 内才能直接 `@Resource` 注入;新模块只能走 system-api Feign + @Primary 本地化,重走 M1 装配教训(记忆 `m1-runtime-bringup-state`:CommonApi 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.0–V10.0.0 两处一致(diff 实证无差异),V10 已被黄金闭环占用**。
|
||
- **本件占 V11.0.0**:`V11.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 与既有表同范式)
|
||
|
||
```sql
|
||
-- 玩家/创作者最小身份(拍板 6:实名字段仅预留,提现前才收集;加密=应用层 AES 落密文)
|
||
CREATE TABLE `game_player` (
|
||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '玩家编号(即 OAuth2 token 的 userId,userType=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 喂 1),MODIFY 放宽 NULL 不破坏存量;anon_id DEFAULT '' 兼容。Flyway 纪律:V11 合入后禁改,回滚写补偿迁移 V11.0.1(§13)。
|
||
|
||
---
|
||
|
||
## 5. 契约先行:新增 `contracts/api-schemas/passport.yaml`
|
||
|
||
> 风格对齐 feed.yaml(openapi 3.0.3 + 头部中文契约注释 + CommonResult 信封 + 错误码段声明 1-002-090/091)。**契约先合入,再动代码**(工作协议 §6)。
|
||
|
||
### 5.1 app 端(产品端 game-studio,全部 `@PermitAll` 或登录态,见处置列)
|
||
|
||
| 端点 | 方法 | 鉴权 | 请求 → 响应(要点) |
|
||
|---|---|---|---|
|
||
| `/app-api/passport/send-sms-code` | POST | `@PermitAll`(R6 安全前置绑定 §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: true`(staging)——关闭后 `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 场景门禁在 Service(status 权威判定);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.2)**:`SessionStartReqVO`(现 5 字段 :22-38)增 `anonId` 可选字段;`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 interact(`AppFeedController.java:63-69`,转化点,拍板 5);studio 全部(`AppStudioController.java:44-79`);aigc 全部(`AppAigcTaskController.java:44-88`);project 全部(`AppProjectController.java:42-81`);trade 全部(`TradeController.java:55-82`)。admin 侧 3 文件(AdminProjectController/ComplianceBanController/TradeAdminController)自 M1 有真实登录,不在面内。
|
||
|
||
### 6.2 广告曝光端点裁决(评审版 §5A 留给本执行版的单独评审项)
|
||
|
||
**裁决:#13/#14 进免登清单,绑定三个并行条件,缺一不得合入。**
|
||
|
||
理由:①匿名玩家是种子期主要流量,曝光端点不免登 = 匿名场景广告曝光全丢 = 变现链路与分成数据在最大流量面上断(与目标 2 冲突);②当前计费为 **mock eCPM**(`AdController.java:54` "mock 算 eCPM 收入落台账"实证),伪造曝光污染的是 mock 台账与 quality 信号,**尚不直接动真金**——真接广告联盟前另有对账闸门;③拍板 7=B 已确认"聚合侧剔除"为既定兜底路径。
|
||
|
||
绑定条件:
|
||
1. **限频**:两端点加 `@RateLimiter`(IP 维度 keyResolver),依赖 §7.2 的 ad pom 补齐;
|
||
2. **幂等已有**:reportImpression/reward 幂等键=traceId(`AdController.java:54,:60` 注释实证),保持;
|
||
3. **剔除桩覆盖**:§8 telemetry 最小桩的异常名单输出必须**同时标注 ad 计费事件**(ad 反作弊正式项 owner=compliance,列 `技术架构与模块.md` 技术项 backlog,§15.2——本件只挂桩不建正式反作弊)。
|
||
|
||
### 6.3 创作/发布 A2 白名单校验(拍板 4 的可信边界落点)
|
||
|
||
前端守卫挡不住直连 API,**必须后端校验**。复用本仓既有跨模块范式(telemetry→FeedApi:`EventIngestServiceImpl.java:86,:195-201` Feign+@Primary 本地化实证):
|
||
|
||
- 新增 `system-api` 的 `PlayerApi#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 每日/每小时为上游 TODO**(`SmsCodeServiceImpl.java:65-66`)。
|
||
- staging `captcha.enable: false`(`application-staging.yaml:154-155`)。
|
||
- `@RateLimiter` 所在 `yudao-spring-boot-starter-protection`:**仅 `yudao-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` 加 `@RateLimiter`(IP 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 重算前)。
|
||
- **最小实现边界(本件只做这么多)**:按 `anonId` 与 `register_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.vue`,Vant)
|
||
|
||
- 两 Tab:①手机号+验证码(主路径,调 send-sms-code/sms-login);②邀请码注册(mobile+inviteCode,调 invite-register;**受限激活期的陌生玩家入口**,页内文案注明"内测邀请")。
|
||
- 隐私政策/用户协议**占位文案**勾选(评审版 §3 口径声明:P-ACC-02 占位提前,正式文案挂 A 轨法务)。
|
||
- 携带 `useUserStore().anonId` 入参(身份衔接);支持 `?redirect=` 回跳。
|
||
|
||
### 9.2 store/user.ts(实读挂点确认)
|
||
|
||
- `setLogin`(`store/user.ts:44-49` 已预留):登录成功调用,**新增 token/userId 落 localStorage 持久化**(现纯内存,刷新即丢);`logout`(:52-57)同步清。
|
||
- token 初值优先级改造(:27-28):`localStorage 真 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 覆盖为登录响应 userId,1001/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.beforeEach`:requiresAuth 且 `!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=当前路由`。
|
||
- 互动即弹一键登录(拍板 5):`views/feed/Feed.vue` 互动入口(:139-167 `onInteract/onShare/onReport` → `feedStore.interact`)前置 `isRealLogin()` 判定,未登录弹 Vant Dialog 引导跳登录(不直接发请求吃 401,体验优先;401 拦截作兜底)。
|
||
- 新增 `src/api/passport.ts`(4 端点封装,复用 request 信封解包)。
|
||
|
||
### 9.5 遥测衔接
|
||
|
||
登录成功补发 `user_login` 事件(buildEnvelope 双带 userId+anonId,`telemetry/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-code(Debug 渠道,admin 查 `system_sms_code` 取码)→ sms-login 得真 token → 创作 → 发布 → admin 审核 → feed 可见 | 全程请求头无 test1(抓包/日志证据);game_project.creator_user_id=真实 userId |
|
||
| E2 | 匿名零门槛(验收 2) | 无任何 token:GET feed/stream、zone、share、runtime package+manifest、POST session/start+end、telemetry batch、ad slot/list-enabled、compliance rating | 全部 200;`game_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?→ 走验证码自动注册(开放注册,拍板 3);**invite-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) | 同浏览器:匿名刷 feed(anonId=X)→ 登录携带 X → 互动 | game_player.first_anon_id=X;`user_login` 事件 user 双带;互动落库 userId=真实 ID(1001/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 手机号/token;refresh 配置核对;登录页占位协议展示 | 日志脱敏;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 → null(`TokenAuthenticationFilter.java:101-104`),免登端点照常匿名通过、登录端点 401——不改框架 |
|
||
| 匿名 session 收口失败 | session/end 幂等(`AppRuntimeController.java:109` 注释实证),sendBeacon 兜底不变 |
|
||
| 剔除桩 Redis 不可用 | 降级放行(聚合照常)+ WARN 日志——桩失效不可阻断遥测主链 |
|
||
|
||
---
|
||
|
||
## 13. 回滚策略
|
||
|
||
- **代码回滚**:passport 全部新文件收敛在独立子包(§3.2),revert 提交即净退;存量改动仅"@PermitAll 注解 + VO 字段 + Service 白名单挂点",逐文件 revert 无连带。
|
||
- **DB 回滚**:Flyway 纪律 V11 合入后禁改——回滚写补偿迁移 `V11.0.1`(DROP 两新表 + game_runtime_session 还原 NOT NULL 前须确认无 NULL 行;anon_id 列可留存不删,无害)。
|
||
- **行为回滚(不动代码)**:邀请码旁路=配置开关关(§5.4);mock 并存本身零切换风险(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 端点上线 staging;14 端点 @PermitAll 生效;白名单校验 5 挂点生效;V11 迁移绿(Flyway 历史无校验失败);
|
||
3. 前端:登录页/守卫/401 拦截/邀请码入口合入,`npm run build` 绿;
|
||
4. §11 用例 E1–E9、E11–E13 全过且留证据(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 = "纯客户端生成的稳定匿名 ID(localStorage 持久化;身份不可信,防伪造/刷量由聚合侧按 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:94`:Wave4 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 配置非 yaml;5 渠道客户端代码已实证存在) |
|
||
| 6 | SmsSendService 真实渠道超时/重试语义 | 切渠道前核对(§12),本件不动 |
|
||
| 7 | 体量估算 3-5 个有效工作日 | 沿用评审版推断,以实际排期为准 |
|
||
|
||
---
|
||
|
||
*所有 file:line 均为 2026-06-10 本仓实读核验;与评审版不符处已在 §2.5 如实修正。下游:实现 agent 按 §4→§5→§6/§7/§8→§9 顺序认领(契约先行),主 agent 按 §11/§14 验收。*
|