本文目录导读:

使用PHP搭建医院挂号系统是一个典型的Web开发项目,下面我会为你梳理一个从零开始的完整架构、技术选型和核心功能实现方案,并附上关键代码示例。
技术栈推荐
| 层级 | 技术选型 | 说明 |
|---|---|---|
| 后端语言 | PHP 8.x | 性能好,语法现代化 |
| 框架 | Laravel 10 / ThinkPHP 8 | 选其一,Laravel生态更好,ThinkPHP上手快 |
| 数据库 | MySQL 8.x + Redis | MySQL存业务数据,Redis存Token和缓存 |
| 前端 | Bootstrap 5 + jQuery | 快速搭建响应式后台 |
| 模板引擎 | Blade (Laravel) | 或 ThinkPHP自带的模板 |
| API格式 | RESTful JSON | 前后端分离或混合开发用 |
| 部署 | Nginx + PHP-FPM | 高并发支持 |
数据库设计(核心表结构)
挂号系统至少需要以下7张核心表:
用户表 (users)
CREATE TABLE `users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL COMMENT '姓名', `phone` varchar(11) NOT NULL COMMENT '手机号', `id_card` varchar(18) DEFAULT NULL COMMENT '身份证号', `password` varchar(255) NOT NULL, `role` tinyint(1) NOT NULL DEFAULT 1 COMMENT '1:患者 2:医生 3:管理员', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_phone` (`phone`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
科室表 (departments)
CREATE TABLE `departments` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '科室名称', `description` text COMMENT '科室介绍', `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '1:启用 0:停用', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
医生表 (doctors)
CREATE TABLE `doctors` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(11) unsigned NOT NULL, `department_id` int(11) unsigned NOT NULL, varchar(50) DEFAULT NULL COMMENT '职称:主任医师/副主任医师等', `intro` text COMMENT '医生简介', `consultation_fee` decimal(8,2) NOT NULL DEFAULT 0.00 COMMENT '挂号费', `max_appointments` int(4) NOT NULL DEFAULT 30 COMMENT '每日最大预约数', PRIMARY KEY (`id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`), FOREIGN KEY (`department_id`) REFERENCES `departments`(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
排班表 (schedules)
CREATE TABLE `schedules` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `doctor_id` int(11) unsigned NOT NULL, `schedule_date` date NOT NULL COMMENT '出诊日期', `time_slot` varchar(20) NOT NULL COMMENT '时段:morning/afternoon/evening', `max_appointments` int(4) NOT NULL DEFAULT 30, `booked_count` int(4) NOT NULL DEFAULT 0 COMMENT '已预约数', `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '1:可预约 0:停诊', PRIMARY KEY (`id`), UNIQUE KEY `uk_doctor_date_slot` (`doctor_id`,`schedule_date`,`time_slot`), FOREIGN KEY (`doctor_id`) REFERENCES `doctors`(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
挂号单表 (appointments) - 关键表
CREATE TABLE `appointments` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(11) unsigned NOT NULL COMMENT '患者ID', `schedule_id` int(11) unsigned NOT NULL COMMENT '排班ID', `appointment_no` varchar(20) NOT NULL COMMENT '挂号单号(如:GH20240305001)', `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '1:待就诊 2:已就诊 3:已取消 4:爽约', `booking_time` datetime NOT NULL COMMENT '预约时间', `visit_time` datetime DEFAULT NULL COMMENT '实际就诊时间', `remark` varchar(255) DEFAULT NULL COMMENT '备注', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_appointment_no` (`appointment_no`), KEY `idx_user_id` (`user_id`), KEY `idx_schedule_id` (`schedule_id`), FOREIGN KEY (`user_id`) REFERENCES `users`(`id`), FOREIGN KEY (`schedule_id`) REFERENCES `schedules`(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
系统配置表 (configs)
- 存放医院名称、科室数量等配置
核心功能模块实现(Laravel示例)
挂号流程(用户端 - 预约接口)
路由 (routes/api.php)
Route::post('/appointments', [AppointmentController::class, 'store'])->middleware('auth:sanctum');
Route::get('/doctors/{departmentId}', [DoctorController::class, 'listByDepartment']);
Route::get('/schedules/{doctorId}', [ScheduleController::class, 'availableSlots']);
控制器核心方法 (AppointmentController@store)
<?php
namespace App\Http\Controllers\Api;
use App\Models\Appointment;
use App\Models\Schedule;
use App\Models\Doctor;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class AppointmentController extends Controller
{
public function store(Request $request)
{
$validated = $request->validate([
'schedule_id' => 'required|exists:schedules,id',
'patient_name' => 'required|string',
'phone' => 'required|string'
]);
$schedule = Schedule::with('doctor')->findOrFail($validated['schedule_id']);
// 1. 校验排班是否有效
if ($schedule->status != 1) {
return response()->json(['message' => '该时段已停诊'], 400);
}
// 2. 校验号源是否充足(乐观锁防超售)
$affected = Schedule::where('id', $schedule->id)
->where('booked_count', '<', $schedule->max_appointments)
->update(['booked_count' => DB::raw('booked_count + 1')]);
if (!$affected) {
return response()->json(['message' => '号源已满,请选择其他时段'], 409);
}
// 3. 生成预约单号
$appointmentNo = 'GH' . date('Ymd') . str_pad(Appointment::max('id') + 1, 3, '0', STR_PAD_LEFT);
// 4. 创建挂号记录
DB::beginTransaction();
try {
$appointment = Appointment::create([
'user_id' => auth()->id(),
'schedule_id' => $schedule->id,
'appointment_no' => $appointmentNo,
'booking_time' => Carbon::now(),
'status' => 1
]);
// 5. 可选:发送短信通知(队列异步)
// SendSmsJob::dispatch($validated['phone'], $appointment);
DB::commit();
return response()->json(['data' => $appointment, 'message' => '预约成功'], 201);
} catch (\Exception $e) {
DB::rollBack();
// 回滚号源计数
Schedule::where('id', $schedule->id)->decrement('booked_count');
return response()->json(['message' => '预约失败,请稍后重试'], 500);
}
}
}
医生排班管理(管理端)
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Schedule;
use Carbon\Carbon;
use Illuminate\Http\Request;
class ScheduleController extends Controller
{
/**
* 生成医生未来7天排班
*/
public function generate(Doctor $doctor)
{
$startDate = Carbon::now()->addDay();
$endDate = Carbon::now()->addDays(7);
$dates = [];
while ($startDate <= $endDate) {
// 假设周一至周五出诊
if (!in_array($startDate->dayOfWeek, [0, 6])) {
$dates[] = $startDate->format('Y-m-d');
}
$startDate->addDay();
}
foreach ($dates as $date) {
// 上午时段
Schedule::firstOrCreate([
'doctor_id' => $doctor->id,
'schedule_date' => $date,
'time_slot' => 'morning',
], [
'max_appointments' => $doctor->max_appointments,
'booked_count' => 0,
'status' => 1
]);
// 下午时段
Schedule::firstOrCreate([
'doctor_id' => $doctor->id,
'schedule_date' => $date,
'time_slot' => 'afternoon',
], [
'max_appointments' => $doctor->max_appointments,
'booked_count' => 0,
'status' => 1
]);
}
return response()->json(['message' => '排班生成成功']);
}
}
取消预约(带锁机制)
public function cancel(Appointment $appointment)
{
// 仅允许取消未就诊的预约
if ($appointment->status != 1) {
return response()->json(['message' => '当前状态不可取消'], 400);
}
// 距离预约时间少于1小时不可取消
if ($appointment->booking_time->diffInHours(Carbon::now()) < 1) {
return response()->json(['message' => '距预约时间不足1小时,不可取消'], 400);
}
DB::transaction(function () use ($appointment) {
// 回滚号源
Schedule::where('id', $appointment->schedule_id)->decrement('booked_count');
// 更新状态
$appointment->update(['status' => 3]);
});
return response()->json(['message' => '取消成功']);
}
关键安全与并发处理
- 号源并发控制:使用
DB::raw('booked_count + 1')配合where条件实现行级乐观锁,避免超售。 - 防重复预约:在
appointments表增加(user_id, schedule_id)唯一索引,或按业务要求限制当天同一医生只能预约一次。 - 接口限流:使用 Laravel 的
throttle中间件,限制用户每分钟请求次数。 - 敏感操作日志:记录取消、修改等操作日志到
logs表(user_id,action,target_type,target_id)。
前端界面简要设计
- 患者端(手机适配)
- 首页:选择科室 → 选择医生 → 查看排班 → 选择时段 → 确认支付(可选)→ 生成挂号单
- 我的挂号:查看历史挂号、取消、支付
- 医生端
- 查看今日待诊列表
- 点击“开始就诊”更改状态为“已就诊”
- 管理员端
- 医生管理、科室管理、排班生成
- 挂号数据统计(日/月报表)
部署与优化建议
-
Redis缓存:
- 缓存科室列表
cache:departments - 缓存各医生未来7天排班
cache:doctor:{id}:schedule
- 缓存科室列表
-
队列处理:
- 短信发送、邮件通知使用 Laravel Queue + Redis
- 号源释放后异步通知排队用户(如微信通知)
-
Nginx配置优化:
server { listen 80; server_name hospital.example.com; root /var/www/hospital/public; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
可扩展功能(高级)
- 在线支付:接入支付宝/微信支付,支付成功后锁定号源。
- 排队叫号系统:实时显示当前叫号(WebSocket 或 SSE)。
- 电子病历关联:挂号后自动关联患者的电子病历。
- 多语言支持:使用 Laravel 的 多语言函数。
一个可用的医院挂号系统,核心在于号源的并发控制和数据库的索引优化,建议先搭建最小可行产品(MVP):患者注册 → 选择科室医生 → 预约成功,后续再逐步加入支付、通知、统计功能。
如果你需要更详细的代码(例如完整的用户认证、支付回调、报表生成),请告诉我,我可以单独为你提供某个模块的完整实现。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播