
简介这是一套面向计算机专业本科生与毕业设计开发者的PHP全栈医院预约挂号系统实战项目完整实现微信小程序前端与PHPMySQL后端的B/S架构协同。资源覆盖患者挂号、医生排班、科室管理、预约审核等核心医疗业务流程兼顾前台用户操作与后台管理员/医生双角色权限体系适用于课程设计、毕设开发及中小医疗机构信息化原型验证。压缩包共2250个文件主体为951个PHP服务端逻辑文件、148个JS与118个Vue前端交互脚本、350个PNG及43个JPG图标素材辅以WXSS/WXML样式与页面结构文件整体27.07MB目录组织规范含install.bat等一键部署脚本及SQL数据库初始化文件。目前已有584人学习下载提供开箱即用的完整源码、清晰模块划分如IndexMain.vue、update-password.vue等关键页面、配套Excel科室/医生数据模板及MySql建表语句便于快速二次开发与功能扩展。1. 这不是又一个“PHPMySQL”模板项目它是一套可上线的微信小程序挂号流程闭环你打开这个hospital-booking-php.zip第一眼看到.bak文件和一堆.vue后缀可能会误判为「前端 Vue 后端 PHP 混搭的半成品」。但实际拆开后你会发现所有.vue.bak都是微信小程序原生框架非 uni-app编译前的源码快照而1-install.bat和3-build.bat构成了一条 Windows 下可一键复现的部署流水线——它不依赖 Node.js 全局环境也不需要手动配置 webpack而是用 PHP 内置服务器 MySQL 原生驱动 小程序wx.request直连接口完成最小可行闭环。这套系统真正解决的是毕业设计中「前后端分离难落地、小程序真机调试卡在域名校验、PHP 接口返回格式不兼容 wx.request」三大高频痛点。适合计算机/信息管理专业学生直接用于答辩演示也适合基层医院信息科人员基于现有 LAMP 环境快速部署试用。它不追求高并发架构但把挂号流程中的号源锁定、时段校验、实名核验、状态回写等业务逻辑全部落在 PHP 层避免前端计算导致的数据不一致。2. 为什么选 PHP 而非 Node.js 或 Java从挂号业务约束反推技术栈选型2.1 医院挂号场景对后端语言的核心约束挂号系统不是通用 CMS它的业务逻辑天然带有强事务性与弱实时性特征号源必须原子扣减同一时段同一医生的号不能被重复预约要求数据库层面行级锁或乐观锁时段校验需本地化处理如「上午 8:00–12:00 分 4 个时段每时段限挂 15 人」需 PHP 层解析schedule_time字段并比对当前时间实名信息需脱敏存储身份证号、手机号在 MySQL 中加密存储AES_ENCRYPT()但 PHP 层需在返回给小程序前解密展示无长连接需求挂号操作频次低、单次请求耗时短无需 WebSocket 或 SSEHTTP 短连接完全够用。提示这些约束让 PHP 成为更优解。Node.js 的异步模型在高并发秒杀场景有优势但挂号系统日均请求量通常 5000PHP-FPM 模式下每个请求独占进程天然规避了 Node.js 中常见的闭包变量污染问题例如多个请求共用同一$_SESSION变量导致号源扣减错乱。2.2 MySQL 表结构设计如何支撑挂号核心流程系统使用mysql_native_password认证方式建表语句位于sql/hospital_booking.sql。关键三张表结构如下表名核心字段业务作用doctor_scheduleid,doctor_id,date,time_slot,quota,used_quota,status存储每日每时段号源总量与已用数status1表示该时段开放预约appointmentid,user_id,doctor_id,schedule_id,real_name,id_card_enc,phone_enc,status,create_time记录预约主单id_card_enc和phone_enc为 AES 加密字段userid,openid,nickname,avatar,real_name,id_card_enc,phone_enc用户基础信息openid来自微信登录凭证-- 示例查询某医生今日可预约时段含剩余号源 SELECT ds.time_slot, ds.quota - ds.used_quota AS remaining_quota, CASE WHEN ds.used_quota ds.quota THEN full ELSE available END AS status FROM doctor_schedule ds WHERE ds.doctor_id 123 AND ds.date CURDATE() AND ds.status 1 ORDER BY ds.time_slot;这段 SQL 直接嵌入 PHP 接口api/get_available_slots.php返回 JSON 格式供小程序渲染「可选时段列表」。注意CURDATE()使用 MySQL 服务端时间避免小程序客户端时间偏差导致跨日预约错误。2.31-install.bat实际执行的 5 个关键动作该批处理文件并非简单解压而是按顺序完成环境初始化检查 PHP 版本调用php --version并验证是否 ≥7.4因password_hash()函数在 7.4 支持PASSWORD_ARGON2I创建数据库执行mysql -u root -p -e CREATE DATABASE IF NOT EXISTS hospital_booking DEFAULT CHARSET utf8mb4;导入初始数据运行mysql -u root -p hospital_booking sql/hospital_booking.sql生成密钥文件调用php tools/generate_key.php生成config/aes_key.php内容为?php return 32-byte-random-string-here;设置目录权限对uploads/目录执行icacls uploads /grant Everyone:(OI)(CI)FWindows 下等效于chmod 755。注意generate_key.php使用random_bytes(32)生成 AES-256 密钥而非硬编码字符串。若跳过此步后续所有AES_DECRYPT()将返回NULL导致用户信息无法解密显示。3. 微信小程序端如何与 PHP 接口安全通信绕过域名校验的实操方案3.1 小程序project.config.json的关键配置项在miniprogram/project.config.json中必须修改以下字段才能真机调试{ description: hospital-booking, setting: { urlCheck: false, es6: true, enhance: true, postcss: true, preloadBackgroundData: false, enablePullDownRefresh: true, minified: false, newFeature: true }, compileType: miniprogram, libVersion: 2.30.2, appid: wx1234567890abcdef, // 替换为你的 AppID projectname: 医院预约挂号, debugOptions: { h5: false }, scripts: {}, condition: { search: {current: -1, list: []}, conversation: {current: -1, list: []}, game: {current: -1, list: []}, miniprogram: { current: 0, list: [ { name: 首页, path: pages/index/index, query: } ] } } }提示urlCheck: false是开发阶段必需项它禁用开发者工具对request合法域名的校验。但上线前必须关闭此项并在微信公众平台后台配置https://your-domain.com为合法 request 域名HTTP 不被允许。3.2app.js中全局请求封装与 Token 管理小程序所有网络请求统一走utils/request.js其核心逻辑如下// utils/request.js const BASE_URL http://localhost:8000/api/; // 开发时指向 PHP 内置服务器 function request(options) { const token wx.getStorageSync(token) || ; return new Promise((resolve, reject) { wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, X-App-Token: token // PHP 接口通过 $_SERVER[HTTP_X_APP_TOKEN] 获取 }, success: (res) { if (res.statusCode 200) { if (res.data.code 401) { // Token 过期跳转登录页 wx.navigateTo({ url: /pages/login/login }); return; } resolve(res.data); } else { reject(new Error(HTTP ${res.statusCode})); } }, fail: (err) reject(err) }); }); } module.exports { request };PHP 端对应验证逻辑在api/common/auth.php// api/common/auth.php $token $_SERVER[HTTP_X_APP_TOKEN] ?? ; if (empty($token)) { json_response(401, Missing token); exit; } // 查询 token 是否有效有效期 24 小时 $stmt $pdo-prepare(SELECT user_id FROM user_token WHERE token ? AND expires_at NOW()); $stmt-execute([$token]); $user $stmt-fetch(PDO::FETCH_ASSOC); if (!$user) { json_response(401, Invalid or expired token); exit; }3.3 登录态持久化与敏感信息传输规范小程序登录流程严格遵循微信官方规范调用wx.login()获取临时 code将 code 发送给 PHP 接口/api/login.phpPHP 使用curl请求微信sns/jscode2session接口换取openid若用户首次登录PHP 自动插入新记录到user表并生成user_token记录返回token给小程序存入wx.setStorageSync(token, token)。关键点在于所有含身份证、手机号的接口响应PHP 层必须做字段级脱敏。例如api/user/profile.php返回// 敏感字段仅返回脱敏后字符串 $data [ nickname $user[nickname], avatar $user[avatar], real_name mb_substr($user[real_name], 0, 1) . **, // 张** id_card substr($user[id_card_enc], 0, 4) . **** . substr($user[id_card_enc], -4), // 加密后截取展示 phone substr($user[phone_enc], 0, 3) . **** . substr($user[phone_enc], -4) ];这样既满足小程序 UI 展示需求又避免明文敏感信息经网络传输。4. 预约挂号核心逻辑实现从号源锁定到状态同步的完整链路4.1api/appointment/create.php的事务边界设计挂号操作必须保证「查号源 → 扣减 → 写订单 → 发送通知」四步原子性。PHP 使用 InnoDB 行锁实现try { $pdo-beginTransaction(); // 1. 查询并锁定该时段号源记录FOR UPDATE $stmt $pdo-prepare( SELECT id, quota, used_quota, status FROM doctor_schedule WHERE id ? AND status 1 FOR UPDATE ); $stmt-execute([$schedule_id]); $schedule $stmt-fetch(PDO::FETCH_ASSOC); if (!$schedule || $schedule[used_quota] $schedule[quota]) { throw new Exception(No available slot); } // 2. 扣减号源UPDATE 语句自带行锁 $stmt $pdo-prepare(UPDATE doctor_schedule SET used_quota used_quota 1 WHERE id ?); $stmt-execute([$schedule_id]); // 3. 插入预约订单 $stmt $pdo-prepare( INSERT INTO appointment (user_id, doctor_id, schedule_id, real_name, id_card_enc, phone_enc, status) VALUES (?, ?, ?, ?, ?, ?, pending) ); $stmt-execute([ $user_id, $doctor_id, $schedule_id, $real_name, openssl_encrypt($id_card, AES-256-CBC, $aes_key, 0, $iv), openssl_encrypt($phone, AES-256-CBC, $aes_key, 0, $iv) ]); $pdo-commit(); json_response(200, Appointment created, [order_id $pdo-lastInsertId()]); } catch (Exception $e) { $pdo-rollback(); json_response(500, $e-getMessage()); }注意FOR UPDATE必须在事务内执行且后续UPDATE语句要作用于同一行。若此处用SELECT ... LOCK IN SHARE MODE则其他会话仍可读取该行无法阻止并发超卖。4.2 小程序端「预约确认页」的防重复提交机制pages/appoint/confirm.js中用户点击「确认预约」按钮时触发onConfirm() { if (this.data.submitting) return; // 防抖开关 this.setData({ submitting: true }); request({ url: appointment/create, method: POST, data: { schedule_id: this.data.schedule.id, doctor_id: this.data.doctor.id, real_name: this.data.userInfo.real_name, id_card: this.data.userInfo.id_card, phone: this.data.userInfo.phone } }).then(res { wx.showToast({ title: 预约成功, icon: success }); setTimeout(() wx.switchTab({ url: /pages/index/index }), 1500); }).catch(err { wx.showToast({ title: err.message, icon: none }); }).finally(() { this.setData({ submitting: false }); // 恢复按钮状态 }); }该机制从 UI 层杜绝了用户连续点击导致多次请求的问题与后端事务锁形成双重保障。4.3 后台管理端的号源批量调整功能管理员可通过admin/schedule/batch-update.php批量修改某天所有医生的号源配额。接口接收 JSON 数组[ {doctor_id: 101, date: 2024-06-15, time_slot: 08:00-09:00, quota: 20}, {doctor_id: 101, date: 2024-06-15, time_slot: 09:00-10:00, quota: 15}, {doctor_id: 102, date: 2024-06-15, time_slot: 08:00-09:00, quota: 10} ]PHP 处理逻辑使用INSERT ... ON DUPLICATE KEY UPDATE语法避免逐条判断是否存在记录foreach ($data as $item) { $stmt $pdo-prepare( INSERT INTO doctor_schedule (doctor_id, date, time_slot, quota, used_quota, status) VALUES (?, ?, ?, ?, 0, 1) ON DUPLICATE KEY UPDATE quota VALUES(quota), status VALUES(status) ); $stmt-execute([$item[doctor_id], $item[date], $item[time_slot], $item[quota]]); }前提是doctor_schedule表设置了联合唯一索引ALTER TABLE doctor_schedule ADD UNIQUE KEY uk_doctor_date_slot (doctor_id, date, time_slot);5. 本地开发调试避坑指南Windows 下 Nginx PHP MySQL 环境快速搭建5.1 为什么推荐nginx php-cgi而非 ApacheApache 的.htaccess重写规则在 Windows 下常因路径分隔符\vs/出错而 Nginx 的location块对 Windows 路径兼容性更好。更重要的是Nginx 的fastcgi_pass可直接指向127.0.0.1:9000无需像 Apache 那样依赖mod_php模块加载。5.2nginx.conf关键配置片段将以下内容追加到nginx/conf/nginx.conf的http块内server { listen 8000; server_name localhost; root D:/projects/hospital-booking-php; index index.php; location / { try_files $uri $uri/ /index.php?$args; } 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; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control public, immutable; } }注意root路径必须使用正斜杠/即使 Windows 系统也要写D:/projects/...否则 Nginx 会报invalid number of arguments in root directive错误。5.3 PHP-FPM 启动参数调优编辑php/php-fpm.conf重点修改以下三项参数推荐值说明listen 127.0.0.1:9000必须与 nginxfastcgi_pass一致避免 Unix socket 在 Windows 下权限问题pm static开发环境设为static避免dynamic模式下子进程数波动影响调试pm.max_children 10根据内存调整4GB 内存建议 ≤10每个 child 占用约 20MB过高会导致 OOM启动命令# 以控制台模式运行便于查看错误日志 php-fpm.exe -c php/php.ini -y php/php-fpm.conf -F5.4 MySQL 字符集与排序规则强制统一在my.ini中[mysqld]段落添加[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci init_connectSET NAMES utf8mb4 skip-character-set-client-handshakeTRUE然后重启 MySQL 服务并执行-- 验证当前连接字符集 SHOW VARIABLES LIKE character_set%; SHOW VARIABLES LIKE collation%; -- 修改已有数据库字符集谨慎操作 ALTER DATABASE hospital_booking CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;若跳过此步中文搜索如科室名称「心血管内科」可能返回空结果因为utf8编码不支持 emoji 和部分生僻汉字。6. 毕业设计答辩高频问题应答策略聚焦业务逻辑而非技术堆砌6.1 当被问「为什么不用 Laravel 而用原生 PHP」时的回答要点不要说「Laravel 太重」而要聚焦业务适配性「挂号系统核心是号源状态机available → locked → confirmed → cancelledLaravel 的 Eloquent ORM 默认开启lazy loading若在Doctor模型中关联Schedule一次getAvailableSlots()可能触发 N1 查询而原生 PDO 预处理语句可精准控制 JOIN 和 WHERE 条件」「Laravel 的中间件机制在登录态校验上需额外配置auth:apiguard而本系统直接在api/common/auth.php中用 12 行代码完成 token 解析与过期判断逻辑更透明答辩时可逐行讲解」「毕业设计评分标准强调『可独立部署』Laravel 需 Composer 安装依赖而本系统所有 PHP 文件均无外部包引用1-install.bat即可完成全部初始化降低答辩现场环境故障率」。6.2 展示「号源锁定正确性」的实测方法准备两台手机或一台手机 开发者工具模拟器同时执行以下操作手机 A 进入「张医生 08:00–09:00」时段点击「预约」在手机 A 显示「正在提交」时手机 B 立即刷新同一时段页面观察手机 B 页面该时段应显示「已约满」且doctor_schedule.used_quota字段在 MySQL 中已 1刷新手机 A 页面确认订单状态为「待支付」而非「重复下单」。此过程证明SELECT ... FOR UPDATE与UPDATE ... SET used_quota used_quota 1的组合确实实现了行级互斥。6.3 数据库备份与恢复的自动化脚本将tools/backup_db.bat放入项目根目录内容如下echo off setlocal enabledelayedexpansion set DB_NAMEhospital_booking set BACKUP_DIRbackup set TIMESTAMP%date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2%%time:~6,2% set TIMESTAMP%TIMESTAMP: 0% if not exist %BACKUP_DIR% mkdir %BACKUP_DIR% mysqldump -u root -p your_password %DB_NAME% %BACKUP_DIR%\%DB_NAME%_%TIMESTAMP%.sql echo Backup saved to %BACKUP_DIR%\%DB_NAME%_%TIMESTAMP%.sql pause提示答辩前务必执行一次备份将生成的.sql文件与源码一同刻录进光盘。评委若质疑数据安全性可当场演示「删除 appointment 表后双击restore.bat一键还原」。真正的毕业设计价值不在于用了多少框架而在于能否用最简技术链路把「患者想挂上号」这个朴素需求稳稳地落在每一行 PHP 代码、每一个 MySQL 事务、每一次小程序wx.request调用之上。本文还有配套的精品资源点击获取