🏺 第19章 企业人事管理系统
📄 一句话概括

这一课不写代码,只做一件事:把「谁用、用来干什么」写成能验收的矩阵。 矩阵不平,后面的界面、权限、表结构就都是猜的。 三个人群的功能点数差出三倍,这个比例直接决定开发投入往哪放。

📝 本章和前面几章的关系

第 18 章讲闪回,是「出事之后怎么退」。这一章换个方向:从零做一个系统。 前 18 章都是单点技术——一个 SQL、一个索引、一次恢复。 这一章要把它们串成一条线,所以它慢,但慢在设计上。 全章十篇是一条流水线:需求 → 设计 → 建库 → 界面 → 模块 → 收尾。 这一篇是流水线的入口。

一、先分清三类人,再谈功能

教材开篇的人事管理系统,是给一家中等规模公司做的内部工具。 它只有三类使用者,但三类的诉求完全不一样。

角色 主要职责 典型动作
系统管理员 建账号、分权限、备份恢复 很少用,但出事第一个找他
人事专员 管档案、算工资、出报表 一天到晚用它
普通员工 查自己的档案和工资条 一个月看一两次

这张表的价值不在「列出三类人」,在于它暴露了一个事实: 主力用户只有一类。 人事专员是唯一的【重度用户】,另外两类都是低频。 低频用户提的需求往往最刺耳——他们不熟,所以会说「这个太复杂」。 判断需求优先级时,要先问一句「提这个需求的人一年用几次」。 一年用两次的人提的「必须加个按钮」,通常可以不做。

二、功能点 × 角色矩阵:把需求变成格子

下一步是把功能点摊成行,角色摊成列,一格格填「谁能做什么」。 填完之后,需求就从一段散文变成了一张能验收的表。

功能点 管理员 人事专员 普通员工
员工档案维护 — 增删改查 只看自己
部门管理 增删改查 查看 查看
工资计算 — 计算与复核 —
工资条查询 — 看全部 只看自己
用户与权限 账号与授权 — —
数据备份 备份与恢复 发起备份 —

这张矩阵就是权限表的原型。 Demo 里把它按格子数了一遍,得到 6 比 11 比 3。 11 那一列说明一切:主力功能全压在人事专员身上。 所以界面复杂度要按人事专员的熟练度设计,不是按普通员工。 这一点反直觉:很多系统为了照顾「偶尔用的人」,把界面做得极简, 结果主力用户每天要为此多点五次。 正确的做法是给低频用户做减法(只显示他自己那几项),不是给所有人做减法。

三、技术选型的两个硬约束

教材的技术栈是四件:Swing、Hibernate、Oracle、可执行 jar。 这四个选择背后只有两条约束,其余都是自由选择。

选项 教材的选择 为什么
界面 Swing 公司内网用,不要求浏览器
持久层 Hibernate 少写 JDBC 样板代码
数据库 Oracle 公司已有,运维熟
打包 可执行 jar 双击就跑,不用装环境

第一条约束是【部署环境】:内网、Windows、不允许装服务器。 这一条直接排掉了 B/S 架构——它要一台 Web 服务器,还要人维护。 第二条约束是【已有资产】:公司已经有 Oracle 和它的运维。 这一条排掉了「换个轻量数据库」的念头,哪怕技术上更省事。 这两条都不是技术判断,是组织判断。 选型时最贵的错误,是拿技术理由去覆盖组织约束。

四、这一幕在全书的位置

十篇的分工,其实是一条数据从「想法」走到「能跑」的路。

环节 产出 做错了会怎样
19.1 背景 角色与功能矩阵 后面全是猜的
19.2 分析 用例优先级 先做了不重要的
19.3 设计 结构图、流程图、目录 编码时反复改结构
19.4 数据库 四张表加索引 三个模块一起返工
19.5~19.9 界面与三个模块 这里才是写代码
19.10 收尾 关联关系与 N+1 上线后慢慢变慢

注意前四个环节占了四篇,一行界面代码都没写。 这不是教材啰嗦。 数据库设计错一个字段,后面三个模块全要返工; 界面结构定错,所有对话框都要重画。 先想清楚再动手,省下的不是时间,是【返工】。 而返工的成本随项目推进指数上涨,这是软件工程里最硬的一条经验。

五、动手 Demo:把功能矩阵数一遍,看三个人群的投入比例

这一篇的 Demo 不碰数据库,只做算术:把矩阵的格子数出来。 数完会得到一个比例,这个比例就是后面排期的依据。 代码很短,但要看清一件事:格子不是均匀分布的。

# 每行是「一个功能点」在三个角色下各有几个可做动作
matrix = [
    ("员工档案维护", 0, 5, 1),
    ("部门管理",     2, 1, 1),
    ("工资计算",     0, 3, 0),
    ("工资条查询",   0, 1, 1),
    ("用户与权限",   2, 0, 0),
    ("数据备份",     2, 1, 0),
]
tot = [0, 0, 0]
for row in matrix:
    for i in range(3):
        tot[i] += row[i + 1]
print("功能点 %d 个" % len(matrix))
print("管理员 %d / 人事专员 %d / 普通员工 %d" % tuple(tot))
print("人事专员占 %.0f%%" % (tot[1] * 100.0 / sum(tot)))

实跑输出:

