本文目录导读:

我来详细讲解如何用PHP搭建一个完整的预约系统,这是一个常见需求,可以从简单到复杂逐步实现。
系统架构设计
基础结构
appointment-system/
├── config/
│ └── database.php
├── includes/
│ ├── functions.php
│ └── validation.php
├── public/
│ ├── index.php
│ ├── book.php
│ └── admin/
├── templates/
│ └── ...
└── database/
└── schema.sql
数据库设计
-- appointments.sql
CREATE TABLE services (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
duration INT NOT NULL, -- 分钟
price DECIMAL(10,2),
description TEXT,
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE time_slots (
id INT PRIMARY KEY AUTO_INCREMENT,
service_id INT,
date DATE NOT NULL,
start_time TIME NOT NULL,
end_time TIME NOT NULL,
is_booked BOOLEAN DEFAULT FALSE,
FOREIGN KEY (service_id) REFERENCES services(id)
);
CREATE TABLE appointments (
id INT PRIMARY KEY AUTO_INCREMENT,
time_slot_id INT,
customer_name VARCHAR(100) NOT NULL,
customer_email VARCHAR(100),
customer_phone VARCHAR(20),
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status ENUM('confirmed', 'cancelled', 'completed') DEFAULT 'confirmed',
FOREIGN KEY (time_slot_id) REFERENCES time_slots(id)
);
核心PHP代码
数据库连接配置
<?php
// config/database.php
class Database {
private $host = "localhost";
private $db_name = "appointment_system";
private $username = "root";
private $password = "";
public $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 $exception) {
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}
预约系统核心类
<?php
// includes/AppointmentSystem.php
class AppointmentSystem {
private $db;
private $conn;
public function __construct() {
$this->db = new Database();
$this->conn = $this->db->getConnection();
}
// 获取可用时间段
public function getAvailableSlots($service_id, $date) {
$query = "SELECT * FROM time_slots
WHERE service_id = :service_id
AND date = :date
AND is_booked = FALSE
ORDER BY start_time";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':service_id', $service_id);
$stmt->bindParam(':date', $date);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 创建预约
public function createAppointment($data) {
try {
$this->conn->beginTransaction();
// 检查时间段是否仍可用
$slot = $this->checkSlotAvailability(
$data['time_slot_id']
);
if (!$slot) {
throw new Exception("时间段已被预订");
}
// 插入预约记录
$query = "INSERT INTO appointments
(time_slot_id, customer_name, customer_email,
customer_phone, notes)
VALUES (:time_slot_id, :name, :email,
:phone, :notes)";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':time_slot_id', $data['time_slot_id']);
$stmt->bindParam(':name', $data['customer_name']);
$stmt->bindParam(':email', $data['customer_email']);
$stmt->bindParam(':phone', $data['customer_phone']);
$stmt->bindParam(':notes', $data['notes']);
$stmt->execute();
// 更新时间段状态
$this->bookTimeSlot($data['time_slot_id']);
$this->conn->commit();
// 发送确认邮件(可选)
$this->sendConfirmation($data);
return ['success' => true, 'message' => '预约成功'];
} catch (Exception $e) {
$this->conn->rollBack();
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 检查时间段可用性
private function checkSlotAvailability($slot_id) {
$query = "SELECT * FROM time_slots
WHERE id = :id AND is_booked = FALSE";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':id', $slot_id);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// 预订时间段
private function bookTimeSlot($slot_id) {
$query = "UPDATE time_slots SET is_booked = TRUE
WHERE id = :id";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':id', $slot_id);
return $stmt->execute();
}
// 生成时间段
public function generateTimeSlots($service_id, $date, $start_time, $end_time, $interval = 30) {
$time = strtotime($start_time);
$end = strtotime($end_time);
while ($time < $end) {
$slot_start = date('H:i:s', $time);
$time += $interval * 60;
$slot_end = date('H:i:s', $time);
// 检查是否已存在
if (!$this->slotExists($service_id, $date, $slot_start)) {
$query = "INSERT INTO time_slots
(service_id, date, start_time, end_time)
VALUES (:service_id, :date, :start, :end)";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':service_id', $service_id);
$stmt->bindParam(':date', $date);
$stmt->bindParam(':start', $slot_start);
$stmt->bindParam(':end', $slot_end);
$stmt->execute();
}
}
}
private function slotExists($service_id, $date, $start_time) {
$query = "SELECT id FROM time_slots
WHERE service_id = :service_id
AND date = :date
AND start_time = :start_time";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':service_id', $service_id);
$stmt->bindParam(':date', $date);
$stmt->bindParam(':start_time', $start_time);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_ASSOC) ? true : false;
}
}
前端预约页面
<?php
// public/book.php
require_once '../config/database.php';
require_once '../includes/AppointmentSystem.php';
$appointmentSystem = new AppointmentSystem();
// 获取所有服务
$query = "SELECT * FROM services WHERE is_active = TRUE";
$services = $conn->query($query)->fetchAll();
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$result = $appointmentSystem->createAppointment($_POST);
echo json_encode($result);
exit;
}
?>
<!DOCTYPE html>
<html>
<head>预约系统</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
</head>
<body>
<div class="container">
<h1>在线预约</h1>
<form id="appointmentForm" method="POST">
<!-- 选择服务 -->
<div class="form-group">
<label>选择服务</label>
<select name="service_id" id="serviceSelect" required>
<option value="">请选择...</option>
<?php foreach ($services as $service): ?>
<option value="<?= $service['id'] ?>"
data-duration="<?= $service['duration'] ?>">
<?= htmlspecialchars($service['name']) ?>
(<?= $service['duration'] ?>分钟)
</option>
<?php endforeach; ?>
</select>
</div>
<!-- 选择日期 -->
<div class="form-group">
<label>选择日期</label>
<input type="text" id="datePicker" name="date"
placeholder="选择日期..." required>
</div>
<!-- 选择时间 -->
<div class="form-group">
<label>选择时间</label>
<select name="time_slot_id" id="timeSlotSelect" required>
<option value="">先选择日期和服务</option>
</select>
</div>
<!-- 客户信息 -->
<div class="form-group">
<label>姓名</label>
<input type="text" name="customer_name" required>
</div>
<div class="form-group">
<label>邮箱</label>
<input type="email" name="customer_email">
</div>
<div class="form-group">
<label>电话</label>
<input type="tel" name="customer_phone" required>
</div>
<div class="form-group">
<label>备注</label>
<textarea name="notes"></textarea>
</div>
<button type="submit">确认预约</button>
</form>
</div>
<script>
// 日期选择器
flatpickr("#datePicker", {
minDate: "today",
dateFormat: "Y-m-d",
disable: [
// 禁用周末
function(date) {
return (date.getDay() === 0 || date.getDay() === 6);
}
],
onChange: function(selectedDates, dateStr, instance) {
loadTimeSlots();
}
});
// 加载可用时间段
document.getElementById('serviceSelect').addEventListener('change', loadTimeSlots);
function loadTimeSlots() {
const serviceId = document.getElementById('serviceSelect').value;
const date = document.getElementById('datePicker').value;
const timeSlotSelect = document.getElementById('timeSlotSelect');
if (!serviceId || !date) {
timeSlotSelect.innerHTML = '<option value="">先选择日期和服务</option>';
return;
}
// AJAX请求获取可用时间段
fetch(`get_slots.php?service_id=${serviceId}&date=${date}`)
.then(response => response.json())
.then(slots => {
timeSlotSelect.innerHTML = '<option value="">选择时间段...</option>';
slots.forEach(slot => {
const option = document.createElement('option');
option.value = slot.id;
option.textContent = `${slot.start_time} - ${slot.end_time}`;
timeSlotSelect.appendChild(option);
});
});
}
// 表单提交
document.getElementById('appointmentForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('book.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('预约成功!');
window.location.reload();
} else {
alert('预约失败: ' + data.message);
}
});
});
</script>
</body>
</html>
获取可用时间段API
<?php
// public/get_slots.php
require_once '../config/database.php';
require_once '../includes/AppointmentSystem.php';
header('Content-Type: application/json');
if (!isset($_GET['service_id']) || !isset($_GET['date'])) {
echo json_encode([]);
exit;
}
$appointmentSystem = new AppointmentSystem();
$slots = $appointmentSystem->getAvailableSlots(
$_GET['service_id'],
$_GET['date']
);
echo json_encode($slots);
高级功能扩展
邮件通知
// includes/EmailNotifier.php
class EmailNotifier {
public function sendConfirmation($appointment) {
$to = $appointment['customer_email'];
$subject = "预约确认 - " . $appointment['service_name'];
$message = "
<html>
<body>
<h2>预约确认</h2>
<p>尊敬的 {$appointment['customer_name']},</p>
<p>您的预约已成功确认:</p>
<ul>
<li>服务:{$appointment['service_name']}</li>
<li>日期:{$appointment['date']}</li>
<li>时间:{$appointment['start_time']} - {$appointment['end_time']}</li>
</ul>
<p>如有变动请提前24小时通知我们。</p>
</body>
</html>
";
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";
return mail($to, $subject, $message, $headers);
}
}
管理后台
<?php
// public/admin/dashboard.php
session_start();
// 检查管理员权限
if (!isset($_SESSION['admin_logged_in'])) {
header('Location: login.php');
exit;
}
require_once '../../config/database.php';
// 获取今日预约
$query = "SELECT a.*, s.name as service_name,
ts.date, ts.start_time, ts.end_time
FROM appointments a
JOIN time_slots ts ON a.time_slot_id = ts.id
JOIN services s ON ts.service_id = s.id
WHERE ts.date = CURDATE()
ORDER BY ts.start_time";
$todayAppointments = $conn->query($query)->fetchAll();
?>
<!DOCTYPE html>
<html>
<head>管理后台 - 预约列表</title>
</head>
<body>
<h1>今日预约</h1>
<table>
<thead>
<tr>
<th>时间</th>
<th>客户</th>
<th>服务</th>
<th>电话</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($todayAppointments as $appointment): ?>
<tr>
<td><?= $appointment['start_time'] ?></td>
<td><?= htmlspecialchars($appointment['customer_name']) ?></td>
<td><?= $appointment['service_name'] ?></td>
<td><?= htmlspecialchars($appointment['customer_phone']) ?></td>
<td><?= $appointment['status'] ?></td>
<td>
<button onclick="cancelAppointment(<?= $appointment['id'] ?>)">
取消
</button>
<button onclick="completeAppointment(<?= $appointment['id'] ?>)">
完成
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</body>
</html>
优化建议
性能优化
- 缓存:使用Redis缓存可用时间段
- 数据库索引:为常用查询字段添加索引
- CDN:静态资源使用CDN加载
安全措施
// includes/security.php
// CSRF保护
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// XSS防护
function sanitizeOutput($data) {
return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
}
// SQL注入防护(使用PDO预处理语句)
部署建议
- 使用PHP 7.4以上版本
- 配置Nginx/Apache
- 启用HTTPS
- 设置适当的文件权限
- 定期备份数据库
这个预约系统可以满足大多数中小企业需求,你也可以根据具体业务需求进行定制扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播