# 数据库设计规范 本文档定义了 HoTime 框架代码生成器所依赖的数据库设计规范。遵循这些规范可以确保代码生成器正确识别表关系、自动生成 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`(详见代码生成配置规范)。 --- ## 字段注释规则 ### 注释语法格式 字段注释支持以下格式组合: ``` 显示名称(前端提示):选项值 附加备注 显示名称(前端提示):选项值 附加备注 显示名称{前端提示}:选项值 附加备注 ``` | 部分 | 分隔符 | 用途 | |------|--------|------| | 显示名称 | 无 | 前端表单/列表的字段标签 | | 前端提示 | `()`、`()` 或 `{}` | 存储到 `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),代码生成器会使用字段名作为默认显示名称。如需自定义,可在配置文件中手动设置(详见代码生成配置规范)。 --- ## 必有字段规则 每张业务表必须包含以下三个字段: ### 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 错配问题)