18 KiB
18 KiB
API-01 用户系统(AUTH + USER)
共 12 个接口:AUTH-01~08 + USER-01~04
AUTH-01 账号密码注册
| 项目 | 说明 |
|---|---|
| 接口路径 | POST /client/api/v1/auth/register |
| 接口用途 | 新用户通过用户名+密码创建账号 |
| 权限要求 | 公开 |
| 涉及表 | user, wallet, user_device |
接口逻辑:
- 校验用户名格式(2-20字符,中英文/数字/下划线,不允许纯数字)
- 查询
user表 检查用户名唯一性(不区分大小写,读) - 校验密码规则(≥8位含字母+数字),confirmPassword 一致性
- 密码加密(bcrypt)
- 写入
user表(含嵌套 profile、privacy_setting、notification_setting 默认值) - 写入
wallet表(初始 balance=0) - 签发 JWT Token(access_token + refresh_token),写入
user_device表 - 返回用户信息和Token
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| username | string | 是 | 用户名,2-20字符,中英文/数字/下划线,不允许纯数字,不区分大小写 |
| password | string | 是 | 密码,≥8位,需含字母+数字 |
| confirmPassword | string | 是 | 确认密码,需与password一致 |
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| userId | string | 用户ID (UUID) |
| username | string | 用户名 |
| token | string | 访问令牌 |
| refreshToken | string | 刷新令牌 |
| expiresAt | timestamp | Token过期时间 |
AUTH-02 账号密码登录
| 项目 | 说明 |
|---|---|
| 接口路径 | POST /client/api/v1/auth/login |
| 接口用途 | 已有用户通过用户名+密码登录 |
| 权限要求 | 公开 |
| 涉及表 | user, user_device |
接口逻辑:
- 查询
user表 根据用户名查找用户(读) - 检查 user.status 是否为 3(已注销)→ 拒绝登录
- 检查 user.login_locked_until 是否未过期 → 账号锁定中
- 校验密码(bcrypt compare),失败则更新
user表 的 login_fail_count+1(更新) - 连续5次错误 → 更新
user表 设置 login_locked_until = 当前时间+15分钟(更新) - 密码正确 → 重置 login_fail_count=0,更新 last_login_at(更新
user) - 踢旧设备:将
user_device表 中该用户所有 is_active=1 的记录更新为 is_active=0(更新) - 签发新 JWT Token,写入
user_device表(写) - 读取 user 嵌套的 profile 子文档,组装响应
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| account | string | 是 | 用户名 |
| password | string | 是 | 密码 |
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| userId | string | 用户ID |
| username | string | 用户名 |
| nickname | string | 昵称(user.profile.display_name) |
| avatar | url | 头像URL(user.profile.avatar_url) |
| isCreator | boolean | 是否为创作者(user.role=2) |
| token | string | 访问令牌 |
| refreshToken | string | 刷新令牌 |
| expiresAt | timestamp | Token过期时间 |
AUTH-03 苹果登录
| 项目 | 说明 |
|---|---|
| 接口路径 | POST /client/api/v1/auth/apple |
| 接口用途 | 通过 Apple Sign In 登录/注册 |
| 权限要求 | 公开 |
| 涉及表 | user, user_auth, wallet, user_device |
接口逻辑:
- 向苹果服务器验证 identityToken 有效性
- 解析 identityToken 获取 provider_user_id
- 查询
user_auth表 检查该 provider=apple + provider_user_id 是否已绑定(读) - 已绑定(老用户):根据 user_auth.user_id 查询
user表(读) - 未绑定(新用户):
- 自动生成用户名,写入
user表(含嵌套 profile、privacy_setting、notification_setting) - 写入
wallet表(初始 balance=0) - 写入
user_auth表(provider=apple)
- 自动生成用户名,写入
- 踢旧设备:更新
user_device表 中旧记录 is_active=0(更新) - 签发 JWT Token,写入
user_device表(写) - 返回用户信息 + isNewUser 标记
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| identityToken | string | 是 | 苹果返回的 identityToken |
| authorizationCode | string | 是 | 苹果返回的 authorizationCode |
| fullName | string | 否 | 苹果首次授权返回的用户全名 |
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| userId | string | 用户ID |
| username | string | 用户名 |
| nickname | string | 昵称 |
| avatar | url | 头像URL |
| isCreator | boolean | 是否为创作者 |
| isNewUser | boolean | 是否为新注册用户 |
| token | string | 访问令牌 |
| refreshToken | string | 刷新令牌 |
| expiresAt | timestamp | Token过期时间 |
AUTH-04 谷歌登录
| 项目 | 说明 |
|---|---|
| 接口路径 | POST /client/api/v1/auth/google |
| 接口用途 | 通过 Google Sign In 登录/注册 |
| 权限要求 | 公开 |
| 涉及表 | user, user_auth, wallet, user_device |
接口逻辑:
- 向 Google 服务器验证 idToken 有效性
- 解析 idToken 获取 provider_user_id 和邮箱
- 查询
user_auth表 检查该 provider=google + provider_user_id 是否已绑定(读) - 已绑定(老用户):根据 user_auth.user_id 查询
user表(读) - 未绑定(新用户):
- 自动生成用户名,写入
user表(含嵌套 profile、privacy_setting、notification_setting) - 写入
wallet表(初始 balance=0) - 写入
user_auth表(provider=google)
- 自动生成用户名,写入
- 踢旧设备:更新
user_device表 中旧记录 is_active=0(更新) - 签发 JWT Token,写入
user_device表(写) - 返回用户信息 + isNewUser 标记
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| idToken | string | 是 | Google返回的 ID Token |
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| userId | string | 用户ID |
| username | string | 用户名 |
| nickname | string | 昵称 |
| avatar | url | 头像URL |
| isCreator | boolean | 是否为创作者 |
| isNewUser | boolean | 是否为新注册用户 |
| token | string | 访问令牌 |
| refreshToken | string | 刷新令牌 |
| expiresAt | timestamp | Token过期时间 |
AUTH-05 Token 刷新
| 项目 | 说明 |
|---|---|
| 接口路径 | POST /client/api/v1/auth/refresh |
| 接口用途 | 刷新过期的认证Token |
| 权限要求 | 公开(需有效refreshToken) |
| 涉及表 | user_device |
接口逻辑:
- 查询
user_device表 根据 refresh_token 查找记录(读) - 校验 refresh_token 是否过期(refresh_token_expired_at)和是否有效(is_active=1)
- 签发新的 access_token 和 refresh_token
- 更新
user_device表 的 access_token、refresh_token 及对应过期时间(更新) - 返回新Token
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| refreshToken | string | 是 | 刷新令牌 |
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| token | string | 新的访问令牌 |
| refreshToken | string | 新的刷新令牌 |
| expiresAt | timestamp | 新Token过期时间 |
AUTH-06 退出登录
| 项目 | 说明 |
|---|---|
| 接口路径 | POST /client/api/v1/auth/logout |
| 接口用途 | 用户主动退出登录 |
| 权限要求 | 需登录 |
| 涉及表 | user_device |
接口逻辑:
- 从请求 Header 中解析当前 access_token
- 查询
user_device表 找到对应记录(读) - 更新该记录的 is_active=0(更新
user_device) - 返回成功
请求参数: 无
响应数据: null
AUTH-07 游客登录
| 项目 | 说明 |
|---|---|
| 接口路径 | POST /client/api/v1/auth/guest |
| 接口用途 | 未注册用户通过设备指纹获取可用 Token,无需提供账号密码;同一设备重复调用会复用历史游客账号 |
| 权限要求 | 公开 |
| 涉及表 | user, user_profile, user_privacy_setting, user_notification_setting, user_device |
接口逻辑:
- 校验 deviceId(必填,≤128 字符),其它可选字段长度限制:deviceType ≤16、deviceName ≤64、appVersion ≤32
- 查询
user_deviceJOINuser表,条件device_id=$1 AND user.is_guest=1 AND user.deleted_at IS NULL AND user.status=1,按user_device.id DESC取最新一条(读) - 命中(同设备老游客):
- 再次校验
user.status,异常则拒绝 - 进入第 5 步签发流程,复用原
userId
- 再次校验
- 未命中(首次访问):
- 生成随机用户名
guest_<12 位十六进制>,插入失败重试 1 次 - 写入
user表(is_guest=1, password_hash=NULL, role=1) - 写入默认
user_profile/user_privacy_setting/user_notification_setting - 回读完整
user行
- 生成随机用户名
- 签发 JWT Token(access_token + refresh_token,与正式用户同一把 secret 和 uid claim)
- 写入
user_device表,带上device_id / device_type / device_name / app_version - 读取
user_profile子资料组装响应
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| deviceId | string | 是 | 设备唯一标识(≤128 字符),用于反查历史游客身份 |
| deviceType | string | 否 | 设备平台:ios / android / web(≤16 字符) |
| deviceName | string | 否 | 机型或浏览器标识(≤64 字符) |
| appVersion | string | 否 | 客户端版本号(≤32 字符) |
响应数据: 结构同 AUTH-02 AuthResp
| 字段 | 类型 | 说明 |
|---|---|---|
| userId | string | 用户ID(同设备复用) |
| username | string | 随机生成的游客用户名,如 guest_a1b2c3d4e5f6 |
| nickname | string | 昵称(首次为空,前端可 fallback 到"游客XXXX") |
| avatar | url | 头像URL(首次为空) |
| isCreator | boolean | 始终为 false |
| token | string | 访问令牌 |
| refreshToken | string | 刷新令牌 |
| expiresAt | timestamp | Token过期时间 |
安全约束:
- 查询严格过滤
is_guest=1,不会将其他用户绑定过该 deviceId 的正式账号误返给游客登录调用者 - 游客账号会被算作普通用户消费各业务模块(钱包、订阅、PPV 等),升级为正式账号后原
userId保持不变
AUTH-08 游客升级为正式账号
| 项目 | 说明 |
|---|---|
| 接口路径 | POST /client/api/v1/auth/upgrade |
| 接口用途 | 当前登录的游客账号设置正式 username/password 完成升级,原 userId 与所有关联数据(profile / 钱包 / 关注 / 订阅 / PPV)全部保留 |
| 权限要求 | 需登录(必须是游客账号) |
| 涉及表 | user, user_device |
接口逻辑:
- 从 Token 解析
uid - 校验 username 格式(2-20字符,中英文/数字/下划线,不允许纯数字)
- 校验 password(≥8位含字母+数字),
confirmPassword一致性 - 查询
user表 加载当前用户(读) - 校验
user.is_guest == 1,否则返回 40901 "账号已是正式用户,无需升级" - 查询
user表 按 username 查重(不区分大小写),命中且非当前 uid 则返回 40901 "用户名已被使用" - 密码加密(bcrypt)
- 原子更新
user表:SET username=$1, password_hash=$2, is_guest=0, username_changed_at=NOW() WHERE id=$3 AND is_guest=1。受影响行数为 0 说明并发升级冲突,返回 40901 - 踢旧设备:将
user_device表 该用户所有is_active=1记录更新为is_active=0(更新) - 签发新 JWT Token,写入
user_device表(写) - 回读最新
user行 +user_profile,组装响应
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| username | string | 是 | 新用户名,2-20字符,中英文/数字/下划线,不允许纯数字 |
| password | string | 是 | 新密码,≥8位,需含字母+数字 |
| confirmPassword | string | 是 | 确认密码,需与 password 一致 |
响应数据: 结构同 AUTH-02 AuthResp,其中 username 为升级后的新用户名,token / refreshToken 为新发放。
注意事项:
- 升级成功后旧的 access_token 与 refresh_token 全部失效,客户端必须使用新 Token 继续请求
- 升级是单次幂等操作:二次调用始终返回 40901,防止覆盖正式账号
- 本次仅实现用户名/密码升级路径;Apple / Google 第三方升级未实现
USER-01 获取当前用户信息
| 项目 | 说明 |
|---|---|
| 接口路径 | GET /client/api/v1/users/me |
| 接口用途 | 获取登录用户的个人资料 |
| 权限要求 | 需登录 |
| 涉及表 | user, wallet, creator_application, creator_profile, follow |
接口逻辑:
- 从Token中解析 user_id
- 查询
user表 获取用户基本信息 + 嵌套的 profile 子文档(读) - 查询
wallet表 获取余额(读) - 查询
creator_application表 获取最新申请记录,计算 creatorStatus(读) - 如果 user.role=2,查询
creator_profile表 获取 follower_count(读) - 查询
follow表 统计 followingCount(COUNT WHERE user_id=me)和 followerCount(COUNT WHERE creator_id=me)(读) - 组装响应数据返回
请求参数: 无
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| userId | string | 用户ID |
| username | string | 用户名 |
| nickname | string | 昵称 |
| avatar | url | 头像URL |
| avatarStatus | string | 头像审核状态:none/reviewing/approved/rejected |
| bio | string | 个人简介 |
| location | object/null | 地理位置 {name, code};当前实现返回 name,code 为空 |
| gender | number | 性别:1男 2女 3其他,null未设置 |
| birthday | string | 生日(YYYY-MM-DD) |
| isCreator | boolean | 是否为创作者(user.role=2) |
| creatorStatus | string | 创作者状态:none/pending/approved/rejected |
| followerCount | number | 粉丝数 |
| followingCount | number | 关注数 |
| balance | number | 糖心币余额 |
| isOnline | boolean | 在线状态(运行时计算) |
| createdAt | timestamp | 注册时间 |
| usernameLastChangedAt | timestamp | 用户名上次修改时间 |
USER-02 修改个人资料
| 项目 | 说明 |
|---|---|
| 接口路径 | PUT /client/api/v1/users/me |
| 接口用途 | 修改昵称、头像、简介、地理位置等 |
| 权限要求 | 需登录 |
| 涉及表 | user, user_profile |
接口逻辑:
- 从Token中解析 user_id
- 如果修改 username:
- 查询
user表 检查 username_changed_at 距今是否≥30天(读) - 查询
user表 检查新用户名唯一性(读) - 更新
user表 的 username 和 username_changed_at(更新)
- 查询
- 如果修改 avatar:
- 更新
user_profile.avatar_url和user_profile.avatar_status=1(审核中)
- 更新
- 如果修改
location:- 校验
location.name长度 ≤64、location.code长度 ≤20 - 当前首版将
location.name持久化到user_profile.city
- 校验
- 其他字段(nickname/bio/gender/birthday)直接更新
user_profile - 返回更新后的用户资料
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| username | string | 否 | 用户名,2-20字符,30天限改1次 |
| nickname | string | 否 | 昵称,2-20字符 |
| avatar | url | 否 | 头像URL(先通过上传接口获取) |
| bio | string | 否 | 个人简介,最多200字 |
| location | object | 否 | 地理位置 {name, code},当前实现持久化 name |
| location.name | string | 否 | 地点显示名,最长64字 |
| location.code | string | 否 | 地点编码,最长20字 |
| gender | number | 否 | 性别:1男 2女 3其他 |
| birthday | string | 否 | 生日(YYYY-MM-DD) |
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| userId | string | 用户ID |
| username | string | 用户名 |
| nickname | string | 昵称 |
| avatar | url | 头像URL |
| avatarStatus | string | 头像审核状态 |
| bio | string | 个人简介 |
| location | object/null | 地理位置 {name, code};当前实现返回 name,code 为空 |
| gender | number | 性别 |
| birthday | string | 生日 |
USER-03 修改密码
| 项目 | 说明 |
|---|---|
| 接口路径 | PUT /client/api/v1/users/me/password |
| 接口用途 | 用户修改登录密码 |
| 权限要求 | 需登录 |
| 涉及表 | user |
接口逻辑:
- 从Token中解析 user_id
- 查询
user表 获取 password_hash(读) - 验证 oldPassword 与 password_hash 是否匹配(bcrypt compare)
- 校验 newPassword 规则(≥8位含字母+数字),confirmPassword 一致性
- 新密码加密(bcrypt),更新
user表 的 password_hash(更新) - 返回成功
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| oldPassword | string | 是 | 旧密码 |
| newPassword | string | 是 | 新密码,≥8位含字母+数字 |
| confirmPassword | string | 是 | 确认新密码 |
响应数据: null
USER-04 注销账号
| 项目 | 说明 |
|---|---|
| 接口路径 | DELETE /client/api/v1/users/me |
| 接口用途 | 用户申请注销账号(苹果审核要求"删除账号") |
| 权限要求 | 需登录 |
| 涉及表 | user, wallet, creator_profile, user_device |
接口逻辑:
- 从Token中解析 user_id
- 校验 confirmPassword(读
user表 的 password_hash + bcrypt compare) - 查询
wallet表 检查 balance 是否为 0(读)→ 不为0则拒绝 - 查询
user表 检查 role 是否为 2(创作者)→ 是创作者则检查creator_profile是否已停用(读) - 更新
user表 的 status=3(已注销),释放用户名(更新) - 将
user_device表 中该用户所有记录的 is_active=0(更新) - 返回成功
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| confirmPassword | string | 是 | 输入密码确认注销操作 |
响应数据: null