用户模型设计:基础属性、扩展属性、状态管理、多租户隔离

做量化平台,第一件事不是写策略,也不是搭行情系统。

而是先把用户模型想清楚。

为什么?因为后面所有的权限、风控、计费,全都要基于这个模型来展开。我见过不少团队,上来就撸代码,结果做到一半发现用户属性不够用,又回头改表结构——那叫一个痛苦。

一、基础属性:用户的“身份证”

基础属性,说白了就是能唯一标识一个用户的那几个字段。我习惯把它们分成三类:

  • 身份标识:user_id(自增主键)、username(登录名)、email、phone
  • 认证信息:password_hash、salt、two_factor_secret
  • 时间戳:created_at、updated_at、last_login_at

这里有个坑——千万别把username当主键。我在项目中遇到过,早期图省事直接用username做关联,结果用户改名时,所有关联表都要跟着改,差点没把DBA逼疯。

核心原则:user_id 用自增整数或UUID,永不对外暴露真实ID。

三、扩展属性:灵活性的关键

量化平台不同于普通网站,用户属性差异很大。

有的用户需要API密钥管理,有的需要策略白名单,有的需要风控参数配置。如果全塞进users表,那表结构会膨胀到没法看。

我的做法是:

-- 扩展属性表(EAV模式)
CREATE TABLE user_attributes (
    user_id     BIGINT NOT NULL,
    attr_key    VARCHAR(64) NOT NULL,
    attr_value  TEXT,
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (user_id, attr_key)
);

-- 示例数据
INSERT INTO user_attributes VALUES
(1001, 'risk_level', 'high'),
(1001, 'max_position', '1000000'),
(1002, 'api_whitelist', '["192.168.1.0/24"]');

嗯,这里要注意——扩展属性只适合存低频访问的配置。如果是高频查询的字段(比如用户等级),还是老老实实加到主表里,或者用Redis做缓存。

我的经验:扩展属性表里加一个version字段,方便做配置的版本管理。回滚时特别有用。

三、状态管理:别让用户“卡住”

用户状态,看起来简单,做起来全是细节。

我一般把状态分为两类:

  • 账户状态:active、inactive、suspended、closed
  • 风控状态:normal、risk_monitoring、trading_restricted、frozen

为什么要分开?因为账户状态是用户主动触发的(比如注销),而风控状态是系统自动触发的(比如检测到异常交易)。混在一起,逻辑会乱成一锅粥。

我曾经遇到过一个案例:用户因为风控被冻结,结果客服查状态发现是“inactive”,以为是用户自己注销了,直接给恢复了——然后用户继续用异常策略刷单,亏了十几万。

避坑指南:状态变更必须记录日志,包括操作人、操作时间、变更前后的值。这是审计和排查问题的救命稻草。

四、多租户架构:用户隔离怎么做?

量化平台通常服务多个机构客户(私募、自营、资管),每个机构就是一个租户。

租户隔离有三种常见模式:

模式 隔离级别 成本 适用场景
独立数据库 最高 大型机构、合规要求高
共享数据库、独立Schema 中型客户、数据隔离要求明确
共享数据库、共享Schema 小型客户、SaaS模式

我个人推荐第二种——共享数据库、独立Schema。隔离性够用,运维成本也可控。每个租户一个schema,表结构完全一样,查询时指定schema即可。

-- 租户1的数据
SELECT * FROM tenant_001.orders WHERE order_id = 123;

-- 租户2的数据
SELECT * FROM tenant_002.orders WHERE order_id = 123;

你想想看,如果所有租户混在一个表里,加个tenant_id字段,那查询时得处处带着where条件,一不小心就查到了别人的数据——这在金融系统里可是致命错误。

核心原则:多租户架构下,数据隔离是底线。宁可多花点存储成本,也别在隔离性上妥协。

五、用户模型的核心结构图

下面这张图,是我做用户模型时必画的。它把基础属性、扩展属性、状态、租户的关系理得清清楚楚:

用户主表 user_id (PK) username, email password_hash created_at, updated_at 扩展属性表 user_id (FK) attr_key attr_value version 状态管理表 user_id (FK) account_status risk_status changed_at 租户表 tenant_id (PK) tenant_name schema_name 用户-租户关联 user_id (FK) tenant_id (FK) role 1:N 1:N N:1 N:1

这张图里,用户主表是核心,扩展属性和状态管理都是它的从属。租户表独立存在,通过关联表与用户建立多对多关系——一个用户可能属于多个租户(比如既是某私募的交易员,又是另一家机构的顾问)。

六、总结一下

用户模型设计,说白了就是回答三个问题:

  1. 用户是谁?——基础属性搞定
  2. 用户有什么特殊配置?——扩展属性搞定
  3. 用户现在什么状态?——状态管理搞定
  4. 用户属于哪个租户?——多租户隔离搞定

把这四个问题想清楚,后面的权限、风控、计费模块,都会顺很多。别问我怎么知道的——当年第一个版本没想清楚,重构了三次才稳定下来。

最后一个小建议:设计用户模型时,多花一天时间做评审,能省下后面一个月的返工时间。相信我,这买卖划算。


公众号:蓝海数据掘金营,微信deep3321