- 契约先行: 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>
89 lines
9.4 KiB
SQL
89 lines
9.4 KiB
SQL
-- =============================================================================
|
||
-- 契约 #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)';
|