Files
hotime/docs/DatabaseDesign_数据库设计规范.md
T
hoteas de17ecbfd5 chore(logging): 更新日志重定向与捕获功能
- 在 .gitignore 中添加调试日志文件的忽略规则,避免不必要的调试信息被提交
- 修改 application.go 中的 stdout 重定向逻辑,使用 log.CaptureStream 以支持更灵活的日志捕获
- 更新 README 文档,增加对日志重定向功能的说明
2026-07-13 07:45:51 +08:00

498 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据库设计规范
本文档是 HoTime **CodeGen 代码生成器的输入契约**:表/字段命名、外键、注释与公共字段等规则直接决定生成结果。请严格按本文设计库表,再配合 [CodeGen_代码生成.md](CodeGen_代码生成.md) 使用代码生成。
遵循这些规范可确保代码生成器正确识别表关系、自动生成 CRUD 接口和管理后台。
---
## 表命名规则
### 1. 关于表名前缀
一般情况下**没必要强行添加前缀分组**,直接使用业务含义命名即可:
```
推荐:user、org、role、article
```
**可以使用前缀的场景**:当项目较大、表较多时,可以用前缀进行模块分组(如 `sys_``cms_`),代码生成器会根据 `_` 分词自动将同前缀的表归为一组。
```
sys_user、sys_role、sys_org → 自动归入 sys 分组
cms_article、cms_category → 自动归入 cms 分组
```
**使用前缀的注意事项**
- 必须遵循外键全局唯一性规则(见下文)
- 前缀分组后,关联 ID 命名会更复杂,容易产生重复
- **外键名不允许重复指向不同的表**
### 2. 可使用简称
较长的表名可以使用常见简称:
| 全称 | 简称 |
|------|------|
| organization | org |
| category | ctg |
| configuration | config |
| administrator | admin |
### 3. 关联表命名
多对多关联表使用 `主表_关联表` 格式:
```
user_org -- 用户与组织的关联
user_role -- 用户与角色的关联
article_tag -- 文章与标签的关联
```
---
## 字段命名规则
### 1. 主键字段
所有表的主键统一命名为 `id`,使用自增整数。
```sql
`id` int(11) NOT NULL AUTO_INCREMENT
```
### 2. 外键字段
外键使用 `完整表名_id` 格式:
```
user_id -- 指向 user 表
org_id -- 指向 org 表
role_id -- 指向 role 表
app_category_id -- 指向 app_category 表
```
### 3. 关联表引用
引用关联表时使用 `关联表名_id`
```
user_org_id -- 指向 user_org 表
user_role_id -- 指向 user_role 表
```
### 4. 外键全局唯一性(重要)
**每个 `xxx_id` 字段名必须全局唯一指向一张表**,代码生成器通过 `_id` 后缀自动识别外键关系。
```
✅ 正确设计:
- user_id 只能指向 user 表
- org_id 只能指向 org 表
- user_org_id 只能指向 user_org 表
- sys_user_id 只能指向 sys_user 表(如果使用前缀分组)
❌ 错误设计:
- dd_id 作为"钉钉外部系统ID",但系统中存在 dd 表
→ 代码生成器会误判为指向 dd 表的外键
- 同时存在 user 表和 sys_user 表,都使用 user_id
→ 外键名重复,代码生成器无法正确识别
```
**使用前缀分组时的外键命名**
如果使用了表名前缀(如 `sys_user`),外键应使用完整表名:
| 表名 | 外键命名 |
|------|----------|
| sys_user | sys_user_id |
| sys_org | sys_org_id |
| cms_article | cms_article_id |
**非外键业务标识字段**:避免使用 `xxx_id` 格式
| 业务含义 | 错误命名 | 正确命名 |
|----------|----------|----------|
| 设备唯一标识 | device_id | device_uuid / device_sn |
| 钉钉用户ID | dingtalk_user_id | dt_user_id / dingtalk_uid |
| 微信OpenID | wechat_id | wechat_openid |
| 外部系统编号 | external_id | external_code / external_sn |
### 5. 外键智能匹配机制
代码生成器会按以下优先级自动匹配外键关联的表:
1. **完全匹配**`user_id` → 查找 `user`
2. **带前缀的完全匹配**:如果没找到,尝试 `user_id` → 查找 `sys_user` 表(默认前缀)
3. **去除字段前缀匹配**:如果字段有前缀且找不到对应表,会去掉前缀匹配
**示例**`admin_user_id` 字段的匹配过程:
1. 先查找 `admin_user` 表 → 如果存在,关联到 `admin_user`
2. 如果不存在,去掉前缀查找 `user` 表 → 如果存在,关联到 `user`
**使用场景**:当一张表需要记录多个用户(如创建人、审核人、处理人)时:
```sql
CREATE TABLE `order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) COMMENT '下单用户',
`audit_user_id` int(11) COMMENT '审核人',
`handle_user_id` int(11) COMMENT '处理人',
...
);
```
如果系统中没有 `audit_user``handle_user` 表,代码生成器会自动将 `audit_user_id``handle_user_id` 识别为指向 `user` 表的外键。
### 6. 关联表冗余外键
关联表应包含必要的冗余外键便于查询:
```sql
-- user_app 表(用户与应用的关联)
CREATE TABLE user_app (
id int(11) NOT NULL AUTO_INCREMENT,
user_id int(11) DEFAULT NULL COMMENT '用户ID',
app_id int(11) DEFAULT NULL COMMENT '应用ID',
app_category_id int(11) DEFAULT NULL COMMENT '应用分类ID(冗余,便于按分类查询)',
...
);
```
### 7. 层级关系字段
树形结构的表使用以下字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| parent_id | int | 父级ID,顶级为 NULL 或 0 |
| parent_ids | varchar(255) | 完整层级路径,格式:`,1,3,5,`(从根到当前,逗号分隔) |
| level | int | 层级深度,从 0 开始 |
**示例**
```
id=1, parent_id=NULL, parent_ids=",1,", level=0 -- 顶级
id=3, parent_id=1, parent_ids=",1,3,", level=1 -- 一级
id=5, parent_id=3, parent_ids=",1,3,5,", level=2 -- 二级
```
`parent_ids` 的设计便于快速查询所有子级:
```sql
SELECT * FROM org WHERE parent_ids LIKE '%,3,%' -- 查询id=3的所有子级
```
---
## 时间字段类型
不同数据库使用对应的时间类型:
| 数据库 | 类型 | 示例 |
|--------|------|------|
| MySQL | datetime | `2024-01-15 10:30:00` |
| SQLite | TEXT | `2024-01-15 10:30:00` |
| PostgreSQL | timestamp | `2024-01-15 10:30:00` |
| 达梦 DM8 | TIMESTAMP | `2024-01-15 10:30:00` |
---
## 表注释规则
### 表备注命名建议
表备注建议使用"XX管理"格式,便于在管理后台显示:
```sql
-- MySQL 表备注示例
CREATE TABLE `user` (
...
) COMMENT='用户管理';
CREATE TABLE `article` (
...
) COMMENT='文章管理';
CREATE TABLE `order` (
...
) COMMENT='订单管理';
```
### SQLite 表备注
SQLite 不支持表备注,代码生成器会使用表名作为默认显示名称。如需自定义,可在配置文件中手动设置 `label`(详见 [CodeGen_代码生成.md](CodeGen_代码生成.md))。
---
## 字段注释规则
### 注释语法格式
字段注释支持以下格式组合:
```
显示名称(前端提示):选项值 附加备注
显示名称(前端提示):选项值 附加备注
显示名称{前端提示}:选项值 附加备注
```
| 部分 | 分隔符 | 用途 |
|------|--------|------|
| 显示名称 | 无 | 前端表单/列表的字段标签 |
| 前端提示 | `()``()``{}` | 存储到 `ps` 字段,前端用于字段说明提示(编辑页输入框右侧、表格/详情鼠标悬停气泡) |
| 选项值 | `:` 冒号 | select 类型的下拉选项(格式:`值-名称,值-名称` |
| 附加备注 | 空格 | 仅在数据库中查看,不传递给前端 |
### 选择类型字段
使用 `标签:值-名称,值-名称` 格式,代码生成器会自动解析为下拉选项:
```sql
`state` int(2) DEFAULT '0' COMMENT '状态:0-正常,1-异常,2-隐藏'
`sex` int(1) DEFAULT '0' COMMENT '性别:0-未知,1-男,2-女'
`status` int(2) DEFAULT '0' COMMENT '审核状态:0-待审核,1-已通过,2-已拒绝'
```
### 普通字段
直接使用中文说明:
```sql
`name` varchar(50) DEFAULT NULL COMMENT '名称'
`phone` varchar(20) DEFAULT NULL COMMENT '手机号'
`email` varchar(100) DEFAULT NULL COMMENT '邮箱'
```
### 附加备注(空格后)
空格后的内容**不会传递给前端**,仅供数据库设计时参考:
```sql
`user_id` int(11) COMMENT '用户ID 关联user表的主键'
`amount` decimal(10,2) COMMENT '金额 单位为元,精确到分'
```
### 前端提示(括号内)
`()``()``{}` 中的内容存储到 `ps` 字段,前端展示为字段说明提示。推荐使用 `()``()``{}` 为兼容旧格式保留。
```sql
`parent_id` int(11) COMMENT '父级ID(顶级为NULL'
`status` int(2) COMMENT '状态(当前审核状态):0-待审,1-通过,2-驳回'
`phone` varchar(20) COMMENT '手机号(请输入11位手机号)'
`email` varchar(100) COMMENT '邮箱{格式:xxx@xxx.com}'
```
前端展示效果:
- **编辑/新增页面**:输入框右侧显示灰色小字提示
- **表格列表/详情页面**:鼠标悬停字段名时显示气泡提示
### 组合使用
```sql
-- 推荐写法:使用圆括号
`status` int(2) DEFAULT '0' COMMENT '状态(选择当前审核状态):0-待审核,1-已通过,2-已拒绝'
`parent_id` int(11) DEFAULT NULL COMMENT '父级ID(顶级为NULL'
-- 兼容写法:使用花括号
`status` int(2) DEFAULT '0' COMMENT '状态{选择当前审核状态}:0-待审核,1-已通过,2-已拒绝'
```
解析结果(以第一行为例):
- `label` = "状态"
- `ps` = "选择当前审核状态"
- `options` = [{name:"待审核", value:"0"}, {name:"已通过", value:"1"}, {name:"已拒绝", value:"2"}]
三种括号 `()``()``{}` 功能相同,优先匹配顺序为:`()` > `()` > `{}`,同一注释中只取第一个匹配到的括号对。
### SQLite 字段备注
SQLite 不支持字段备注(COMMENT),代码生成器会使用字段名作为默认显示名称。如需自定义,可在配置文件中手动设置(详见 [CodeGen_代码生成.md](CodeGen_代码生成.md))。
---
## 必有字段规则
每张业务表必须包含以下三个字段:
### MySQL
```sql
`state` int(2) DEFAULT '0' COMMENT '状态:0-正常,1-异常,2-隐藏',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`modify_time` datetime DEFAULT NULL COMMENT '变更时间',
```
### SQLite
```sql
"state" INTEGER DEFAULT 0, -- 状态:0-正常,1-异常,2-隐藏
"create_time" TEXT DEFAULT NULL, -- 创建日期
"modify_time" TEXT DEFAULT NULL, -- 变更时间
```
### PostgreSQL
```sql
"state" INTEGER DEFAULT 0, -- 状态:0-正常,1-异常,2-隐藏
"create_time" TIMESTAMP DEFAULT NULL, -- 创建日期
"modify_time" TIMESTAMP DEFAULT NULL, -- 变更时间
```
### 达梦 DM8
```sql
"state" INT DEFAULT 0, -- 状态:0-正常,1-异常,2-隐藏
"create_time" TIMESTAMP DEFAULT NULL, -- 创建日期
"modify_time" TIMESTAMP DEFAULT NULL, -- 变更时间
```
---
## 完整建表示例
### MySQL 示例
```sql
-- 用户表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(50) DEFAULT NULL COMMENT '用户名',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`password` varchar(64) DEFAULT NULL COMMENT '密码',
`org_id` int(11) DEFAULT NULL COMMENT '组织ID',
`role_id` int(11) DEFAULT NULL COMMENT '角色ID',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`sex` int(1) DEFAULT '0' COMMENT '性别:0-未知,1-男,2-女',
`state` int(2) DEFAULT '0' COMMENT '状态:0-正常,1-异常,2-隐藏',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`modify_time` datetime DEFAULT NULL COMMENT '变更时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 组织表(树形结构)
CREATE TABLE `org` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(100) DEFAULT NULL COMMENT '组织名称',
`parent_id` int(11) DEFAULT NULL COMMENT '父级ID',
`parent_ids` varchar(255) DEFAULT NULL COMMENT '层级路径',
`level` int(2) DEFAULT '0' COMMENT '层级深度',
`sort` int(5) DEFAULT '0' COMMENT '排序',
`state` int(2) DEFAULT '0' COMMENT '状态:0-正常,1-异常,2-隐藏',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`modify_time` datetime DEFAULT NULL COMMENT '变更时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='组织表';
-- 用户组织关联表
CREATE TABLE `user_org` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`org_id` int(11) DEFAULT NULL COMMENT '组织ID',
`state` int(2) DEFAULT '0' COMMENT '状态:0-正常,1-异常,2-隐藏',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`modify_time` datetime DEFAULT NULL COMMENT '变更时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户组织关联表';
```
### SQLite 示例
```sql
-- 用户表
CREATE TABLE "user" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"name" TEXT DEFAULT NULL,
"phone" TEXT DEFAULT NULL,
"password" TEXT DEFAULT NULL,
"org_id" INTEGER DEFAULT NULL,
"role_id" INTEGER DEFAULT NULL,
"avatar" TEXT DEFAULT NULL,
"sex" INTEGER DEFAULT 0,
"state" INTEGER DEFAULT 0,
"create_time" TEXT DEFAULT NULL,
"modify_time" TEXT DEFAULT NULL
);
```
### 达梦 DM8 示例
达梦建表有以下关键差异:
- 自增主键使用 `IDENTITY(1,1)`(对应 MySQL 的 `AUTO_INCREMENT`
- 长文本使用 `CLOB`(对应 MySQL 的 `LONGTEXT`
- 标识符建议用**双引号包裹小写名称**,与框架保持一致
- `admin``user``order` 等是 DM 保留字,必须加双引号
```sql
-- 用户表
CREATE TABLE "user" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(50) DEFAULT NULL,
"phone" VARCHAR(20) DEFAULT NULL,
"password" VARCHAR(64) DEFAULT NULL,
"org_id" INT DEFAULT NULL,
"role_id" INT DEFAULT NULL,
"avatar" VARCHAR(255) DEFAULT NULL,
"sex" INT DEFAULT 0,
"state" INT DEFAULT 0,
"create_time" TIMESTAMP DEFAULT NULL,
"modify_time" TIMESTAMP DEFAULT NULL
);
-- 组织表(树形结构)
CREATE TABLE "org" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(100) DEFAULT NULL,
"parent_id" INT DEFAULT NULL,
"parent_ids" VARCHAR(255) DEFAULT NULL,
"level" INT DEFAULT 0,
"sort" INT DEFAULT 0,
"state" INT DEFAULT 0,
"create_time" TIMESTAMP DEFAULT NULL,
"modify_time" TIMESTAMP DEFAULT NULL
);
-- 用户组织关联表
CREATE TABLE "user_org" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"user_id" INT DEFAULT NULL,
"org_id" INT DEFAULT NULL,
"state" INT DEFAULT 0,
"create_time" TIMESTAMP DEFAULT NULL,
"modify_time" TIMESTAMP DEFAULT NULL
);
```
> **注意**:达梦中 DDL 的表名/列名大小写敏感(双引号包裹时),建议统一使用小写,与框架自动生成的 SQL 保持一致。
---
## 规范检查清单
在设计数据库时,请确认:
- [ ] 表名无系统前缀
- [ ] 主键统一为 `id`
- [ ] 外键格式为 `表名_id`
- [ ] 所有 `xxx_id` 字段都指向实际存在的表
- [ ] 非外键业务标识不使用 `xxx_id` 格式
- [ ] 树形表包含 parent_id、parent_ids、level
- [ ] 所有表包含 state、create_time、modify_time
- [ ] 选择字段注释格式正确(`标签:值-名称`
### 达梦 DM8 额外检查项
- [ ] 建表时标识符使用**双引号包裹小写名称**(如 `"user"``"id"`
- [ ] 保留字(`admin``user``order``key` 等)必须加双引号
- [ ] 自增主键使用 `IDENTITY(1,1)` 而非 `AUTO_INCREMENT`
- [ ] 长文本字段使用 `CLOB` 而非 `LONGTEXT`
- [ ] 检测表是否存在时用 `COUNT(*)` 查询,不用 `USER_TABLES`(存在 schema 错配问题)
---
## 相关文档
- [CodeGen_代码生成.md](CodeGen_代码生成.md) — 按本规范建表后运行代码生成
- [HoTimeDB_使用说明.md](HoTimeDB_使用说明.md) — 运行时 ORM(与建表规范分开)
- [文档索引](README.md)