-- Users: Snowflake PK (not AUTO_INCREMENT), public spark_id, email map, login devices. CREATE TABLE IF NOT EXISTS users ( id BIGINT PRIMARY KEY COMMENT 'Snowflake 用户 id(uid),时间有序,非自增', spark_id VARCHAR(32) NOT NULL COMMENT '对外展示的账号,用户可改,全局唯一', nickname VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', avatar VARCHAR(512) NOT NULL DEFAULT '' COMMENT '头像对象存储 key 或 URL,不存图片二进制', qrcode VARCHAR(512) NOT NULL DEFAULT '' COMMENT '个人二维码对象存储 key 或 URL,不存图片二进制', birthday DATE NULL COMMENT '生日,未填为空', gender TINYINT NOT NULL DEFAULT 0 COMMENT '0=未知 1=男 2=女', status TINYINT NOT NULL DEFAULT 1 COMMENT '1=正常 0=禁用', last_login_at DATETIME(3) NULL COMMENT '最近一次登录时间', last_active_at DATETIME(3) NULL COMMENT '最近一次活跃时间(登录、心跳、发消息等)', created_at DATETIME(3) NOT NULL COMMENT '注册时间(create_time)', updated_at DATETIME(3) NOT NULL COMMENT '资料最近更新时间(update_time)', UNIQUE KEY uk_spark_id (spark_id) ) COMMENT='用户账号。主键 Snowflake 即 uid;登录身份在 user_emails,设备在 user_devices'; CREATE TABLE IF NOT EXISTS user_emails ( email VARCHAR(255) NOT NULL COMMENT '规范化后的小写邮箱,登录账号', user_id BIGINT NOT NULL COMMENT '绑定的 users.id,一对一', created_at DATETIME(3) NOT NULL COMMENT '绑定时间', PRIMARY KEY (email), UNIQUE KEY uk_user_id (user_id) ) COMMENT='邮箱与用户的一对一映射。登录用邮箱查此表再读 users'; CREATE TABLE IF NOT EXISTS user_devices ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '行 id,表内自增,仅内部用;业务唯一键是 (user_id, device_id)', user_id BIGINT NOT NULL COMMENT '所属用户', device_id BIGINT UNSIGNED NOT NULL COMMENT '客户端设备 id,写入 JWT did', platform VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'ios/android/web 等', device_name VARCHAR(128) NOT NULL DEFAULT '' COMMENT '设备展示名', app_version VARCHAR(32) NOT NULL DEFAULT '' COMMENT '最近一次登录的客户端版本', language VARCHAR(16) NOT NULL DEFAULT '' COMMENT '最近一次登录语言,如 zh-CN', last_ip VARCHAR(64) NOT NULL DEFAULT '' COMMENT '最近一次登录的 PROXY RealIP,禁止用环回地址', last_login_at DATETIME(3) NOT NULL COMMENT '最近一次登录时间', created_at DATETIME(3) NOT NULL COMMENT '该设备首次登录时间', PRIMARY KEY (id), UNIQUE KEY uk_user_device (user_id, device_id), KEY idx_user_id (user_id) ) COMMENT='登录过的设备。同 user+device 覆盖更新;踢人按 device_id';