2026-05-15 17:29:57 +08:00

154 lines
4.0 KiB
Markdown
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.

# iOS 登录流程 —— 数据库设计
---
## 一、users 表
用户主表,存储用户基础信息。
```prisma
model User {
id String @id @default(cuid())
email String?
nickname String?
avatarUrl String?
role UserRole @default(USER)
status UserStatus @default(ACTIVE)
onboardingCompleted Boolean @default(false)
authAccounts AuthAccount[]
refreshTokens RefreshToken[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum UserRole {
USER
ADMIN
SUPER_ADMIN
}
enum UserStatus {
ACTIVE
DISABLED
DELETED
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | String (cuid) | 主键 |
| `email` | String? | 邮箱可选Apple 登录首次可能提供) |
| `nickname` | String? | 昵称 |
| `avatarUrl` | String? | 头像 URL |
| `role` | UserRole | 角色,默认 USER |
| `status` | UserStatus | 状态,默认 ACTIVE |
| `onboardingCompleted` | Boolean | 是否完成引导,默认 false |
| `authAccounts` | 关联 | 一对多关联 auth_accounts |
| `refreshTokens` | 关联 | 一对多关联 refresh_tokens |
---
## 二、auth_accounts 表
记录用户通过什么方式provider登录支持一个用户绑定多个登录方式。
```prisma
model AuthAccount {
id String @id @default(cuid())
userId String
provider AuthProvider
providerUserId String
email String?
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([provider, providerUserId])
@@index([userId])
}
enum AuthProvider {
DEV
APPLE
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | String (cuid) | 主键 |
| `userId` | String | 关联 users 表 |
| `provider` | AuthProvider | 登录提供商DEV / APPLE |
| `providerUserId` | String | 提供商侧的用户唯一 ID |
| `email` | String? | 提供商侧邮箱 |
| `@@unique([provider, providerUserId])` | 约束 | 同一个提供商的用户唯一 |
**查找逻辑**
```
Apple 登录时:
provider = APPLE
providerUserId = identityToken 里校验出来的 sub
→ 如果不存在,创建 User + AuthAccount
→ 如果存在,直接找到对应 User
```
---
## 三、refresh_tokens 表
**重要refreshToken 不要明文存数据库,只存 hash。**
```prisma
model RefreshToken {
id String @id @default(cuid())
userId String
tokenHash String
expiresAt DateTime
revokedAt DateTime?
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | String (cuid) | 主键,同时写入 JWT payload 作为 `tokenId` |
| `userId` | String | 关联 users 表 |
| `tokenHash` | String | refreshToken 的 hash 值SHA-256 |
| `expiresAt` | DateTime | 过期时间 |
| `revokedAt` | DateTime? | 撤销时间(登出时设置) |
**刷新时的校验链**
```
1. 解析 refreshToken JWT拿到 userId + tokenId
2. 查 refresh_tokens 表,找到对应记录
3. 对比 tokenHash
4. 确认 revokedAt 为 null未撤销
5. 确认 expiresAt 未过期
6. 签发新的 accessToken可选轮换新的 refreshToken
```
**登出时**:将对应记录的 `revokedAt` 设为当前时间。
---
## 四、ER 关系总结
```
User (1) ──── (N) AuthAccount 一个用户可有多种登录方式
User (1) ──── (N) RefreshToken 一个用户可有多个活跃 refreshToken多设备
```
- 用户与登录方式是解耦的:用户是一个独立实体,通过 `auth_accounts` 关联到具体的第三方身份。
- 这种设计天然支持未来扩展更多登录方式(如 Google、微信等只需在 `AuthProvider` 枚举中添加即可。