三层架构重构项目文档
📖 项目概述
本项目是一个员工部门管理系统,使用C# Windows Forms开发。为了代码更清晰、更易于维护,我们将原本在一个文件中的代码拆分成三层架构。
🎯 什么是三层架构?
三层架构就像盖房子:
- UI层:房子的外观和门窗(用户看到的界面)
- 业务逻辑层:房子的水电和管道(处理业务规则)
- 数据访问层:房子的地基(与数据库打交道)
📁 项目文件结构
studayDemo260112/ # 主项目文件夹
├── studayDemo260112.sln # Visual Studio解决方案文件
│
├── studayDemo260112/ # UI层(你看到的界面)
│ ├── Properties/ # 项目属性
│ ├── Forms/ # 窗体文件
│ │ ├── ModuleEmpGroup.cs # 主窗体逻辑代码
│ │ └── ModuleEmpGroup.Designer.cs # 主窗体界面设计代码
│ └── Program.cs # 程序入口
│
├── BusinessLayer/ # 业务逻辑层(处理业务规则)
│ ├── EmployeeBLL.cs # 员工业务逻辑
│ ├── DepartmentBLL.cs # 部门业务逻辑
│ ├── LoginBLL.cs # 登录业务逻辑
│ └── ExportBLL.cs # 导出业务逻辑
│
├── DataAccessLayer/ # 数据访问层(操作数据库)
│ ├── DatabaseHelper.cs # 数据库连接助手
│ ├── EmployeeDAL.cs # 员工数据访问
│ ├── DepartmentDAL.cs # 部门数据访问
│ └── LoginDAL.cs # 登录数据访问
│
├── Models/ # 实体模型层(数据模型)
│ ├── Employee.cs # 员工实体类
│ ├── Department.cs # 部门实体类
│ └── SearchCondition.cs # 搜索条件实体类
│
└── Common/ # 公共工具类
├── ExcelExporter.cs # Excel导出工具
└── PasswordHelper.cs # 密码处理工具(可选)
🔧 各层详细说明
第1层:Models(实体模型层)
这一层定义了我们程序中的"数据模型",就像定义表格的字段一样。
Employee.cs - 员工实体
csharp
using System;
namespace studayDemo260112.Models
{
// Employee类:表示一个员工的所有信息
public class Employee
{
// 员工ID,数据库中的主键
public int EmpID { get; set; }
// 员工姓名
public string EmpName { get; set; }
// 员工性别
public string EmpGender { get; set; }
// 员工年龄
public int EmpAge { get; set; }
// 部门ID(关联到Department表)
public int DepID { get; set; }
// 入职时间
public DateTime EmpHireDate { get; set; }
// 员工密码(登录用)
public string EmpPassword { get; set; }
// 所属部门名称(显示用,不是数据库字段)
public string DepName { get; set; }
// 部门负责人
public string DepManager { get; set; }
// 部门密码
public string DepPassword { get; set; }
// 部门描述
public string DepDesc { get; set; }
}
}
Department.cs - 部门实体
csharp
namespace studayDemo260112.Models
{
// Department类:表示一个部门的所有信息
public class Department
{
// 部门ID,主键
public int DepID { get; set; }
// 部门名称
public string DepName { get; set; }
// 部门负责人
public string DepManager { get; set; }
// 部门密码(负责人登录用)
public string DepPassword { get; set; }
// 部门描述
public string DepDesc { get; set; }
}
}
SearchCondition.cs - 搜索条件实体
csharp
namespace studayDemo260112.Models
{
// SearchCondition类:搜索时使用的条件
public class SearchCondition
{
// 要搜索的员工姓名(模糊搜索)
public string EmpName { get; set; }
// 要搜索的部门名称(模糊搜索)
public string DeptName { get; set; }
// 要搜索的部门负责人(模糊搜索)
public string Manager { get; set; }
}
}
第2层:DataAccessLayer(数据访问层)
这一层专门负责和数据库打交道,执行SQL语句。
DatabaseHelper.cs - 数据库连接助手
csharp
using Microsoft.Data.SqlClient;
using System.Configuration;
namespace studayDemo260112.DataAccessLayer
{
// DatabaseHelper类:提供数据库连接,所有数据库操作都通过这里连接
public class DatabaseHelper
{
// 数据库连接字符串,包含服务器地址、数据库名、用户名密码等信息
private static readonly string connectionString =
@"Data Source=USER-20250118MH; // 服务器地址
Initial Catalog=studay260112; // 数据库名
User ID=sa; // 用户名
Password=Lmy20040204; // 密码
Encrypt=True; // 加密连接
Trust Server Certificate=True"; // 信任服务器证书
// GetConnection方法:创建一个新的数据库连接
public static SqlConnection GetConnection()
{
// 创建并返回一个新的SqlConnection对象
return new SqlConnection(connectionString);
}
}
}
EmployeeDAL.cs - 员工数据访问
csharp
using Microsoft.Data.SqlClient;
using System.Data;
using studayDemo260112.Models;
using System.Collections.Generic;
namespace studayDemo260112.DataAccessLayer
{
// EmployeeDAL类:专门处理员工相关的数据库操作
public class EmployeeDAL
{
// GetAllEmployees方法:获取所有员工数据
public DataTable GetAllEmployees()
{
// SQL语句:从视图获取所有员工信息
string sql = "SELECT * FROM View_EmployeeDepartmentInfo;";
// using语句:确保数据库连接在使用后自动关闭
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
// 创建数据适配器,用于填充DataTable
SqlDataAdapter adapter = new SqlDataAdapter(sql, conn);
DataTable dt = new DataTable(); // 创建一个空的数据表
adapter.Fill(dt); // 执行SQL并填充数据
return dt; // 返回填充好的数据表
}
}
// SearchEmployees方法:根据条件搜索员工
public DataTable SearchEmployees(SearchCondition condition)
{
// 基础SQL查询语句
string baseSql = @"
SELECT e.EmpID AS 员工ID,
e.EmpName AS 员工姓名,
e.EmpGender AS 员工性别,
e.EmpAge AS 员工年龄,
e.EmpHireDate AS 入职时间,
d.DepName AS 所属部门,
e.EmpPassword AS 员工密码,
d.DepManager AS 部门负责人,
d.DepPassword AS 部门密码
FROM Employees e
INNER JOIN Department d ON e.DepID = d.DepID
WHERE 1=1"; // WHERE 1=1是为了方便拼接条件
// 存储查询条件的列表
List<string> whereConditions = new List<string>();
// 存储SQL参数的列表
List<SqlParameter> parameters = new List<SqlParameter>();
// 如果有员工姓名条件
if (!string.IsNullOrEmpty(condition.EmpName))
{
whereConditions.Add("e.EmpName LIKE @EmpName"); // 添加条件
parameters.Add(new SqlParameter("@EmpName", $"%{condition.EmpName}%")); // 添加参数
}
// 如果有部门名称条件
if (!string.IsNullOrEmpty(condition.DeptName))
{
whereConditions.Add("d.DepName LIKE @DeptName");
parameters.Add(new SqlParameter("@DeptName", $"%{condition.DeptName}%"));
}
// 如果有部门负责人条件
if (!string.IsNullOrEmpty(condition.Manager))
{
whereConditions.Add("d.DepManager LIKE @Manager");
parameters.Add(new SqlParameter("@Manager", $"%{condition.Manager}%"));
}
// 如果有条件,拼接WHERE语句
if (whereConditions.Count > 0)
{
baseSql += " AND " + string.Join(" AND ", whereConditions);
}
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
// 创建SQL命令
SqlCommand cmd = new SqlCommand(baseSql, conn);
// 添加所有参数
cmd.Parameters.AddRange(parameters.ToArray());
// 执行查询
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
// AddEmployee方法:添加新员工
public bool AddEmployee(Employee emp)
{
// INSERT SQL语句
string sql = @"INSERT INTO Employees
(EmpName, EmpGender, EmpAge, DepID, EmpHireDate, EmpPassword)
VALUES (@EmpName, @EmpGender, @EmpAge, @DepID, @EmpHireDate, @EmpPassword)";
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
SqlCommand cmd = new SqlCommand(sql, conn);
// 设置参数值
cmd.Parameters.AddWithValue("@EmpName", emp.EmpName);
cmd.Parameters.AddWithValue("@EmpGender", emp.EmpGender);
cmd.Parameters.AddWithValue("@EmpAge", emp.EmpAge);
cmd.Parameters.AddWithValue("@DepID", emp.DepID);
cmd.Parameters.AddWithValue("@EmpHireDate", emp.EmpHireDate);
cmd.Parameters.AddWithValue("@EmpPassword", emp.EmpPassword);
conn.Open(); // 打开连接
// 执行SQL,返回影响的行数是否大于0
return cmd.ExecuteNonQuery() > 0;
}
}
// UpdateEmployee方法:更新员工信息
public bool UpdateEmployee(Employee emp)
{
string sql = @"UPDATE Employees
SET EmpName = @EmpName,
EmpGender = @EmpGender,
EmpAge = @EmpAge,
DepID = @DepID,
EmpHireDate = @EmpHireDate,
EmpPassword = @EmpPassword
WHERE EmpID = @EmpID";
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@EmpName", emp.EmpName);
cmd.Parameters.AddWithValue("@EmpGender", emp.EmpGender);
cmd.Parameters.AddWithValue("@EmpAge", emp.EmpAge);
cmd.Parameters.AddWithValue("@DepID", emp.DepID);
cmd.Parameters.AddWithValue("@EmpHireDate", emp.EmpHireDate);
cmd.Parameters.AddWithValue("@EmpPassword", emp.EmpPassword);
cmd.Parameters.AddWithValue("@EmpID", emp.EmpID);
conn.Open();
return cmd.ExecuteNonQuery() > 0;
}
}
// DeleteEmployee方法:删除员工
public bool DeleteEmployee(int empId)
{
string sql = "DELETE FROM Employees WHERE EmpID = @EmpID";
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@EmpID", empId);
conn.Open();
return cmd.ExecuteNonQuery() > 0;
}
}
}
}
DepartmentDAL.cs - 部门数据访问
csharp
using Microsoft.Data.SqlClient;
using System.Data;
using studayDemo260112.Models;
namespace studayDemo260112.DataAccessLayer
{
// DepartmentDAL类:专门处理部门相关的数据库操作
public class DepartmentDAL
{
// GetAllDepartments方法:获取所有部门
public DataTable GetAllDepartments()
{
string sql = "SELECT DepID, DepName, DepManager, DepPassword, DepDesc FROM Department;";
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
SqlDataAdapter adapter = new SqlDataAdapter(sql, conn);
DataTable dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
// AddDepartment方法:添加新部门
public bool AddDepartment(Department dept)
{
string sql = @"INSERT INTO Department
(DepName, DepDesc, DepManager, DepPassword)
VALUES (@DepName, @DepDesc, @DepManager, @DepPassword)";
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@DepName", dept.DepName);
cmd.Parameters.AddWithValue("@DepDesc", dept.DepDesc);
cmd.Parameters.AddWithValue("@DepManager", dept.DepManager);
cmd.Parameters.AddWithValue("@DepPassword", dept.DepPassword);
conn.Open();
return cmd.ExecuteNonQuery() > 0;
}
}
// UpdateDepartment方法:更新部门信息
public bool UpdateDepartment(Department dept)
{
string sql = @"UPDATE Department
SET DepPassword = @DepPassword,
DepDesc = @DepDesc,
DepManager = @DepManager
WHERE DepID = @DepID";
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@DepPassword", dept.DepPassword);
cmd.Parameters.AddWithValue("@DepDesc", dept.DepDesc);
cmd.Parameters.AddWithValue("@DepManager", dept.DepManager);
cmd.Parameters.AddWithValue("@DepID", dept.DepID);
conn.Open();
return cmd.ExecuteNonQuery() > 0;
}
}
// DeleteDepartment方法:删除部门
public bool DeleteDepartment(int deptId, int defaultDeptId)
{
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
conn.Open();
// 使用事务:要么全部成功,要么全部失败
using (SqlTransaction transaction = conn.BeginTransaction())
{
try
{
// 第一步:将该部门下的员工转移到默认部门
string updateEmpSql = "UPDATE Employees SET DepID = @DefaultDepID WHERE DepID = @OldDepID";
SqlCommand empCmd = new SqlCommand(updateEmpSql, conn, transaction);
empCmd.Parameters.AddWithValue("@DefaultDepID", defaultDeptId);
empCmd.Parameters.AddWithValue("@OldDepID", deptId);
empCmd.ExecuteNonQuery();
// 第二步:删除部门
string deleteDeptSql = "DELETE FROM Department WHERE DepID = @DepID";
SqlCommand deptCmd = new SqlCommand(deleteDeptSql, conn, transaction);
deptCmd.Parameters.AddWithValue("@DepID", deptId);
int result = deptCmd.ExecuteNonQuery();
transaction.Commit(); // 提交事务
return result > 0;
}
catch
{
transaction.Rollback(); // 回滚事务
throw; // 重新抛出异常
}
}
}
}
}
}
LoginDAL.cs - 登录数据访问
csharp
using Microsoft.Data.SqlClient;
namespace studayDemo260112.DataAccessLayer
{
// LoginDAL类:专门处理登录相关的数据库操作
public class LoginDAL
{
// ValidateEmployeeLogin方法:验证员工登录
public bool ValidateEmployeeLogin(string account, string password)
{
// 查询是否有匹配的员工账户
string sql = "SELECT COUNT(1) FROM Employees WHERE EmpName = @Account AND EmpPassword = @Password";
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@Account", account);
cmd.Parameters.AddWithValue("@Password", password);
conn.Open();
// ExecuteScalar返回第一行第一列的值(COUNT的结果)
return (int)cmd.ExecuteScalar() > 0;
}
}
// ValidateDepartmentLogin方法:验证部门负责人登录
public bool ValidateDepartmentLogin(string account, string password)
{
// 查询是否有匹配的部门负责人账户
string sql = "SELECT COUNT(1) FROM Department WHERE DepManager = @Account AND DepPassword = @Password";
using (SqlConnection conn = DatabaseHelper.GetConnection())
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@Account", account);
cmd.Parameters.AddWithValue("@Password", password);
conn.Open();
return (int)cmd.ExecuteScalar() > 0;
}
}
}
}
第3层:BusinessLayer(业务逻辑层)
这一层处理业务规则,比如数据验证、业务逻辑处理等。
EmployeeBLL.cs - 员工业务逻辑
csharp
using studayDemo260112.DataAccessLayer;
using studayDemo260112.Models;
using System.Data;
using System.Linq; // 需要添加using System.Linq
namespace studayDemo260112.BusinessLayer
{
// EmployeeBLL类:处理员工的业务逻辑
public class EmployeeBLL
{
// 创建数据访问层对象
private EmployeeDAL _employeeDAL = new EmployeeDAL();
private DepartmentDAL _departmentDAL = new DepartmentDAL();
// GetAllEmployees方法:获取所有员工(调用DAL层)
public DataTable GetAllEmployees()
{
return _employeeDAL.GetAllEmployees();
}
// SearchEmployees方法:搜索员工(调用DAL层)
public DataTable SearchEmployees(SearchCondition condition)
{
return _employeeDAL.SearchEmployees(condition);
}
// AddEmployee方法:添加员工(包含业务验证)
public bool AddEmployee(Employee emp)
{
// 业务规则验证1:员工姓名不能为空
if (string.IsNullOrWhiteSpace(emp.EmpName))
throw new System.Exception("员工姓名不能为空");
// 业务规则验证2:员工年龄必须在18-65岁之间
if (emp.EmpAge < 18 || emp.EmpAge > 65)
throw new System.Exception("员工年龄必须在18-65岁之间");
// 验证通过,调用DAL层添加员工
return _employeeDAL.AddEmployee(emp);
}
// UpdateEmployee方法:更新员工信息
public bool UpdateEmployee(Employee emp)
{
// 业务规则验证:员工姓名不能为空
if (string.IsNullOrWhiteSpace(emp.EmpName))
throw new System.Exception("员工姓名不能为空");
return _employeeDAL.UpdateEmployee(emp);
}
// DeleteEmployee方法:删除员工
public bool DeleteEmployee(int empId)
{
return _employeeDAL.DeleteEmployee(empId);
}
// GetAllDepartments方法:获取所有部门
public DataTable GetAllDepartments()
{
return _departmentDAL.GetAllDepartments();
}
}
}
DepartmentBLL.cs - 部门业务逻辑
csharp
using studayDemo260112.DataAccessLayer;
using studayDemo260112.Models;
using System.Linq; // 需要添加using System.Linq
namespace studayDemo260112.BusinessLayer
{
// DepartmentBLL类:处理部门的业务逻辑
public class DepartmentBLL
{
// 创建数据访问层对象
private DepartmentDAL _departmentDAL = new DepartmentDAL();
// AddDepartment方法:添加部门
public bool AddDepartment(Department dept)
{
// 业务规则验证1:部门名称不能为空
if (string.IsNullOrWhiteSpace(dept.DepName))
throw new System.Exception("部门名称不能为空");
// 业务规则验证2:部门负责人不能为空
if (string.IsNullOrWhiteSpace(dept.DepManager))
throw new System.Exception("部门负责人不能为空");
return _departmentDAL.AddDepartment(dept);
}
// UpdateDepartment方法:更新部门
public bool UpdateDepartment(Department dept)
{
return _departmentDAL.UpdateDepartment(dept);
}
// DeleteDepartment方法:删除部门
public bool DeleteDepartment(int deptId, int defaultDeptId = 1)
{
// 业务规则:不能删除默认部门(ID为1的部门)
if (deptId == defaultDeptId)
throw new System.Exception("不能删除默认部门");
return _departmentDAL.DeleteDepartment(deptId, defaultDeptId);
}
// GetManagers方法:获取所有部门负责人(去重)
public string[] GetManagers()
{
var dt = _departmentDAL.GetAllDepartments();
// 使用LINQ查询所有负责人,并去重
var managers = dt.AsEnumerable()
.Select(row => row["DepManager"].ToString())
.Distinct() // 去重
.ToArray(); // 转换为数组
return managers;
}
}
}
LoginBLL.cs - 登录业务逻辑
csharp
using studayDemo260112.DataAccessLayer;
namespace studayDemo260112.BusinessLayer
{
// LoginBLL类:处理登录的业务逻辑
public class LoginBLL
{
// 创建数据访问层对象
private LoginDAL _loginDAL = new LoginDAL();
// ValidateLogin方法:验证登录
public bool ValidateLogin(string account, string password)
{
// 业务规则验证1:账户不能为空
if (string.IsNullOrEmpty(account))
throw new System.Exception("请输入账户");
// 业务规则验证2:密码不能为空
if (string.IsNullOrEmpty(password))
throw new System.Exception("请输入密码");
// 先验证员工登录
if (_loginDAL.ValidateEmployeeLogin(account, password))
return true;
// 再验证部门负责人登录
if (_loginDAL.ValidateDepartmentLogin(account, password))
return true;
// 两者都不匹配,返回false
return false;
}
}
}
ExportBLL.cs - 导出业务逻辑
csharp
using OfficeOpenXml;
using OfficeOpenXml.Style;
using System.Data;
using System.IO;
namespace studayDemo260112.BusinessLayer
{
// ExportBLL类:处理Excel导出的业务逻辑
public class ExportBLL
{
// ExportToExcel方法:将DataTable导出为Excel文件
public void ExportToExcel(DataTable data, string filePath)
{
// 使用EPPlus库创建Excel文件
using (ExcelPackage excelPackage = new ExcelPackage())
{
// 创建一个工作表,命名为"员工数据"
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add("员工数据");
// 第一步:写入表头
for (int col = 0; col < data.Columns.Count; col++)
{
// 设置表头单元格的值(第1行,第col+1列)
worksheet.Cells[1, col + 1].Value = data.Columns[col].ColumnName;
// 设置表头样式:加粗
worksheet.Cells[1, col + 1].Style.Font.Bold = true;
// 设置表头样式:居中
worksheet.Cells[1, col + 1].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
}
// 第二步:写入数据行
for (int row = 0; row < data.Rows.Count; row++)
{
for (int col = 0; col < data.Columns.Count; col++)
{
// 设置数据单元格的值(第row+2行,第col+1列)
worksheet.Cells[row + 2, col + 1].Value = data.Rows[row][col];
}
}
// 第三步:自动调整列宽,让内容显示完整
worksheet.Cells.AutoFitColumns();
// 第四步:保存Excel文件
excelPackage.SaveAs(new FileInfo(filePath));
}
}
}
}
第4层:Common(公共工具层)
这一层提供通用的工具方法,可以被各层调用。
ExcelExporter.cs - Excel导出工具
csharp
using System.Windows.Forms;
namespace studayDemo260112.Common
{
// ExcelExporter类:提供Excel导出的相关工具方法
public static class ExcelExporter
{
// GetSaveFilePath方法:打开"另存为"对话框,获取用户选择的保存路径
public static string GetSaveFilePath()
{
// 创建"另存为"对话框
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
// 设置文件过滤器:只能选择.xlsx文件
saveFileDialog.Filter = "Excel文件 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*";
// 设置对话框标题
saveFileDialog.Title = "导出员工数据到Excel";
// 设置默认文件名:员工数据_当前时间.xlsx
saveFileDialog.FileName = $"员工数据_{System.DateTime.Now:yyyyMMddHHmmss}.xlsx";
// 记住上次打开的目录
saveFileDialog.RestoreDirectory = true;
// 显示对话框,如果用户点击了"保存"
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
// 返回用户选择的文件路径
return saveFileDialog.FileName;
}
// 用户点击了"取消",返回null
return null;
}
}
// OpenFileLocation方法:打开文件所在文件夹,并选中文件
public static void OpenFileLocation(string filePath)
{
// 使用Windows资源管理器打开文件所在文件夹,并选中文件
System.Diagnostics.Process.Start("explorer.exe", $"/select, \"{filePath}\"");
}
}
}
第5层:UI层(用户界面层)
这一层就是用户看到的界面,现在代码大大简化了。
Forms/ModuleEmpGroup.cs(简化版)
csharp
using studayDemo260112.BusinessLayer;
using studayDemo260112.Common;
using studayDemo260112.Models;
using System;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace studayDemo260112.Forms
{
public partial class ModuleEmpGroup : Form
{
// 业务逻辑对象:通过这些对象调用业务逻辑
private EmployeeBLL _employeeBLL = new EmployeeBLL();
private DepartmentBLL _departmentBLL = new DepartmentBLL();
private LoginBLL _loginBLL = new LoginBLL();
private ExportBLL _exportBLL = new ExportBLL();
// 数据表:存储从数据库获取的数据
private DataTable _employeesTable; // 员工数据表
private DataTable _departmentTable; // 部门数据表
// 其他成员变量
private DataGridViewRow _selectedRow; // 当前选中的行
private bool _isLoggedIn = false; // 是否已登录
private readonly List<string> _genderList = new List<string> { "男", "女" }; // 性别列表
private readonly List<int> _ageList = new List<int>(); // 年龄列表
public ModuleEmpGroup()
{
InitializeComponent(); // 初始化界面控件
InitAgeList(); // 初始化年龄列表(18-65)
InitDataGridView(); // 初始化表格
InitContextMenu(); // 初始化右键菜单
InitGroupXxLmyControls(); // 初始化员工信息显示区域
HideAllData(); // 登录前隐藏数据
TxtInUserLmy.Text = "admin"; // 设置默认用户名
LoadRememberedPassword(); // 加载记住的密码
}
#region 核心方法重构
// 加载所有数据(异步)
private void LoadAllDataAsync()
{
// 在新线程中加载数据,避免界面卡顿
System.Threading.Tasks.Task.Run(() =>
{
LoadDepartmentData(); // 加载部门数据
LoadEmployeeData(); // 加载员工数据
}).ContinueWith(task =>
{
// 数据加载完成后,在主线程中更新界面
this.Invoke(new Action(() =>
{
if (EmpDataView.Rows.Count > 0)
{
EmpDataView.Rows[0].Selected = true; // 选中第一行
OnRowSelectedShowText(null, EventArgs.Empty); // 显示选中行的详细信息
}
InitSearchComboBoxes(); // 初始化搜索下拉框
}));
});
}
// 加载部门数据
private void LoadDepartmentData()
{
try
{
// 调用业务逻辑层获取部门数据
_departmentTable = _employeeBLL.GetAllDepartments();
}
catch (Exception ex)
{
// 在主线程中显示错误信息
this.Invoke(new Action(() =>
MessageBox.Show($"加载部门数据失败:{ex.Message}", "错误")));
}
}
// 加载员工数据
private void LoadEmployeeData()
{
try
{
// 调用业务逻辑层获取员工数据
_employeesTable = _employeeBLL.GetAllEmployees();
// 在主线程中更新表格
this.Invoke(new Action(() =>
{
// 设置表格数据源
EmpDataView.DataSource = _employeesTable;
// 刷新表格布局
EmpDataView.PerformLayout();
EmpDataView.RefreshEdit();
// 清空选中状态
EmpDataView.ClearSelection();
EmpDataView.CurrentCell = null;
}));
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
MessageBox.Show($"加载员工数据失败:{ex.Message}", "错误");
EmpDataView.ClearSelection();
EmpDataView.CurrentCell = null;
}));
}
}
// 搜索按钮点击事件
private void BtnSearchLmy_Click(object sender, EventArgs e)
{
// 检查是否已登录
if (!_isLoggedIn)
{
MessageBox.Show("请先登录后再操作!", "提示");
return;
}
try
{
// 创建搜索条件对象
SearchCondition condition = new SearchCondition
{
EmpName = TxtLELmy.Text.Trim(), // 获取员工姓名条件
DeptName = TxtLDLmy.Text.Trim(), // 获取部门名称条件
Manager = TxtLBossLmy.Text.Trim() // 获取负责人条件
};
// 调用业务逻辑层搜索员工
_employeesTable = _employeeBLL.SearchEmployees(condition);
// 更新表格数据
EmpDataView.DataSource = _employeesTable;
// 如果有搜索结果
if (EmpDataView.Rows.Count > 0)
{
EmpDataView.Rows[0].Selected = true;
OnRowSelectedShowText(null, EventArgs.Empty);
}
else
{
MessageBox.Show("未找到匹配的数据!", "查询结果");
ClearGroupBoxText(); // 清空信息显示区域
}
ClearSearchComboBoxes(); // 清空搜索框
}
catch (Exception ex)
{
MessageBox.Show($"查询失败:{ex.Message}", "错误");
ClearSearchComboBoxes();
RefreshData(); // 刷新数据
}
}
// 导出按钮点击事件
private void BtnExportLmy_Click(object sender, EventArgs e)
{
// 检查是否有数据可导出
if (EmpDataView.Rows.Count == 0)
{
MessageBox.Show("暂无可导出的员工数据!", "提示");
return;
}
// 打开"另存为"对话框,获取保存路径
string filePath = ExcelExporter.GetSaveFilePath();
if (!string.IsNullOrEmpty(filePath))
{
try
{
// 调用业务逻辑层导出数据
_exportBLL.ExportToExcel(_employeesTable, filePath);
// 询问用户是否打开文件所在文件夹
DialogResult result = MessageBox.Show(
$"员工数据导出成功!\n文件路径:{filePath}\n是否打开文件所在文件夹?",
"导出成功",
MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
ExcelExporter.OpenFileLocation(filePath);
}
}
catch (Exception ex)
{
MessageBox.Show($"导出失败:{ex.Message}", "错误");
}
}
}
// 登录按钮点击事件
private void BtnLogin_Click(object sender, EventArgs e)
{
// 获取用户名和密码
string account = TxtInUserLmy.Text.Trim();
string password = TxtInPasswordLmy.Text.Trim();
try
{
// 调用业务逻辑层验证登录
if (_loginBLL.ValidateLogin(account, password))
{
LoginSuccess(); // 登录成功
}
else
{
MessageBox.Show("账户或密码错误!", "登录失败");
TxtInPasswordLmy.Text = ""; // 清空密码框
TxtInPasswordLmy.Focus(); // 焦点回到密码框
}
}
catch (Exception ex)
{
MessageBox.Show($"登录失败:{ex.Message}", "错误");
}
}
// 登录成功后的处理
private void LoginSuccess()
{
MessageBox.Show("登录成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
SaveRememberedPassword(); // 保存记住的密码
TxtInUserLmy.Text = ""; // 清空用户名框
TxtInPasswordLmy.Text = ""; // 清空密码框
ShowAllData(); // 显示所有数据
}
#endregion
// 其他原有方法保持不变...
// 如:InitAgeList、InitDataGridView、OnRowSelectedShowText等方法
}
}
🚀 如何运行项目
步骤1:创建项目结构
- 打开Visual Studio
- 新建一个Windows Forms应用项目
- 在解决方案资源管理器中,右键点击解决方案 → 添加 → 新建项目
- 选择"类库(.NET Framework)",分别创建:
- BusinessLayer
- DataAccessLayer
- Models
- Common
步骤2:添加引用
-
右键点击UI层项目 → 添加 → 引用
-
添加对以下项目的引用:
- BusinessLayer
- Models
- Common
-
右键点击BusinessLayer项目 → 添加 → 引用
- DataAccessLayer
- Models
-
所有项目都需要添加NuGet包:
- EPPlus (用于Excel导出)
- Microsoft.Data.SqlClient (用于数据库访问)
步骤3:复制代码
- 按照上面的代码,在对应的项目中创建对应的文件
- 将代码复制到相应的文件中
- 修改命名空间(如果需要)
步骤4:运行测试
- 按F5运行项目
- 测试所有功能是否正常
📊 三层架构流程图
调用业务方法
验证业务规则
通过
不通过
执行SQL
返回数据
返回数据
返回结果
提供数据模型
提供数据模型
提供工具方法
提供工具方法
用户界面 UI层
业务逻辑层 BLL
业务规则验证
数据访问层 DAL
返回错误信息
数据库
Models层
Common层
💡 常见问题解答
Q1:为什么要用三层架构?
A:就像盖房子,如果所有东西都混在一起,修水管就要拆墙。三层架构让代码更清晰,维护更容易。
Q2:每层之间的调用关系是什么?
A:UI层 → BLL层 → DAL层 → 数据库。不能越级调用,比如UI层不能直接调用DAL层。
Q3:如果我想添加新功能怎么办?
A:比如要添加一个"请假管理"功能:
- Models层:创建Leave.cs实体类
- DAL层:创建LeaveDAL.cs
- BLL层:创建LeaveBLL.cs
- UI层:在窗体中添加请假管理的界面
Q4:数据库连接字符串在哪里修改?
A:在DataAccessLayer/DatabaseHelper.cs文件的connectionString变量中修改。
Q5:如何调试三层架构?
A:可以逐层调试:
- 先测试DAL层:确保SQL语句正确
- 再测试BLL层:确保业务逻辑正确
- 最后测试UI层:确保界面交互正确
🔍 注意事项
- 命名空间:确保每个文件的命名空间正确
- 引用关系:确保项目引用关系正确
- 异常处理:每层都要有适当的异常处理
- 数据验证:业务规则在BLL层验证
- 连接关闭:使用using语句确保数据库连接关闭
📝 总结
通过三层架构重构,我们的代码:
- ✅ 更清晰:每层职责明确
- ✅ 更易维护:修改数据库不影响界面
- ✅ 更安全:业务规则集中管理
- ✅ 更易测试:各层可以单独测试
- ✅ 更易扩展:添加新功能更方便
现在你可以像搭积木一样开发程序了!🎉