SQL Server 2019 开发企业人事管理系统 --- 语法知识点及使用方法详解
📅 当前环境 :SQL Server 2019 + .NET Framework 4.8 / .NET 6+ + Visual Studio 2022
📌 适用对象 :.NET 开发人员、数据库开发工程师、企业系统架构师
🧩 技术栈:C# + WinForms/WPF + ADO.NET + SQL Server 2019
一、需求分析与系统功能结构(非语法,略)
二、构建开发环境
2.1 安装 SQL Server 2019 开发版
✅ 推荐使用 SQL Server 2019 Developer Edition(免费,功能完整)
2.2 创建数据库项目(Visual Studio)
sql
-- 在 SQL Server Management Studio (SSMS) 中创建数据库
CREATE DATABASE HRMS2025;
GO
USE HRMS2025;
GO
2.3 配置连接字符串(App.config / appsettings.json)
WinForms(App.config)
xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="HRMSConnectionString"
connectionString="Server=localhost;Database=HRMS2025;Integrated Security=true;Encrypt=false;TrustServerCertificate=true;"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
.NET 6+(appsettings.json)
json
{
"ConnectionStrings": {
"HRMSConnectionString": "Server=localhost;Database=HRMS2025;Integrated Security=true;Encrypt=false;TrustServerCertificate=true;"
}
}
三、数据库设计
3.1 数据库实体 E-R 图(略,概念设计)
3.2 数据库表设计(核心表结构 + SQL 脚本)
表1:员工档案表(Employees)
sql
-- 员工主表
CREATE TABLE dbo.Employees (
EmployeeID INT IDENTITY(1,1) PRIMARY KEY, -- 员工ID,自增主键
EmployeeCode NVARCHAR(20) UNIQUE NOT NULL, -- 工号(唯一)
FullName NVARCHAR(50) NOT NULL, -- 姓名
Gender CHAR(1) CHECK (Gender IN ('M', 'F')), -- 性别 M/F
BirthDate DATE, -- 出生日期
IDCard NVARCHAR(18) UNIQUE, -- 身份证号(唯一)
DepartmentID INT NOT NULL, -- 部门ID(外键)
Position NVARCHAR(50), -- 职位
HireDate DATE NOT NULL, -- 入职日期
Salary DECIMAL(10,2), -- 月薪
Phone NVARCHAR(20), -- 电话
Email NVARCHAR(100), -- 邮箱
Address NVARCHAR(200), -- 地址
Photo VARBINARY(MAX), -- 照片(二进制存储)
Status NVARCHAR(20) DEFAULT '在职' CHECK (Status IN ('在职', '离职', '休假')), -- 状态
CreatedAt DATETIME2 DEFAULT GETDATE(), -- 创建时间
UpdatedAt DATETIME2 DEFAULT GETDATE() -- 更新时间
);
GO
表2:部门表(Departments)
sql
CREATE TABLE dbo.Departments (
DepartmentID INT IDENTITY(1,1) PRIMARY KEY,
DepartmentName NVARCHAR(50) NOT NULL UNIQUE,
ManagerID INT NULL, -- 部门经理(可为空,自引用)
Description NVARCHAR(200),
CreatedAt DATETIME2 DEFAULT GETDATE()
);
GO
-- 添加外键约束(自引用)
ALTER TABLE dbo.Departments
ADD CONSTRAINT FK_Departments_Manager
FOREIGN KEY (ManagerID) REFERENCES dbo.Employees(EmployeeID);
GO
-- 员工表引用部门表
ALTER TABLE dbo.Employees
ADD CONSTRAINT FK_Employees_Department
FOREIGN KEY (DepartmentID) REFERENCES dbo.Departments(DepartmentID);
GO
表3:用户登录表(Users)
sql
CREATE TABLE dbo.Users (
UserID INT IDENTITY(1,1) PRIMARY KEY,
Username NVARCHAR(50) UNIQUE NOT NULL, -- 登录名
PasswordHash NVARCHAR(256) NOT NULL, -- 密码哈希(禁止明文)
FullName NVARCHAR(50), -- 对应员工姓名
EmployeeID INT NULL, -- 关联员工ID(可选)
Role NVARCHAR(20) NOT NULL CHECK (Role IN ('Admin', 'HR', 'User')), -- 角色
IsActive BIT DEFAULT 1, -- 是否启用
LastLogin DATETIME2 NULL,
CreatedAt DATETIME2 DEFAULT GETDATE()
);
GO
-- 关联员工(可选)
ALTER TABLE dbo.Users
ADD CONSTRAINT FK_Users_Employee
FOREIGN KEY (EmployeeID) REFERENCES dbo.Employees(EmployeeID);
GO
表4:权限表(Permissions)--- RBAC 模型
sql
-- 权限定义表
CREATE TABLE dbo.Permissions (
PermissionID INT IDENTITY(1,1) PRIMARY KEY,
PermissionName NVARCHAR(50) NOT NULL UNIQUE, -- 如:ViewEmployee, EditEmployee, BackupDB
Description NVARCHAR(200)
);
GO
-- 角色权限关联表
CREATE TABLE dbo.RolePermissions (
Role NVARCHAR(20) NOT NULL, -- 对应 Users.Role
PermissionID INT NOT NULL,
PRIMARY KEY (Role, PermissionID),
FOREIGN KEY (PermissionID) REFERENCES dbo.Permissions(PermissionID)
);
GO
-- 插入基础权限
INSERT INTO dbo.Permissions (PermissionName, Description) VALUES
('ViewEmployee', '查看员工档案'),
('EditEmployee', '编辑员工档案'),
('DeleteEmployee', '删除员工档案'),
('ViewSalary', '查看薪资'),
('EditSalary', '编辑薪资'),
('BackupDatabase', '备份数据库'),
('RestoreDatabase', '还原数据库'),
('ManageUsers', '管理用户'),
('ManagePermissions', '管理权限');
GO
-- 分配角色权限(示例:HR角色)
INSERT INTO dbo.RolePermissions (Role, PermissionID) VALUES
('HR', 1), -- ViewEmployee
('HR', 2), -- EditEmployee
('HR', 3), -- DeleteEmployee
('HR', 4), -- ViewSalary
('HR', 5); -- EditSalary
-- Admin 拥有所有权限
INSERT INTO dbo.RolePermissions (Role, PermissionID)
SELECT 'Admin', PermissionID FROM dbo.Permissions;
GO
表5:日常记事表(Diaries)
sql
CREATE TABLE dbo.Diaries (
DiaryID INT IDENTITY(1,1) PRIMARY KEY,
UserID INT NOT NULL, -- 创建者
Title NVARCHAR(100) NOT NULL,
Content NVARCHAR(MAX),
ReminderDate DATETIME2 NULL, -- 提醒时间
IsReminder BIT DEFAULT 0, -- 是否提醒
IsCompleted BIT DEFAULT 0, -- 是否完成
CreatedAt DATETIME2 DEFAULT GETDATE(),
UpdatedAt DATETIME2 DEFAULT GETDATE(),
FOREIGN KEY (UserID) REFERENCES dbo.Users(UserID)
);
GO
四、开发前的准备工作 --- C# 数据库连接类
4.1 定义数据库连接方法(DbHelper.cs)
csharp
using System;
using System.Configuration; // WinForms
// using Microsoft.Extensions.Configuration; // .NET 6+
using System.Data.SqlClient;
public class DbHelper
{
// 获取连接字符串(WinForms 示例)
private static string GetConnectionString()
{
return ConfigurationManager.ConnectionStrings["HRMSConnectionString"].ConnectionString;
}
// 执行非查询语句(INSERT, UPDATE, DELETE)
public static int ExecuteNonQuery(string sql, SqlParameter[] parameters = null)
{
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
if (parameters != null)
cmd.Parameters.AddRange(parameters);
conn.Open();
return cmd.ExecuteNonQuery(); // 返回影响行数
}
}
}
// 执行查询并返回 SqlDataReader(需手动关闭)
public static SqlDataReader ExecuteReader(string sql, SqlParameter[] parameters = null)
{
SqlConnection conn = new SqlConnection(GetConnectionString());
SqlCommand cmd = new SqlCommand(sql, conn);
if (parameters != null)
cmd.Parameters.AddRange(parameters);
conn.Open();
// CommandBehavior.CloseConnection:当Reader关闭时自动关闭连接
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
// 执行标量查询(返回单个值)
public static object ExecuteScalar(string sql, SqlParameter[] parameters = null)
{
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
if (parameters != null)
cmd.Parameters.AddRange(parameters);
conn.Open();
return cmd.ExecuteScalar();
}
}
}
// 执行查询并返回 DataTable(适合数据绑定)
public static DataTable ExecuteDataTable(string sql, SqlParameter[] parameters = null)
{
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
if (parameters != null)
cmd.Parameters.AddRange(parameters);
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
}
}
}
💡 关键点:
- 使用
using确保连接自动释放。- 使用参数化查询防止 SQL 注入。
CommandBehavior.CloseConnection用于DataReader场景。
五、用户登录模块
5.1 防止窗口被关闭(WinForms)
csharp
// 在 LoginForm.cs 中重写 OnFormClosing
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
// 询问是否退出
var result = MessageBox.Show("确定要退出系统吗?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.No)
{
e.Cancel = true; // 取消关闭
}
}
base.OnFormClosing(e);
}
5.2 验证用户名和密码(带权限检查)
csharp
public class LoginService
{
// 验证用户并返回用户信息
public static (bool success, string message, User currentUser) ValidateLogin(string username, string password)
{
try
{
string sql = @"
SELECT UserID, Username, FullName, Role, IsActive, EmployeeID
FROM dbo.Users
WHERE Username = @Username AND PasswordHash = @PasswordHash";
// 计算密码哈希(推荐 SHA256 + Salt,此处简化)
string passwordHash = ComputePasswordHash(password);
SqlParameter[] parameters = {
new SqlParameter("@Username", username),
new SqlParameter("@PasswordHash", passwordHash)
};
using (SqlDataReader reader = DbHelper.ExecuteReader(sql, parameters))
{
if (reader.Read())
{
bool isActive = (bool)reader["IsActive"];
if (!isActive)
return (false, "账户已被禁用!", null);
User user = new User
{
UserID = (int)reader["UserID"],
Username = reader["Username"].ToString(),
FullName = reader["FullName"].ToString(),
Role = reader["Role"].ToString(),
EmployeeID = reader["EmployeeID"] as int? // 可空
};
// 更新最后登录时间
UpdateLastLogin(user.UserID);
return (true, "登录成功!", user);
}
else
{
return (false, "用户名或密码错误!", null);
}
}
}
catch (Exception ex)
{
return (false, $"登录异常:{ex.Message}", null);
}
}
private static string ComputePasswordHash(string password)
{
// 简化版:实际应加 Salt 并使用 SHA256/BCrypt
using (var sha256 = System.Security.Cryptography.SHA256.Create())
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(password);
byte[] hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
private static void UpdateLastLogin(int userId)
{
string sql = "UPDATE dbo.Users SET LastLogin = GETDATE() WHERE UserID = @UserID";
SqlParameter[] parameters = { new SqlParameter("@UserID", userId) };
DbHelper.ExecuteNonQuery(sql, parameters);
}
}
// 用户实体类
public class User
{
public int UserID { get; set; }
public string Username { get; set; }
public string FullName { get; set; }
public string Role { get; set; }
public int? EmployeeID { get; set; }
}
🔐 安全建议:
- 密码必须哈希存储(加 Salt)。
- 使用 HTTPS 传输。
- 登录失败次数限制。
- Session 管理。
六、人事档案管理模块
6.1 界面开发(WinForms DataGridView 绑定)
csharp
// EmployeeForm.cs
public partial class EmployeeForm : Form
{
public EmployeeForm()
{
InitializeComponent();
LoadEmployees();
}
private void LoadEmployees()
{
string sql = @"
SELECT
e.EmployeeID,
e.EmployeeCode,
e.FullName,
CASE e.Gender WHEN 'M' THEN '男' ELSE '女' END AS Gender,
e.BirthDate,
d.DepartmentName,
e.Position,
e.HireDate,
e.Salary,
e.Status
FROM dbo.Employees e
INNER JOIN dbo.Departments d ON e.DepartmentID = d.DepartmentID
ORDER BY e.EmployeeID";
DataTable dt = DbHelper.ExecuteDataTable(sql);
dataGridView1.DataSource = dt;
FormatDataGridView();
}
private void FormatDataGridView()
{
// 设置列标题
dataGridView1.Columns["EmployeeID"].HeaderText = "ID";
dataGridView1.Columns["EmployeeCode"].HeaderText = "工号";
dataGridView1.Columns["FullName"].HeaderText = "姓名";
dataGridView1.Columns["Gender"].HeaderText = "性别";
dataGridView1.Columns["BirthDate"].HeaderText = "出生日期";
dataGridView1.Columns["DepartmentName"].HeaderText = "部门";
dataGridView1.Columns["Position"].HeaderText = "职位";
dataGridView1.Columns["HireDate"].HeaderText = "入职日期";
dataGridView1.Columns["Salary"].HeaderText = "月薪";
dataGridView1.Columns["Status"].HeaderText = "状态";
// 设置列宽和格式
dataGridView1.Columns["Salary"].DefaultCellStyle.Format = "C2"; // 货币格式
dataGridView1.Columns["BirthDate"].DefaultCellStyle.Format = "yyyy-MM-dd";
dataGridView1.Columns["HireDate"].DefaultCellStyle.Format = "yyyy-MM-dd";
}
}
6.2 添加和编辑员工(含照片)
csharp
public class EmployeeService
{
// 添加员工
public static bool AddEmployee(Employee emp, byte[] photoData = null)
{
string sql = @"
INSERT INTO dbo.Employees (
EmployeeCode, FullName, Gender, BirthDate, IDCard,
DepartmentID, Position, HireDate, Salary, Phone, Email,
Address, Photo, Status, CreatedAt, UpdatedAt
) VALUES (
@EmployeeCode, @FullName, @Gender, @BirthDate, @IDCard,
@DepartmentID, @Position, @HireDate, @Salary, @Phone, @Email,
@Address, @Photo, @Status, GETDATE(), GETDATE()
)";
SqlParameter[] parameters = {
new SqlParameter("@EmployeeCode", emp.EmployeeCode),
new SqlParameter("@FullName", emp.FullName),
new SqlParameter("@Gender", emp.Gender),
new SqlParameter("@BirthDate", emp.BirthDate ?? (object)DBNull.Value),
new SqlParameter("@IDCard", emp.IDCard ?? (object)DBNull.Value),
new SqlParameter("@DepartmentID", emp.DepartmentID),
new SqlParameter("@Position", emp.Position ?? (object)DBNull.Value),
new SqlParameter("@HireDate", emp.HireDate),
new SqlParameter("@Salary", emp.Salary ?? (object)DBNull.Value),
new SqlParameter("@Phone", emp.Phone ?? (object)DBNull.Value),
new SqlParameter("@Email", emp.Email ?? (object)DBNull.Value),
new SqlParameter("@Address", emp.Address ?? (object)DBNull.Value),
new SqlParameter("@Photo", photoData ?? (object)DBNull.Value), // 照片
new SqlParameter("@Status", emp.Status ?? "在职")
};
try
{
int rows = DbHelper.ExecuteNonQuery(sql, parameters);
return rows > 0;
}
catch (Exception ex)
{
MessageBox.Show($"添加失败:{ex.Message}");
return false;
}
}
// 编辑员工
public static bool UpdateEmployee(Employee emp, byte[] photoData = null)
{
string sql = @"
UPDATE dbo.Employees SET
EmployeeCode = @EmployeeCode,
FullName = @FullName,
Gender = @Gender,
BirthDate = @BirthDate,
IDCard = @IDCard,
DepartmentID = @DepartmentID,
Position = @Position,
HireDate = @HireDate,
Salary = @Salary,
Phone = @Phone,
Email = @Email,
Address = @Address,
Photo = @Photo, -- 可选更新
Status = @Status,
UpdatedAt = GETDATE()
WHERE EmployeeID = @EmployeeID";
SqlParameter[] parameters = {
new SqlParameter("@EmployeeID", emp.EmployeeID),
new SqlParameter("@EmployeeCode", emp.EmployeeCode),
new SqlParameter("@FullName", emp.FullName),
new SqlParameter("@Gender", emp.Gender),
new SqlParameter("@BirthDate", emp.BirthDate ?? (object)DBNull.Value),
new SqlParameter("@IDCard", emp.IDCard ?? (object)DBNull.Value),
new SqlParameter("@DepartmentID", emp.DepartmentID),
new SqlParameter("@Position", emp.Position ?? (object)DBNull.Value),
new SqlParameter("@HireDate", emp.HireDate),
new SqlParameter("@Salary", emp.Salary ?? (object)DBNull.Value),
new SqlParameter("@Phone", emp.Phone ?? (object)DBNull.Value),
new SqlParameter("@Email", emp.Email ?? (object)DBNull.Value),
new SqlParameter("@Address", emp.Address ?? (object)DBNull.Value),
new SqlParameter("@Photo", photoData != null ? photoData : (object)DBNull.Value),
new SqlParameter("@Status", emp.Status ?? "在职")
};
try
{
int rows = DbHelper.ExecuteNonQuery(sql, parameters);
return rows > 0;
}
catch (Exception ex)
{
MessageBox.Show($"更新失败:{ex.Message}");
return false;
}
}
// 根据ID获取员工(含照片)
public static Employee GetEmployeeById(int employeeId)
{
string sql = @"
SELECT * FROM dbo.Employees WHERE EmployeeID = @EmployeeID";
SqlParameter[] parameters = { new SqlParameter("@EmployeeID", employeeId) };
using (SqlDataReader reader = DbHelper.ExecuteReader(sql, parameters))
{
if (reader.Read())
{
Employee emp = new Employee
{
EmployeeID = (int)reader["EmployeeID"],
EmployeeCode = reader["EmployeeCode"].ToString(),
FullName = reader["FullName"].ToString(),
Gender = reader["Gender"].ToString(),
BirthDate = reader["BirthDate"] as DateTime?,
IDCard = reader["IDCard"]?.ToString(),
DepartmentID = (int)reader["DepartmentID"],
Position = reader["Position"]?.ToString(),
HireDate = (DateTime)reader["HireDate"],
Salary = reader["Salary"] as decimal?,
Phone = reader["Phone"]?.ToString(),
Email = reader["Email"]?.ToString(),
Address = reader["Address"]?.ToString(),
Status = reader["Status"].ToString(),
Photo = reader["Photo"] as byte[] // 读取照片
};
return emp;
}
return null;
}
}
}
// 员工实体类
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeCode { get; set; }
public string FullName { get; set; }
public string Gender { get; set; }
public DateTime? BirthDate { get; set; }
public string IDCard { get; set; }
public int DepartmentID { get; set; }
public string Position { get; set; }
public DateTime HireDate { get; set; }
public decimal? Salary { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public byte[] Photo { get; set; } // 照片二进制数据
public string Status { get; set; }
}
WinForms 照片上传/显示
csharp
// 在 EmployeeEditForm.cs 中
private void btnUploadPhoto_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp";
if (ofd.ShowDialog() == DialogResult.OK)
{
// 读取图片为字节数组
photoData = File.ReadAllBytes(ofd.FileName);
// 显示预览
using (MemoryStream ms = new MemoryStream(photoData))
{
pictureBox1.Image = Image.FromStream(ms);
}
}
}
}
// 保存时调用 EmployeeService.AddEmployee 或 UpdateEmployee,并传入 photoData
七、用户设置模块
7.1 添加和修改用户信息
csharp
public class UserService
{
public static bool AddUser(User user, string plainPassword)
{
string sql = @"
INSERT INTO dbo.Users (Username, PasswordHash, FullName, EmployeeID, Role, IsActive, CreatedAt)
VALUES (@Username, @PasswordHash, @FullName, @EmployeeID, @Role, @IsActive, GETDATE())";
string passwordHash = ComputePasswordHash(plainPassword);
SqlParameter[] parameters = {
new SqlParameter("@Username", user.Username),
new SqlParameter("@PasswordHash", passwordHash),
new SqlParameter("@FullName", user.FullName ?? (object)DBNull.Value),
new SqlParameter("@EmployeeID", user.EmployeeID ?? (object)DBNull.Value),
new SqlParameter("@Role", user.Role),
new SqlParameter("@IsActive", user.IsActive ? 1 : 0)
};
return DbHelper.ExecuteNonQuery(sql, parameters) > 0;
}
public static bool UpdateUser(User user, string plainPassword = null)
{
string sql;
List<SqlParameter> parameters = new List<SqlParameter>
{
new SqlParameter("@UserID", user.UserID),
new SqlParameter("@Username", user.Username),
new SqlParameter("@FullName", user.FullName ?? (object)DBNull.Value),
new SqlParameter("@EmployeeID", user.EmployeeID ?? (object)DBNull.Value),
new SqlParameter("@Role", user.Role),
new SqlParameter("@IsActive", user.IsActive ? 1 : 0)
};
if (!string.IsNullOrEmpty(plainPassword))
{
string passwordHash = ComputePasswordHash(plainPassword);
sql = @"
UPDATE dbo.Users SET
Username = @Username,
PasswordHash = @PasswordHash,
FullName = @FullName,
EmployeeID = @EmployeeID,
Role = @Role,
IsActive = @IsActive
WHERE UserID = @UserID";
parameters.Add(new SqlParameter("@PasswordHash", passwordHash));
}
else
{
sql = @"
UPDATE dbo.Users SET
Username = @Username,
FullName = @FullName,
EmployeeID = @EmployeeID,
Role = @Role,
IsActive = @IsActive
WHERE UserID = @UserID";
}
return DbHelper.ExecuteNonQuery(sql, parameters.ToArray()) > 0;
}
// 简化密码哈希(生产环境请加 Salt)
private static string ComputePasswordHash(string password)
{
using (var sha256 = System.Security.Cryptography.SHA256.Create())
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(password);
byte[] hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
}
7.2 设置用户权限(RBAC)
csharp
public class PermissionService
{
// 获取用户所有权限
public static List<string> GetUserPermissions(string role)
{
string sql = @"
SELECT p.PermissionName
FROM dbo.RolePermissions rp
INNER JOIN dbo.Permissions p ON rp.PermissionID = p.PermissionID
WHERE rp.Role = @Role";
SqlParameter[] parameters = { new SqlParameter("@Role", role) };
DataTable dt = DbHelper.ExecuteDataTable(sql, parameters);
List<string> permissions = new List<string>();
foreach (DataRow row in dt.Rows)
{
permissions.Add(row["PermissionName"].ToString());
}
return permissions;
}
// 检查用户是否有某权限
public static bool HasPermission(string role, string permissionName)
{
string sql = @"
SELECT COUNT(1)
FROM dbo.RolePermissions rp
INNER JOIN dbo.Permissions p ON rp.PermissionID = p.PermissionID
WHERE rp.Role = @Role AND p.PermissionName = @PermissionName";
SqlParameter[] parameters = {
new SqlParameter("@Role", role),
new SqlParameter("@PermissionName", permissionName)
};
object result = DbHelper.ExecuteScalar(sql, parameters);
return result != null && (int)result > 0;
}
// 在界面中控制按钮权限
public static void ApplyUserPermissions(User currentUser, Form mainForm)
{
List<string> permissions = GetUserPermissions(currentUser.Role);
// 示例:控制按钮可见性
var editEmployeeBtn = mainForm.Controls.Find("btnEditEmployee", true).FirstOrDefault() as Button;
if (editEmployeeBtn != null)
editEmployeeBtn.Visible = permissions.Contains("EditEmployee");
var backupBtn = mainForm.Controls.Find("btnBackup", true).FirstOrDefault() as Button;
if (backupBtn != null)
backupBtn.Visible = permissions.Contains("BackupDatabase");
}
}
八、数据库维护模块
8.1 数据库备份功能
csharp
public class BackupService
{
public static bool BackupDatabase(string backupPath, string databaseName = "HRMS2025")
{
try
{
// 构造备份SQL
string sql = $@"
BACKUP DATABASE [{databaseName}]
TO DISK = @BackupPath
WITH FORMAT, MEDIANAME = 'HRMSBackup', NAME = 'Full Backup of {databaseName}'";
SqlParameter[] parameters = { new SqlParameter("@BackupPath", backupPath) };
int rows = DbHelper.ExecuteNonQuery(sql, parameters);
return true; // BACKUP 无返回行数,成功则无异常
}
catch (Exception ex)
{
MessageBox.Show($"备份失败:{ex.Message}");
return false;
}
}
}
// 调用示例(WinForms)
private void btnBackup_Click(object sender, EventArgs e)
{
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "Backup Files|*.bak";
sfd.FileName = $"HRMS_{DateTime.Now:yyyyMMdd_HHmmss}.bak";
if (sfd.ShowDialog() == DialogResult.OK)
{
bool success = BackupService.BackupDatabase(sfd.FileName);
if (success)
MessageBox.Show("备份成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
8.2 数据库还原功能
csharp
public class RestoreService
{
public static bool RestoreDatabase(string backupPath, string databaseName = "HRMS2025")
{
try
{
// 首先将数据库设置为单用户模式
string setSingleUser = $@"
ALTER DATABASE [{databaseName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
DbHelper.ExecuteNonQuery(setSingleUser);
// 执行还原
string restoreSql = $@"
RESTORE DATABASE [{databaseName}]
FROM DISK = @BackupPath
WITH REPLACE, RECOVERY";
SqlParameter[] parameters = { new SqlParameter("@BackupPath", backupPath) };
DbHelper.ExecuteNonQuery(restoreSql, parameters);
// 恢复多用户模式
string setMultiUser = $@"
ALTER DATABASE [{databaseName}] SET MULTI_USER";
DbHelper.ExecuteNonQuery(setMultiUser);
return true;
}
catch (Exception ex)
{
MessageBox.Show($"还原失败:{ex.Message}");
// 尝试恢复多用户模式
try
{
string setMultiUser = $@"
ALTER DATABASE [{databaseName}] SET MULTI_USER";
DbHelper.ExecuteNonQuery(setMultiUser);
}
catch { }
return false;
}
}
}
// 调用示例
private void btnRestore_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "Backup Files|*.bak";
if (ofd.ShowDialog() == DialogResult.OK)
{
var confirm = MessageBox.Show("还原将覆盖当前数据,是否继续?", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (confirm == DialogResult.Yes)
{
bool success = RestoreService.RestoreDatabase(ofd.FileName);
if (success)
{
MessageBox.Show("还原成功!请重新登录。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
Application.Restart(); // 重启应用
}
}
}
}
}
⚠️ 注意:
- 还原前断开所有连接(设为单用户模式)。
- 还原后恢复多用户模式。
- 生产环境应在维护窗口操作。
九、系统运行界面示例(代码片段)
9.1 主界面权限控制(MainForm.cs)
csharp
public partial class MainForm : Form
{
private User currentUser;
public MainForm(User user)
{
InitializeComponent();
this.currentUser = user;
lblWelcome.Text = $"欢迎,{user.FullName} ({user.Role})";
ApplyPermissions();
}
private void ApplyPermissions()
{
// 应用权限控制
PermissionService.ApplyUserPermissions(currentUser, this);
// 或手动控制
if (!PermissionService.HasPermission(currentUser.Role, "ManageUsers"))
{
menuUserManagement.Visible = false; // 隐藏菜单
}
}
private void menuEmployeeManage_Click(object sender, EventArgs e)
{
if (PermissionService.HasPermission(currentUser.Role, "ViewEmployee"))
{
EmployeeForm empForm = new EmployeeForm();
empForm.MdiParent = this;
empForm.Show();
}
else
{
MessageBox.Show("您没有权限访问此模块!", "权限不足", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
9.2 员工信息提醒界面(生日/合同到期)
csharp
public partial class ReminderForm : Form
{
public ReminderForm()
{
InitializeComponent();
LoadReminders();
}
private void LoadReminders()
{
// 查询本月生日员工
string sql = @"
SELECT
FullName,
FORMAT(BirthDate, 'MM-dd') AS BirthDateStr,
'生日提醒' AS ReminderType
FROM dbo.Employees
WHERE MONTH(BirthDate) = MONTH(GETDATE())
AND Status = '在职'
UNION ALL
-- 可添加合同到期提醒等
SELECT
FullName,
FORMAT(DATEADD(YEAR, 1, HireDate), 'yyyy-MM-dd') AS ExpireDate,
'入职周年' AS ReminderType
FROM dbo.Employees
WHERE MONTH(HireDate) = MONTH(GETDATE())
AND DAY(HireDate) = DAY(GETDATE())
AND Status = '在职'
ORDER BY ReminderType, FullName";
DataTable dt = DbHelper.ExecuteDataTable(sql);
dataGridView1.DataSource = dt;
}
}
9.3 员工通讯录界面
csharp
private void LoadContacts()
{
string sql = @"
SELECT
e.FullName AS 姓名,
d.DepartmentName AS 部门,
e.Position AS 职位,
e.Phone AS 电话,
e.Email AS 邮箱
FROM dbo.Employees e
INNER JOIN dbo.Departments d ON e.DepartmentID = d.DepartmentID
WHERE e.Status = '在职'
ORDER BY d.DepartmentName, e.FullName";
dataGridViewContacts.DataSource = DbHelper.ExecuteDataTable(sql);
}
9.4 日常记事界面(增删改查)
csharp
public partial class DiaryForm : Form
{
private int currentUserId;
public DiaryForm(int userId)
{
InitializeComponent();
this.currentUserId = userId;
LoadDiaries();
}
private void LoadDiaries()
{
string sql = @"
SELECT
DiaryID,
Title,
LEFT(Content, 50) + '...' AS ContentPreview,
ReminderDate,
IsReminder,
IsCompleted,
CreatedAt
FROM dbo.Diaries
WHERE UserID = @UserID
ORDER BY IsCompleted ASC, ReminderDate DESC, CreatedAt DESC";
SqlParameter[] parameters = { new SqlParameter("@UserID", currentUserId) };
dataGridView1.DataSource = DbHelper.ExecuteDataTable(sql, parameters);
}
private void btnAdd_Click(object sender, EventArgs e)
{
// 打开编辑窗口
DiaryEditForm editForm = new DiaryEditForm(currentUserId);
if (editForm.ShowDialog() == DialogResult.OK)
{
LoadDiaries(); // 刷新
}
}
}
// DiaryEditForm.cs 保存逻辑
private void btnSave_Click(object sender, EventArgs e)
{
string sql;
SqlParameter[] parameters;
if (diaryId > 0) // 更新
{
sql = @"
UPDATE dbo.Diaries SET
Title = @Title,
Content = @Content,
ReminderDate = @ReminderDate,
IsReminder = @IsReminder,
IsCompleted = @IsCompleted,
UpdatedAt = GETDATE()
WHERE DiaryID = @DiaryID AND UserID = @UserID";
parameters = new SqlParameter[]
{
new SqlParameter("@DiaryID", diaryId),
new SqlParameter("@UserID", userId),
new SqlParameter("@Title", txtTitle.Text),
new SqlParameter("@Content", txtContent.Text),
new SqlParameter("@ReminderDate", dtpReminder.Value),
new SqlParameter("@IsReminder", chkReminder.Checked ? 1 : 0),
new SqlParameter("@IsCompleted", chkCompleted.Checked ? 1 : 0)
};
}
else // 插入
{
sql = @"
INSERT INTO dbo.Diaries (UserID, Title, Content, ReminderDate, IsReminder, IsCompleted, CreatedAt, UpdatedAt)
VALUES (@UserID, @Title, @Content, @ReminderDate, @IsReminder, @IsCompleted, GETDATE(), GETDATE())";
parameters = new SqlParameter[]
{
new SqlParameter("@UserID", userId),
new SqlParameter("@Title", txtTitle.Text),
new SqlParameter("@Content", txtContent.Text),
new SqlParameter("@ReminderDate", dtpReminder.Value),
new SqlParameter("@IsReminder", chkReminder.Checked ? 1 : 0),
new SqlParameter("@IsCompleted", chkCompleted.Checked ? 1 : 0)
};
}
if (DbHelper.ExecuteNonQuery(sql, parameters) > 0)
{
MessageBox.Show("保存成功!");
this.DialogResult = DialogResult.OK;
this.Close();
}
}
十、综合性案例
综合案例1:员工入职流程自动化
csharp
public class OnboardingService
{
// 事务:添加员工 + 创建用户 + 分配默认权限
public static bool CompleteOnboarding(Employee emp, string loginUsername, string loginPassword, byte[] photo = null)
{
using (SqlConnection conn = new SqlConnection(DbHelper.GetConnectionString()))
{
conn.Open();
using (SqlTransaction tran = conn.BeginTransaction())
{
try
{
// 1. 插入员工记录
string empSql = @"
INSERT INTO dbo.Employees (
EmployeeCode, FullName, Gender, BirthDate, IDCard,
DepartmentID, Position, HireDate, Salary, Phone, Email,
Address, Photo, Status, CreatedAt, UpdatedAt
) VALUES (
@EmployeeCode, @FullName, @Gender, @BirthDate, @IDCard,
@DepartmentID, @Position, @HireDate, @Salary, @Phone, @Email,
@Address, @Photo, @Status, GETDATE(), GETDATE()
);
SELECT SCOPE_IDENTITY();"; // 返回新员工ID
SqlCommand empCmd = new SqlCommand(empSql, conn, tran);
// ... 设置参数(略)
object newEmpIdObj = empCmd.ExecuteScalar();
if (newEmpIdObj == null) throw new Exception("员工插入失败");
int newEmpId = Convert.ToInt32(newEmpIdObj);
// 2. 创建用户
string userSql = @"
INSERT INTO dbo.Users (Username, PasswordHash, FullName, EmployeeID, Role, IsActive, CreatedAt)
VALUES (@Username, @PasswordHash, @FullName, @EmployeeID, @Role, 1, GETDATE())";
string passwordHash = ComputePasswordHash(loginPassword);
SqlCommand userCmd = new SqlCommand(userSql, conn, tran);
userCmd.Parameters.AddRange(new SqlParameter[]
{
new SqlParameter("@Username", loginUsername),
new SqlParameter("@PasswordHash", passwordHash),
new SqlParameter("@FullName", emp.FullName),
new SqlParameter("@EmployeeID", newEmpId),
new SqlParameter("@Role", "User") // 默认角色
});
int userRows = userCmd.ExecuteNonQuery();
if (userRows == 0) throw new Exception("用户创建失败");
// 3. 记录入职日志(可选)
string logSql = "INSERT INTO dbo.SystemLogs (Action, Description, CreatedBy) VALUES (@Action, @Desc, @User)";
SqlCommand logCmd = new SqlCommand(logSql, conn, tran);
logCmd.Parameters.AddRange(new SqlParameter[]
{
new SqlParameter("@Action", "Onboarding"),
new SqlParameter("@Desc", $"员工 {emp.FullName} 入职,工号 {emp.EmployeeCode}"),
new SqlParameter("@User", "System")
});
logCmd.ExecuteNonQuery();
tran.Commit();
return true;
}
catch (Exception ex)
{
tran.Rollback();
throw new Exception($"入职流程失败:{ex.Message}");
}
}
}
}
private static string ComputePasswordHash(string password)
{
using (var sha256 = System.Security.Cryptography.SHA256.Create())
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(password);
byte[] hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
}
✅ 事务保证:员工、用户、日志要么全部成功,要么全部回滚。
综合案例2:基于角色的动态菜单生成
csharp
public partial class MainForm : Form
{
private User currentUser;
public MainForm(User user)
{
InitializeComponent();
this.currentUser = user;
InitializeDynamicMenu();
}
private void InitializeDynamicMenu()
{
// 清空现有菜单(除"系统"外)
for (int i = menuStrip1.Items.Count - 1; i >= 0; i--)
{
if (menuStrip1.Items[i].Text != "系统")
menuStrip1.Items.RemoveAt(i);
}
// 获取用户权限
List<string> permissions = PermissionService.GetUserPermissions(currentUser.Role);
// 定义菜单结构
var menuMap = new Dictionary<string, List<(string text, string permission, EventHandler handler)>>
{
["人事管理"] = new List<(string, string, EventHandler)>
{
("员工档案", "ViewEmployee", menuEmployeeManage_Click),
("部门管理", "ViewDepartment", menuDeptManage_Click),
("薪资管理", "ViewSalary", menuSalaryManage_Click)
},
["系统管理"] = new List<(string, string, EventHandler)>
{
("用户管理", "ManageUsers", menuUserManage_Click),
("权限设置", "ManagePermissions", menuPermission_Click),
("数据备份", "BackupDatabase", menuBackup_Click),
("数据还原", "RestoreDatabase", menuRestore_Click)
}
};
// 动态添加菜单
foreach (var menuGroup in menuMap)
{
ToolStripMenuItem parentMenu = new ToolStripMenuItem(menuGroup.Key);
foreach (var item in menuGroup.Value)
{
if (permissions.Contains(item.permission))
{
ToolStripMenuItem menuItem = new ToolStripMenuItem(item.text);
menuItem.Click += item.handler;
parentMenu.DropDownItems.Add(menuItem);
}
}
if (parentMenu.DropDownItems.Count > 0)
{
menuStrip1.Items.Add(parentMenu);
}
}
}
// 事件处理方法(略)
private void menuEmployeeManage_Click(object sender, EventArgs e) { /* 打开员工管理 */ }
private void menuBackup_Click(object sender, EventArgs e) { /* 打开备份界面 */ }
// ...
}
🌟 优势:
- 权限变更无需修改代码。
- 自动隐藏无权限菜单项。
- 提升用户体验和安全性。
项目总结
技术亮点:
-
安全架构:
- 密码哈希存储。
- RBAC 权限控制。
- 参数化查询防注入。
-
智能数据库利用:
- 使用 SQL Server 2019 的智能优化(自动内存反馈等)。
- 高效索引设计(部门、工号、状态等字段)。
-
模块化设计:
- 分层架构(UI、Service、DAL)。
- 可复用的
DbHelper类。
-
用户体验:
- 动态权限菜单。
- 照片上传与显示。
- 数据备份还原一键操作。
-
健壮性:
- 事务处理关键流程。
- 异常捕获与友好提示。
- 数据验证。
✅ 部署建议:
- 数据库:SQL Server 2019 Standard/Enterprise。
- 应用服务器:Windows Server + .NET Framework 4.8。
- 客户端:Win10/Win11 + .NET 运行时。
- 备份策略:每日全备 + 事务日志备份。
🚀 扩展方向:
- Web 版(ASP.NET Core + Blazor)。
- 移动端(Xamarin / MAUI)。
- 集成 AD 域登录。
- 工作流引擎(请假、审批)。
本系统完整覆盖企业人事管理核心需求,代码结构清晰、安全可靠、易于维护,是学习 SQL Server 2019 与 C# 企业级开发的优秀范例。