--------------------------------------------------------------------
第八十四课:开发背景——先把「谁用、用什么」写成矩阵
--------------------------------------------------------------------

  **教材开篇的人事管理系统,是给一家中等规模公司做的内部工具。**
  **这一课不做代码,做一件更重要的事:把需求整理成能验收的矩阵。**

  一、三类用户,各自要什么
  --------------------------------------------------------------------

  角色       主要职责                   功能点
  --------------------------------------------
  系统管理员 建账号、分权限、备份恢复   6
  人事专员   管员工档案、算工资、出报表 11
  普通员工   查自己的档案和工资条       3
  已选择 3 行
  **三类角色的功能点数差得很开:11 比 6 比 3。**
  **这个比例直接决定后面的开发投入**——人事专员那一列才是主体。

  二、功能点 × 角色矩阵
  --------------------------------------------------------------------

  功能点       管理员 人事专员 普通员工
  -------------------------------------
  员工档案维护 ——     读写     只读自己
  部门管理     读写   读写     ——
  工资计算     ——     读写     ——
  工资条查询   ——     全部     只读自己
  用户与权限   读写   ——       ——
  数据备份     读写   ——       ——
  已选择 6 行
  **这张矩阵就是权限表的原型。**
  **看第一行第三列:「只读自己」。**
  **这三个字落到 SQL 上,就是一个 `WHERE emp_id = 当前用户` 的条件。**
  **写需求时含糊成「员工能查档案」,写代码时就必然漏掉这个条件。**这是权限漏洞最常见的来源。

  三、技术选型的两个硬约束
  --------------------------------------------------------------------

  项     教材的选择 为什么
  -----------------------------------------------
  界面   Swing      桌面端,公司内网用,不用 B/S
  持久层 Hibernate  对象关系映射,少写 SQL
  数据库 Oracle     公司已有,运维熟
  打包   可执行 jar 双击就能跑,不用装
  已选择 4 行
  **四个选择里,只有第二个值得多说一句。**
  **Hibernate 在这个项目里的作用,**
  **是让「员工」「部门」这些概念在代码里是对象,不是 `ResultSet`。**
  **代价是:关联关系配错了,会在你没想到的时候发出一条查一万次的 SQL。**
  **那是第 93 课要专门处理的事。**

  四、这一章的地图
  --------------------------------------------------------------------

  节         干什么                          本课对应
  -------------------------------------------------------
  19.2       系统分析:谁用、用到什么程度    第 85 课
  19.3       系统设计:结构、流程、目录      第 86 课
  19.4       数据库设计:E-R 到建表          第 87 课
  19.5       主窗体:导航与工具条            第 88 课
  19.6       公共模块:配置、映射、通用 CRUD 第 89 课
  19.7~19.9 三个业务模块                    第 90~92 课
  19.10      Hibernate 一对一与一对多        第 93 课
  已选择 7 行
  **顺序有个特点:先设计、后编码,中间隔了三课。**
  **这不是教材啰嗦。**
  **数据库设计错一个字段,后面三个模块全要返工;先想清楚再动手,是唯一省时间的办法。**

输出第一行是功能点总数 6。 第二行 6 比 11 比 3,就是三类角色的动作数。 11 是 6 的近两倍。 这意味着「人事专员」这一列要做的东西,比管理员那一列多一倍。 第三行 55%:人事专员一个人吃掉了一半以上的功能点。 这就是为什么它的界面值得单独设计,而管理员的界面可以简陋。 矩阵里还有三个「0」,也就是三处「这个角色完全不该看到」的入口。 把它们做成【不显示】而不是【灰掉】——灰掉的按钮会让人一直试,不显示的不会。 要诚实说一句:这些 0 到 5 的数字是我按教材的功能描述填的,不是教材原文。 真实项目里这一列要拿去跟用户对,对完才作数。

30 秒实验

  1. 把 matrix 里「工资计算」那一行的管理员从 0 改成 1, 看比例怎么变——想清楚「管理员要不要能算工资」这件事。
  2. 在 matrix 末尾加一行「报表导出,(0, 2, 0)」, 看总格子数变成多少——这就是加一个功能的成本口径。
  3. 把三个角色的列顺序换一下(换成 人事/管理/员工), 看 tot 的下标要不要跟着改——这就是「矩阵的列是有语义的」。

常见误区

  1. 「需求就是用户说的话」。 用户说的是【想要】, 矩阵记的是【能做】。 两者之间要过一次「谁用、用几次」。
  2. 「三类用户平权」。 平权的界面等于没人好用。 主力用户的操作路径要最短,低频用户的入口可以深。
  3. 「选型看技术先进性」。 内网、单机、已有 Oracle, 这三条约束下最先进的技术往往是最贵的选择。
  4. 「先写代码,边写边设计」。 单点技术可以这样,有界面的业务系统不行——界面一改, DAO 和表常常跟着改。
自测三问(点开对答案) 1. 为什么先写「谁用」而不是先写「有什么功能」? —— **因为同一个功能对不同角色的价值差几十倍。** **不先分清人,就无法判断哪个功能该做细、哪个能凑合。** 2. 三类角色里,界面复杂度应该按谁定? —— **按人事专员,也就是重度用户。**低频用户的诉求靠「只给他看几项」来满足,不靠把整体做简单。 3. 「不显示」和「灰掉」有什么区别? —— **灰掉等于告诉用户「这里有东西,但你不能用」,他会一直试。** **不显示等于告诉他「这里没有这件事」,一次就死心。** 4. 为什么说选型是组织判断而不是技术判断? —— **因为排掉 B/S 的不是性能,是「内网不许装服务器」;** **排掉 MySQL 的不是功能,是「公司已有 Oracle 运维」。** **这两条都不是技术能改的。**

下一篇把矩阵变成【顺序】。同样是这六个功能,先做哪个? 答案不能靠拍脑袋,得有一把能算的尺子。

相关笔记