# 4.9 数据库设计 :::tip 概述 25 张业务表、统一审计列、13 个唯一索引、9 张租户级表、注释枚举驱动字典——本节给出数据模型全景与 ER 关系。五方言定稿脚本位于 document/{mysql,postgresql,sqlserver,oracle,sqlite}/,MySQL 版为规范基准。 ::: ## ER 关系图(逻辑外键,无物理外键约束) ```text goframe_tenant ──< goframe_user >──┬──< goframe_user_role >── goframe_role │ │ │ goframe_role_menu │ │ ├──< goframe_dept ▼ ├──< goframe_level goframe_menu ├──< goframe_position ▲ │ (user.level_id/position_id/dept_id 引用) │ goframe_config ──< goframe_config_item goframe_dict ──< goframe_dict_item goframe_category(树 parent_id) ──< goframe_article(tenant_id, category_id) article: cover 单图 / images 多图 / content 富文本 goframe_notice / goframe_link(tenant_id) goframe_city(树 pid + parent_code,全国行政区划,无租户) goframe_job ──< goframe_job_log(job_id, job_name 冗余) goframe_operation_log / goframe_login_log(独立日志表,无租户) goframe_file_template(全局文件模板,code 唯一) goframe_param(全局系统参数,code 唯一) goframe_example(案例演示表,导入导出/状态演示用) ">" 侧为多端;如 user >-- user_role --< role 为多对多。 ``` ## 全表统一规范 ```text 表前缀 goframe_;存储引擎 InnoDB;字符集 utf8mb4。 每张表统一审计列(25 表齐备): id int NOT NULL AUTO_INCREMENT 主键(数据库自增,禁用雪花) create_user varchar(50) 创建人(ctx 用户名) create_time datetime DEFAULT (now()) 创建时间 update_user varchar(50) 更新人 update_time datetime DEFAULT (now()) 更新时间 del_flag int DEFAULT 0 0-正常 1-已删除(软删) 租户级表另有 tenant_id int DEFAULT 0(0=平台级),共 9 张。 删除一律软删(deleteHook 改写);查询一律经 dao.Ctx 过滤 del_flag=0。 ``` ## 25 张表清单 | 表名 | 中文注释 | 域 | 租户级 | 特征列/说明 | |------|---------|-----|--------|------------| | goframe_article | 文章表 | content | 是 | category_id/cover/images(多图)/content(富文本)/status/click/sort | | goframe_category | 文章分类表 | content | 是 | 树形 parent_id、category_name/code/sort | | goframe_city | 城市行政区划表 | data | 否 | 树形 pid+parent_code、city_code/area_code/level、lng/lat/pinyin(大数据量表) | | goframe_config | 系统配置表 | data | 否 | name/code/sort | | goframe_config_item | 配置项表 | data | 否 | config_id/value/options/type/status/sort | | goframe_dept | 部门表 | system | 是 | 树形、删除有子级/用户守卫 | | goframe_dict | 数据字典表 | data | 否 | name/code;缓存 dict:item:{code} | | goframe_dict_item | 字典项表 | data | 否 | dict_id/name/value/sort | | goframe_example | 案例演示表 | tool | 否 | 七件套+导入导出+状态演示 | | goframe_file_template | 文件模板表 | tool | 否 | code 唯一;getTemplateByCode | | goframe_job | 定时任务表 | monitor | 否 | job_name/cron_expression/execute_policy/is_sync/url/params/status(0未发布/1运行/2暂停) | | goframe_job_log | 定时任务日志表 | monitor | 否 | job_id/status/耗时/exception_info | | goframe_level | 职级表 | system | 是 | 导入导出启用;user_count 展示 | | goframe_link | 友链表 | content | 是 | 无 status 列 | | goframe_login_log | 登录日志表 | monitor | 否 | log_type(1登录/2退出)/ip/location/os/browser/status | | goframe_menu | 菜单表 | system | 否 | 树形;type(0菜单/1权限节点)/permission/status/hide/sort | | goframe_notice | 通知公告表 | data | 是 | status | | goframe_operation_log | 操作日志表 | monitor | 否 | title/type(log_type枚举)/param/result/ip/location/consume_time/status/error | | goframe_param | 系统参数表 | data | 否 | code 唯一 | | goframe_position | 岗位表 | system | 是 | | | goframe_role | 角色表 | system | 是 | code/data_scope(四档行级数据权限) | | goframe_role_menu | 角色菜单关联表 | system | 否 | role_id+menu_id(无模块文件,归 role logic) | | goframe_tenant | 租户表 | system | 否 | code(T+ID)/status/expire_time/max_user_num | | goframe_user | 用户表 | system | 是 | username/password(bcrypt)/salt/mobile/email/avatar/dept_id/level_id/position_id/status/code | | goframe_user_role | 用户角色关联表 | system | 否 | user_id+role_id(无模块文件,归 user logic) | ## 唯一索引清单(13 个) | 表 | 索引名 | 列 | 语义 | |----|--------|-----|------| | goframe_category | uk_tenant_name | (tenant_id, category_name, del_flag) | 租户内分类名唯一 | | goframe_config | uk_code | (code) | 全局配置编码唯一 | | goframe_dept | uk_tenant_name | (tenant_id, name, del_flag) | 租户内部门名唯一 | | goframe_dict | uk_code | (code) | 全局字典编码唯一 | | goframe_file_template | uk_code | (code) | 全局模板编码唯一 | | goframe_level | uk_tenant_name | (tenant_id, name, del_flag) | 租户内职级名唯一 | | goframe_param | uk_code | (code) | 全局参数编码唯一 | | goframe_position | uk_tenant_name | (tenant_id, name, del_flag) | 租户内岗位名唯一 | | goframe_role | uk_tenant_code | (tenant_id, code, del_flag) | 租户内角色编码唯一 | | goframe_tenant | uk_code | (code) | 租户编码全局唯一 | | goframe_user | uk_tenant_username | (tenant_id, username, del_flag) | 租户内用户名唯一 | | goframe_user | uk_tenant_mobile | (tenant_id, mobile, del_flag) | 租户内手机号唯一 | | goframe_user | uk_tenant_code | (tenant_id, code, del_flag) | 租户内用户编码唯一 | 要点:索引含 del_flag(软删行不占坑,与 dao.Ctx 恒过滤语义一致);含 tenant_id 的索引,模块唯一预检范围必须等于索引范围。 ## 枚举列约定(列注释驱动字典与生成器) ```text status(账号/租户/字典项等) 1-正常 2-禁用 article.status 0-正常 1-下架 city.level 0-省份 1-城市 2-县区 3-街道 4-社区 menu.type 0-菜单 1-权限节点 job.status 0-未发布 1-运行中 2-暂停 job.execute_policy 1-立即执行 2-执行一次 3-放弃执行 job.is_sync 1-同步 0-异步 role.data_scope 1-全部 2-本部门及以下 3-本部门 4-仅本人 del_flag 0-正常 1-已删除 login_log.log_type 1-登录 2-退出 operation_log.type 见 consts/log_type.go(41 个值,静态映射不走字典) ``` ## 大表与性能注意 + **goframe_city** 全国行政区划大表(AUTO_INCREMENT 已至 78 万+):查询走 parent_code/pid 索引路径(级联懒加载 getCityList),避免全表 List;导入/迁移注意批大小。 + **日志三表**(operation_log/login_log/job_log)只增不改,依赖 LogClean 任务按 `job.logRetentionDays`(默认 90 天)分批物理清理。 + **导出接口**统一 ChunkQuery 分批(500/页)防 OOM。 ## 建表/变更工作流 ```text 1) 以 document/mysql/ 定稿为基准补 ALTER(含列注释枚举、uk_* 唯一索引、uk_/idx_ 命名规范) 2) gf gen dao -c hack/config.yaml 刷新 dao/entity/do(service 手维护) 3) 同步 document/ 其余四方言脚本(人工校对) 4) 租户级表 → 修改 hooks.tenantTables 5) go build ./... 重启;必要时 go run ./cmd/migrator 同步到其他环境库 ```