games-development-ai/contracts/db-schemas/V11.0.0__create_passport_player_invite.sql
zizi b0850a2ad9 feat(passport): 鉴权建设波——V11契约+system passport子包+14端点@PermitAll+白名单挂点+剔除桩+前端登录守卫
- 契约先行: V11.0.0 双落点(game_player/game_invite_code/runtime_session匿名兼容ALTER) + passport.yaml(4 app+4 admin) + events登记user_login
- B1 system侧: passport独立子包(fork隔离), 验证码+邀请码旁路登录注册一体, OAuth2 MEMBER token(client=default已核), 错误码1_002_090/091远段, PlayerApi @Primary本地化(mock豁免裁决=无行+mockEnable放行), R6限频(IP 10次/h+Redis日/时双闸), 25单测
- B2 存量: @PermitAll恰好14端点(主agent grep复核, 不放行名单零触碰), 匿名session(player_user_id可空+anon_id), 白名单5挂点(studio/aigc/project), ad计费限频, telemetry剔除桩(照落库不进聚合+Redis降级放行), EnvelopeReqVO文案回写
- F1 前端: 登录页两Tab+占位协议, isRealLogin守卫(isLogin恒真不可用), 401统一拦截, 互动转正onInteract单入口gating, user_login遥测双带; test1批跑路径原样保留(主agent复核)
- 三面对抗核验全pass零blocker; 构建门待mini-desktop(本机禁构建铁律)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:40:10 +00:00

89 lines
9.4 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- =============================================================================
-- 契约 #2 DB 迁移 | 主题:真实鉴权与匿名玩家身份(HJ-PASSPORT-EXEC-001 §4.2)| owner:passport(system 内扩展,方案乙)
-- 文件:V11.0.0__create_passport_player_invite.sql(Flyway,只新增/ALTER;已合入禁止修改,回滚写新补偿迁移 V11.0.1)
-- 拍板依据:2026-06-10 鉴权七项(评审版 §9.1,全拍)——
-- 拍板1=C 验证码为主+邀请码旁路 / 拍板2 报备前受限激活 / 拍板4 创作限 A2 白名单 / 拍板6 实名提现前收集 / 拍板7=B 纯客户端 anonId。
-- 内容:passport 扩展的两张新表 + 一处 ALTER(共三段):
-- 1. game_player —— 玩家/创作者最小身份(即 OAuth2 token 的 userId,userType=MEMBER;实名字段仅预留);
-- 2. game_invite_code —— 邀请码(报备前受限激活旁路,生成/核销/限量/有效期/退役全生命周期;报备后走配置开关退役);
-- 3. game_runtime_session ALTER —— player_user_id 放宽 NULL(匿名试玩)+ 补 anon_id(匿名会话归属)。
-- 守门①(必守,沿用 V10 守门②):本 V11 跨 system(player/invite) + runtime(session) 多表,只放仓根 contracts/db-schemas/(源)
-- + yudao-server 执行目录(唯一执行副本),不放任何单模块 -server/db/migration/(避免同版本 V11 出现在多个 classpath jar 触发 Flyway 重复校验失败)。
-- 守门②:含中文 SQL,mini-desktop 执行必须 --default-character-set=utf8mb4。
-- 让位声明(§4.1):V10 已被黄金闭环占、V11 被本件占,Wave4 community/biz 原规划的 V10/V11 顺延 V12/V13。
-- 约定:InnoDB + utf8mb4;显式列;含 Yudao 审计列 + tenant_id(MVP 单租户=1,tenant.enable=false);状态机用 tinyint,非法流转由服务端拒绝。
-- 错误码段:玩家身份 = 1-002-090-***;邀请码 = 1-002-091-***(落 system 段,见 passport.yaml 与 ErrorCodeConstants)。
-- =============================================================================
-- -----------------------------------------------------------------------------
-- 表:game_player —— 玩家/创作者最小身份(system passport 扩展,方案乙)
-- id 即 OAuth2 token 的 userId(userType=MEMBER),登录主键=mobile。
-- 拍板6:real_name/id_card_no 仅预留(AES 密文,应用层加密),提现前才收集,本件不写入。
-- 拍板4:creator_flag 白名单位,A2 种子名单(~5 人),置位走 admin 端点 /admin-api/passport/player/set-creator。
-- 目标3(评审版):first_anon_id 落注册时携带的客户端 anonId,做匿名↔登录归因衔接。
-- -----------------------------------------------------------------------------
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 '最近登录时间',
-- 标准审计列(与既有 game_* 表同范式,对齐 V3 game_runtime_session)
`creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者(Yudao 审计列)',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除:0未删 1已删',
`tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID(Yudao 多租户兼容,MVP 单租户=0)',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_mobile` (`mobile`, `deleted`, `tenant_id`) COMMENT '手机号唯一(含 deleted/tenant_id 适配逻辑删+多租户,同 V3 uk_version 范式;并发重复注册由此键兜底)'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '玩家/创作者最小身份(system passport 扩展,方案乙)';
-- -----------------------------------------------------------------------------
-- 表:game_invite_code —— 邀请码(拍板1=C / 拍板2 受限激活:生成/核销/限量/有效期/退役全生命周期)
-- 报备前陌生玩家凭「手机号+邀请码」零短信注册;报备后 invite-register 走配置开关退役(§5.4)。
-- 核销语义(防并发超发,写明给实现 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 即核销失败(耗尽/停用/过期分支由前置查询给精确错误码,条件更新做最终一致兜底,建号事务整体回滚)。
-- -----------------------------------------------------------------------------
CREATE TABLE `game_invite_code` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '编号',
`code` VARCHAR(16) NOT NULL COMMENT '邀请码/口令(生成时随机大写字母数字,避易混淆字符;uk_code 冲突重试)',
`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 '备注(发放对象/用途)',
-- 标准审计列(与 game_player 同范式)
`creator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建者(Yudao 审计列)',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '更新者',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除:0未删 1已删',
`tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户 ID(MVP 单租户=0)',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_code` (`code`, `deleted`, `tenant_id`) COMMENT '码值唯一(含 deleted/tenant_id 适配逻辑删+多租户)'
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '邀请码(报备前玩家受限激活旁路;报备后退役,拍板1C/2)';
-- -----------------------------------------------------------------------------
-- ALTER:game_runtime_session 匿名试玩兼容(§2.3 ⚠ 硬边界 / §4.2 / §4.3)
-- 背景:V3 建表 player_user_id BIGINT NOT NULL;匿名开试玩会话(session/start userId=null)会撞 NOT NULL,
-- RuntimeSessionServiceImpl.setPlayerUserId(null) 直落 → "匿名试玩" 半截。本件放宽 NULL + 补 anon_id。
-- 存量兼容(§4.3):V3 存量行 player_user_id 均有值(mock 喂 1),MODIFY 放宽 NULL 不破坏存量;
-- anon_id DEFAULT '' 兼容存量。回滚(V11.0.1)还原 NOT NULL 前须确认无 NULL 行;anon_id 列可留存不删,无害。
-- 口径:anon_id 与 telemetry game_telemetry_event.anon_id(V5)同口径(纯客户端 anonId,拍板7=B)。
-- -----------------------------------------------------------------------------
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 同口径,拍板7=B)';