
用户表是几乎所有产品都会有的第一张表。很多人第一次设计时,会把所有和用户有关的字段都塞进去:邮箱、昵称、头像、密码、手机号、套餐、余额、登录次数、邀请人、偏好设置、公司信息、权限、最后访问时间。
短期看这样很方便,长期看会变成一个越来越臃肿的“大杂烩”。用户表不应该承载所有用户相关数据,它应该稳定承载最核心的身份、状态和基础资料。其他变化频繁、业务属性强的数据,应该拆到更合适的表里。
设计用户表的关键,是先分清哪些信息属于“用户是谁”,哪些属于“用户拥有什么”,哪些属于“用户做过什么”,哪些只是“用户偏好”。
用户表最核心的职责,是标识一个用户。它要回答:这个用户是谁,怎么联系,什么时候创建,当前是否可用。
因此,第一版用户表通常只需要几个稳定字段:用户 ID、邮箱、昵称、头像、认证来源、账号状态、创建时间、更新时间。密码哈希可以放在用户表,也可以放在单独的认证表,取决于你使用的认证方案。
不要把订单、订阅、权限、用量、偏好、行为日志都塞进用户表。它们和用户有关,但不一定属于用户表。
用户表越稳定,系统越好扩展。它应该像身份底座,而不是业务垃圾桶。
对独立开发者和出海 SaaS 来说,邮箱通常是最稳的主标识。它跨设备、适合通知、能和支付系统对应,也方便第三方登录合并账号。
用户表里可以有 email 字段,并对规范化后的邮箱做唯一约束。规范化通常包括转小写、去除前后空格。不要把大小写不同的邮箱当成两个账号。
如果你支持 Google、GitHub、Apple 登录,不要直接把第三方 ID 当成用户主键。更好的做法是用户表有自己的 id,第三方身份放在 user_identities 或类似表里。这样一个用户可以绑定多个登录方式。
手机号也可以作为标识,但对出海产品会涉及国家区号、短信成本、合规和送达问题。除非你的产品强依赖手机号,否则第一版不必把手机号作为主账号。
很多早期产品会在用户表里加 plan、credits、is_pro、expired_at。第一版这样做很快,但后面一旦出现订阅、退款、团队、赠送额度、用量记录,就会变得混乱。
更好的方式是把订阅和用量拆出来。用户表只保存用户身份和基础状态。套餐信息放在 subscriptions 或 user_plans,用量放在 usage_records 或 credit_accounts。
当然,如果你的产品非常简单,短期只有免费和付费两档,在用户表里放一个缓存字段也可以。但要把它当成冗余状态,而不是唯一事实来源。真正的支付和订阅状态应该来自订单或订阅表。
用户表不应该成为计费系统。用户是谁,和用户买了什么,是两类问题。
用户表应该有一个账号状态字段,比如 active、pending、disabled、deleted。不要只用多个布尔值表达状态,比如 is_active、is_deleted、is_banned 同时存在,很容易出现矛盾组合。
状态字段要有清楚语义。pending 可能表示邮箱未验证,active 表示可正常使用,disabled 表示被封禁或手动停用,deleted 表示用户已注销或软删除。
如果需要记录封禁原因、删除时间、邮箱验证时间,可以增加 disabled_reason、deleted_at、email_verified_at 这类字段。主状态表达当前状态,辅助字段表达原因和时间。
账号状态会影响登录、权限、数据展示和后台操作,所以必须由后端统一判断。
昵称、头像、展示名称可以放在用户表,因为它们是基础资料,变化不大,常用。更复杂的个人资料,比如公司、职位、网站、简介、社交链接,可以放在 user_profiles。
用户设置更应该拆出去。语言、时区、通知偏好、主题、默认导出格式、邮件订阅开关,这些都属于偏好配置。它们变化频繁,也可能随着产品功能增加而变多。
可以建一张 user_settings,也可以在早期用 JSON 存偏好。关键是不要让用户表不断膨胀。
一个实用原则:登录和权限经常需要的字段放用户表;只是展示或偏好的字段尽量拆出去。
如果你支持多种登录方式,建议把认证身份单独建表。比如:
user_identities
- id
- user_id
- provider
- provider_user_id
- email
- created_at
这样用户可以同时绑定邮箱密码、Google、GitHub、Apple。用户表只代表产品内的用户,身份表代表外部登录来源。
这种设计还能处理账号合并。只要两个登录方式对应同一个用户,就把它们都绑定到同一个 user_id。如果后来要增加新的登录方式,也不需要修改用户表结构。
如果第一版只有邮箱登录,可以暂时不拆。但只要你计划支持第三方登录,提前拆身份表会更干净。
第一版用户表可以这样设计:
users
- id
- email
- email_normalized
- name
- avatar_url
- status
- email_verified_at
- last_login_at
- created_at
- updated_at
- deleted_at
配套表可以是:
user_identities
- id, user_id, provider, provider_user_id, email, created_at
user_profiles
- user_id, company, role, website, bio, created_at, updated_at
user_settings
- user_id, locale, timezone, notification_json, preference_json, updated_at
subscriptions
- id, user_id, plan, status, provider, current_period_end
usage_records
- id, user_id, feature, amount, period, created_at
你不一定要一次性建完这些表,但要理解它们的职责边界。用户表负责身份,身份表负责登录方式,资料表负责展示信息,设置表负责偏好,订阅和用量表负责权益。
用户表不是万能表。它应该稳定承载用户身份、基础资料和账号状态,而不是承载所有业务数据。邮箱作为主标识通常最稳,套餐、用量、设置、行为日志都应该谨慎拆分。
第一版用户表越清楚,后面接登录、支付、权限、后台、团队协作就越容易。不要为了省一张表,把未来的复杂度全部塞进 users。
status 字段,并写清每个状态含义。user_identities 表。支付系统提前做还是最后做:支付不是简单接一个按钮,而是和定价、权限、订单、交付和客服强相关。