本文目录导读:

我来为您提供一个完整的PHP调休管理系统实现方案,这个系统包括员工调休申请、审批流程、余额管理等核心功能。
数据库设计
创建数据表
-- 员工表
CREATE TABLE `employees` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`employee_no` VARCHAR(20) NOT NULL UNIQUE,
`name` VARCHAR(50) NOT NULL,
`department` VARCHAR(50),
`position` VARCHAR(50),
`email` VARCHAR(100),
`phone` VARCHAR(20),
`status` TINYINT DEFAULT 1, -- 1:在职 0:离职
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 调休余额表
CREATE TABLE `leave_balances` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`employee_id` INT NOT NULL,
`year` YEAR NOT NULL,
`total_hours` DECIMAL(6,1) DEFAULT 0, -- 总调休小时数
`used_hours` DECIMAL(6,1) DEFAULT 0, -- 已使用小时数
`remaining_hours` DECIMAL(6,1) AS (total_hours - used_hours) STORED,
FOREIGN KEY (employee_id) REFERENCES employees(id),
UNIQUE KEY `employee_year` (employee_id, year)
);
-- 调休申请记录表
CREATE TABLE `overtime_records` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`employee_id` INT NOT NULL,
`overtime_date` DATE NOT NULL,
`start_time` TIME NOT NULL,
`end_time` TIME NOT NULL,
`hours` DECIMAL(4,1) NOT NULL,
`reason` TEXT,
`status` ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`approved_by` INT,
`approved_at` TIMESTAMP,
FOREIGN KEY (employee_id) REFERENCES employees(id)
);
-- 调休申请表
CREATE TABLE `leave_requests` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`employee_id` INT NOT NULL,
`start_date` DATE NOT NULL,
`end_date` DATE NOT NULL,
`total_hours` DECIMAL(4,1) NOT NULL,
`reason` TEXT,
`status` ENUM('pending', 'approved', 'rejected', 'cancelled') DEFAULT 'pending',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`approved_by` INT,
`approved_at` TIMESTAMP,
`remarks` TEXT,
FOREIGN KEY (employee_id) REFERENCES employees(id)
);
-- 审批流程表
CREATE TABLE `approval_flow` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`request_type` ENUM('overtime', 'leave') NOT NULL,
`request_id` INT NOT NULL,
`approver_id` INT NOT NULL,
`level` INT DEFAULT 1, -- 审批层级
`status` ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
`comment` TEXT,
`processed_at` TIMESTAMP
);
核心PHP类实现
数据库连接配置 (config/database.php)
<?php
class Database {
private $host = 'localhost';
private $db_name = 'leave_management';
private $username = 'root';
private $password = '';
private $conn;
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO(
"mysql:host={$this->host};dbname={$this->db_name}",
$this->username,
$this->password
);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
echo "Connection error: " . $e->getMessage();
}
return $this->conn;
}
}
调休管理类 (classes/LeaveManagement.php)
<?php
require_once 'config/database.php';
class LeaveManagement {
private $db;
private $conn;
public function __construct() {
$this->db = new Database();
$this->conn = $this->db->getConnection();
}
// 获取员工调休余额
public function getLeaveBalance($employeeId, $year = null) {
$year = $year ?? date('Y');
$query = "SELECT total_hours, used_hours, remaining_hours
FROM leave_balances
WHERE employee_id = :employee_id AND year = :year";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':employee_id', $employeeId);
$stmt->bindParam(':year', $year);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result) {
return ['total_hours' => 0, 'used_hours' => 0, 'remaining_hours' => 0];
}
return $result;
}
// 添加加班记录(增加调休余额)
public function addOvertime($employeeId, $date, $startTime, $endTime, $reason) {
try {
$this->conn->beginTransaction();
// 计算加班小时数
$hours = $this->calculateHours($startTime, $endTime);
// 插入加班记录
$query = "INSERT INTO overtime_records
(employee_id, overtime_date, start_time, end_time, hours, reason, status)
VALUES (:employee_id, :overtime_date, :start_time, :end_time, :hours, :reason, 'pending')";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':employee_id', $employeeId);
$stmt->bindParam(':overtime_date', $date);
$stmt->bindParam(':start_time', $startTime);
$stmt->bindParam(':end_time', $endTime);
$stmt->bindParam(':hours', $hours);
$stmt->bindParam(':reason', $reason);
$stmt->execute();
$overtimeId = $this->conn->lastInsertId();
$this->conn->commit();
return ['success' => true, 'message' => '加班记录提交成功', 'id' => $overtimeId];
} catch (Exception $e) {
$this->conn->rollBack();
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 提交调休申请
public function submitLeaveRequest($employeeId, $startDate, $endDate, $hours, $reason) {
try {
// 检查调休余额
$balance = $this->getLeaveBalance($employeeId);
if ($balance['remaining_hours'] < $hours) {
return ['success' => false, 'message' => '调休余额不足'];
}
$this->conn->beginTransaction();
// 插入调休申请
$query = "INSERT INTO leave_requests
(employee_id, start_date, end_date, total_hours, reason, status)
VALUES (:employee_id, :start_date, :end_date, :total_hours, :reason, 'pending')";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':employee_id', $employeeId);
$stmt->bindParam(':start_date', $startDate);
$stmt->bindParam(':end_date', $endDate);
$stmt->bindParam(':total_hours', $hours);
$stmt->bindParam(':reason', $reason);
$stmt->execute();
$requestId = $this->conn->lastInsertId();
$this->conn->commit();
return ['success' => true, 'message' => '调休申请提交成功', 'id' => $requestId];
} catch (Exception $e) {
$this->conn->rollBack();
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 审批调休申请
public function approveLeaveRequest($requestId, $approverId, $status, $comment = '') {
try {
$this->conn->beginTransaction();
// 获取申请详情
$request = $this->getLeaveRequest($requestId);
if (!$request) {
return ['success' => false, 'message' => '申请记录不存在'];
}
// 更新申请状态
$query = "UPDATE leave_requests
SET status = :status,
approved_by = :approved_by,
approved_at = NOW(),
remarks = :remarks
WHERE id = :id";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':status', $status);
$stmt->bindParam(':approved_by', $approverId);
$stmt->bindParam(':remarks', $comment);
$stmt->bindParam(':id', $requestId);
$stmt->execute();
// 如果审批通过,更新调休余额
if ($status === 'approved') {
$this->updateLeaveBalance($request['employee_id'], $request['total_hours'], 'deduct');
}
$this->conn->commit();
return ['success' => true, 'message' => '审批完成'];
} catch (Exception $e) {
$this->conn->rollBack();
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 更新调休余额
private function updateLeaveBalance($employeeId, $hours, $type) {
$year = date('Y');
// 检查是否有余额记录
$query = "SELECT id FROM leave_balances
WHERE employee_id = :employee_id AND year = :year";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':employee_id', $employeeId);
$stmt->bindParam(':year', $year);
$stmt->execute();
if ($stmt->rowCount() == 0) {
// 创建新记录
$query = "INSERT INTO leave_balances (employee_id, year, total_hours, used_hours)
VALUES (:employee_id, :year, 0, 0)";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':employee_id', $employeeId);
$stmt->bindParam(':year', $year);
$stmt->execute();
}
// 更新余额
if ($type === 'add') {
$query = "UPDATE leave_balances
SET total_hours = total_hours + :hours
WHERE employee_id = :employee_id AND year = :year";
} else if ($type === 'deduct') {
$query = "UPDATE leave_balances
SET used_hours = used_hours + :hours
WHERE employee_id = :employee_id AND year = :year";
}
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':hours', $hours);
$stmt->bindParam(':employee_id', $employeeId);
$stmt->bindParam(':year', $year);
$stmt->execute();
}
// 计算时间段小时数
private function calculateHours($startTime, $endTime) {
$start = strtotime($startTime);
$end = strtotime($endTime);
$diff = $end - $start;
return round($diff / 3600, 1);
}
// 获取调休申请详情
public function getLeaveRequest($requestId) {
$query = "SELECT * FROM leave_requests WHERE id = :id";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':id', $requestId);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// 获取员工的调休申请列表
public function getEmployeeLeaveRequests($employeeId) {
$query = "SELECT * FROM leave_requests WHERE employee_id = :employee_id
ORDER BY created_at DESC";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':employee_id', $employeeId);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 获取待审批列表
public function getPendingApprovals($approverId = null) {
$query = "SELECT lr.*, e.name as employee_name, e.department
FROM leave_requests lr
JOIN employees e ON lr.employee_id = e.id
WHERE lr.status = 'pending'";
if ($approverId) {
$query .= " AND lr.approved_by = :approver_id";
}
$query .= " ORDER BY lr.created_at DESC";
$stmt = $this->conn->prepare($query);
if ($approverId) {
$stmt->bindParam(':approver_id', $approverId);
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 取消调休申请
public function cancelLeaveRequest($requestId, $employeeId) {
try {
$request = $this->getLeaveRequest($requestId);
if (!$request || $request['employee_id'] != $employeeId) {
return ['success' => false, 'message' => '无权限取消此申请'];
}
if ($request['status'] !== 'pending') {
return ['success' => false, 'message' => '只能取消待审批的申请'];
}
$query = "UPDATE leave_requests SET status = 'cancelled' WHERE id = :id";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':id', $requestId);
$stmt->execute();
return ['success' => true, 'message' => '申请已取消'];
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 获取部门调休统计
public function getDepartmentLeaveStats($department, $year = null) {
$year = $year ?? date('Y');
$query = "SELECT
e.department,
COUNT(DISTINCT e.id) as employee_count,
SUM(lb.total_hours) as total_overtime_hours,
SUM(lb.used_hours) as total_used_hours,
SUM(lb.remaining_hours) as total_remaining_hours
FROM employees e
LEFT JOIN leave_balances lb ON e.id = lb.employee_id AND lb.year = :year
WHERE e.department = :department AND e.status = 1
GROUP BY e.department";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':department', $department);
$stmt->bindParam(':year', $year);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_ASSOC);
}
}
前端页面示例
员工调休申请页面 (apply_leave.php)
<?php
require_once 'classes/LeaveManagement.php';
session_start();
// 假设已登录员工的ID
$employeeId = $_SESSION['employee_id'];
$leaveMgmt = new LeaveManagement();
// 获取调休余额
$balance = $leaveMgmt->getLeaveBalance($employeeId);
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$result = $leaveMgmt->submitLeaveRequest(
$employeeId,
$_POST['start_date'],
$_POST['end_date'],
$_POST['total_hours'],
$_POST['reason']
);
if ($result['success']) {
echo "<script>alert('申请提交成功!');</script>";
} else {
echo "<script>alert('{$result['message']}');</script>";
}
}
?>
<!DOCTYPE html>
<html>
<head>调休申请</title>
<meta charset="utf-8">
<style>
.container {
max-width: 800px;
margin: 20px auto;
padding: 20px;
}
.balance-info {
background: #f5f5f5;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select, textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.history-table {
width: 100%;
border-collapse: collapse;
margin-top: 30px;
}
.history-table th, .history-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.status-pending {
color: #ff9800;
}
.status-approved {
color: #4CAF50;
}
.status-rejected {
color: #f44336;
}
</style>
</head>
<body>
<div class="container">
<h1>调休申请</h1>
<!-- 调休余额显示 -->
<div class="balance-info">
<h3>我的调休余额</h3>
<p>总调休时长:<?php echo $balance['total_hours']; ?> 小时</p>
<p>已使用:<?php echo $balance['used_hours']; ?> 小时</p>
<p><strong>可用余额:<?php echo $balance['remaining_hours']; ?> 小时</strong></p>
</div>
<!-- 申请表单 -->
<form method="POST" onsubmit="return validateForm()">
<div class="form-group">
<label>开始日期:</label>
<input type="date" name="start_date" required>
</div>
<div class="form-group">
<label>结束日期:</label>
<input type="date" name="end_date" required>
</div>
<div class="form-group">
<label>调休小时数:</label>
<input type="number" name="total_hours" step="0.5"
min="0.5" max="<?php echo $balance['remaining_hours']; ?>" required>
<small>可用余额:<?php echo $balance['remaining_hours']; ?> 小时</small>
</div>
<div class="form-group">
<label>申请原因:</label>
<textarea name="reason" rows="4" required></textarea>
</div>
<button type="submit">提交申请</button>
</form>
<!-- 历史申请记录 -->
<h2>我的申请记录</h2>
<table class="history-table">
<thead>
<tr>
<th>申请日期</th>
<th>调休日期</th>
<th>小时数</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
$requests = $leaveMgmt->getEmployeeLeaveRequests($employeeId);
foreach ($requests as $request):
?>
<tr>
<td><?php echo date('Y-m-d', strtotime($request['created_at'])); ?></td>
<td><?php echo $request['start_date'] . ' ~ ' . $request['end_date']; ?></td>
<td><?php echo $request['total_hours']; ?>小时</td>
<td class="status-<?php echo $request['status']; ?>">
<?php
$statusMap = [
'pending' => '待审批',
'approved' => '已批准',
'rejected' => '已拒绝',
'cancelled' => '已取消'
];
echo $statusMap[$request['status']];
?>
</td>
<td>
<?php if ($request['status'] === 'pending'): ?>
<button onclick="cancelRequest(<?php echo $request['id']; ?>)">取消</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<script>
function validateForm() {
var startDate = document.querySelector('[name="start_date"]').value;
var endDate = document.querySelector('[name="end_date"]').value;
var hours = parseFloat(document.querySelector('[name="total_hours"]').value);
var maxHours = parseFloat('<?php echo $balance['remaining_hours']; ?>');
if (startDate > endDate) {
alert('开始日期不能晚于结束日期');
return false;
}
if (hours > maxHours) {
alert('调休小时数不能超过可用余额');
return false;
}
return confirm('确认提交调休申请?');
}
function cancelRequest(requestId) {
if (confirm('确定要取消此申请吗?')) {
window.location.href = 'cancel_leave.php?id=' + requestId;
}
}
</script>
</body>
</html>
API接口示例 (api/leaves.php)
<?php
header('Content-Type: application/json');
require_once '../classes/LeaveManagement.php';
$leaveMgmt = new LeaveManagement();
$method = $_SERVER['REQUEST_METHOD'];
switch ($method) {
case 'GET':
// 获取调休信息
if (isset($_GET['action'])) {
switch ($_GET['action']) {
case 'balance':
$result = $leaveMgmt->getLeaveBalance(
$_GET['employee_id'],
$_GET['year'] ?? date('Y')
);
break;
case 'requests':
$result = $leaveMgmt->getEmployeeLeaveRequests(
$_GET['employee_id']
);
break;
case 'pending':
$result = $leaveMgmt->getPendingApprovals(
$_GET['approver_id'] ?? null
);
break;
case 'stats':
$result = $leaveMgmt->getDepartmentLeaveStats(
$_GET['department'],
$_GET['year'] ?? date('Y')
);
break;
default:
$result = ['success' => false, 'message' => '无效的操作'];
}
}
break;
case 'POST':
// 提交调休申请或加班记录
$data = json_decode(file_get_contents('php://input'), true);
if ($data['type'] === 'overtime') {
$result = $leaveMgmt->addOvertime(
$data['employee_id'],
$data['date'],
$data['start_time'],
$data['end_time'],
$data['reason']
);
} elseif ($data['type'] === 'leave') {
$result = $leaveMgmt->submitLeaveRequest(
$data['employee_id'],
$data['start_date'],
$data['end_date'],
$data['total_hours'],
$data['reason']
);
}
break;
case 'PUT':
// 审批调休申请
$data = json_decode(file_get_contents('php://input'), true);
$result = $leaveMgmt->approveLeaveRequest(
$data['request_id'],
$data['approver_id'],
$data['status'],
$data['comment'] ?? ''
);
break;
case 'DELETE':
// 取消调休申请
$result = $leaveMgmt->cancelLeaveRequest(
$_GET['id'],
$_GET['employee_id']
);
break;
default:
$result = ['success' => false, 'message' => '不支持的请求方法'];
}
echo json_encode($result);
使用示例
// 初始化调休管理系统
$leaveMgmt = new LeaveManagement();
// 员工提交调休申请
$result = $leaveMgmt->submitLeaveRequest(
1, // 员工ID
'2024-01-15', // 开始日期
'2024-01-15', // 结束日期
8, // 调休小时数
'调休补休加班'
);
// 审批调休申请
$result = $leaveMgmt->approveLeaveRequest(
1, // 申请ID
2, // 审批人ID
'approved', // 状态
'同意调休' // 备注
);
// 获取员工调休余额
$balance = $leaveMgmt->getLeaveBalance(1);
// 获取待审批列表
$pendingList = $leaveMgmt->getPendingApprovals();
安全建议
// 添加CSRF保护
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die('CSRF token validation failed');
}
}
// SQL注入防护(已使用PDO预处理语句)
// XSS防护
function sanitizeOutput($data) {
return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
}
// 权限验证
function checkPermission($employeeId, $requiredRole) {
// 实现权限检查逻辑
}
这个系统提供了完整的调休管理功能,包括:
- 员工端:查看余额、提交申请、查看记录
- 管理端:审批申请、统计报表、余额管理
- 核心功能:加班记录管理、调休申请、审批流程、余额自动更新
您可以根据实际需求进一步扩展功能,如添加邮件通知、批量导入导出、更复杂的审批流程等。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播