最近在帮朋友公司搭建一个简单的内部员工管理系统,从需求分析到上线部署走了一遍完整流程。这类系统在企业日常运营中非常实用,但网上很多教程要么过于简单只贴代码,要么过于复杂引入了不必要的框架。本文将分享一套基于 PHP + MySQL 的轻量级员工管理系统实战开发教程,从环境搭建、数据库设计、前后端编码到安全优化,手把手带你构建一个功能完整、代码清晰、可直接用于课程设计或小型团队管理的系统。无论你是 PHP 初学者想练手,还是需要快速搭建一个内部管理工具,都能从本文中找到可复用的代码和思路。

1. 系统概述与核心功能设计

在开始编码之前,明确系统要做什么至关重要。一个基础的员工管理系统,核心是围绕“人”的信息进行增删改查(CRUD)管理,并区分不同角色的操作权限。

1.1 系统角色与功能模块

根据常见的业务场景,我们将系统用户分为两类角色:

  1. 管理员 :拥有系统的最高权限,负责管理整个系统的数据和用户。
  2. 普通员工 :只能查看和修改自己的相关信息。

对应的功能模块拆分如下:

管理员端功能:

  • 员工信息管理 :添加新员工、查看所有员工列表、编辑员工信息、删除离职员工。
  • 部门管理 :创建、编辑、删除部门,并将员工分配至相应部门。
  • 薪资信息管理 :录入、修改、查看员工的薪资记录。
  • 系统用户管理 :管理所有员工的登录账号和权限。

员工端功能:

  • 个人信息查看与编辑 :查看个人资料,并可修改部分非核心信息(如联系电话、住址)。
  • 个人薪资查询 :查看自己的历史薪资单。

1.2 技术栈选型与优势

  • 后端语言:PHP :语法简单易学,内置函数丰富,非常适合快速开发Web应用,且有庞大的社区支持。
  • 数据库:MySQL :开源、流行、与PHP搭配成熟(常被称为“黄金组合”),足以应对中小型系统的数据存储需求。
  • 前端技术:HTML, CSS, JavaScript (少量) :用于构建用户界面。为了保持简洁,我们主要使用原生技术,并引入 Bootstrap 5 框架来快速美化界面。
  • 服务器:Apache / Nginx :本文演示环境使用 Apache。

选择这套技术栈的原因在于其学习曲线平缓、环境搭建简单,能让开发者更专注于业务逻辑的实现,而非复杂的配置。

2. 开发环境准备

工欲善其事,必先利其器。一个统一的开发环境能避免很多后续的兼容性问题。

2.1 集成环境安装

对于新手,强烈推荐使用集成环境包,它一次性安装了PHP、MySQL、Web服务器和必要的工具。这里推荐两款:

  1. XAMPP :适用于 Windows, macOS, Linux。包含 Apache, MySQL, PHP 和 Perl。
  2. phpStudy :国内开发者常用的集成环境,对中文支持友好,切换PHP版本方便。

以 XAMPP 为例的安装步骤:

  1. 访问 Apache Friends 官网下载对应操作系统的 XAMPP 安装包。
  2. 运行安装程序,建议安装路径不要包含中文和空格(如 C:\xampp )。
  3. 安装完成后,启动 XAMPP 控制面板,点击 Start 按钮启动 Apache MySQL 服务。当旁边的状态灯变为绿色,表示服务已成功运行。

2.2 检查环境与创建项目目录

  1. 测试PHP环境 :在浏览器中访问 http://localhost ,如果看到 XAMPP 的欢迎页面,说明 Apache 运行正常。在 XAMPP 的安装目录下,找到 htdocs 文件夹(这是网站的根目录)。
  2. 创建项目文件夹 :在 htdocs 文件夹内,新建一个文件夹,命名为 employee_management 。我们所有的项目文件都将放在这里。
  3. 测试MySQL :在浏览器中访问 http://localhost/phpmyadmin ,使用用户名 root (XAMPP 默认密码为空)登录。能成功进入 phpMyAdmin 管理界面,说明 MySQL 服务正常。

