基于VB.NET与SQL Server的企业人力资源管理系统课程设计实战:hrsys库、三层架构与存储过程
简介这份企业人力资源管理系统设计说明书面向计算机相关专业的课程设计、毕业设计学生及需要撰写开题报告与概要设计文档的开发者围绕人事信息管理这一典型场景提供从需求分析到模块实现的完整设计思路。资源包共1个doc文件约530KB内容按需求分析、数据库设计、各功能模块设计与实现、考核评价点四大部分组织涵盖部门信息管理、员工信息管理、工资管理、用户管理四大模块并给出部门表、员工表、工资表、权限表、日志表等数据库组成方案以及开发环境选型、栏目设计、增删改查与查询输出等具体实现要点。目前已有71人学习下载。读者可据此快速搭建课程设计文档框架理清功能模块划分与数据库表结构掌握需求分析、总体设计原则与考核评价维度的写作方法适合作为开题报告、概要设计说明书及答辩准备的参考模板。1. 从一份 .doc 设计说明书说起hrsys 库、四张表与三层结构到底怎么落地很多同学拿到「企业人力资源管理系统设计说明书」这类课程设计资源第一反应是当成开题报告模板复制粘贴改改封面就交了。但真正做过答辩的人都知道老师翻两页就会问你的数据库建了几张表部门表和员工表怎么关联工资的实发金额是存字段还是算出来的这份说明书的价值不在文字而在它把一套可运行系统的骨架写清楚了——数据库名 hrsys四张核心表用户表、部门表、员工基本信息表、员工工资表开发环境是 Visual Basic.NET SQL Server 2000功能覆盖部门管理、职工管理、工资管理、用户管理四大模块还附了 12 条考核评价点。它适合两类人一是要交课程设计、需要一份能跑通的最小系统参考的在校生二是想快速搭一个 C/S 架构增删改查练手项目的新手。下面我按「说明书讲了什么 → 怎么照着建库建表 → 代码怎么写 → 哪里容易翻车」的顺序拆一遍能直接抄的部分我都给出来。2. 数据库设计hrsys 四张表怎么建、主外键怎么连2.1 先想清楚表关系再动手写 CREATE TABLE说明书里给了数据表关系图但没把字段类型写全。我按它描述的业务补全一套能直接执行的建表语句。核心关系是部门表是主表员工基本信息表通过部门编号关联它员工工资表通过员工编号关联员工表用户表独立只管登录和权限。这里有个选型理由要说清楚——为什么部门编号、员工编号用字符型而不是自增整型因为课程设计答辩常要求「编号可读」比如 D001、E1001方便演示时肉眼核对而且导入历史工资时用业务编号做匹配比自增 ID 更稳。-- 创建数据库 CREATE DATABASE hrsys; GO USE hrsys; GO -- 部门表部门编号为主键 CREATE TABLE Department ( DeptID VARCHAR(10) PRIMARY KEY, -- 部门编号如 D001 DeptName VARCHAR(50) NOT NULL, -- 部门名称 EmpCount INT DEFAULT 0, -- 在职人数 Phone VARCHAR(20) -- 联系方式 ); -- 员工基本信息表EmpID 主键DeptID 外键指向部门表 CREATE TABLE Employee ( EmpID VARCHAR(10) PRIMARY KEY, -- 员工编号 EmpName VARCHAR(30) NOT NULL, -- 姓名 Gender CHAR(2), -- 性别 BirthDate DATETIME, -- 出生日期 DeptID VARCHAR(10), -- 所在部门 Education VARCHAR(20), -- 学历 CONSTRAINT fk_emp_dept FOREIGN KEY (DeptID) REFERENCES Department(DeptID) ); -- 员工工资表以工资月份员工编号做联合主键避免同月重复发放 CREATE TABLE Salary ( SalaryMonth VARCHAR(7), -- 工资月份如 2024-06 EmpID VARCHAR(10), -- 员工编号 BaseSalary DECIMAL(10,2), -- 基本工资 FoodAllow DECIMAL(10,2), -- 伙食补贴 MedAllow DECIMAL(10,2), -- 医疗补贴 RealSalary DECIMAL(10,2), -- 实发工资 PRIMARY KEY (SalaryMonth, EmpID), CONSTRAINT fk_sal_emp FOREIGN KEY (EmpID) REFERENCES Employee(EmpID) ); -- 用户表权限 0 为管理员1 为普通用户 CREATE TABLE SysUser ( UserName VARCHAR(20) PRIMARY KEY, Password VARCHAR(50) NOT NULL, UserRole INT DEFAULT 1 );建表顺序不能乱先 Department再 Employee最后 Salary否则外键引用会报「对象不存在」。参数上金额字段统一用 DECIMAL(10,2)别用 FLOAT否则工资累加会出现 0.30000000000000004 这种玄学尾数答辩演示时对不上账很尴尬。实发工资 RealSalary 我建议存成字段而不是每次查询现算原因是说明书里明确有「导入历史工资」功能历史月份的补贴可能和当月规则不同现算会篡改历史数据。2.2 用存储过程封装增删改别把 SQL 散在窗体里说明书特别强调「本设计使用的是存储过程」这是它比一般课程设计高一档的地方。把业务规则放服务器端前台只传参既减轻网络传输也避免 SQL 注入。下面以部门维护为例给三个存储过程。-- 新增部门 CREATE PROCEDURE sp_AddDept DeptID VARCHAR(10), DeptName VARCHAR(50), EmpCount INT, Phone VARCHAR(20) AS BEGIN INSERT INTO Department(DeptID, DeptName, EmpCount, Phone) VALUES(DeptID, DeptName, EmpCount, Phone); END GO -- 修改部门 CREATE PROCEDURE sp_UpdateDept DeptID VARCHAR(10), DeptName VARCHAR(50), Phone VARCHAR(20) AS BEGIN UPDATE Department SET DeptNameDeptName, PhonePhone WHERE DeptIDDeptID; END GO -- 删除部门先校验该部门下是否还有员工 CREATE PROCEDURE sp_DeleteDept DeptID VARCHAR(10) AS BEGIN IF EXISTS(SELECT 1 FROM Employee WHERE DeptIDDeptID) RETURN -1; -- 有员工禁止删除 DELETE FROM Department WHERE DeptIDDeptID; RETURN 0; END逻辑说明sp_DeleteDept 里那个 EXISTS 判断是血泪经验。说明书 3.3.1 只写了「删除部门前需确认该部门下无员工信息关联」但没给实现。如果不加这层校验直接 DELETE 会因为外键约束报错程序抛异常弹一堆英文演示时很难看。返回 -1 让前台弹「该部门下仍有员工无法删除」体验就正常了。参数说明DeptID 是字符型调用时前台传字符串别传数字否则类型不匹配。3. 三层结构与窗体实现登录鉴权、DataGrid 绑定、工资计算3.1 严格三层数据访问层单列考核点第 1 和第 11 条都盯这个说明书考核评价点里「使用直连数据库读取、提交数据数据访问层单列严格按照 3 层结构分层」出现了两次合计 15 分是权重最高的必做项。三层指表示层窗体、业务逻辑层BLL、数据访问层DAL。很多同学图省事把 SqlConnection 直接写在按钮点击事件里这样表示层和数据层耦合老师一看代码就扣分。正确做法是 DAL 里写一个统一的数据库访问类。 DAL/DbHelper.vb —— 数据访问层所有窗体共用 Imports System.Data.SqlClient Public Class DbHelper 连接字符串集中管理改服务器只改这一处 Private Shared connStr As String _ Data Source.;Initial Cataloghrsys;Integrated SecurityTrue 执行增删改返回受影响行数 Public Shared Function ExecuteNonQuery(ByVal spName As String, _ ByVal params() As SqlParameter) As Integer Using conn As New SqlConnection(connStr) Using cmd As New SqlCommand(spName, conn) cmd.CommandType CommandType.StoredProcedure If params IsNot Nothing Then cmd.Parameters.AddRange(params) conn.Open() Return cmd.ExecuteNonQuery() End Using End Using End Function 执行查询返回 DataTable 供 DataGrid 绑定 Public Shared Function ExecuteQuery(ByVal spName As String, _ ByVal params() As SqlParameter) As DataTable Dim dt As New DataTable() Using conn As New SqlConnection(connStr) Using cmd As New SqlCommand(spName, conn) cmd.CommandType CommandType.StoredProcedure If params IsNot Nothing Then cmd.Parameters.AddRange(params) Using da As New SqlDataAdapter(cmd) da.Fill(dt) End Using End Using End Using Return dt End Function End Class逻辑说明ExecuteNonQuery 用于增删改ExecuteQuery 用于查询后绑定 DataGrid。参数用 SqlParameter 数组传入天然防注入。连接字符串里 Data Source 用点号代表本机Integrated SecurityTrue 表示用 Windows 身份验证省去写账号密码。如果你用的是 SQL Server 账号登录改成User IDsa;Password你的密码。注意 Using 语句会自动释放连接别手动 Close 又 Dispose重复释放会抛异常。3.2 登录窗体与权限控制菜单可见性按 UserRole 走说明书 3.2 写「管理员默认权限为 0一般用户默认权限为 1管理员拥有比一般用户更多的系统管理权限」考核点第 2 条要求「登录后按照权限控制菜单可见性」。实现思路是登录成功后把 UserRole 存到一个全局模块主窗体加载时按角色隐藏菜单。 登录按钮点击事件 Private Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click Dim params() As SqlParameter { New SqlParameter(UserName, txtUser.Text.Trim()), New SqlParameter(Password, txtPwd.Text.Trim()) } Dim dt As DataTable DbHelper.ExecuteQuery(sp_CheckLogin, params) If dt.Rows.Count 0 Then MessageBox.Show(用户名或密码错误) Return End If 全局变量记录当前角色 CurrentUser dt.Rows(0)(UserName).ToString() CurrentRole CInt(dt.Rows(0)(UserRole)) Me.DialogResult DialogResult.OK End Sub 主窗体 Load 事件普通用户隐藏用户管理菜单 Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load If CurrentRole 1 Then mnuUserManage.Visible False -- 普通用户看不到用户管理 End If End Sub对应的存储过程 sp_CheckLogin 就是SELECT UserName, UserRole FROM SysUser WHERE UserNameUserName AND PasswordPassword。参数说明密码这里为了演示是明文比对实际项目要做哈希但课程设计阶段明文能跑通即可答辩时主动说明「生产环境需加密」反而是加分项。注意 txtUser.Text.Trim() 的 Trim 不能省用户复制粘贴带空格是高频翻车点。3.3 工资计算导入历史 当月调整实发金额怎么算说明书 3.5.1 描述得很清楚第一次使用需人工输入基本工资和各项补贴点【计算当月工资】算出实发工资以后月份可【导入工资历史】再单独调整。考核点第 9 条给了 15 分是难度最高的三星项。实现分两步导入历史用 INSERT INTO ... SELECT计算用 UPDATE。-- 导入历史工资把上个月的记录复制到本月实发先置 0 CREATE PROCEDURE sp_ImportHistory NewMonth VARCHAR(7), OldMonth VARCHAR(7) AS BEGIN INSERT INTO Salary(SalaryMonth, EmpID, BaseSalary, FoodAllow, MedAllow, RealSalary) SELECT NewMonth, EmpID, BaseSalary, FoodAllow, MedAllow, 0 FROM Salary WHERE SalaryMonth OldMonth; END GO -- 计算实发工资基本工资 伙食补贴 医疗补贴 CREATE PROCEDURE sp_CalcSalary SalaryMonth VARCHAR(7) AS BEGIN UPDATE Salary SET RealSalary BaseSalary FoodAllow MedAllow WHERE SalaryMonth SalaryMonth; END逻辑说明先导入再计算顺序不能反。导入时 RealSalary 置 0是为了让「未计算」和「已计算」有区分前台可以用颜色标记。计算逻辑里如果企业有扣税、扣社保就在 UPDATE 里继续减说明书摘要提到「扣除个人所得税和社会保险费用」可以加- Tax - Insurance两个字段。参数说明NewMonth 和 OldMonth 格式必须是 yyyy-MM如果前台用 DateTimePicker 取值记得.ToString(yyyy-MM)直接 ToString 会带时分秒导致匹配不上。4. 避坑与排查课程设计答辩前必须过的五道坎4.1 现象DataGrid 改了数据但数据库没变原因DataGrid 默认只是展示用户在里面直接编辑单元格改的是内存里的 DataTable没有回写数据库。说明书 3.3.1 写的是「选中信息绑定到 textbox 再修改」所以正确交互是选中行→填到文本框→点修改按钮走存储过程。解决把 DataGrid 的 ReadOnly 设为 True禁止直接编辑所有修改走按钮。4.2 现象删除部门时报外键约束错误原因该部门下还有员工直接 DELETE 触发 fk_emp_dept 约束。解决用 2.2 里的 sp_DeleteDept先 EXISTS 判断再删返回 -1 时前台提示「请先转移或删除该部门员工」。4.3 现象查询员工按姓名查不到明明库里有原因SQL Server 默认排序规则对中文不敏感但 LIKE 拼接时如果写成% Name %而参数类型是 NVARCHAR 之外的可能匹配失败更常见的是前后空格没 Trim。解决存储过程里用WHERE EmpName LIKE % LTRIM(RTRIM(EmpName)) %前台传参也 Trim 一次。4.4 现象打印按钮点了没反应或导出 Excel 乱码原因说明书 3.4.2 说打印通过 Excel 样式输出如果没装 Office 或者用了错误的 COM 引用就会静默失败。解决确认项目引用了 Microsoft.Office.Interop.Excel且目标机器装了 Excel导出时用Encoding.Default而非 UTF-8避免中文乱码。4.5 现象换台电脑运行就连不上数据库原因连接字符串写死了Data Source192.168.x.x或某个实例名。解决把连接字符串抽到配置文件 app.config部署时只改配置不改代码。这也是考核点第 11 条「数据访问层单列」的延伸——连接信息也该单列。5. 进阶技巧把说明书变成可演示的答辩利器到这一步系统能跑了但答辩拿高分还差一点。我的习惯是给这份设计说明书配一个「演示脚本」先以普通用户登录展示部门查询和职工查询证明权限控制生效菜单里没有用户管理退出后用管理员登录演示新增部门、新增员工、导入历史工资、计算当月工资、打印工资条最后展示用户管理里新增一个普通用户。整个流程 5 分钟正好覆盖 12 个考核点。再给一个验证数据一致性的技巧在 SQL Server 里跑一句对账查询确认工资表的实发金额等于三项之和答辩时如果老师质疑计算逻辑直接把这个结果贴出来。-- 对账找出实发工资不等于三项之和的异常记录 SELECT SalaryMonth, EmpID, BaseSalary, FoodAllow, MedAllow, RealSalary, (BaseSalary FoodAllow MedAllow) AS ShouldBe FROM Salary WHERE RealSalary (BaseSalary FoodAllow MedAllow);正常应该返回 0 行。如果返回了记录说明有月份没执行 sp_CalcSalary或者导入历史后忘了重算。这个查询我每次交付前都强制走一遍比肉眼核对几十行数据靠谱得多。还有一个容易被忽略的点说明书里「合理冗余」原则提到「为了提高系统的响应时间合理的数据冗余也是必要的」。部门表里的 EmpCount在职人数就是典型冗余字段——它可以从 Employee 表 COUNT 出来但每次查部门都 COUNT 一次没必要。做法是在新增/删除员工时同步更新 EmpCount用触发器或存储过程都行。触发器写法-- 员工新增后对应部门在职人数 1 CREATE TRIGGER trg_EmpInsert ON Employee AFTER INSERT AS BEGIN UPDATE Department SET EmpCount EmpCount 1 WHERE DeptID IN (SELECT DeptID FROM inserted); END注意 inserted 是触发器里的虚拟表代表本次插入的行。删除员工时对应写一个 AFTER DELETE 触发器做 -1。这样部门列表页直接读 EmpCount不用联表 COUNT响应快也符合说明书的设计原则。从那以后我每次做这类课程设计都会先把冗余字段的同步逻辑用触发器锁死再写前台代码否则演示到一半人数对不上解释起来很被动。希望帮到你。本文还有配套的精品资源点击获取