1. 项目概述
这个基于PHP+MySQL的五表请假管理系统,是我去年为本地一所职业技术学院开发的教务辅助工具。系统采用经典的B/S架构,主要解决传统纸质请假流程效率低下、数据难以统计的问题。从技术选型到功能设计,整个开发周期约两个月,目前已在学院稳定运行半年多,日均处理请假申请约120人次。
系统最核心的创新点在于通过五张数据表的关联设计(学生表、教师表、管理员表、请假记录表、审批记录表),实现了学生、教师、管理员三大角色的全流程线上协作。相比市面上常见的三表结构,我们增加的审批记录表和扩展的请假记录表字段,使得请假类型统计、审批时效分析等管理功能成为可能。
2. 系统架构设计
2.1 技术栈选型考量
选择PHP+MySQL这对经典组合主要基于三点考虑:
- 学院现有服务器环境为LAMP(Linux+Apache+MySQL+PHP),无需额外配置
- 开发团队对PHP框架熟悉度高,能快速迭代开发
- MySQL 5.7的JSON字段支持,便于存储动态表单数据
具体版本:
- PHP 7.4(兼顾性能与语法特性)
- MySQL 5.7.32(学院IT部门指定版本)
- AdminLTE 3.0(后台UI框架)
- jQuery 3.5(前端交互)
2.2 数据库设计详解
五张核心表结构如下:
students表
sql复制CREATE TABLE `students` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_no` varchar(20) NOT NULL COMMENT '学号',
`name` varchar(50) NOT NULL,
`class_id` int(11) NOT NULL,
`password` varchar(255) NOT NULL COMMENT 'bcrypt加密',
`contact_phone` varchar(20) DEFAULT NULL,
`emergency_contact` varchar(50) DEFAULT NULL,
`status` tinyint(1) DEFAULT '1' COMMENT '1正常 0禁用',
PRIMARY KEY (`id`),
UNIQUE KEY `student_no` (`student_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
teachers表
sql复制CREATE TABLE `teachers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`teacher_no` varchar(20) NOT NULL,
`name` varchar(50) NOT NULL,
`department_id` int(11) NOT NULL,
`password` varchar(255) NOT NULL,
`is_advisor` tinyint(1) DEFAULT '0' COMMENT '是否班主任',
`approve_limit` int(11) DEFAULT '3' COMMENT '可审批天数上限',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
leave_requests表(核心业务表)
sql复制CREATE TABLE `leave_requests` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_id` int(11) NOT NULL,
`type` enum('病假','事假','公假') NOT NULL,
`start_time` datetime NOT NULL,
`end_time` datetime NOT NULL,
`days` decimal(5,1) NOT NULL COMMENT '自动计算',
`reason` text NOT NULL,
`attachments` json DEFAULT NULL COMMENT '存储附件路径',
`status` enum('pending','approved','rejected','canceled') NOT NULL DEFAULT 'pending',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `student_id` (`student_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
关键设计点:days字段通过触发器自动计算,避免业务逻辑重复计算;attachments使用JSON类型存储多附件路径。
3. 核心功能实现
3.1 多级审批流程
系统实现了三级审批机制:
- 班主任审批(<=3天)
- 系主任审批(3-7天)
- 教务处审批(>7天)
审批逻辑代码片段:
php复制public function approveLeave($requestId, $approverId, $role, $comment) {
$request = $this->getLeaveRequest($requestId);
$days = $request['days'];
if ($role == 'teacher') {
if ($days > 3 && $days <= 7) {
$this->forwardToDepartmentHead($requestId);
} elseif ($days > 7) {
$this->forwardToDeanOffice($requestId);
} else {
$this->updateStatus($requestId, 'approved');
}
}
// 记录审批日志
$this->addApprovalLog($requestId, $approverId, $role, $comment);
}
3.2 实时状态同步
使用Server-Sent Events(SSE)实现审批状态实时推送:
javascript复制// 学生端JS代码
const eventSource = new EventSource('/leave/status_updates.php');
eventSource.onmessage = function(e) {
const data = JSON.parse(e.data);
if (data.request_id == currentRequestId) {
document.getElementById('status-badge').className = 'badge ' + getStatusClass(data.status);
document.getElementById('status-text').innerText = getStatusText(data.status);
}
};
4. 安全防护措施
4.1 防SQL注入方案
- 预处理语句全覆盖
php复制$stmt = $conn->prepare("SELECT * FROM students WHERE student_no = ?");
$stmt->bind_param("s", $studentNo);
$stmt->execute();
- 输入过滤层
php复制function sanitizeInput($input) {
if (is_array($input)) {
return array_map('sanitizeInput', $input);
}
return htmlspecialchars(strip_tags(trim($input)));
}
4.2 会话安全
- 会话固定防护:登录后regenerate_session_id()
- Cookie设置:HttpOnly + Secure
- 密码存储:bcrypt算法,cost=12
5. 性能优化实践
5.1 数据库优化
- 为高频查询字段添加复合索引:
sql复制ALTER TABLE leave_requests ADD INDEX `idx_status_created` (`status`, `created_at`);
- 大表分页优化:
php复制// 避免使用LIMIT offset, size
$sql = "SELECT * FROM leave_requests WHERE id > ? ORDER BY id LIMIT 20";
5.2 前端优化技巧
- 懒加载审批记录:
javascript复制function loadMoreRecords(lastId) {
fetch(`/api/approvals?last_id=${lastId}`)
.then(response => response.json())
.then(data => {
// 渲染新数据
});
}
- 本地缓存策略:
javascript复制if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
6. 部署与维护
6.1 服务器配置建议
- PHP配置:
ini复制memory_limit = 128M
max_execution_time = 30
upload_max_filesize = 10M
- MySQL配置:
ini复制innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
6.2 日常维护脚本
- 自动备份脚本(backup.sh):
bash复制#!/bin/bash
DATE=$(date +%Y%m%d)
mysqldump -u backup_user -p'password' leave_system > /backups/leave_$DATE.sql
find /backups -type f -mtime +30 -delete
7. 扩展功能开发
7.1 微信通知集成
通过微信公众号模板消息推送审批结果:
php复制public function sendWechatNotice($openId, $templateId, $data) {
$api = "https://api.weixin.qq.com/cgi-bin/message/template/send";
$payload = [
'touser' => $openId,
'template_id' => $templateId,
'data' => $data
];
// 调用微信API...
}
7.2 数据统计报表
使用Chart.js实现可视化:
php复制// 获取月度请假数据
$monthlyData = $db->query("
SELECT DATE_FORMAT(created_at, '%Y-%m') AS month,
COUNT(*) AS total,
SUM(type = '病假') AS sick_leave
FROM leave_requests
GROUP BY month
ORDER BY month
");
8. 踩坑经验分享
- 时区问题:初期发现请假天数计算不准,原因是MySQL时区与PHP时区不一致。解决方案:
php复制date_default_timezone_set('Asia/Shanghai');
$db->exec("SET time_zone = '+8:00'");
- 并发审批冲突:多个审批人同时操作时出现状态覆盖。最终采用乐观锁解决:
sql复制UPDATE leave_requests
SET status = 'approved', version = version + 1
WHERE id = ? AND version = ?
- 文件上传漏洞:最初仅校验文件后缀名,后被绕过。改进方案:
php复制$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['file']['tmp_name']);
if (!in_array($mime, ['image/jpeg', 'application/pdf'])) {
throw new Exception('非法文件类型');
}
这个系统从技术实现角度看并不复杂,但真正考验功力的是对业务细节的把握。比如请假天数计算要排除节假日、审批链路的异常处理、移动端的适配优化等。建议开发类似系统时,前期一定要与最终用户充分沟通,把各种边界情况考虑周全。
