三层架构重构项目文档

三层架构重构项目文档

📖 项目概述

本项目是一个员工部门管理系统,使用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:创建项目结构

  1. 打开Visual Studio
  2. 新建一个Windows Forms应用项目
  3. 在解决方案资源管理器中,右键点击解决方案 → 添加 → 新建项目
  4. 选择"类库(.NET Framework)",分别创建:
    • BusinessLayer
    • DataAccessLayer
    • Models
    • Common

步骤2:添加引用

  1. 右键点击UI层项目 → 添加 → 引用

  2. 添加对以下项目的引用:

    • BusinessLayer
    • Models
    • Common
  3. 右键点击BusinessLayer项目 → 添加 → 引用

    • DataAccessLayer
    • Models
  4. 所有项目都需要添加NuGet包:

    • EPPlus (用于Excel导出)
    • Microsoft.Data.SqlClient (用于数据库访问)

步骤3:复制代码

  1. 按照上面的代码,在对应的项目中创建对应的文件
  2. 将代码复制到相应的文件中
  3. 修改命名空间(如果需要)

步骤4:运行测试

  1. 按F5运行项目
  2. 测试所有功能是否正常

📊 三层架构流程图

调用业务方法
验证业务规则
通过
不通过
执行SQL
返回数据
返回数据
返回结果
提供数据模型
提供数据模型
提供工具方法
提供工具方法
用户界面 UI层
业务逻辑层 BLL
业务规则验证
数据访问层 DAL
返回错误信息
数据库
Models层
Common层

💡 常见问题解答

Q1:为什么要用三层架构?

A:就像盖房子,如果所有东西都混在一起,修水管就要拆墙。三层架构让代码更清晰,维护更容易。

Q2:每层之间的调用关系是什么?

A:UI层 → BLL层 → DAL层 → 数据库。不能越级调用,比如UI层不能直接调用DAL层。

Q3:如果我想添加新功能怎么办?

A:比如要添加一个"请假管理"功能:

  1. Models层:创建Leave.cs实体类
  2. DAL层:创建LeaveDAL.cs
  3. BLL层:创建LeaveBLL.cs
  4. UI层:在窗体中添加请假管理的界面

Q4:数据库连接字符串在哪里修改?

A:在DataAccessLayer/DatabaseHelper.cs文件的connectionString变量中修改。

Q5:如何调试三层架构?

A:可以逐层调试:

  1. 先测试DAL层:确保SQL语句正确
  2. 再测试BLL层:确保业务逻辑正确
  3. 最后测试UI层:确保界面交互正确

🔍 注意事项

  1. 命名空间:确保每个文件的命名空间正确
  2. 引用关系:确保项目引用关系正确
  3. 异常处理:每层都要有适当的异常处理
  4. 数据验证:业务规则在BLL层验证
  5. 连接关闭:使用using语句确保数据库连接关闭

📝 总结

通过三层架构重构,我们的代码:

  • ✅ 更清晰:每层职责明确
  • ✅ 更易维护:修改数据库不影响界面
  • ✅ 更安全:业务规则集中管理
  • ✅ 更易测试:各层可以单独测试
  • ✅ 更易扩展:添加新功能更方便

现在你可以像搭积木一样开发程序了!🎉

相关推荐
诗词在线4 小时前
从算法重构到场景复用:古诗词数字化的技术破局与落地实践
python·算法·重构
代码游侠4 小时前
学习笔笔记——ARM 嵌入式系统与内核架构
arm开发·笔记·嵌入式硬件·学习·架构
VekiSon5 小时前
ARM架构——从嵌入式系统底层到指令执行解析
linux·arm开发·架构
国科安芯5 小时前
AS32X601的I2C模块操作EEPROM详解
stm32·单片机·嵌入式硬件·架构·安全威胁分析·安全性测试
迪捷软件5 小时前
以车身域控制器为起点,重构整车软件测试体系
重构
安卓理事人6 小时前
鸿蒙的“官方推荐”架构MVVM
华为·架构·harmonyos
哈__6 小时前
金仓数据库多模融合架构:重塑文档数据库技术范式与国产化实践
数据库·架构
清水白石0086 小时前
重构有序字典:手写一个线程安全且更快的 OrderedDict
python·安全·spring·重构
飞Link6 小时前
预训练阶段中的模型自我提升、通用模型蒸馏和数据增强中的数据重构和非LLM驱动的数据增强
算法·重构·数据挖掘