12 KiB
Dating App 数据库表设计与关系
1. 设计范围
本次先设计注册和用户资料相关的 Function 数据库,使用 MySQL 8.0,字符集为 utf8mb4,排序规则为 utf8mb4_unicode_ci。
当前范围包括:账号注册、密码登录、微信/谷歌登录、用户资料、自我介绍视频和身份证/护照文件。
2026-08-31 MVP 更新:邮箱和手机号统一使用
users.email_or_phone一列;MVP 不使用 OTP,新注册账号直接激活。下方旧版验证码描述不再适用于当前接口,数据库迁移见sql/migrate_auth_mvp_no_otp.sql。
2. ER 关系图
简化关系:
users (用户主表)
│ 1
├──────── 0..1 user_profiles 用户资料
├──────── 0..1 user_locations 用户最近位置
├──────── 0..N auth_identities 微信/谷歌登录绑定
├──────── 0..N user_media 头像、视频、身份证/护照文件
├──────── 0..N swipes 发出的左滑/右滑/超级喜欢
├──────── 0..N matches 已建立的双向匹配
├──────── 0..N posts Explore 帖子
├──────── 0..N post_likes 帖子点赞
├──────── 0..N post_comments 帖子评论
└──────── 0..N chat_messages 聊天消息
verification_codes (验证码记录)
└── 通过 target + channel 与注册账号逻辑关联
erDiagram
USERS ||--o| USER_PROFILES : "拥有一份资料"
USERS ||--o| USER_LOCATIONS : "拥有一个最近位置"
USERS ||--o{ AUTH_IDENTITIES : "绑定登录方式"
USERS ||--o{ USER_MEDIA : "上传媒体/证件"
USERS ||--o{ SWIPES : "发起滑动"
USERS ||--o{ MATCHES : "形成匹配"
USERS ||--o{ POSTS : "发布帖子"
POSTS ||--o{ POST_LIKES : "收到点赞"
POSTS ||--o{ POST_COMMENTS : "收到评论"
MATCHES ||--o| CONVERSATIONS : "拥有会话"
CONVERSATIONS ||--o{ CHAT_MESSAGES : "包含消息"
USERS {
int id PK
varchar email UK
varchar phone UK
varchar password_hash
varchar status
boolean email_verified
boolean phone_verified
datetime created_at
datetime updated_at
}
USER_PROFILES {
int id PK
int user_id FK_UK
varchar full_name
varchar gender
varchar birth_date
varchar city
varchar country
text bio
}
AUTH_IDENTITIES {
int id PK
int user_id FK
varchar provider
varchar provider_user_id
datetime created_at
}
USER_MEDIA {
int id PK
int user_id FK
varchar media_type
varchar storage_path
varchar original_name
varchar mime_type
varchar review_status
datetime created_at
}
VERIFICATION_CODES {
int id PK
varchar target
varchar channel
varchar code_hash
datetime expires_at
datetime used_at
int attempts
datetime created_at
}
说明:VERIFICATION_CODES 暂时不设置 user_id 外键。用户还没有注册成功时,就需要先发送验证码,因此验证码通过 target + channel(邮箱或手机号)关联注册流程;验证成功后再更新 USERS.email_verified 或 USERS.phone_verified。
3. 表结构
3.1 users:用户主表
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
INT | 主键、自增 | 用户唯一编号 |
email |
VARCHAR(320) | 唯一、可为空 | 邮箱账号 |
phone |
VARCHAR(32) | 唯一、可为空 | 手机号账号 |
password_hash |
VARCHAR(256) | 可为空 | 加密后的密码;第三方登录用户可以为空 |
status |
VARCHAR(20) | 非空 | pending、active、suspended |
email_verified |
BOOLEAN | 默认 false | 邮箱是否已验证 |
phone_verified |
BOOLEAN | 默认 false | 手机号是否已验证 |
created_at |
DATETIME | 非空 | 注册时间 |
updated_at |
DATETIME | 非空 | 最后更新时间 |
注册账号至少提供邮箱或手机号之一。邮箱和手机号都允许为空,是为了支持只使用第三方账号注册。
3.2 user_profiles:用户个人资料表
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
INT | 主键、自增 | 资料记录编号 |
user_id |
INT | 非空、唯一、外键 | 关联 users.id |
full_name |
VARCHAR(120) | 可为空 | 姓名/昵称 |
gender |
VARCHAR(30) | 可为空 | 性别 |
birth_date |
VARCHAR(10) | 可为空 | 生日,格式 YYYY-MM-DD |
relationship_goal |
VARCHAR(30) | 可为空 | 交友目标;新注册必须从 serious_relationship、casual_dates、new_friends、marriage 中单选 |
city |
VARCHAR(120) | 可为空 | 所在城市 |
country |
VARCHAR(120) | 可为空 | 所在国家,用于推荐和搜索排序 |
bio |
TEXT | 可为空 | 个人简介 |
关系:一个用户最多有一份个人资料;删除用户时级联删除资料。
3.3 auth_identities:第三方登录绑定表
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
INT | 主键、自增 | 绑定记录编号 |
user_id |
INT | 非空、外键 | 关联 users.id |
provider |
VARCHAR(30) | 非空 | wechat 或 google |
provider_user_id |
VARCHAR(255) | 非空 | 第三方平台用户编号 |
created_at |
DATETIME | 非空 | 绑定时间 |
唯一约束:provider + provider_user_id,防止同一个微信或谷歌账号绑定多个用户。
关系:一个用户可以绑定多个第三方登录方式;一条第三方绑定记录只属于一个用户。
3.4 verification_codes:验证码表
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
INT | 主键、自增 | 验证码记录编号 |
target |
VARCHAR(320) | 非空、索引 | 邮箱地址或手机号 |
channel |
VARCHAR(10) | 非空 | email 或 phone |
code_hash |
VARCHAR(256) | 非空 | 验证码哈希值,不保存明文验证码 |
expires_at |
DATETIME | 非空 | 过期时间 |
used_at |
DATETIME | 可为空 | 使用时间,未使用时为空 |
attempts |
INT | 默认 0 | 验证失败次数 |
created_at |
DATETIME | 非空 | 生成时间 |
关系:验证码属于一次验证请求,不直接绑定用户。验证成功后更新 users 表的验证状态。
3.5 user_media:用户媒体和身份文件表
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
INT | 主键、自增 | 文件记录编号 |
user_id |
INT | 非空、外键 | 关联 users.id |
media_type |
VARCHAR(20) | 非空 | intro_video 或 identity_document |
storage_path |
VARCHAR(500) | 非空 | 文件存储地址 |
original_name |
VARCHAR(255) | 可为空 | 原始文件名 |
mime_type |
VARCHAR(100) | 可为空 | 文件类型 |
review_status |
VARCHAR(20) | 默认 pending | pending、approved、rejected |
created_at |
DATETIME | 非空 | 上传时间 |
关系:一个用户可以上传多个文件。数据库只保存文件地址和审核信息,视频、身份证和护照文件本身不直接存进数据库。
4. 关系汇总
| 主表 | 从表 | 关系 | 外键 | 删除策略 |
|---|---|---|---|---|
users |
user_profiles |
一对零或一 | user_profiles.user_id |
删除用户时级联删除 |
users |
auth_identities |
一对多 | auth_identities.user_id |
删除用户时级联删除 |
users |
user_media |
一对多 | user_media.user_id |
删除用户时级联删除 |
users |
verification_codes |
逻辑关联 | target + channel |
验证码独立过期 |
5. 注册流程对应的数据变化
- 用户提交邮箱/手机号和密码,写入
users,初始状态为pending。 - 用户填写个人信息,写入
user_profiles。 - 系统发送验证码,写入
verification_codes,只保存验证码哈希。 - 验证成功后,更新
users.email_verified或users.phone_verified,并将状态改为active。 - 用户上传视频或证件,在
user_media中保存文件记录,初始审核状态为pending。 - 微信/谷歌登录时,在
auth_identities保存第三方账号和对应的user_id。
6. 当前实现文件
- 数据模型:app/models.py
- 数据库连接:app/database.py
- 接口实现:app/main.py
- MySQL 建库脚本:sql/create_database.sql
7. Meet 与 Search 扩展表(2026-08-25)
| 表 | 关键字段 | 关系与用途 |
|---|---|---|
user_locations |
user_id、latitude、longitude |
与用户一对一;用于附近的人和距离排序;位置请求的城市、区县保存在 user_profiles |
swipes |
actor_id、target_id、direction |
记录 like/pass/super_like;同一用户对唯一,撤销时删除记录 |
matches |
user1_id、user2_id、matched_at |
双方相互喜欢(like 或 super_like)后生成;用户编号按小到大保存,确保一对用户只有一个匹配 |
隐私规则:user_media 中只有 profile_image 和 intro_video 会出现在 Meet/Search 响应里;identity_document 仅文件所属用户可访问。图片内容相似搜索依赖 ML,留到 Function 阶段完成后实现。
8. Explore 信息流扩展表(2026-08-26)
| 表 | 关键字段 | 用途 |
|---|---|---|
posts |
user_id、content、post_type、content_format、feeling、location_name、location_latitude、location_longitude |
保存帖子文字、Markdown 格式、心情和所选地点的快照 |
post_media |
post_id、media_type、storage_path、position |
保存帖子图片或视频,最多 9 张图片或 1 个视频 |
post_likes |
post_id、user_id |
记录用户对帖子的点赞;同一用户对同一帖子只能点赞一次 |
post_comments |
post_id、user_id、content |
保存帖子评论,删除帖子时级联删除 |
图片/视频附件已通过 post_media 接入;帖子创建后再上传媒体。音乐、直播和 ML 帖子分类尚未接入。
9. Chat 会话扩展表(2026-08-26)
| 表 | 关键字段 | 用途 |
|---|---|---|
conversations |
match_id |
一个互相匹配关系最多对应一个会话 |
chat_messages |
conversation_id、sender_id、content |
保存文本消息;消息只能由会话双方访问 |
聊天权限来自 matches 表:没有互相喜欢形成的匹配关系,不能创建会话或发送消息。消息撤回采用 deleted_at 标记,历史记录仍保留但对客户端隐藏正文。
10. 登录会话表(Login_Registration.pdf 要求)
user_sessions 保存 refresh token 会话,不保存明文 token:
| 字段 | 说明 |
|---|---|
user_id |
会话所属用户 |
refresh_token_hash |
refresh token 的哈希值 |
expires_at |
30 天默认过期时间 |
revoked_at |
logout 或 token 轮换后的撤销时间 |
access token 默认 15 分钟,refresh token 默认 30 天。应用启动时调用 /api/v1/auth/me;refresh 接口采用轮换策略,旧 refresh token 立即失效。
11. 帖子媒体与 AI Feed 问题(2026-08-27)
| 表 | 关键字段 | 用途 |
|---|---|---|
post_media |
post_id、media_type、storage_path |
保存帖子图片/视频的文件信息,一条帖子最多 9 张图片或 1 个视频 |
feed_questions |
code、question_text、answer_type |
保存系统提供的偏好问题,支持布尔和文字回答 |
user_question_answers |
user_id、question_id、回答内容 |
保存用户答案;同一用户对同一问题只有一条最新答案 |
删除帖子时级联删除 post_media 数据库记录,同时由接口删除实际文件。删除用户或问题时级联删除对应答案。AI 阶段将使用这些结构化答案训练或计算用户兴趣与匹配特征。
12. 国家和匹配度推荐(2026-09-01)
user_profiles.country 保存用户所在国家。Meet、用户搜索和 Explore 信息流按同城、同国家、其他国家分组;同组内按匹配百分比排序。匹配百分比由现有 user_interests 和双方共同回答的 user_question_answers 实时计算,因此不新增持久化分数字段,避免用户资料变化后产生过期分数。
user_profiles.county 保存区县,和国家 country 不同;与经纬度一同通过位置接口提交。位置接口同步更新 user_profiles.city,供同城推荐使用。历史用户的区县可为空。