第三章:用户与组织架构设计
好,咱们今天聊聊组织架构。说实话,这是OA系统里最绕不开、也最容易踩坑的地方。我做了这么多年企业级应用,见过太多项目因为组织架构设计不合理,后期改得痛不欲生。
你想想看,一个公司少则几十人,多则上万人。人怎么管?部门怎么挂?岗位怎么设?这些搞不清楚,后面所有流程审批、权限控制全得乱套。
3.1 用户表(user)—— 系统的灵魂
用户表,说白了就是记录「谁在用系统」。但别小看它,字段设计得不好,后面加个手机号登录都费劲。
我个人习惯,用户表的核心字段就这么几个:
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '登录账号',
`password` varchar(100) NOT NULL COMMENT '密码(加密存储)',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`avatar` varchar(200) DEFAULT NULL COMMENT '头像路径',
`status` tinyint(4) DEFAULT '1' COMMENT '状态:0禁用 1启用',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
嗯,这里要注意:密码千万别存明文。我见过有公司直接把用户密码明文存数据库,结果被脱库后全网通报,那叫一个惨。用bcrypt或者PBKDF2加密,这是底线。
3.2 部门表(dept)—— 组织的骨架
部门表,就是公司的组织架构树。你想想,一个集团下面有子公司,子公司下面有事业部,事业部下面有部门,部门下面还有小组。这怎么存?
最直接的方式,就是加一个parent_id字段,指向父部门。这就是邻接表模型,简单粗暴。
CREATE TABLE `sys_dept` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '部门ID',
`dept_name` varchar(100) NOT NULL COMMENT '部门名称',
`parent_id` bigint(20) DEFAULT '0' COMMENT '父部门ID',
`order_num` int(11) DEFAULT '0' COMMENT '显示顺序',
`leader` varchar(50) DEFAULT NULL COMMENT '负责人',
`status` tinyint(4) DEFAULT '1' COMMENT '状态',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门表';
但邻接表有个硬伤——查子树特别麻烦。比如你要查「技术部」下面所有子部门,得递归查询。数据量小还好,上万条部门数据时,性能直接崩掉。
3.3 岗位表(position)—— 职责的标签
岗位和部门不一样。部门是「你在哪个组」,岗位是「你干什么活」。比如张三在技术部,岗位是「Java开发工程师」;李四也在技术部,岗位是「测试工程师」。
岗位表设计起来很简单:
CREATE TABLE `sys_position` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '岗位ID',
`position_name` varchar(100) NOT NULL COMMENT '岗位名称',
`position_code` varchar(50) DEFAULT NULL COMMENT '岗位编码',
`status` tinyint(4) DEFAULT '1' COMMENT '状态',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='岗位表';
岗位编码我建议用有意义的缩写,比如JAVA_DEV、TEST_ENG。这样在权限控制时可以直接用编码匹配,不用每次都查数据库。
3.4 用户-部门关联表—— 多对多的艺术
一个用户能不能属于多个部门?当然可以。比如一个人既是「技术部」的成员,又是「项目A组」的成员。这时候就需要一张关联表。
CREATE TABLE `sys_user_dept` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`dept_id` bigint(20) NOT NULL COMMENT '部门ID',
`is_primary` tinyint(4) DEFAULT '0' COMMENT '是否主部门:0否 1是',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_dept` (`user_id`, `dept_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户部门关联表';
这里我加了一个is_primary字段。为什么?因为一个人可能有多个部门,但总得有个「主部门」吧?比如发工资、算绩效,默认按主部门来。这个字段在报表统计时特别有用。
3.5 组织树结构设计—— 闭包表 vs 路径枚举
好,重头戏来了。组织树怎么存才能既好查又好改?
我常用的方案有三种:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 邻接表 | 结构简单,增删改方便 | 查询子树需要递归,性能差 | 部门层级浅(3层以内) |
| 路径枚举 | 查询子树快,支持排序 | 移动节点时需更新所有子节点路径 | 树结构相对稳定 |
| 闭包表 | 查询任意层级快,支持复杂操作 | 占用空间大,插入删除成本高 | 树结构频繁查询、不常变动 |
我个人最推荐闭包表。为什么?因为OA系统里,组织树是「读多写少」的。你想想,部门调整一年能改几次?但查组织树、查下属部门、查上级部门,每天可能几百上千次。
闭包表的结构是这样的:
CREATE TABLE `sys_dept_closure` (
`ancestor` bigint(20) NOT NULL COMMENT '祖先节点ID',
`descendant` bigint(20) NOT NULL COMMENT '后代节点ID',
`depth` int(11) NOT NULL COMMENT '层级深度(0表示自身)',
PRIMARY KEY (`ancestor`, `descendant`),
KEY `idx_descendant` (`descendant`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门闭包表';
举个例子。假设技术部(ID=1)下面有前端组(ID=2)和后端组(ID=3)。闭包表里会存这些记录:
- (1, 1, 0) —— 自己到自己
- (1, 2, 1) —— 技术部到前端组
- (1, 3, 1) —— 技术部到后端组
- (2, 2, 0) —— 前端组自己
- (3, 3, 0) —— 后端组自己
这样,你想查技术部下面所有子部门,只需要一条SQL:
SELECT d.* FROM sys_dept d
JOIN sys_dept_closure c ON d.id = c.descendant
WHERE c.ancestor = 1 AND c.depth > 0;
是不是很爽?不用递归,不用循环,一条SQL搞定。
3.6 用户-岗位关联—— 职责的细化
最后,用户和岗位也是多对多关系。一个人可能有多个岗位,比如既是「开发工程师」又是「项目组长」。
CREATE TABLE `sys_user_position` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`position_id` bigint(20) NOT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_position` (`user_id`, `position_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户岗位关联表';
嗯,这里要注意:岗位和权限是挂钩的。比如「财务经理」这个岗位,天然就有查看财务报表的权限。所以岗位设计得越细,权限控制就越灵活。
好了,用户与组织架构这块就聊到这儿。说白了,核心就是三张主表(用户、部门、岗位)加两张关联表(用户-部门、用户-岗位),再加一张闭包表处理树结构。这套设计我在多个项目中验证过,稳定、高效、易扩展。你照着这个思路走,基本不会出大问题。