Compare commits

...

2 Commits

Author SHA1 Message Date
远舟
36b41cb815 Merge remote-tracking branch 'origin/main' 2026-04-11 11:26:26 +08:00
远舟
4e07d91874 docs(auth): 补充 AUTH-07 游客登录与 AUTH-08 游客升级接口
- API-01 用户系统新增 AUTH-07 POST /auth/guest、AUTH-08 POST /auth/upgrade
- API README 模块索引与接口总览同步至 12 个接口
- 数据库文档补充 user.is_guest、user_device.app_version 与 device_id 部分索引
2026-04-11 11:25:17 +08:00
3 changed files with 105 additions and 4 deletions

View File

@ -1,6 +1,6 @@
# API-01 用户系统AUTH + USER
> 共 10 个接口AUTH-01~06 + USER-01~04
> 共 12 个接口AUTH-01~08 + USER-01~04
---
@ -228,6 +228,97 @@
---
#### AUTH-07 游客登录
| 项目 | 说明 |
|------|------|
| 接口路径 | POST /client/api/v1/auth/guest |
| 接口用途 | 未注册用户通过设备指纹获取可用 Token无需提供账号密码同一设备重复调用会复用历史游客账号 |
| 权限要求 | 公开 |
| 涉及表 | user, user_profile, user_privacy_setting, user_notification_setting, user_device |
**接口逻辑:**
1. 校验 deviceId必填≤128 字符其它可选字段长度限制deviceType ≤16、deviceName ≤64、appVersion ≤32
2. 查询 `user_device` JOIN `user` 表,条件 `device_id=$1 AND user.is_guest=1 AND user.deleted_at IS NULL AND user.status=1`,按 `user_device.id DESC` 取最新一条(读)
3. **命中(同设备老游客)**
- 再次校验 `user.status`,异常则拒绝
- 进入第 5 步签发流程,复用原 `userId`
4. **未命中(首次访问)**
- 生成随机用户名 `guest_<12 位十六进制>`,插入失败重试 1 次
- 写入 `user` 表(`is_guest=1, password_hash=NULL, role=1`
- 写入默认 `user_profile` / `user_privacy_setting` / `user_notification_setting`
- 回读完整 `user`
5. 签发 JWT Tokenaccess_token + refresh_token与正式用户同一把 secret 和 uid claim
6. 写入 `user_device` 表,带上 `device_id / device_type / device_name / app_version`
7. 读取 `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 |
**接口逻辑:**
1. 从 Token 解析 `uid`
2. 校验 username 格式2-20字符中英文/数字/下划线,不允许纯数字)
3. 校验 password≥8位含字母+数字),`confirmPassword` 一致性
4. 查询 `user` 表 加载当前用户(读)
5. 校验 `user.is_guest == 1`,否则返回 40901 "账号已是正式用户,无需升级"
6. 查询 `user` 表 按 username 查重(不区分大小写),命中且非当前 uid 则返回 40901 "用户名已被使用"
7. 密码加密bcrypt
8. 原子更新 `user` 表:`SET username=$1, password_hash=$2, is_guest=0, username_changed_at=NOW() WHERE id=$3 AND is_guest=1`。受影响行数为 0 说明并发升级冲突,返回 40901
9. 踢旧设备:将 `user_device` 表 该用户所有 `is_active=1` 记录更新为 `is_active=0`(更新)
10. 签发新 JWT Token写入 `user_device` 表(写)
11. 回读最新 `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 获取当前用户信息
| 项目 | 说明 |

View File

@ -11,7 +11,7 @@
| # | 模块 | 接口数 | 文件 |
|---|------|--------|------|
| 01 | [用户系统](API-01-用户系统.md) | 10 | AUTH-01~06 + USER-01~04 |
| 01 | [用户系统](API-01-用户系统.md) | 12 | AUTH-01~08 + USER-01~04 |
| 02 | [媒体处理](API-02-媒体处理.md) | 4 | MEDIA-01~04 |
| 03 | [代币支付](API-03-代币支付.md) | 10 | COIN-01~08 + IAP-01~02 |
| 04 | [内容系统](API-04-内容系统.md) | 10 | CONTENT-01~10 |
@ -28,13 +28,15 @@
## 接口总览
### 01 用户系统10个)
### 01 用户系统12个)
- AUTH-01 账号密码注册 — `POST /client/api/v1/auth/register`
- AUTH-02 账号密码登录 — `POST /client/api/v1/auth/login`
- AUTH-03 苹果登录 — `POST /client/api/v1/auth/apple`
- AUTH-04 谷歌登录 — `POST /client/api/v1/auth/google`
- AUTH-05 Token 刷新 — `POST /client/api/v1/auth/refresh`
- AUTH-06 退出登录 — `POST /client/api/v1/auth/logout`
- AUTH-07 游客登录 — `POST /client/api/v1/auth/guest`
- AUTH-08 游客升级为正式账号 — `POST /client/api/v1/auth/upgrade`
- USER-01 获取当前用户信息 — `GET /client/api/v1/users/me`
- USER-02 修改个人资料 — `PUT /client/api/v1/users/me`
- USER-03 修改密码 — `PUT /client/api/v1/users/me/password`

View File

@ -71,6 +71,7 @@ CREATE TABLE "user" (
username_changed_at TIMESTAMPTZ,
last_login_at TIMESTAMPTZ,
is_first_recharged SMALLINT NOT NULL DEFAULT 0 CHECK (is_first_recharged IN (0,1)),
is_guest SMALLINT NOT NULL DEFAULT 0 CHECK (is_guest IN (0,1)),
search_vector TSVECTOR,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
@ -83,10 +84,12 @@ CREATE INDEX idx_user_role ON "user" (role);
CREATE INDEX idx_user_created ON "user" (created_at);
CREATE INDEX idx_user_search ON "user" USING GIN (search_vector);
COMMENT ON COLUMN "user".username IS '用户名 2-20 字符,唯一,不区分大小写';
COMMENT ON COLUMN "user".username IS '用户名 2-20 字符,唯一,不区分大小写;游客形如 guest_<12hex>';
COMMENT ON COLUMN "user".password_hash IS '密码哈希 bcrypt游客账号为 NULL升级后补齐';
COMMENT ON COLUMN "user".role IS '角色 1=普通用户 2=创作者';
COMMENT ON COLUMN "user".status IS '状态 1=正常 2=封禁 3=已注销';
COMMENT ON COLUMN "user".is_first_recharged IS '是否完成首充 0=否 1=是';
COMMENT ON COLUMN "user".is_guest IS '游客标记 0=正式用户 1=游客(由 AUTH-07 按设备指纹创建,升级后回落至 0';
COMMENT ON COLUMN "user".search_vector IS '昵称/用户名全文搜索向量M2 接入';
```
@ -197,6 +200,7 @@ CREATE TABLE user_device (
device_type VARCHAR(16),
device_name VARCHAR(64),
device_id VARCHAR(128),
app_version VARCHAR(32),
access_token VARCHAR(512) NOT NULL,
refresh_token VARCHAR(512) NOT NULL,
access_token_expired_at TIMESTAMPTZ NOT NULL,
@ -213,6 +217,10 @@ CREATE INDEX idx_user_device_user ON user_device (user_id);
CREATE INDEX idx_user_device_access ON user_device (access_token);
CREATE INDEX idx_user_device_refresh ON user_device (refresh_token);
CREATE INDEX idx_user_device_active ON user_device (is_active);
CREATE INDEX idx_user_device_device_id ON user_device (device_id) WHERE device_id IS NOT NULL;
COMMENT ON COLUMN user_device.device_id IS '客户端设备指纹AUTH-07 游客登录按此反查复用历史游客账号';
COMMENT ON COLUMN user_device.app_version IS '客户端 App 版本号,便于按平台/版本统计与排查';
```
---