2.3 代码编辑器选择

你可以使用任何熟悉的文本编辑器,如 VS Code、Sublime Text、PHPStorm 等。VS Code 配合 PHP 插件能提供很好的代码高亮和提示。

3. 数据库设计与实现

数据库是系统的基石,良好的设计是项目成功的关键。

3.1 数据库概念设计 (ER图)

我们至少需要四张核心表:

  1. departments :部门表
  2. employees :员工基本信息表
  3. salaries :薪资记录表
  4. users :系统用户表(用于登录认证)

表之间的关系:一个部门有多个员工(一对多),一个员工有多条薪资记录(一对多),一个员工对应一个系统用户(一对一)。

3.2 数据表结构SQL

打开 phpMyAdmin ( http://localhost/phpmyadmin ),新建一个数据库,命名为 company_db ,排序规则选择 utf8mb4_general_ci 。然后执行以下 SQL 语句创建表。

-- 创建部门表
CREATE TABLE `departments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL COMMENT '部门名称',
  `description` text COMMENT '部门描述',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门信息表';

-- 创建员工基本信息表
CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `employee_id` varchar(20) NOT NULL COMMENT '员工工号',
  `name` varchar(50) NOT NULL COMMENT '姓名',
  `gender` enum('男','女','其他') DEFAULT NULL COMMENT '性别',
  `email` varchar(100) NOT NULL COMMENT '邮箱',
  `phone` varchar(20) DEFAULT NULL COMMENT '电话',
  `address` text COMMENT '住址',
  `department_id` int(11) DEFAULT NULL COMMENT '所属部门ID',
  `position` varchar(100) DEFAULT NULL COMMENT '职位',
  `hire_date` date NOT NULL COMMENT '入职日期',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `employee_id` (`employee_id`),
  UNIQUE KEY `email` (`email`),
  KEY `department_id` (`department_id`),
  CONSTRAINT `employees_ibfk_1` FOREIGN KEY (`department_id`) REFERENCES `departments` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工基本信息表';

-- 创建系统用户表(与登录相关)
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `employee_id` varchar(20) NOT NULL COMMENT '关联的员工工号',
  `username` varchar(50) NOT NULL COMMENT '登录用户名',
  `password_hash` varchar(255) NOT NULL COMMENT '加密后的密码',
  `role` enum('admin','employee') NOT NULL DEFAULT 'employee' COMMENT '角色:admin-管理员, employee-普通员工',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  UNIQUE KEY `employee_id` (`employee_id`),
  CONSTRAINT `users_ibfk_1` FOREIGN KEY (`employee_id`) REFERENCES `employees` (`employee_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';

-- 创建薪资记录表
CREATE TABLE `salaries` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `employee_id` varchar(20) NOT NULL COMMENT '员工工号',
  `base_salary` decimal(10,2) NOT NULL COMMENT '基本工资',
  `bonus` decimal(10,2) DEFAULT '0.00' COMMENT '奖金',
  `deductions` decimal(10,2) DEFAULT '0.00' COMMENT '扣款',
  `payment_date` date NOT NULL COMMENT '发薪月份(通常为月初)',
  `notes` text COMMENT '备注',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `employee_id` (`employee_id`),
  CONSTRAINT `salaries_ibfk_1` FOREIGN KEY (`employee_id`) REFERENCES `employees` (`employee_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工薪资记录表';

关键字段说明与设计思路:

  • 使用 employee_id 作为关联键 employees 表的主键是自增 id ,但我们同时创建了业务上唯一的 employee_id (工号)。 users salaries 表通过 employee_id employees 关联,这样即使员工内部ID变化,业务关联也不会断裂。
  • 密码存储 users 表中的 password_hash 字段用于存储 加密后的密码 ,绝对禁止明文存储。我们将使用 PHP 的 password_hash() 函数。
  • 外键约束 :使用了 FOREIGN KEY ON DELETE CASCADE/SET NULL 来保证数据完整性。例如,删除一个员工,其对应的登录用户和薪资记录也会自动删除。
  • 字符集 :使用 utf8mb4 以支持存储 Emoji 等所有 Unicode 字符。

4. 项目结构搭建与公共组件

清晰的目录结构能让代码更易维护。

4.1 项目目录结构

htdocs/employee_management 文件夹内,创建如下目录和文件:

employee_management/
├── assets/          # 静态资源
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── script.js
├── config/          # 配置文件
│   └── database.php
├── includes/        # 公共包含文件
│   ├── header.php
│   ├── footer.php
│   ├── auth.php     # 认证相关函数
│   └── functions.php # 通用函数
├── admin/           # 管理员后台页面
│   ├── index.php
│   ├── employees.php
│   ├── departments.php
│   └── salaries.php
├── employee/        # 员工前端页面
│   ├── index.php
│   ├── profile.php
│   └── my_salary.php
├── index.php        # 网站首页/登录页
├── login.php        # 登录处理
├── logout.php       # 退出登录
└── dashboard.php    # 登录后根据角色跳转的仪表板

4.2 数据库连接配置

创建 config/database.php ,这是整个系统连接数据库的入口。

<?php
// config/database.php
session_start(); // 在需要会话的页面包含此文件

define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', ''); // XAMPP默认密码为空
define('DB_NAME', 'company_db');

// 创建数据库连接
$link = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);

// 检查连接
if($link === false){
    die("错误:无法连接到数据库。 " . mysqli_connect_error());
}
// 设置字符集为 utf8mb4
mysqli_set_charset($link, "utf8mb4");
?>

安全提醒 :在生产环境中,务必使用强密码,并将数据库配置信息放在 Web 根目录之外,通过 include 引入。

4.3 页面公共头部与尾部

创建 includes/header.php includes/footer.php 以实现页面布局统一。

<!-- includes/header.php -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>员工管理系统 - <?php echo $page_title ?? '主页'; ?></title>
    <!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- 自定义CSS -->
    <link href="/employee_management/assets/css/style.css" rel="stylesheet">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <div class="container-fluid">
            <a class="navbar-brand" href="/employee_management/index.php">员工管理系统</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav ms-auto">
                    <?php if(isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true): ?>
                        <li class="nav-item">
                            <span class="nav-link">欢迎,<?php echo htmlspecialchars($_SESSION['username']); ?> (<?php echo $_SESSION['role']; ?>)</span>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="/employee_management/dashboard.php">控制台</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="/employee_management/logout.php">退出</a>
                        </li>
                    <?php else: ?>
                        <li class="nav-item">
                            <a class="nav-link" href="/employee_management/index.php">登录</a>
                        </li>
                    <?php endif; ?>
                </ul>
            </div>
        </div>
    </nav>
    <div class="container mt-4">
<!-- includes/footer.php -->
    </div> <!-- 关闭 container div -->
    <!-- Bootstrap 5 JS Bundle -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <!-- 自定义JS -->
    <script src="/employee_management/assets/js/script.js"></script>
</body>
</html>

4.4 用户认证与权限检查

创建 includes/auth.php ,包含登录状态检查和权限验证函数。

<?php
// includes/auth.php
function isLoggedIn() {
    return isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true;
}

function requireLogin() {
    if (!isLoggedIn()) {
        header("location: /employee_management/index.php");
        exit;
    }
}

function requireAdmin() {
    requireLogin();
    if ($_SESSION['role'] !== 'admin') {
        // 非管理员,重定向到员工仪表板或显示错误
        header("location: /employee_management/employee/index.php");
        exit;
    }
}

function requireEmployee() {
    requireLogin();
    // 此函数确保用户已登录,角色可以是 admin 或 employee
}
?>

5. 核心功能模块实现

接下来,我们实现系统的几个核心页面。

5.1 用户登录与会话管理

登录页面 ( index.php ):

<?php
// index.php
$page_title = "用户登录";
include('includes/header.php');

// 如果用户已登录,直接跳转到仪表板
if(isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true){
    header("location: dashboard.php");
    exit;
}
?>
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card">
            <div class="card-header">
                <h4 class="mb-0">用户登录</h4>
            </div>
            <div class="card-body">
                <?php
                // 显示错误信息(来自 login.php)
                if(isset($_SESSION['error'])){
                    echo '<div class="alert alert-danger">' . $_SESSION['error'] . '</div>';
                    unset($_SESSION['error']);
                }
                ?>
                <form action="login.php" method="post">
                    <div class="mb-3">
                        <label for="username" class="form-label">用户名</label>
                        <input type="text" class="form-control" id="username" name="username" required>
                    </div>
                    <div class="mb-3">
                        <label for="password" class="form-label">密码</label>
                        <input type="password" class="form-control" id="password" name="password" required>
                    </div>
                    <button type="submit" class="btn btn-primary w-100">登录</button>
                </form>
                <p class="mt-3 text-center text-muted">默认管理员账号:admin / 密码:123456</p>
            </div>
        </div>
    </div>
</div>
<?php include('includes/footer.php'); ?>

登录处理 ( login.php ):

<?php
// login.php
session_start();
require_once('config/database.php');

if($_SERVER["REQUEST_METHOD"] == "POST"){
    $username = trim($_POST['username']);
    $password = $_POST['password'];

    // 参数校验
    if(empty($username) || empty($password)){
        $_SESSION['error'] = "用户名和密码不能为空。";
        header("location: index.php");
        exit;
    }

    // 准备SQL,防止SQL注入
    $sql = "SELECT u.id, u.username, u.password_hash, u.role, u.employee_id, e.name as employee_name FROM users u LEFT JOIN employees e ON u.employee_id = e.employee_id WHERE u.username = ?";
    if($stmt = mysqli_prepare($link, $sql)){
        mysqli_stmt_bind_param($stmt, "s", $param_username);
        $param_username = $username;
        if(mysqli_stmt_execute($stmt)){
            mysqli_stmt_store_result($stmt);
            if(mysqli_stmt_num_rows($stmt) == 1){
                mysqli_stmt_bind_result($stmt, $id, $username, $hashed_password, $role, $employee_id, $employee_name);
                if(mysqli_stmt_fetch($stmt)){
                    // 验证密码
                    if(password_verify($password, $hashed_password)){
                        // 密码正确,创建会话
                        $_SESSION['loggedin'] = true;
                        $_SESSION['id'] = $id;
                        $_SESSION['username'] = $username;
                        $_SESSION['role'] = $role;
                        $_SESSION['employee_id'] = $employee_id;
                        $_SESSION['employee_name'] = $employee_name;

                        // 重定向到仪表板
                        header("location: dashboard.php");
                    } else {
                        $_SESSION['error'] = "密码错误。";
                        header("location: index.php");
                    }
                }
            } else {
                $_SESSION['error'] = "用户名不存在。";
                header("location: index.php");
            }
        } else {
            echo "糟糕!出错了,请稍后再试。";
        }
        mysqli_stmt_close($stmt);
    }
    mysqli_close($link);
}
?>

仪表板与路由 ( dashboard.php ):

<?php
// dashboard.php
session_start();
require_once('includes/auth.php');
requireLogin();

// 根据角色跳转到不同的主页
if ($_SESSION['role'] == 'admin') {
    header("location: admin/index.php");
} else {
    header("location: employee/index.php");
}
exit;
?>

退出登录 ( logout.php ):

<?php
// logout.php
session_start();
// 销毁会话中的所有变量
$_SESSION = array();
// 销毁会话
session_destroy();
// 重定向到登录页
header("location: index.php");
exit;
?>

5.2 管理员后台 - 员工管理

这是系统的核心CRUD功能。我们以 admin/employees.php 为例,展示列表、添加、编辑、删除功能。

<?php
// admin/employees.php
$page_title = "员工管理";
require_once('../includes/auth.php');
requireAdmin(); // 只有管理员能访问
include('../includes/header.php');
require_once('../config/database.php');

// 处理删除请求
if(isset($_GET['delete_id']) && !empty($_GET['delete_id'])){
    $delete_id = trim($_GET['delete_id']);
    // 注意:由于外键约束,删除员工会级联删除 users 和 salaries 表中的记录
    $sql = "DELETE FROM employees WHERE id = ?";
    if($stmt = mysqli_prepare($link, $sql)){
        mysqli_stmt_bind_param($stmt, "i", $param_id);
        $param_id = $delete_id;
        if(mysqli_stmt_execute($stmt)){
            $_SESSION['success'] = "员工记录已成功删除。";
            header("location: employees.php");
            exit();
        } else{
            $_SESSION['error'] = "删除失败,请稍后再试。";
        }
        mysqli_stmt_close($stmt);
    }
}

// 获取所有员工信息,并关联部门名称
$sql = "SELECT e.*, d.name as department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.id ORDER BY e.id DESC";
$result = mysqli_query($link, $sql);
?>
<div class="d-flex justify-content-between align-items-center mb-4">
    <h2>员工管理</h2>
    <a href="employee_add.php" class="btn btn-success">+ 添加新员工</a>
</div>

<?php
// 显示成功/错误消息
if(isset($_SESSION['success'])){
    echo '<div class="alert alert-success">' . $_SESSION['success'] . '</div>';
    unset($_SESSION['success']);
}
if(isset($_SESSION['error'])){
    echo '<div class="alert alert-danger">' . $_SESSION['error'] . '</div>';
    unset($_SESSION['error']);
}
?>

<div class="table-responsive">
    <table class="table table-bordered table-striped table-hover">
        <thead class="table-dark">
            <tr>
                <th>ID</th>
                <th>工号</th>
                <th>姓名</th>
                <th>部门</th>
                <th>职位</th>
                <th>邮箱</th>
                <th>电话</th>
                <th>入职日期</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <?php
            if(mysqli_num_rows($result) > 0){
                while($row = mysqli_fetch_assoc($result)){
                    echo "<tr>";
                    echo "<td>" . $row['id'] . "</td>";
                    echo "<td>" . htmlspecialchars($row['employee_id']) . "</td>";
                    echo "<td>" . htmlspecialchars($row['name']) . "</td>";
                    echo "<td>" . htmlspecialchars($row['department_name'] ?? '未分配') . "</td>";
                    echo "<td>" . htmlspecialchars($row['position']) . "</td>";
                    echo "<td>" . htmlspecialchars($row['email']) . "</td>";
                    echo "<td>" . htmlspecialchars($row['phone']) . "</td>";
                    echo "<td>" . $row['hire_date'] . "</td>";
                    echo '<td>
                            <a href="employee_edit.php?id=' . $row['id'] . '" class="btn btn-sm btn-warning">编辑</a>
                            <a href="employees.php?delete_id=' . $row['id'] . '" class="btn btn-sm btn-danger" onclick="return confirm(\'确定要删除员工 ‘' . htmlspecialchars($row['name']) . '’ 吗?此操作不可逆。\');">删除</a>
                          </td>';
                    echo "</tr>";
                }
            } else {
                echo '<tr><td colspan="9" class="text-center">暂无员工数据。</td></tr>';
            }
            mysqli_free_result($result);
            ?>
        </tbody>
    </table>
</div>
<?php
mysqli_close($link);
include('../includes/footer.php');
?>

添加员工页面 ( admin/employee_add.php ) 的关键表单处理部分:

// ... 页面头部和权限检查 ...
if($_SERVER["REQUEST_METHOD"] == "POST"){
    // 接收并清理数据
    $employee_id = trim($_POST['employee_id']);
    $name = trim($_POST['name']);
    $email = trim($_POST['email']);
    // ... 接收其他字段 ...

    // 验证数据
    if(empty($employee_id) || empty($name) || empty($email)){
        $error = "工号、姓名和邮箱为必填项。";
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $error = "邮箱格式无效。";
    } else {
        // 检查工号和邮箱是否已存在
        $check_sql = "SELECT id FROM employees WHERE employee_id = ? OR email = ?";
        if($check_stmt = mysqli_prepare($link, $check_sql)){
            mysqli_stmt_bind_param($check_stmt, "ss", $employee_id, $email);
            mysqli_stmt_execute($check_stmt);
            mysqli_stmt_store_result($check_stmt);
            if(mysqli_stmt_num_rows($check_stmt) > 0){
                $error = "该工号或邮箱已被使用。";
            } else {
                // 插入数据
                $sql = "INSERT INTO employees (employee_id, name, gender, email, phone, address, department_id, position, hire_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
                if($stmt = mysqli_prepare($link, $sql)){
                    mysqli_stmt_bind_param($stmt, "ssssssiss", $param_employee_id, $param_name, $param_gender, $param_email, $param_phone, $param_address, $param_dept, $param_position, $param_hire_date);
                    // 绑定参数
                    $param_employee_id = $employee_id;
                    $param_name = $name;
                    $param_gender = $_POST['gender'];
                    $param_email = $email;
                    $param_phone = $_POST['phone'];
                    $param_address = $_POST['address'];
                    $param_dept = !empty($_POST['department_id']) ? $_POST['department_id'] : NULL;
                    $param_position = $_POST['position'];
                    $param_hire_date = $_POST['hire_date'];

                    if(mysqli_stmt_execute($stmt)){
                        $new_employee_id = $employee_id;
                        // 可选:同时为这位员工创建一个默认的登录用户
                        // $default_password = '123456'; // 应改为随机生成
                        // $hashed_password = password_hash($default_password, PASSWORD_DEFAULT);
                        // $user_sql = "INSERT INTO users (employee_id, username, password_hash, role) VALUES (?, ?, ?, 'employee')";
                        // ... 执行插入用户 ...

                        $_SESSION['success'] = "员工添加成功。";
                        header("location: employees.php");
                        exit();
                    } else{
                        $error = "执行SQL语句时出错。";
                    }
                    mysqli_stmt_close($stmt);
                }
            }
            mysqli_stmt_close($check_stmt);
        }
    }
}
// ... 显示表单的HTML代码 ...

5.3 员工前端 - 个人信息与薪资查询

员工登录后,只能查看和修改自己的信息。

员工仪表板 ( employee/index.php ):

<?php
// employee/index.php
$page_title = "员工控制台";
require_once('../includes/auth.php');
requireEmployee(); // 确保已登录
include('../includes/header.php');
?>
<div class="row">
    <div class="col-md-12">
        <div class="card">
            <div class="card-header">
                <h5>欢迎回来,<?php echo htmlspecialchars($_SESSION['employee_name']); ?>!</h5>
            </div>
            <div class="card-body">
                <p>您的工号:<strong><?php echo htmlspecialchars($_SESSION['employee_id']); ?></strong></p>
                <p>您的角色:<span class="badge bg-info"><?php echo $_SESSION['role']; ?></span></p>
                <hr>
                <div class="row">
                    <div class="col-md-6">
                        <div class="card text-center">
                            <div class="card-body">
                                <h5 class="card-title">我的档案</h5>
                                <p class="card-text">查看和编辑您的个人信息。</p>
                                <a href="profile.php" class="btn btn-primary">前往</a>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-6">
                        <div class="card text-center">
                            <div class="card-body">
                                <h5 class="card-title">我的薪资</h5>
                                <p class="card-text">查询您的历史薪资记录。</p>
                                <a href="my_salary.php" class="btn btn-primary">前往</a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<?php include('../includes/footer.php'); ?>

员工个人信息页面 ( employee/profile.php ) 的逻辑与管理员编辑类似,但关键区别在于 SQL 查询的 WHERE 条件:

// 在 profile.php 中,查询条件基于当前登录的员工
$sql = "SELECT e.*, d.name as department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.id WHERE e.employee_id = ?";
// 绑定参数时使用 $_SESSION['employee_id']
mysqli_stmt_bind_param($stmt, "s", $_SESSION['employee_id']);

6. 系统安全与最佳实践

一个可用的系统还必须是一个安全的系统。以下是必须实施的几个关键点:

6.1 SQL注入防护

我们已经在代码中使用了 预处理语句 (Prepared Statements) ,这是防止 SQL 注入最有效的方法。永远不要将用户输入直接拼接到 SQL 字符串中。

错误示例(危险!):

$sql = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "' AND password = '" . $_POST['password'] . "'";

正确示例(使用预处理):

$sql = "SELECT * FROM users WHERE username = ? AND password_hash = ?";
$stmt = mysqli_prepare($link, $sql);
mysqli_stmt_bind_param($stmt, "ss", $username, $hashed_password);

6.2 密码存储

绝对禁止明文存储密码。必须使用单向哈希函数。

  • 存储密码时: 使用 password_hash()
    $hashed_password = password_hash($raw_password, PASSWORD_DEFAULT);
    
  • 验证密码时: 使用 password_verify()
    if (password_verify($input_password, $stored_hash)) {
        // 密码正确
    }
    

6.3 会话安全

  • 使用 session_start() 开启会话。
  • 登录后立即重置会话 ID session_regenerate_id(true) 可以防止会话固定攻击。
  • 设置会话过期时间 :可以在 php.ini 中配置,或通过代码控制。
  • 退出时销毁会话 :如 logout.php 所示。

6.4 输出转义

为了防止跨站脚本攻击(XSS),所有从数据库或用户输入中取出并显示在 HTML 页面的数据,都必须使用 htmlspecialchars() 函数进行转义。

echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');

6.5 权限控制

本系统通过 includes/auth.php 中的 requireAdmin() requireEmployee() 函数实现了简单的页面级权限控制。在实际更复杂的系统中,可能还需要更细粒度的功能级权限控制(RBAC)。

7. 部署与优化建议

7.1 基础部署

  1. 购买域名与服务器 :可以选择云服务器(如阿里云ECS、腾讯云CVM)。
  2. 配置LAMP/LNMP环境 :在服务器上安装 Linux, Apache/Nginx, MySQL, PHP。
  3. 上传代码 :使用 FTP/SFTP 或 Git 将项目代码上传到服务器的 Web 目录(如 /var/www/html )。
  4. 导入数据库 :将本地开发时导出的 SQL 文件在服务器上导入。
  5. 修改配置文件 :更新 config/database.php 中的数据库连接信息(服务器地址、用户名、密码)。
  6. 设置文件权限 :确保 Web 服务器用户(如 www-data )对项目目录有适当的读写权限(通常 755 用于目录, 644 用于文件),对上传目录可能需要 775

7.2 性能与安全优化

  1. 启用 HTTPS :为域名申请 SSL 证书(Let‘s Encrypt 提供免费证书),强制所有流量使用 HTTPS。
  2. 配置 Web 服务器
    • Apache :启用 mod_rewrite 进行 URL 重写,隐藏 .php 扩展名。
    • Nginx :配置正确的 try_files 和 PHP-FPM 转发。
  3. PHP 配置
    • 在生产环境 php.ini 中,设置 display_errors = Off log_errors = On
    • 调整 upload_max_filesize post_max_size 以适应需求。
    • 启用 OpCache 以提升 PHP 脚本执行速度。
  4. MySQL 优化 :为经常查询的字段(如 employee_id , email , department_id )建立索引。定期进行数据库备份。
  5. 代码层面
    • 将常量配置(如数据库信息)移出 Web 可访问目录。
    • 实现更完善的错误处理与日志记录。
    • 对管理功能增加操作日志。

7.3 功能扩展方向

这个基础系统可以作为一个起点,根据实际需求进行扩展:

  1. 考勤管理模块 :添加打卡、请假、加班申请与审批流程。
  2. 公告通知系统 :管理员发布公告,员工端显示。
  3. 文件共享 :部门或公司级别的文档上传与下载。
  4. 报表统计 :使用图表库(如 Chart.js)可视化员工人数、部门分布、薪资成本等数据。
  5. API 接口 :为未来可能的移动端 App 提供数据接口。
  6. 更复杂的权限系统 :实现基于角色的访问控制(RBAC),支持多级管理员和权限组。

开发过程中,务必遵循“先完成,再优化”的原则。首先确保核心流程跑通,然后再逐步迭代,增加新功能和优化用户体验。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