# 03-数据库设计文档 - **Schema**: `xlyweberp_vibe_erp_test` - **Migration 清单**: `sql/migrations/V*.sql`(由 Flyway 顺序 apply) - **生成方式**: 由 A3 `db-design-gen` 基于 `docs/01-需求清单//REQ-*.md` REQ 卡片正向设计生成(schema SSoT)。 ## 项目标准列约定 下文每张业务表的字段清单都自动包含以下 5 个标准列(匈牙利前缀 `i` int / `s` varchar / `t` datetime)。渲染时由 `docs-03-table-template.md` 模板内置原样输出。 | 列名 | 类型 | 可空 | 主键 | 说明 | |---|---|---|---|---| | `iIncrement` | int | 否 | 是 | 整数主键 ID(自增方式由实现决定:DB `AUTO_INCREMENT` 或应用 / 触发器分配) | | `sId` | varchar(100) | 是 | — | 业务 ID(对外暴露的字符串标识,如 UUID / 人类可读编号) | | `sBrandsId` | varchar(100) | 是 | — | 品牌 ID(多租户隔离) | | `sSubsidiaryId` | varchar(100) | 是 | — | 子公司 ID(组织层级隔离) | | `tCreateDate` | datetime | 否 | — | 记录创建时间 | 字典 / 辅助表如有豁免,在该表业务注记里注明豁免原因。 ## ER 关系概览 USR 用户管理模块共 6 张表,围绕「用户」为核心: - `t_user`(用户主表)通过 `iEmployeeId` 关联 `t_employee`(职员),实现「用户号 / 员工名」一对一联动。 - `t_employee`(职员表)通过 `iDepartmentId` 关联 `t_department`(部门表),支撑 REQ-USR-004 按部门筛选。 - `t_user` 与 `t_permission`(权限分类字典)通过 `t_user_permission`(用户-权限关联表)建立多对多关系,实现 REQ-USR-002 / 003 的「权限组」分配。 - `t_company`(公司版本字典)独立于 `t_user`,仅为 REQ-USR-001 登录入参「版本」下拉提供可选项,不与用户产生强引用。 依赖方向:`t_user → t_employee → t_department`、`t_user ↔ t_permission`(多对多)、`t_company` 独立。 ## 表清单 - `t_user` — 系统用户主表,承载登录认证、用户类型、语言偏好、单据权限等基础属性 - `t_employee` — 公司职员主档,与用户表通过 `iEmployeeId` 关联,提供姓名 / 工号 / 部门归属 - `t_department` — 部门组织树,支撑职员归属与按部门筛选 - `t_permission` — 权限分类字典,定义系统可分配的权限项 - `t_user_permission` — 用户-权限分类多对多关联表 - `t_company` — 公司 / 版本字典(标准版 / 专业版 / 旗舰版),登录时「版本」下拉数据源 ## `t_user` — 系统用户主表,承载登录认证与基础属性 ### 字段 | 字段 | 类型 | Nullable | 默认 | 业务含义 | |---|---|---|---|---| | `iIncrement` | int | 否 | 自增函数 | 整数主键 ID(标准列) | | `sId` | varchar(100) | 是 | uuid 函数 | 业务 ID(标准列) | | `sBrandsId` | varchar(100) | 是 | `1111111111` | 品牌 ID(多租户隔离,标准列) | | `sSubsidiaryId` | varchar(100) | 是 | `1111111111` | 子公司 ID(组织层级隔离,标准列) | | `tCreateDate` | datetime | 否 | 当前时间 | 创建时间(标准列) | | `sUserNo` | varchar(50) | 否 | — | 用户号;关联职员后自动同步员工号;系统内唯一 | | `sUserName` | varchar(50) | 否 | — | 登录用户名;系统内唯一;3-50 位 | | `iEmployeeId` | int | 是 | NULL | 关联职员 `t_employee.iIncrement`;可空(非员工账号如系统管理员) | | `sPasswordHash` | varchar(255) | 否 | — | 密码哈希(BCrypt / Argon2);禁止明文;初始密码 `666666` 哈希后存入 | | `sUserType` | varchar(20) | 否 | `NORMAL` | 用户类型枚举:`NORMAL`(普通用户)/ `SUPER_ADMIN`(超级管理员) | | `sLanguage` | varchar(10) | 否 | `zh-CN` | 语言枚举:`zh-CN`(中文)/ `en-US`(英文)/ `zh-TW`(繁体) | | `bModifyDoc` | tinyint(1) | 否 | 0 | 单据修改权限:0 否 / 1 是 | | `bVoid` | tinyint(1) | 否 | 0 | 作废标记(软删除):0 启用 / 1 已作废 | | `iLoginFailCount` | int | 否 | 0 | 连续登录失败次数;达到阈值触发临时锁定;登录成功后清零 | | `tLockUntil` | datetime | 是 | NULL | 锁定截止时间;NULL 表示未锁定 | | `tLastLoginDate` | datetime | 是 | NULL | 最近一次登录时间 | | `sCreator` | varchar(100) | 是 | NULL | 制单人(创建该账号的操作员用户名) | ### 索引 - `uk_user_username` (UNIQUE): `sUserName` - `uk_user_userno` (UNIQUE): `sUserNo` - `idx_user_employee` (BTREE): `iEmployeeId` - `idx_user_tenant` (BTREE): `sBrandsId`, `sSubsidiaryId` - `idx_user_void` (BTREE): `bVoid` ### 外键 - `fk_user_employee`: `iEmployeeId` → `t_employee.iIncrement` (ON DELETE SET NULL / ON UPDATE RESTRICT) ### 业务注记 - 登录认证主体表;密码以哈希形式存储,登录失败计数与锁定时间均落库以保证服务重启不丢;亦可由 Redis 镜像加速,但 DB 字段为权威。 - `bVoid = 1` 视为已作废账号,不可登录、不出现在默认查询结果中(REQ-USR-004 列表默认隐藏作废)。 - 用户名 / 用户号唯一性由 `uk_user_username` / `uk_user_userno` 保证;新增 / 修改时若冲突由 DB 抛唯一约束错误,Service 转译为 `40001 用户名已存在`。 ## `t_employee` — 公司职员主档 ### 字段 | 字段 | 类型 | Nullable | 默认 | 业务含义 | |---|---|---|---|---| | `iIncrement` | int | 否 | 自增函数 | 整数主键 ID(标准列) | | `sId` | varchar(100) | 是 | uuid 函数 | 业务 ID(标准列) | | `sBrandsId` | varchar(100) | 是 | `1111111111` | 品牌 ID(多租户隔离,标准列) | | `sSubsidiaryId` | varchar(100) | 是 | `1111111111` | 子公司 ID(组织层级隔离,标准列) | | `tCreateDate` | datetime | 否 | 当前时间 | 创建时间(标准列) | | `sEmployeeNo` | varchar(50) | 否 | — | 员工号;系统内唯一 | | `sName` | varchar(100) | 否 | — | 姓名 | | `iDepartmentId` | int | 是 | NULL | 部门 ID,关联 `t_department.iIncrement` | | `sPhone` | varchar(20) | 是 | NULL | 手机号 | | `sEmail` | varchar(100) | 是 | NULL | 邮箱 | | `bDisabled` | tinyint(1) | 否 | 0 | 是否离职:0 在职 / 1 离职 | ### 索引 - `uk_employee_no` (UNIQUE): `sEmployeeNo` - `idx_employee_dept` (BTREE): `iDepartmentId` - `idx_employee_name` (BTREE): `sName` - `idx_employee_tenant` (BTREE): `sBrandsId`, `sSubsidiaryId` ### 外键 - `fk_employee_department`: `iDepartmentId` → `t_department.iIncrement` (ON DELETE SET NULL / ON UPDATE RESTRICT) ### 业务注记 - 用户表通过 `iEmployeeId` 关联本表;REQ-USR-002 / 003 选择员工时下拉数据源;REQ-USR-004 查询字段「员工名 / 部门」均联表本表。 - 离职员工保留记录(`bDisabled = 1`),其关联用户应被自动作废(应用层逻辑,未做 DB 触发器)。 ## `t_department` — 部门组织树 ### 字段 | 字段 | 类型 | Nullable | 默认 | 业务含义 | |---|---|---|---|---| | `iIncrement` | int | 否 | 自增函数 | 整数主键 ID(标准列) | | `sId` | varchar(100) | 是 | uuid 函数 | 业务 ID(标准列) | | `sBrandsId` | varchar(100) | 是 | `1111111111` | 品牌 ID(多租户隔离,标准列) | | `sSubsidiaryId` | varchar(100) | 是 | `1111111111` | 子公司 ID(组织层级隔离,标准列) | | `tCreateDate` | datetime | 否 | 当前时间 | 创建时间(标准列) | | `sName` | varchar(100) | 否 | — | 部门名称 | | `sCode` | varchar(50) | 否 | — | 部门编码;系统内唯一 | | `iParentId` | int | 是 | NULL | 上级部门 ID,NULL 表示根部门 | | `iSortOrder` | int | 否 | 0 | 排序值,小者靠前 | ### 索引 - `uk_department_code` (UNIQUE): `sCode` - `idx_department_parent` (BTREE): `iParentId` - `idx_department_tenant` (BTREE): `sBrandsId`, `sSubsidiaryId` ### 外键 - `fk_department_parent`: `iParentId` → `t_department.iIncrement` (ON DELETE RESTRICT / ON UPDATE RESTRICT) ### 业务注记 - 自引用形成部门树,根部门 `iParentId IS NULL`。 - 部门删除采用「拒绝删除有下级 / 有员工的部门」策略(应用层校验);DB 外键 RESTRICT 兜底。 ## `t_permission` — 权限分类字典 ### 字段 | 字段 | 类型 | Nullable | 默认 | 业务含义 | |---|---|---|---|---| | `iIncrement` | int | 否 | 自增函数 | 整数主键 ID(标准列) | | `sId` | varchar(100) | 是 | uuid 函数 | 业务 ID(标准列) | | `sBrandsId` | varchar(100) | 是 | `1111111111` | 品牌 ID(多租户隔离,标准列) | | `sSubsidiaryId` | varchar(100) | 是 | `1111111111` | 子公司 ID(组织层级隔离,标准列) | | `tCreateDate` | datetime | 否 | 当前时间 | 创建时间(标准列) | | `sCode` | varchar(50) | 否 | — | 权限码,例如 `USR:ADD` / `USR:EDIT`;系统内唯一 | | `sName` | varchar(100) | 否 | — | 权限分类名称(展示用) | | `iSortOrder` | int | 否 | 0 | 同分类内排序 | ### 索引 - `uk_permission_code` (UNIQUE): `sCode` ### 外键 - 无 ### 业务注记 - 字典表,启动时由初始化脚本灌入;不允许业务删除,禁用通过软标记(暂未引入 `bVoid`,由 docs/01 补充时再加)。 - 权限码格式 `<模块代码>:<动作>`,与后端 `@PreAuthorize('hasAuthority(...)')` 一一对应(docs/06 § 1.3)。 ## `t_user_permission` — 用户-权限分类关联表 ### 字段 | 字段 | 类型 | Nullable | 默认 | 业务含义 | |---|---|---|---|---| | `iIncrement` | int | 否 | 自增函数 | 整数主键 ID(标准列) | | `sId` | varchar(100) | 是 | uuid 函数 | 业务 ID(标准列) | | `sBrandsId` | varchar(100) | 是 | `1111111111` | 品牌 ID(多租户隔离,标准列) | | `sSubsidiaryId` | varchar(100) | 是 | `1111111111` | 子公司 ID(组织层级隔离,标准列) | | `tCreateDate` | datetime | 否 | 当前时间 | 创建时间(标准列) | | `iUserId` | int | 否 | — | 用户 ID,关联 `t_user.iIncrement` | | `iPermissionId` | int | 否 | — | 权限分类 ID,关联 `t_permission.iIncrement` | ### 索引 - `uk_user_perm` (UNIQUE): `iUserId`, `iPermissionId` - `idx_user_perm_perm` (BTREE): `iPermissionId` ### 外键 - `fk_userperm_user`: `iUserId` → `t_user.iIncrement` (ON DELETE CASCADE / ON UPDATE RESTRICT) - `fk_userperm_perm`: `iPermissionId` → `t_permission.iIncrement` (ON DELETE RESTRICT / ON UPDATE RESTRICT) ### 业务注记 - 关联表豁免 `sBrandsId` / `sSubsidiaryId` 的业务语义但保留列以遵守项目标准;写入时复用 `t_user` 的对应租户标识。 - 删除用户时级联清理本表行(`ON DELETE CASCADE`);删除字典项保护已分配数据(`ON DELETE RESTRICT`)。 - 唯一约束 `(iUserId, iPermissionId)` 确保同一用户对同一权限分类不重复授权。 ## `t_company` — 公司 / 版本字典 ### 字段 | 字段 | 类型 | Nullable | 默认 | 业务含义 | |---|---|---|---|---| | `iIncrement` | int | 否 | 自增函数 | 整数主键 ID(标准列) | | `sId` | varchar(100) | 是 | uuid 函数 | 业务 ID(标准列) | | `sBrandsId` | varchar(100) | 是 | `1111111111` | 品牌 ID(多租户隔离,标准列) | | `sSubsidiaryId` | varchar(100) | 是 | `1111111111` | 子公司 ID(组织层级隔离,标准列) | | `tCreateDate` | datetime | 否 | 当前时间 | 创建时间(标准列) | | `sCode` | varchar(50) | 否 | — | 公司 / 版本编码;系统内唯一 | | `sName` | varchar(100) | 否 | — | 显示名称 | ### 索引 - `uk_company_code` (UNIQUE): `sCode` ### 外键 - 无 ### 业务注记 - REQ-USR-001 登录入参「版本」下拉数据源;启动时由初始化脚本插入至少一行默认公司数据;具体「版本」字段语义待后续业务确认后再补列。 - 当前 REQ 描述不足以确认与 `t_user` 是否需要强关联,故先做独立字典;后续如需「用户绑定公司 / 版本」再于 `t_user` 增列。