
简介这份企业人力资源管理系统设计说明书面向计算机相关专业的课程设计、毕业设计学生及需要撰写开题报告与概要设计文档的开发者。内容围绕需求分析、数据库设计、各功能模块设计与实现、考核评价点四大部分展开涵盖部门信息管理、员工信息管理、工资管理、用户管理四大模块并给出部门表、员工表、工资表、权限表、日志表等数据库组成方案以及部门信息维护与查询、职工信息维护与输出、当月工资计算与查询、多级权限管理等具体设计思路可直接作为课程设计说明书撰写与系统开发的参考模板。资源包共1个doc文件约530KB结构完整、目录清晰便于按章节检索与借鉴。目前已有71人学习下载适合需要快速搭建人力资源管理类课程设计文档框架、理清功能模块划分与数据库设计思路的读者参考使用。1. 从一份课程设计说明书说起企业人力资源管理系统到底该怎么落地如果你正在做课程设计或者毕业设计选题是“企业人力资源管理系统”大概率会遇到一个尴尬局面网上能搜到的要么是纯理论的需求分析文档要么是跑不起来的半成品源码真正能把“设计说明书”和“可运行系统”对上的资料少得可怜。我手里这份《企业人力资源管理系统设计说明书》就是一份典型的课程设计交付物它覆盖了从需求分析、数据库设计到各功能模块实现的完整链路技术栈是 VB.NET SQL Server 2000采用 C/S 架构核心模块包括部门信息管理、职工信息管理、工资管理和用户管理四块。它适合两类人一是需要交课程设计、开题报告或概要设计的同学可以直接参考它的文档结构和功能划分二是想拿一个完整 CRUD 项目练手三层架构和数据绑定的开发者。这份说明书最值钱的地方不在于代码有多复杂而在于它把“考核评价点”列得清清楚楚——12 项功能每项多少分、难度几颗星等于把评分标准摊开给你看了。2. 需求分析与数据库设计四张表撑起一个系统2.1 需求分析到底要写什么才不会被导师打回很多同学写需求分析容易犯一个毛病把“企业需要管理员工信息”这句话翻来覆去说三遍。这份说明书里的需求分析部分之所以能过关是因为它把需求拆到了可操作的粒度。它先区分了两类信息实体——部门信息和员工信息然后指出它们“各有不同之处但也有相同点”这个判断直接决定了后面数据库表的设计思路相同字段抽出来做公共处理不同字段各自建表。具体来说需求分析里明确了三个核心功能点。第一是信息管理功能包括部门与员工信息的添加、删除、修改并且特别强调了权限控制——“只有管理员才有添加修改和删除记录的权限”。第二是综合查询功能要求查询结果在 DataGrid 中显示时不可修改只有满足一定条件后才能修改原档案。第三是信息打印功能查询出结果后可以直接打印。这三个功能点对应到考核评价点里就是第 6、7、8、9、10 项占了 55 分。写需求分析的时候我建议你按“实体识别 → 操作定义 → 权限约束 → 异常处理”这个顺序来组织。实体识别就是把系统里涉及的所有数据对象列出来这份说明书里就是部门、员工、工资、用户四个。操作定义就是每个实体上能做哪些动作增删改查分别对应什么业务场景。权限约束要写清楚谁能做什么、谁不能做什么。异常处理则是考虑误操作、数据冲突等情况下的系统行为。把这四块写全了需求分析基本不会被挑毛病。2.2 数据库表结构四张核心表的字段设计与关系这份说明书用的是 SQL Server 2000数据库名为 hrsys包含四张表用户表、部门表、员工基本信息表、员工工资表。虽然 SQL Server 2000 现在看起来有点年代感但表结构设计的思路放到 MySQL 或 PostgreSQL 上完全通用。先看部门表。核心字段包括部门编号、部门名称、在职人数、联系方式。部门编号是主键在职人数这个字段值得注意——它是一个冗余字段因为理论上可以通过 count 员工表算出来。但说明书里明确说了“合理的数据冗余也是必要的”因为部门列表页需要频繁显示每个部门的人数每次都去 count 员工表性能开销大。这种设计取舍在实际项目里很常见。员工基本信息表的字段比较多员工编号、姓名、性别、出生日期、所在部门、籍贯、学历、政治面貌、毕业学校等。其中“所在部门”是外键关联部门表的部门编号。这里有一个容易翻车的地方如果部门表里的某条记录被删除了员工表里关联该部门的记录怎么办说明书里在部门信息维护部分提到了“删除部门前需确认该部门下无员工信息关联”这就是一种应用层的约束处理而不是靠数据库的外键级联删除。工资表的字段包括工资月份、基本工资、伙食补贴、医疗补贴、实发工资以及关联的员工编号。实发工资是一个计算字段等于基本工资加各项补贴再减去扣款。这里的设计逻辑是不存储冗余的计算结果而是在查询时实时计算或者通过存储过程在插入时算好写入。用户表相对简单用户名、密码、权限。权限字段用 0 表示管理员1 表示普通用户。这个设计很朴素但够用。如果你要扩展可以加一个角色表做 RBAC但对于课程设计来说两个权限级别已经能覆盖考核评价点里“登录后按照权限控制菜单可见性”的要求了。下面用 SQL 把四张表的建表语句整理出来你可以直接拿去在 MySQL 里跑-- 部门表 CREATE TABLE department ( dept_id VARCHAR(10) PRIMARY KEY, -- 部门编号 dept_name VARCHAR(50) NOT NULL, -- 部门名称 headcount INT DEFAULT 0, -- 在职人数冗余字段 contact VARCHAR(50) -- 联系方式 ); -- 员工基本信息表 CREATE TABLE employee ( emp_id VARCHAR(10) PRIMARY KEY, -- 员工编号 emp_name VARCHAR(30) NOT NULL, -- 姓名 gender CHAR(2), -- 性别 birthdate DATE, -- 出生日期 dept_id VARCHAR(10), -- 所在部门外键 hometown VARCHAR(50), -- 籍贯 education VARCHAR(20), -- 学历 political VARCHAR(20), -- 政治面貌 school VARCHAR(50), -- 毕业学校 FOREIGN KEY (dept_id) REFERENCES department(dept_id) ); -- 员工工资表 CREATE TABLE salary ( salary_id INT AUTO_INCREMENT PRIMARY KEY, emp_id VARCHAR(10) NOT NULL, -- 员工编号 salary_month VARCHAR(7) NOT NULL, -- 工资月份格式 YYYY-MM base_salary DECIMAL(10,2) DEFAULT 0, -- 基本工资 meal_allow DECIMAL(10,2) DEFAULT 0, -- 伙食补贴 medical_allow DECIMAL(10,2) DEFAULT 0, -- 医疗补贴 actual_salary DECIMAL(10,2) DEFAULT 0, -- 实发工资 FOREIGN KEY (emp_id) REFERENCES employee(emp_id) ); -- 用户表 CREATE TABLE sys_user ( user_id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT DEFAULT 1 -- 0管理员1普通用户 );建表的时候有几个参数需要留意。dept_id和emp_id用 VARCHAR 而不是 INT是因为实际业务里编号可能带字母前缀比如 “D001”、“E2024001”。salary_month用 VARCHAR(7) 存 “2024-06” 这种格式比用 DATE 类型更直观查询的时候直接字符串匹配就行。actual_salary字段在插入时由程序计算好写入避免每次查询都做加法。2.3 功能模块划分与三层架构的对应关系说明书里把系统分成四个功能模块部门信息管理、员工信息管理、工资管理、用户管理。这个划分方式直接对应到考核评价点里的分层要求——“使用直连数据库读取、提交数据数据访问层单列严格按照 3 层结构分层”这一项占了 10 分难度三颗星是整个考核里权重最高的之一。三层架构的标准分法是表示层UI、业务逻辑层BLL、数据访问层DAL。表示层负责界面展示和用户交互业务逻辑层处理业务规则和计算数据访问层只管跟数据库打交道。这份说明书里虽然没有明确写出“BLL”这个词但从它对存储过程的描述可以看出来它把业务规则放在了数据库端的存储过程里。说明书原文说“用后台的存储过程建立应用系统的业务规则business Rule”这其实是一种把业务逻辑下沉到数据库的做法。对于课程设计来说你可以选择两种路线。路线一是标准三层UI 层调 BLL 层BLL 层调 DAL 层DAL 层用 ADO.NET 执行 SQL。路线二是把业务逻辑写在存储过程里UI 层直接调 DAL 层执行存储过程。两种都能拿到那 10 分但路线一更符合“数据访问层单列”的字面要求。我一般会建议用路线一因为代码结构更清晰调试也方便。具体到模块划分部门信息管理模块包含部门信息的增删改查员工信息管理模块包含员工信息的增删改查和打印输出工资管理模块包含当月工资计算、历史工资导入和工资查询用户管理模块包含用户增删和密码修改。每个模块在 BLL 层对应一个类在 DAL 层也对应一个类这样分层清晰也方便考核的时候展示代码结构。3. 核心模块实现从部门维护到工资计算的完整链路3.1 部门信息维护DataGrid 绑定与存储过程调用部门信息维护是整个系统里最基础也最典型的模块搞懂了它员工信息维护和工资管理基本就是换张表的事。这个模块的核心交互逻辑是这样的页面上有一个 DataGrid 控件展示所有部门信息用户点击某一行该行数据会被绑定到下方的 TextBox 控件里修改 TextBox 里的内容后点击【修改】按钮更新数据库点击【删除】按钮则删除选中行对应的记录。说明书里特别提到了用存储过程来实现数据库操作。存储过程的好处是第一次执行时编译并缓存后续执行直接走缓存省去了编译时间。对于课程设计来说用存储过程还有一个隐性加分项——考核评价点里“编码规范”那一项用存储过程会让代码看起来更专业。下面是一个部门信息维护的存储过程示例包含增删改查四个操作-- 新增部门 CREATE PROCEDURE sp_add_department dept_id VARCHAR(10), dept_name VARCHAR(50), contact VARCHAR(50) AS BEGIN INSERT INTO department (dept_id, dept_name, headcount, contact) VALUES (dept_id, dept_name, 0, contact); END; -- 修改部门信息 CREATE PROCEDURE sp_update_department dept_id VARCHAR(10), dept_name VARCHAR(50), contact VARCHAR(50) AS BEGIN UPDATE department SET dept_name dept_name, contact contact WHERE dept_id dept_id; END; -- 删除部门先检查是否有员工关联 CREATE PROCEDURE sp_delete_department dept_id VARCHAR(10) AS BEGIN IF EXISTS (SELECT 1 FROM employee WHERE dept_id dept_id) BEGIN RAISERROR(该部门下存在员工无法删除, 16, 1); RETURN; END DELETE FROM department WHERE dept_id dept_id; END; -- 查询所有部门 CREATE PROCEDURE sp_query_departments AS BEGIN SELECT dept_id, dept_name, headcount, contact FROM department; END;在 VB.NET 里调用这些存储过程核心代码结构是这样的 数据访问层执行存储过程并返回 DataTable Public Function ExecuteQuery(ByVal procName As String, _ ByVal params As SqlParameter()) As DataTable Dim dt As New DataTable() Using conn As New SqlConnection(connStr) Using cmd As New SqlCommand(procName, conn) cmd.CommandType CommandType.StoredProcedure If params IsNot Nothing Then cmd.Parameters.AddRange(params) End If conn.Open() Dim adapter As New SqlDataAdapter(cmd) adapter.Fill(dt) End Using End Using Return dt End Function这段代码的关键参数是CommandType.StoredProcedure它告诉 ADO.NET 要执行的是一个存储过程而不是普通 SQL 语句。SqlParameter()数组用来传递存储过程的参数顺序要和存储过程定义里的参数顺序一致。返回的 DataTable 可以直接绑定到 DataGrid 的 DataSource 属性上。DataGrid 的数据绑定逻辑是这样的页面加载时调用sp_query_departments拿到所有部门数据赋给 DataGrid.DataSource再调 DataGrid.DataBind()。用户点击某一行时在 ItemCommand 事件里通过e.Item.Cells[0].Text取出部门编号再用这个编号去数据库查完整记录填充到 TextBox 里。修改按钮的点击事件里从 TextBox 读取新值调用sp_update_department执行更新然后重新绑定 DataGrid。这里有一个容易忽略的细节DataGrid 的选中行索引和数据库里的记录 ID 不是一回事。如果你在查询的时候用了 ORDER BYDataGrid 里第 3 行可能对应的是数据库里第 7 条记录。所以一定要用部门编号作为唯一标识来定位记录不能靠行号。3.2 职工信息查询与打印条件拼接与 Excel 导出职工信息查询模块比部门查询多了一个打印输出功能。查询部分的核心是根据用户输入的条件动态拼接 SQL 的 WHERE 子句。说明书里提到“输入查询条件到编辑框如果满足编辑框的条件可从数据库中提取所需要的信息”这意味着查询条件是可选的——用户可能只填姓名也可能只填部门也可能两个都填。动态条件拼接的常见做法是用 StringBuilder 拼 WHERE 子句但要注意 SQL 注入问题。虽然课程设计里导师不一定查这个但养成好习惯没坏处。下面是一个参数化查询的示例 业务逻辑层根据条件查询员工信息 Public Function QueryEmployees(ByVal name As String, _ ByVal deptId As String) As DataTable Dim sql As New StringBuilder(SELECT e.*, d.dept_name FROM employee e ) sql.Append(LEFT JOIN department d ON e.dept_id d.dept_id WHERE 11 ) Dim params As New List(Of SqlParameter)() If Not String.IsNullOrEmpty(name) Then sql.Append(AND e.emp_name LIKE name ) params.Add(New SqlParameter(name, % name %)) End If If Not String.IsNullOrEmpty(deptId) Then sql.Append(AND e.dept_id deptId ) params.Add(New SqlParameter(deptId, deptId)) End If Return dal.ExecuteQueryText(sql.ToString(), params.ToArray()) End FunctionWHERE 11是一个常用技巧这样后面每个条件都可以直接用AND开头不用判断是不是第一个条件。LIKE name配合%通配符实现模糊查询用户输入“张”就能查出所有姓张的员工。参数化查询用name占位符而不是直接拼字符串避免了 SQL 注入的风险。打印功能的实现思路是查询结果绑定到 DataGrid 后点击【打印】按钮时把 DataGrid 的数据导出到一个 Excel 模板文件里。说明书里说“信息即通过 excel 设计的样式输出”这意味着打印不是直接调打印机而是先生成 Excel 文件再打印。VB.NET 里操作 Excel 一般用 COM 互操作Microsoft.Office.Interop.Excel核心步骤是创建 Excel Application 对象、打开模板文件、往指定单元格写数据、另存为新文件。这里有一个血泪经验COM 互操作操作 Excel 的时候一定要记得释放 COM 对象否则 Excel 进程会在后台越积越多最后把内存吃满。释放的顺序是先释放 Worksheet再释放 Workbook再释放 Application每一步都要调Marshal.ReleaseComObject()。这个坑我在早期项目里踩过调试了半天才发现是 Excel 进程没退出。3.3 工资计算模块历史导入与实发工资的推导逻辑工资管理模块是整个系统里业务逻辑最重的一块考核评价点里给了 15 分难度三颗星是所有功能项里分值最高的。它的核心流程是第一次使用时人工录入基本工资和各项补贴点击【计算当月工资】按钮算出实发工资后续月份可以点击【导入工资历史】把上个月的数据导过来只需要调整有变化的项就行。这个“导入历史”的设计思路很实用。因为员工的工资构成大部分月份是稳定的基本工资、伙食补贴、医疗补贴这些通常不会每月变动真正会变的是加班费、扣款这些。所以导入历史数据后只需要修改变动项再重新计算实发工资即可。实发工资的计算公式是实发工资 基本工资 伙食补贴 医疗补贴 - 扣款。说明书里没有提到扣款字段但实际业务里肯定有社保扣除、个税扣除这些。你可以根据考核评价点的要求来决定要不要加扣款字段——如果只要求“导入历史工资、工资计算以及工资发放”那不加扣款也能跑通。工资计算的存储过程可以这样写-- 计算并更新实发工资 CREATE PROCEDURE sp_calc_salary emp_id VARCHAR(10), salary_month VARCHAR(7) AS BEGIN UPDATE salary SET actual_salary base_salary meal_allow medical_allow WHERE emp_id emp_id AND salary_month salary_month; END; -- 导入历史工资从上个月复制到下个月 CREATE PROCEDURE sp_import_history from_month VARCHAR(7), to_month VARCHAR(7) AS BEGIN INSERT INTO salary (emp_id, salary_month, base_salary, meal_allow, medical_allow, actual_salary) SELECT emp_id, to_month, base_salary, meal_allow, medical_allow, base_salary meal_allow medical_allow FROM salary WHERE salary_month from_month; END;sp_import_history这个存储过程用了一条 INSERT INTO ... SELECT 语句把上个月的所有工资记录复制一份月份改成新的月份实发工资重新计算。执行之前要检查目标月份是否已经有数据避免重复导入。可以在存储过程开头加一个IF EXISTS判断如果目标月份已有记录就先删除或者直接返回错误。工资查询模块支持按工资月份和职工编号两个条件查询。查询结果同样绑定到 DataGrid打印工资单的逻辑和员工信息打印类似也是导出到 Excel。这里有一个细节工资单的格式通常比员工信息表更正式需要包含公司名称、工资月份、员工姓名、各项金额和实发合计。如果你要做得精细一点可以准备一个 Excel 模板文件里面预设好格式和公式程序只需要往指定单元格填数据就行。4. 避坑与排查课程设计里最容易翻车的五个地方4.1 登录验证绕过了但菜单权限没生效现象登录窗口输入正确的用户名和密码后能进入主窗体但普通用户也能看到管理员才有的菜单项权限控制形同虚设。原因登录验证只做了用户名密码的比对没有把当前用户的权限级别保存到全局变量或 Session 里主窗体的菜单可见性判断没有依据。解决登录成功后把用户的 role 字段值存到一个模块级变量或静态属性里主窗体加载时根据这个值设置每个菜单项的 Visible 属性。管理员 role0 显示全部菜单普通用户 role1 隐藏用户管理和工资计算菜单。代码大概是这样 登录成功后保存权限 Module GlobalVar Public CurrentUserRole As Integer 1 默认普通用户 End Module 主窗体加载时控制菜单可见性 Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load If GlobalVar.CurrentUserRole 0 Then mnuUserManage.Visible True mnuSalaryCalc.Visible True Else mnuUserManage.Visible False mnuSalaryCalc.Visible False End If End Sub4.2 DataGrid 选中行后 TextBox 数据不更新现象点击 DataGrid 里的某一行下方的 TextBox 里还是上一次选中的数据没有刷新成当前行的内容。原因DataGrid 的选中事件没有正确触发或者触发后取数据的索引取错了。常见的情况是用了DataGrid.CurrentRowIndex但 DataGrid 的数据源在重新绑定后索引没有重置。解决在 DataGrid 的 MouseUp 或 ItemCommand 事件里处理选中逻辑用HitTest方法判断点击的是哪一行然后从 DataTable 数据源里按行号取数据填充 TextBox。不要依赖 DataGrid 的 SelectedIndex因为它在数据绑定后可能不准。更稳妥的做法是给 DataGrid 加一个隐藏列存主键 ID选中行时从隐藏列取 ID再用 ID 去数据库查完整记录。4.3 工资导入历史后出现重复记录现象点击【导入工资历史】按钮后目标月份的工资记录出现了两份查询的时候看到重复行。原因导入之前没有检查目标月份是否已有数据直接执行了 INSERT INTO ... SELECT导致重复插入。解决在导入存储过程开头加一个存在性检查如果目标月份已有记录就提示用户“该月份工资已存在是否覆盖”用户确认后再先 DELETE 再 INSERT。或者用 MERGE 语句做 upsert 操作。对于课程设计来说最简单的做法是在导入前先执行一条DELETE FROM salary WHERE salary_month to_month然后再导入。4.4 打印导出 Excel 时进程残留现象每次点击打印按钮后任务管理器里就多一个 EXCEL.EXE 进程点几次之后内存占用飙升系统变卡。原因VB.NET 通过 COM 互操作调用 Excel 后没有释放 COM 对象引用Excel 进程无法正常退出。解决在导出完成后按顺序释放所有 COM 对象并调用GC.Collect()强制回收。关键代码 释放 Excel COM 对象的正确顺序 System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet) System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook) System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp) worksheet Nothing workbook Nothing excelApp Nothing GC.Collect() GC.WaitForPendingFinalizers()4.5 删除部门时没有检查关联员工现象删除某个部门后该部门下的员工记录还在但查询员工信息时“所在部门”显示为空或者报错。原因删除部门之前没有检查 employee 表里是否有该部门的员工记录直接删除了部门表里的记录导致员工表里的外键指向了一个不存在的部门。解决在删除部门的存储过程里加一个 EXISTS 检查如果有员工关联就抛出错误提示阻止删除。或者在应用层先查一下该部门的员工数量如果大于零就弹窗提示“该部门下还有 N 名员工请先转移或删除员工后再删除部门”。两种方式都可以存储过程的方式更安全因为不管从哪个入口调用都会执行检查。5. 从说明书到可运行系统三层架构的代码组织与验证技巧把说明书里的设计变成能跑的系统最关键的一步是代码组织。考核评价点里明确要求“数据访问层单列严格按照 3 层结构分层”这一项占了 10 分而且难度标了三颗星。我一般会按下面的目录结构来组织代码HRSystem/ ├── UI/ # 表示层所有窗体文件 │ ├── LoginForm.vb │ ├── MainForm.vb │ ├── DeptForm.vb │ ├── EmployeeForm.vb │ ├── SalaryForm.vb │ └── UserForm.vb ├── BLL/ # 业务逻辑层业务规则和计算 │ ├── DeptBLL.vb │ ├── EmployeeBLL.vb │ ├── SalaryBLL.vb │ └── UserBLL.vb ├── DAL/ # 数据访问层只跟数据库打交道 │ ├── SqlHelper.vb # 公共数据库操作类 │ ├── DeptDAL.vb │ ├── EmployeeDAL.vb │ ├── SalaryDAL.vb │ └── UserDAL.vb └── Model/ # 实体类跟数据库表一一对应 ├── Department.vb ├── Employee.vb ├── Salary.vb └── SysUser.vbSqlHelper 类是整个 DAL 层的核心它封装了所有跟数据库交互的底层操作。一个典型的 SqlHelper 包含这几个方法ExecuteNonQuery执行增删改、ExecuteScalar返回单个值、ExecuteDataTable返回 DataTable、ExecuteReader返回 DataReader。每个方法都接收 SQL 语句或存储过程名以及参数数组内部处理连接的打开和关闭。验证三层架构是否真的分层了有一个简单的检查方法看 UI 层的代码里有没有出现SqlConnection、SqlCommand这些 ADO.NET 的对象。如果 UI 层直接用了这些说明分层没做干净DAL 层被架空了。正确的做法是 UI 层只调用 BLL 层的方法BLL 层只调用 DAL 层的方法DAL 层才出现 ADO.NET 的代码。再分享一个验证数据库连接是否正常的技巧。在 DAL 层的 SqlHelper 里加一个测试方法Public Function TestConnection() As Boolean Try Using conn As New SqlConnection(connStr) conn.Open() Return True End Using Catch ex As Exception MessageBox.Show(数据库连接失败 ex.Message) Return False End Try End Function在系统启动的时候先调一下这个方法如果返回 False 就直接退出避免后面每个操作都报连接错误。连接字符串一般放在 App.config 文件里不要硬编码在代码里这样换数据库的时候只需要改配置文件。从那以后我每次拿到一份设计说明书都会先做一件事把里面的功能模块和考核评价点对照着列一张表每完成一个功能就在表里打个勾确保没有遗漏。这份说明书最实用的地方就在于它把评分标准直接写出来了你照着考核评价点一项一项做基本不会跑偏。希望帮到你。本文还有配套的精品资源点击获取