第三章:用户与组织架构设计

好,咱们今天聊聊组织架构。说实话,这是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='部门表';

但邻接表有个硬伤——查子树特别麻烦。比如你要查「技术部」下面所有子部门,得递归查询。数据量小还好,上万条部门数据时,性能直接崩掉。

避坑指南:我曾经在一个项目中用纯邻接表存了5000个部门,结果每次查组织树要跑几十次SQL,页面加载要5秒。后来不得不重构,换成了闭包表。

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_DEVTEST_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搞定。

核心要点:闭包表虽然插入和删除时要维护多条记录,但查询性能极佳。在OA系统这种读多写少的场景下,闭包表是最优解。

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='用户岗位关联表';

嗯,这里要注意:岗位和权限是挂钩的。比如「财务经理」这个岗位,天然就有查看财务报表的权限。所以岗位设计得越细,权限控制就越灵活。

好了,用户与组织架构这块就聊到这儿。说白了,核心就是三张主表(用户、部门、岗位)加两张关联表(用户-部门、用户-岗位),再加一张闭包表处理树结构。这套设计我在多个项目中验证过,稳定、高效、易扩展。你照着这个思路走,基本不会出大问题。