# 数据模型设计 ## 实现状态说明 (2026-03-29 更新) 本文档描述的是**设计目标**数据库结构,实际实现与设计存在以下差异: ### 与实际实现的差异 | 设计表格 | 实现状态 | 说明 | |----------|----------|------| | user_credentials | ⚠️ 合并实现 | 密码凭证存储在 users.password,TOTP数据在 users 表;社交账号在 user_social_accounts 表 | | audit_logs | ⚠️ 命名差异 | 实际实现为 operation_logs 表 | | verification_codes | ❌ 未实现 | 验证码当前在内存/Redis中管理,无独立表 | | token_blacklist | ❌ 未实现 | JWT吊销使用JTI机制,无需独立表 | | user_custom_fields | ❌ 未实现 | 当前版本未支持此功能 | | system_configs | ⚠️ 替代方案 | 系统配置通过 config.yaml 文件管理,无数据库表 | ### 实际实现的数据库表 当前 GORM AutoMigrate 实际创建的表: - users - roles - permissions - user_roles - role_permissions - devices - login_logs - operation_logs - user_social_accounts - webhooks - webhook_deliveries - password_history --- ## 概述 本文档描述用户管理系统的核心数据库表结构和字段定义。 ### 支持的数据库 | 数据库 | 用途 | 特点 | |--------|------|------| | **SQLite** | 默认数据库 | 无需独立部署,单文件存储,适合单机场景 | | **PostgreSQL** | 生产环境可选 | 功能强大,支持高级特性,适合中大型应用 | | **MySQL** | 生产环境可选 | 广泛使用,社区成熟,适合中大型应用 | | **MongoDB** | 文档存储可选 | 灵活的文档存储,适合特定场景 | **注意**:SQLite 作为默认数据库,所有表结构都兼容其他关系型数据库(PostgreSQL/MySQL),可通过配置文件平滑切换。 ## 表结构设计 ### 1. 用户表 (users) 用户基础信息表,存储用户的基本资料。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 用户 ID(主键) | | username | VARCHAR | 50 | 否 | NULL | 用户名(唯一索引) | | email | VARCHAR | 100 | 否 | NULL | 邮箱(唯一索引) | | phone | VARCHAR | 20 | 否 | NULL | 手机号(唯一索引) | | nickname | VARCHAR | 50 | 否 | NULL | 昵称 | | avatar | VARCHAR | 255 | 否 | NULL | 头像 URL | | gender | TINYINT | - | 否 | 0 | 性别:0-未知,1-男,2-女 | | birthday | DATE | - | 否 | NULL | 生日 | | region | VARCHAR | 50 | 否 | NULL | 所在地区 | | bio | VARCHAR | 500 | 否 | NULL | 个性签名 | | status | TINYINT | - | 是 | 1 | 状态:0-待激活,1-正常,2-锁定,3-禁用 | | last_login_time | DATETIME | - | 否 | NULL | 最后登录时间 | | last_login_ip | VARCHAR | 50 | 否 | NULL | 最后登录 IP | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | | updated_at | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE | 更新时间 | | deleted_at | DATETIME | - | 否 | NULL | 删除时间(软删除) | **索引设计:** - PRIMARY KEY (`id`) - UNIQUE KEY `uk_username` (`username`) - UNIQUE KEY `uk_email` (`email`) - UNIQUE KEY `uk_phone` (`phone`) - KEY `idx_status` (`status`) - KEY `idx_created_at` (`created_at`) --- ### 2. 用户凭证表 (user_credentials) 用户凭证表,存储密码、社交绑定等信息。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 凭证 ID(主键) | | user_id | BIGINT | - | 是 | - | 用户 ID(外键) | | credential_type | VARCHAR | 20 | 是 | - | 凭证类型:password/wechat/qq/alipay/douyin/github/google | | identifier | VARCHAR | 100 | 是 | - | 标识符(openid、unionid 等) | | credential_value | VARCHAR | 500 | 否 | NULL | 凭证值(加密后的密码等) | | salt | VARCHAR | 100 | 否 | NULL | 盐值(用于密码加密) | | is_primary | TINYINT | - | 是 | 1 | 是否主要凭证:0-否,1-是 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | | updated_at | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE | 更新时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_user_id` (`user_id`) - UNIQUE KEY `uk_user_type_identifier` (`user_id`, `credential_type`, `identifier`) --- ### 3. 角色表 (roles) 角色表,定义系统角色。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 角色 ID(主键) | | name | VARCHAR | 50 | 是 | - | 角色名称(唯一) | | code | VARCHAR | 50 | 是 | - | 角色代码(唯一) | | description | VARCHAR | 200 | 否 | NULL | 角色描述 | | parent_id | BIGINT | - | 否 | NULL | 父角色 ID | | level | INT | - | 是 | 1 | 角色层级 | | is_system | TINYINT | - | 是 | 0 | 是否系统角色:0-否,1-是 | | is_default | TINYINT | - | 是 | 0 | 是否默认角色:0-否,1-是 | | status | TINYINT | - | 是 | 1 | 状态:0-禁用,1-启用 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | | updated_at | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE | 更新时间 | **索引设计:** - PRIMARY KEY (`id`) - UNIQUE KEY `uk_name` (`name`) - UNIQUE KEY `uk_code` (`code`) - KEY `idx_parent_id` (`parent_id`) - KEY `idx_level` (`level`) - KEY `idx_is_default` (`is_default`) **初始默认角色:** - `id=1, code='admin', name='管理员', is_system=1, is_default=0` - 系统管理员角色,拥有所有权限 - `id=2, code='user', name='普通用户', is_system=1, is_default=1` - 普通用户角色,基本权限 --- ### 4. 权限表 (permissions) 权限表,定义系统权限。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 权限 ID(主键) | | name | VARCHAR | 50 | 是 | - | 权限名称 | | code | VARCHAR | 100 | 是 | - | 权限代码(格式:resource:action) | | resource | VARCHAR | 50 | 是 | - | 资源名称 | | action | VARCHAR | 20 | 是 | - | 操作类型(read/write/delete/execute) | | description | VARCHAR | 200 | 否 | NULL | 权限描述 | | type | VARCHAR | 20 | 是 | - | 权限类型:api/page/button | | group_id | BIGINT | - | 否 | NULL | 权限分组 ID | | status | TINYINT | - | 是 | 1 | 状态:0-禁用,1-启用 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | | updated_at | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE | 更新时间 | **索引设计:** - PRIMARY KEY (`id`) - UNIQUE KEY `uk_code` (`code`) - KEY `idx_resource` (`resource`) - KEY `idx_group_id` (`group_id`) - KEY `idx_type` (`type`) --- ### 5. 用户角色关联表 (user_roles) 用户和角色的多对多关联表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 关联 ID(主键) | | user_id | BIGINT | - | 是 | - | 用户 ID | | role_id | BIGINT | - | 是 | - | 角色 ID | | assigned_by | BIGINT | - | 否 | NULL | 分配人 ID | | assigned_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 分配时间 | | expire_at | DATETIME | - | 否 | NULL | 过期时间(NULL 表示永久) | **索引设计:** - PRIMARY KEY (`id`) - UNIQUE KEY `uk_user_role` (`user_id`, `role_id`) - KEY `idx_role_id` (`role_id`) --- ### 6. 角色权限关联表 (role_permissions) 角色和权限的多对多关联表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 关联 ID(主键) | | role_id | BIGINT | - | 是 | - | 角色 ID | | permission_id | BIGINT | - | 是 | - | 权限 ID | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | **索引设计:** - PRIMARY KEY (`id`) - UNIQUE KEY `uk_role_permission` (`role_id`, `permission_id`) - KEY `idx_permission_id` (`permission_id`) --- ### 7. 设备管理表 (devices) 用户设备管理表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 设备 ID(主键) | | user_id | BIGINT | - | 是 | - | 用户 ID | | device_id | VARCHAR | 100 | 是 | - | 设备唯一标识 | | device_name | VARCHAR | 50 | 否 | NULL | 设备名称 | | device_type | VARCHAR | 20 | 是 | - | 设备类型:pc/mobile/tablet | | os | VARCHAR | 50 | 否 | NULL | 操作系统 | | browser | VARCHAR | 50 | 否 | NULL | 浏览器 | | ip | VARCHAR | 50 | 否 | NULL | IP 地址 | | location | VARCHAR | 100 | 否 | NULL | 地理位置 | | is_trusted | TINYINT | - | 是 | 0 | 是否信任:0-否,1-是 | | last_active_time | DATETIME | - | 否 | NULL | 最后活跃时间 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_user_id` (`user_id`) - UNIQUE KEY `uk_device_id` (`device_id`) - KEY `idx_last_active_time` (`last_active_time`) --- ### 8. 登录日志表 (login_logs) 用户登录日志表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 日志 ID(主键) | | user_id | BIGINT | - | 否 | NULL | 用户 ID | | login_type | VARCHAR | 20 | 是 | - | 登录方式:password/code/wechat/qq/... | | login_method | VARCHAR | 20 | 否 | NULL | 认证方式 | | ip | VARCHAR | 50 | 否 | NULL | IP 地址 | | location | VARCHAR | 100 | 否 | NULL | 地理位置 | | device_id | VARCHAR | 100 | 否 | NULL | 设备 ID | | user_agent | VARCHAR | 500 | 否 | NULL | User-Agent | | status | TINYINT | - | 是 | - | 状态:0-失败,1-成功 | | failure_reason | VARCHAR | 200 | 否 | NULL | 失败原因 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 登录时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_user_id` (`user_id`) - KEY `idx_ip` (`ip`) - KEY `idx_status` (`status`) - KEY `idx_created_at` (`created_at`) **分区设计(MySQL):** - 按月分区,保留最近 12 个月数据 --- ### 9. 审计日志表 (audit_logs) 系统审计日志表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 日志 ID(主键) | | user_id | BIGINT | - | 否 | NULL | 操作人 ID | | action_type | VARCHAR | 50 | 是 | - | 操作类型 | | resource_type | VARCHAR | 50 | 是 | - | 资源类型 | | resource_id | BIGINT | - | 否 | NULL | 资源 ID | | action | VARCHAR | 20 | 是 | - | 操作动作:create/update/delete | | old_value | TEXT | - | 否 | NULL | 操作前值 | | new_value | TEXT | - | 否 | NULL | 操作后值 | | ip | VARCHAR | 50 | 否 | NULL | IP 地址 | | user_agent | VARCHAR | 500 | 否 | NULL | User-Agent | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 操作时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_user_id` (`user_id`) - KEY `idx_resource_type` (`resource_type`) - KEY `idx_created_at` (`created_at`) **分区设计(MySQL):** - 按月分区,保留最近 24 个月数据 --- ### 10. 验证码表 (verification_codes) 验证码表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 验证码 ID(主键) | | code | VARCHAR | 20 | 是 | - | 验证码 | | type | VARCHAR | 20 | 是 | - | 类型:register/login/reset_password/bind_phone/bind_email | | identifier | VARCHAR | 100 | 是 | - | 标识符(邮箱或手机号) | | expire_at | DATETIME | - | 是 | - | 过期时间 | | used | TINYINT | - | 是 | 0 | 是否已使用:0-否,1-是 | | ip | VARCHAR | 50 | 否 | NULL | IP 地址 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_identifier_type` (`identifier`, `type`) - KEY `idx_expire_at` (`expire_at`) --- ### 11. Token 黑名单表 (token_blacklist) Token 黑名单表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 记录 ID(主键) | | user_id | BIGINT | - | 是 | - | 用户 ID | | token | VARCHAR | 500 | 是 | - | Token | | token_type | VARCHAR | 20 | 是 | - | Token 类型:access/refresh | | expire_at | DATETIME | - | 是 | - | 过期时间 | | revoked_by | BIGINT | - | 否 | NULL | 吊销人 ID | | revoked_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 吊销时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_user_id` (`user_id`) - KEY `idx_expire_at` (`expire_at`) --- ### 12. 用户自定义字段表 (user_custom_fields) 用户自定义字段表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 字段 ID(主键) | | user_id | BIGINT | - | 是 | - | 用户 ID | | field_key | VARCHAR | 50 | 是 | - | 字段键名 | | field_value | TEXT | - | 否 | NULL | 字段值(JSON 格式) | | field_type | VARCHAR | 20 | 是 | - | 字段类型:string/number/boolean/date | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | | updated_at | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE | 更新时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_user_id` (`user_id`) - UNIQUE KEY `uk_user_key` (`user_id`, `field_key`) --- ### 13. Webhook 配置表 (webhook_configs) Webhook 配置表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 配置 ID(主键) | | name | VARCHAR | 50 | 是 | - | Webhook 名称 | | event_types | TEXT | - | 是 | - | 事件类型(JSON 数组) | | url | VARCHAR | 255 | 是 | - | 回调 URL | | secret | VARCHAR | 100 | 否 | NULL | 签名密钥 | | headers | TEXT | - | 否 | NULL | 自定义请求头(JSON) | | is_active | TINYINT | - | 是 | 1 | 是否启用:0-否,1-是 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | | updated_at | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE | 更新时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_is_active` (`is_active`) --- ### 14. Webhook 日志表 (webhook_logs) Webhook 日志表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 日志 ID(主键) | | webhook_id | BIGINT | - | 是 | - | Webhook 配置 ID | | event_type | VARCHAR | 50 | 是 | - | 事件类型 | | event_data | TEXT | - | 是 | - | 事件数据(JSON) | | request_url | VARCHAR | 255 | 是 | - | 请求 URL | | request_headers | TEXT | - | 否 | NULL | 请求头(JSON) | | request_body | TEXT | - | 否 | NULL | 请求体(JSON) | | response_status | INT | - | 否 | NULL | 响应状态码 | | response_body | TEXT | - | 否 | NULL | 响应体 | | retry_count | INT | - | 是 | 0 | 重试次数 | | status | VARCHAR | 20 | 是 | - | 状态:pending/success/failed | | error_message | TEXT | - | 否 | NULL | 错误信息 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | **索引设计:** - PRIMARY KEY (`id`) - KEY `idx_webhook_id` (`webhook_id`) - KEY `idx_event_type` (`event_type`) - KEY `idx_status` (`status`) - KEY `idx_created_at` (`created_at`) **分区设计(MySQL):** - 按月分区,保留最近 12 个月数据 --- ### 15. 系统配置表 (system_configs) 系统配置表。 | 字段名 | 类型 | 长度 | 是否必填 | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | 是 | - | 配置 ID(主键) | | config_key | VARCHAR | 100 | 是 | - | 配置键(唯一) | | config_value | TEXT | - | 否 | NULL | 配置值 | | config_type | VARCHAR | 20 | 是 | - | 配置类型:string/number/boolean/json | | description | VARCHAR | 200 | 否 | NULL | 配置描述 | | is_system | TINYINT | - | 是 | 0 | 是否系统配置:0-否,1-是 | | created_at | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | | updated_at | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE | 更新时间 | **索引设计:** - PRIMARY KEY (`id`) - UNIQUE KEY `uk_config_key` (`config_key`) --- ## ER 图 ```mermaid erDiagram users ||--o{ user_credentials : "has" users ||--o{ user_roles : "has" users ||--o{ devices : "has" users ||--o{ login_logs : "has" users ||--o{ audit_logs : "has" users ||--o{ user_custom_fields : "has" users ||--o{ token_blacklist : "has" roles ||--o{ user_roles : "assigned to" roles ||--o{ role_permissions : "has" roles ||--o{ roles : "inherits from" permissions ||--o{ role_permissions : "assigned to" webhook_configs ||--o{ webhook_logs : "has" ``` --- ## MongoDB 结构设计 如果使用 MongoDB,建议采用以下集合结构: ### users 集合 ```json { "_id": ObjectId("..."), "username": "john_doe", "email": "john@example.com", "phone": "+86138xxxxxxxx", "nickname": "John", "avatar": "https://...", "profile": { "gender": 1, "birthday": "1990-01-01", "region": "北京", "bio": "...", "customFields": { "company": "ABC Inc.", "position": "Developer" } }, "credentials": [ { "type": "password", "hash": "...", "salt": "...", "isPrimary": true }, { "type": "wechat", "openid": "...", "unionid": "..." } ], "roles": [1, 2], "devices": [ { "deviceId": "...", "deviceName": "iPhone 15", "deviceType": "mobile", "isTrusted": true, "lastActiveTime": ISODate("2026-03-10T10:00:00Z") } ], "status": 1, "lastLogin": { "time": ISODate("2026-03-10T10:00:00Z"), "ip": "192.168.1.1" }, "createdAt": ISODate("2026-01-01T00:00:00Z"), "updatedAt": ISODate("2026-03-10T10:00:00Z"), "deletedAt": null } ``` ### roles 集合 ```json { "_id": ObjectId("..."), "name": "普通用户", "code": "user", "description": "普通用户角色", "parentId": null, "level": 1, "isSystem": false, "status": 1, "permissions": [1, 2, 3], "createdAt": ISODate("2026-01-01T00:00:00Z"), "updatedAt": ISODate("2026-01-01T00:00:00Z") } ``` ### permissions 集合 ```json { "_id": ObjectId("..."), "name": "查看用户", "code": "user:read", "resource": "user", "action": "read", "description": "查看用户信息", "type": "api", "groupId": 1, "status": 1, "createdAt": ISODate("2026-01-01T00:00:00Z"), "updatedAt": ISODate("2026-01-01T00:00:00Z") } ``` ### login_logs 集合 ```json { "_id": ObjectId("..."), "userId": ObjectId("..."), "loginType": "password", "ip": "192.168.1.1", "location": "北京市", "device": { "deviceId": "...", "deviceName": "iPhone 15", "deviceType": "mobile", "os": "iOS 17", "browser": "Safari" }, "status": 1, "failureReason": null, "createdAt": ISODate("2026-03-10T10:00:00Z") } ``` --- ## 索引策略 ### 索引设计原则 1. **主键索引**:所有表必须有主键 2. **唯一索引**:用户名、邮箱、手机号等唯一字段 3. **复合索引**:经常一起查询的字段组合 4. **覆盖索引**:避免回表查询 5. **分区索引**:大表按时间分区 ### 常用查询优化 ```sql -- 用户登录查询 SELECT * FROM users WHERE (username = ? OR email = ? OR phone = ?) AND status = 1; -- 角色权限查询 SELECT p.* FROM permissions p INNER JOIN role_permissions rp ON p.id = rp.permission_id WHERE rp.role_id IN (SELECT role_id FROM user_roles WHERE user_id = ?); -- 登录日志统计 SELECT DATE(created_at) as date, COUNT(*) as count FROM login_logs WHERE user_id = ? GROUP BY DATE(created_at) ORDER BY date DESC; ``` --- ## 数据迁移 ### 初始化脚本 提供数据库初始化 SQL 脚本,包括: 1. 建表语句 2. 初始数据(默认角色、权限) 3. 索引创建 4. 分区设置 ### 版本管理 使用数据库迁移工具(如 Flyway、Liquibase)管理数据库版本: - 每次数据库结构变更需要迁移脚本 - 支持版本回滚 - 记录迁移历史 --- *本文档持续更新中,如有疑问请联系技术团队。*