如何用PHP项目搭建医院挂号系统?

wen java案例 10

本文目录导读:

如何用PHP项目搭建医院挂号系统?

  1. 技术栈推荐
  2. 数据库设计(核心表结构)
  3. 核心功能模块实现(Laravel示例)
  4. 关键安全与并发处理
  5. 前端界面简要设计
  6. 部署与优化建议
  7. 可扩展功能(高级)

使用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' => '取消成功']);
}

关键安全与并发处理

  1. 号源并发控制:使用 DB::raw('booked_count + 1') 配合 where 条件实现行级乐观锁,避免超售。
  2. 防重复预约:在 appointments 表增加 (user_id, schedule_id) 唯一索引,或按业务要求限制当天同一医生只能预约一次。
  3. 接口限流:使用 Laravel 的 throttle 中间件,限制用户每分钟请求次数。
  4. 敏感操作日志:记录取消、修改等操作日志到 logs 表(user_id, action, target_type, target_id)。

前端界面简要设计

  1. 患者端(手机适配)
    • 首页:选择科室 → 选择医生 → 查看排班 → 选择时段 → 确认支付(可选)→ 生成挂号单
    • 我的挂号:查看历史挂号、取消、支付
  2. 医生端
    • 查看今日待诊列表
    • 点击“开始就诊”更改状态为“已就诊”
  3. 管理员端
    • 医生管理、科室管理、排班生成
    • 挂号数据统计(日/月报表)

部署与优化建议

  1. Redis缓存

    • 缓存科室列表 cache:departments
    • 缓存各医生未来7天排班 cache:doctor:{id}:schedule
  2. 队列处理

    • 短信发送、邮件通知使用 Laravel Queue + Redis
    • 号源释放后异步通知排队用户(如微信通知)
  3. 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;
        }
    }

可扩展功能(高级)

  1. 在线支付:接入支付宝/微信支付,支付成功后锁定号源。
  2. 排队叫号系统:实时显示当前叫号(WebSocket 或 SSE)。
  3. 电子病历关联:挂号后自动关联患者的电子病历。
  4. 多语言支持:使用 Laravel 的 多语言函数。

一个可用的医院挂号系统,核心在于号源的并发控制数据库的索引优化,建议先搭建最小可行产品(MVP):患者注册 → 选择科室医生 → 预约成功,后续再逐步加入支付、通知、统计功能。

如果你需要更详细的代码(例如完整的用户认证、支付回调、报表生成),请告诉我,我可以单独为你提供某个模块的完整实现。

抱歉,评论功能暂时关闭!