zizi 063ec4edf2 docs(rename): ③ 文档/契约漂移清理——contracts+docs+根文档 yudao 标识同步 com.wanxiang.huijing(follow-up,脚本)
- 范围:全仓除已完成的 game-cloud/game-admin(及排除区);91 文件
- cn.iocoder.yudao→com.wanxiang.huijing / cn.wanxiang.game→com.wanxiang.huijing.game / cn.iocoder.cloud→com.wanxiang / Yudao*→Huijing* / bare yudao→huijing
- 含 contracts/api-schemas(apiInterface FQCN)、docs/architecture+agent-specs(含中文名,git ls-files -z 修复)、AGENTS.md/CLAUDE.md 等根文档
- 保留不动:上游归属 URL(gitee/github yudaocode·YunaiV,改则断链+违反署名)、game-cloud Flyway 迁移注释(保 checksum)、lockfile(装包重生)
- 纯文档/字符串,零构建影响(game-cloud/game-admin 字节码未动)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:32:44 +00:00

309 lines
17 KiB
YAML
Raw Permalink 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.

openapi: 3.0.3
# 契约 #8 API | 模块passport真实鉴权 / 匿名玩家 / 邀请码受限激活)| ownerpassportsystem 内扩展,方案乙),全员 review
# 端:/app-api产品端 game-studio手机号验证码登录 / 邀请码注册 / 当前用户)
# /admin-api管理端 game-admin邀请码生成/分页/停用 + 玩家创作者白名单置位RBAC
# 前缀由 huijing 框架按 controller.app/admin 包名自动添加;本文件 path 直接写含前缀的完整对外路径。
# 错误码段(落 system 段 1-002-***-***,模块归属决定,不占 game 业务 1-1xx 段;取远段避上游增长区,现最大 1-002-028
# 1-002-090-*** = 玩家身份(手机号已注册/未注册/被禁用/非白名单创作被拒…)
# 1-002-091-*** = 邀请码(无效/已停用/已过期/已耗尽/旁路已退役…)
# 响应统一 Huijing CommonResult 信封:{ code, data, msg }code=0 成功。
# 拍板依据2026-06-10 鉴权七项(评审版 §9.1——拍板1=C 验证码为主+邀请码旁路 / 拍板3 开放注册(受限期凭码)/
# 拍板4 创作限 A2 白名单 / 拍板5 互动即弹一键登录 / 拍板7=B 纯客户端 anonId。
# 鉴权口径send-sms-code / sms-login / invite-register 三个 @PermitAll免登me 登录态(不加 @PermitAll
# 发 token=OAuth2TokenService.createAccessToken(userId, UserTypeEnum.MEMBER(1), clientId, scopes)refresh 7d/30d 由 client 配置承载。
# 安全基线:日志与响应手机号一律脱敏(如 138****0000token 不落日志验收5
# 防枚举口径send-sms-code 响应不区分手机号是否已注册(见端点 description
# 边界 OUT不做三方社交登录 / 不开多租户tenant.enable=false新表 tenant_id=1/ 不做实名收集流程(仅表字段预留)/
# 不做匿名影子账号合并拍板5=A/ 不做服务端 anonId 校验签名拍板7=B 既定代价)/ game-admin 前端零改动(邀请码经 Knife4j/脚本调 admin-apiRBAC 管控§5.3)。
info:
title: 绘境AI passport 模块 API
version: 1.0.0
description: >-
真实鉴权与匿名玩家身份:种子创作者手机号验证码登录(发真 userType=MEMBER OAuth2 token+
陌生玩家邀请码受限激活注册(零短信依赖,报备后退役)+ 当前用户信息(前端守卫/白名单 UI 依据)+
管理端邀请码全生命周期运营(生成/分页/停用与创作者白名单置位A2 名单)。
匿名玩家免登刷 feed/试玩/遥测(读端点 @PermitAll见 feed/runtime/telemetry 契约),互动才弹一键登录。
覆盖产品功能 P-ACC账号鉴权受限激活与 A2 白名单口径。
前端据此 vite-plugin-mock 自动生成 mock。
servers:
- url: http://localhost:48080
description: 本地Swagger/Knife4j http://localhost:48080/doc.html
paths:
# ===========================================================================
# app 端(产品端 game-studio验证码登录 / 邀请码注册 / 当前用户
# ===========================================================================
/app-api/passport/send-sms-code:
post:
tags: [app-passport]
summary: 发送登录短信验证码(@PermitAll拍板1C 主路径)
description: >-
场景固定 SmsSceneEnum.MEMBER_LOGIN(1);种子期走 Debug 渠道(码落 system_sms_code 表,运营后台查码)。
鉴权:@PermitAll 免登R6 安全前置:发码端点加 @RateLimiter IP 维度限频 + IP 每日/每小时上限,切真实渠道前补图形验证码,见 execution §7
防枚举口径:响应不区分手机号是否已注册(已注册→登录路径用;未注册→自动注册路径用,均发码),统一返回成功,避免手机号是否注册被探测。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/SendSmsCodeReqVO' }
responses:
'200':
description: 成功(发码受理;不区分手机号是否已注册)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultBoolean' }
/app-api/passport/sms-login:
post:
tags: [app-passport]
summary: 手机号验证码登录(已注册登录 / 未注册自动注册一体,@PermitAll拍板3 开放注册)
description: >-
鉴权:@PermitAll 免登。已注册→登录未注册→自动注册register_channel=sms+ 登录一体(验证码即占有证明)。
anonId 可选:仅注册时落 game_player.first_anon_id 一次匿名↔登录归因衔接目标3缺失落空串归因降级不阻断登录
竞态兜底uk_mobile 唯一键冲突 → 静默转登录语义(与防枚举口径协调)。
前端登录成功后另补发 user_login 遥测事件envelope.user 双带 userId+anonId见 events.schema.json
响应安全基线手机号脱敏、token 不落日志。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/SmsLoginReqVO' }
responses:
'200':
description: 成功(返回真 OAuth2 token 与玩家身份摘要)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultLogin' }
/app-api/passport/invite-register:
post:
tags: [app-passport]
summary: 邀请码受限激活注册(手机号+邀请码,零短信依赖,@PermitAll拍板1C/2/3
description: >-
鉴权:@PermitAll 免登,受退役开关 wanxiang.passport.invite-register-enabled 管控§5.4,关闭后返回 1-002-091 旁路已退役错误码)。
报备前陌生玩家旁路mobile+inviteCode 注册register_channel=invite关联核销的 invite_code_id零短信依赖拍板1C
核销语义:核销与建号同一本地事务,条件更新原子累加(耗尽/停用/过期由前置查询给精确 1-002-091 错误码,条件更新做最终一致兜底),建号失败整体回滚。
手机号已注册 → 提示直接验证码登录(错误码 1-002-090anonId 可选,落 first_anon_id同 sms-login 口径)。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/InviteRegisterReqVO' }
responses:
'200':
description: 成功(返回真 OAuth2 token 与玩家身份摘要,同 sms-login 响应)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultLogin' }
/app-api/passport/me:
get:
tags: [app-passport]
summary: 当前登录玩家信息(登录态,不加 @PermitAll前端守卫/白名单 UI 依据)
description: >-
鉴权:登录态必需(无 @PermitAll未登录返回 401
返回当前 token 对应玩家身份摘要手机号脱敏138****0000。前端据 creatorFlag 渲染创作入口/守卫。
responses:
'200':
description: 成功
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultPlayerMe' }
# ===========================================================================
# admin 端(管理端 game-admin邀请码生成/分页/停用 + 玩家创作者白名单置位
# RBAC 权限位:邀请码 system:passport-invite:*;玩家 system:passport-player:*信任边界在后端不依赖前端§5.3
# ===========================================================================
/admin-api/passport/invite-code/generate:
post:
tags: [admin-passport]
summary: 批量生成邀请码RBAC system:passport-invite:create拍板2 受限激活)
description: >-
随机生成 count 个邀请码大写字母数字、避易混淆字符uk_code 冲突重试),统一批次号/限量/有效期。
权限位 system:passport-invite:create。运营经 Knife4j(/doc.html) 或脚本调用game-admin 零页面§5.3)。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/InviteCodeGenerateReqVO' }
responses:
'200':
description: 成功(返回生成的码值列表)
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultInviteCodeList' }
/admin-api/passport/invite-code/page:
get:
tags: [admin-passport]
summary: 邀请码分页查询RBAC system:passport-invite:query
description: 按 batchNo/status 过滤分页查邀请码,含 used_count 核销进度。权限位 system:passport-invite:query。
parameters:
- { name: batchNo, in: query, required: false, schema: { type: string }, description: 批次号过滤 }
- { name: status, in: query, required: false, schema: { type: integer, enum: [0, 1] }, description: '状态过滤0启用 1停用' }
- { name: pageNo, in: query, required: false, schema: { type: integer, default: 1 } }
- { name: pageSize, in: query, required: false, schema: { type: integer, default: 10 } }
responses:
'200':
description: 成功
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultInviteCodePage' }
/admin-api/passport/invite-code/disable:
put:
tags: [admin-passport]
summary: 单码停用/退役RBAC system:passport-invite:update
description: >-
将指定邀请码 status 置 1单码退役全量旁路退役走配置开关 §5.4)。权限位 system:passport-invite:update。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/InviteCodeDisableReqVO' }
responses:
'200':
description: 成功
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultBoolean' }
/admin-api/passport/player/set-creator:
put:
tags: [admin-passport]
summary: 玩家创作者白名单置位RBAC system:passport-player:update拍板4 A2 名单入口)
description: >-
置位/取消玩家的 creator_flagA2 种子白名单的运营动作落点拍板4
置位后该玩家方可走创作/发布写入口(后端 PlayerApi#validateCreator 校验,信任边界在后端)。
权限位 system:passport-player:update。
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/PlayerSetCreatorReqVO' }
responses:
'200':
description: 成功
content:
application/json:
schema: { $ref: '#/components/schemas/CommonResultBoolean' }
components:
schemas:
# ---- Huijing CommonResult 信封 ----
CommonResultBoolean:
type: object
properties:
code: { type: integer, description: '0=成功非0=错误码 1-002-090/091-***' }
data: { type: boolean }
msg: { type: string }
CommonResultLogin:
type: object
properties:
code: { type: integer }
data: { $ref: '#/components/schemas/LoginRespVO' }
msg: { type: string }
CommonResultPlayerMe:
type: object
properties:
code: { type: integer }
data: { $ref: '#/components/schemas/PlayerMeRespVO' }
msg: { type: string }
CommonResultInviteCodeList:
type: object
properties:
code: { type: integer }
data: { type: array, items: { $ref: '#/components/schemas/InviteCodeRespVO' } }
msg: { type: string }
CommonResultInviteCodePage:
type: object
properties:
code: { type: integer }
data:
type: object
properties:
list: { type: array, items: { $ref: '#/components/schemas/InviteCodeRespVO' } }
total: { type: integer, format: int64 }
msg: { type: string }
# ---- app 端请求 ----
SendSmsCodeReqVO:
type: object
required: [mobile]
properties:
mobile: { type: string, maxLength: 11, description: '手机号(场景固定 MEMBER_LOGIN响应不区分是否已注册防枚举' }
SmsLoginReqVO:
type: object
required: [mobile, code]
properties:
mobile: { type: string, maxLength: 11, description: 手机号(登录主键) }
code: { type: string, description: '短信验证码Debug 渠道运营查 system_sms_code 表)' }
anonId: { type: string, maxLength: 64, description: '客户端 anonId可选仅注册时落 first_anon_id 一次匿名↔登录归因拍板7=B' }
InviteRegisterReqVO:
type: object
required: [mobile, inviteCode]
properties:
mobile: { type: string, maxLength: 11, description: 手机号(登录主键) }
inviteCode: { type: string, maxLength: 16, description: '邀请码(核销语义见 execution §4.2;无效/停用/过期/耗尽返回 1-002-091' }
anonId: { type: string, maxLength: 64, description: '客户端 anonId可选同 sms-login 口径)' }
nickname: { type: string, maxLength: 30, description: '昵称(可选;不传则默认生成)' }
# ---- app 端响应 ----
LoginRespVO:
type: object
description: 登录/注册成功响应sms-login 与 invite-register 共用token 不落日志,手机号不在此返回
properties:
userId: { type: integer, format: int64, description: '玩家编号(即 OAuth2 token 的 userIduserType=MEMBER' }
accessToken: { type: string, description: '访问令牌Bearer不得落日志' }
refreshToken: { type: string, description: '刷新令牌refresh 7d/30d 由 OAuth2 client 配置承载§3.4' }
expiresTime: { type: integer, format: int64, description: '访问令牌过期时间戳(毫秒)' }
nickname: { type: string, description: 昵称 }
creatorFlag: { type: integer, description: '创作者白名单位0玩家 1创作者拍板4前端据此渲染创作入口' }
PlayerMeRespVO:
type: object
description: 当前登录玩家信息(前端守卫/白名单 UI 依据);手机号脱敏返回
properties:
userId: { type: integer, format: int64, description: 玩家编号 }
mobile: { type: string, description: '脱敏手机号(如 138****0000安全基线禁回明文' }
nickname: { type: string, description: 昵称 }
avatar: { type: string, description: 头像 URL }
creatorFlag: { type: integer, description: '创作者白名单位0玩家 1创作者' }
# ---- admin 端请求 ----
InviteCodeGenerateReqVO:
type: object
required: [count, maxUses]
properties:
count: { type: integer, minimum: 1, maximum: 100, description: '本批生成数量≤100' }
maxUses: { type: integer, minimum: 1, default: 1, description: '单码最大核销次数(限量)' }
expireTime: { type: string, format: date-time, description: '有效期(可选;不传=不过期)' }
batchNo: { type: string, maxLength: 32, description: '批次号(可选;运营按渠道发放归因,如 batch-xhs-01' }
remark: { type: string, maxLength: 255, description: '备注(可选;发放对象/用途)' }
InviteCodeDisableReqVO:
type: object
required: [id]
properties:
id: { type: integer, format: int64, description: '邀请码编号(停用即 status=1单码退役' }
PlayerSetCreatorReqVO:
type: object
required: [userId, creatorFlag]
properties:
userId: { type: integer, format: int64, description: '玩家编号game_player.id' }
creatorFlag: { type: integer, enum: [0, 1], description: '创作者白名单位0取消 1置位A2 名单拍板4' }
# ---- admin 端响应 ----
InviteCodeRespVO:
type: object
description: 邀请码(生成返回 / 分页项;含核销进度)
properties:
id: { type: integer, format: int64, description: 编号 }
code: { type: string, description: 邀请码/口令 }
batchNo: { type: string, description: 批次号 }
maxUses: { type: integer, description: 最大核销次数 }
usedCount: { type: integer, description: '已核销次数(核销进度)' }
expireTime: { type: string, format: date-time, description: '有效期NULL=不过期)' }
status: { type: integer, description: '状态0启用 1停用' }
remark: { type: string, description: 备注 }
createTime: { type: string, format: date-time, description: 创建时间 }