PHP互联网医院在线问诊系统源码解析与二次开发实践

发布时间:2026/10/7 9:26:28
PHP互联网医院在线问诊系统源码解析与二次开发实践 简介这份源码面向医疗信息化开发者、创业团队及计算机专业学生提供一套基于PHP、HTML、JavaScript、CSS与Shell技术栈的互联网医院在线问诊系统实现方案可用于搭建在线问诊、在线开方、在线药房、随访、慢病管理及家庭医生等业务模块并涉及互联网医院牌照申请相关流程适合作为毕业设计、课程项目或中小型医疗平台的二次开发基础。资源包共2000个文件约160.15MB以1224个HTML页面、409个JavaScript脚本、162个CSS样式表为主另含properties配置、json数据、pdf文档及少量xml与md说明前端页面、交互逻辑与样式资源分层清晰便于按模块定位与改造。目前已有118人学习下载。整体目录结构完整涵盖问诊流程、药房管理与后台配置等典型场景读者可据此快速理解互联网医院系统的功能划分与前后端协作方式并在此基础上进行功能裁剪或扩展节省从零搭建的时间成本。1. 从一份 PHP 互联网医院源码说起在线问诊系统到底怎么落地前阵子帮一个做基层医疗信息化的朋友看项目他手里拿到一份「基于 PHP/HTML/JavaScript/CSS/Shell 的互联网医院在线问诊设计源码」第一反应是「这不就是个网页吗能跑起来吗」。我拆完目录之后发现这套东西的价值不在界面多花哨而在于它把在线问诊里最麻烦的几件事——患者建档、医生排班、图文问诊会话、处方与订单状态流转——用一套很朴素的 LAMP 思路串了起来。对中小团队或者个人开发者来说它更像一个能直接改的业务骨架而不是一个成品 SaaS。它适合三类人一是想快速搭一个问诊 Demo 去验证业务闭环的产品或全栈二是需要一份可读性强的 PHP 分层代码来对照学习 MVC 落地三是手里有医疗资源、想低成本先跑通线上咨询流程的创业者。技术栈上后端 PHP 负责接口与业务逻辑HTML/CSS/JavaScript 撑起患者端和医生端页面Shell 脚本则承担部署、定时任务、日志清理这类运维动作。下面我按「它是什么 → 怎么跑起来 → 坑在哪 → 怎么改」的顺序把这份源码拆开讲清楚。2. 目录结构与运行环境先让系统在本地跑起来拿到源码包别急着改代码先确认它能不能在你机器上跑通。这一步做扎实后面调业务逻辑才不会被环境问题干扰。2.1 目录分层与各技术栈的职责边界一份典型的 PHP 问诊系统目录大致会分成入口层、应用层、静态资源和运维脚本四块。入口层是index.php这类单入口文件负责路由分发和会话初始化应用层按模块拆成controller、model、view问诊会话、医生排班、订单各自独立静态资源放public/下的 CSS、JavaScript、图片Shell 脚本一般放在scripts/或项目根目录处理备份、清理、启动服务。目录技术栈职责/根目录PHP入口文件、配置文件、路由app/controllerPHP接收请求、调度业务、返回 JSON 或视图app/modelPHP数据库读写、状态机、事务app/viewHTML/PHP患者端、医生端页面模板public/cssCSS布局、响应式、表单样式public/jsJavaScript表单校验、轮询消息、异步提交scripts/Shell部署、定时任务、日志轮转这里要提醒一句PHP 项目最容易出现「入口文件散落各处」的问题。如果源码里每个功能都有一个独立xxx.php直接对外那说明它没做统一路由后期加权限校验会很痛苦。常见做法是保留一个index.php作为唯一入口用?ccontrolleraaction或 PATH_INFO 方式分发。2.2 环境准备与依赖安装PHP 版本建议 7.4 到 8.1 之间太老的 5.x 跑不动现代写法8.2 以上又可能碰到部分扩展兼容问题。数据库用 MySQL 5.7 或 8.0 都行字符集统一utf8mb4否则患者姓名里的生僻字会变问号。Web 服务器用 Nginx 或 Apache 都可以Nginx 配 PHP-FPM 更常见。# 以 Ubuntu 为例安装 PHP 及常用扩展 sudo apt update sudo apt install -y php8.1-fpm php8.1-mysql php8.1-mbstring php8.1-curl php8.1-gd # 安装 MySQL 与 Nginx sudo apt install -y mysql-server nginx # 确认 PHP-FPM 运行 systemctl status php8.1-fpm这段命令做了三件事装 PHP 运行时和问诊系统必需的扩展mbstring处理中文、curl调外部接口、gd生成验证码或缩略图装数据库和 Web 服务器最后确认 PHP-FPM 进程活着。参数上php8.1-fpm的版本号要和你系统源里实际有的版本对齐装不上就换成php7.4-fpm。数据库初始化一般源码里会带一个.sql文件导入时注意先建库再导表mysql -u root -p -e CREATE DATABASE telemed DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -u root -p telemed database/telemed.sql第一句建库并指定字符集第二句把表结构和初始数据灌进去。如果导入报「Unknown character set」说明 SQL 文件里写的是utf8mb4_0900_ai_ci而你的 MySQL 是 5.7把排序规则改成utf8mb4_general_ci即可。2.3 配置文件与首次访问验证PHP 项目的配置通常集中在config/database.php或.env文件里。需要改的项一般有四项数据库主机、库名、用户名、密码。改完之后访问http://你的域名/index.php能看到登录页或患者首页说明入口通了。// config/database.php 典型结构 return [ host 127.0.0.1, port 3306, dbname telemed, user telemed_user, pass your_password, charset utf8mb4, ];这段配置返回一个数组业务层用require引入后拿 PDO 连接。参数里charset必须和建库时一致否则中文乱码host用127.0.0.1比localhost更稳因为后者在某些系统上会走 socket 而不是 TCP排查连接问题时容易绕晕。首次访问如果白屏先看 PHP 错误日志再看 Nginx 的error.log。常见原因是runtime/或storage/目录没有写权限Shell 里一条chmod -R 775 runtime就能解决。这一步过了系统才算真正「活着」。3. 在线问诊核心链路会话、排班与订单怎么串环境跑通只是开始真正决定这套源码能不能用的是问诊业务链路是否闭环。这一章拆三个关键模块图文问诊会话、医生排班、订单与处方状态。3.1 图文问诊会话的建立与消息轮询在线问诊最核心的动作是「患者发起咨询 → 医生接诊 → 双方发消息 → 结束问诊」。PHP 本身不适合长连接所以这类系统普遍用「短轮询 消息表」实现。患者端 JavaScript 每隔几秒请求一次接口拉取新消息。// public/js/consult.js 消息轮询片段 let lastId 0; async function pollMessages(sessionId) { try { const res await fetch(/index.php?cconsultapollsession_id${sessionId}last_id${lastId}); const data await res.json(); if (data.code 0 data.list.length) { data.list.forEach(msg renderMessage(msg)); lastId data.list[data.list.length - 1].id; // 更新游标避免重复拉取 } } catch (e) { console.warn(轮询失败稍后重试, e); } } setInterval(() pollMessages(currentSessionId), 3000);这段代码用lastId做增量游标每次只拉比上次更新的消息避免全量刷新。setInterval设 3000 毫秒是常见折中太短会给服务器压力太长医生回复会有明显延迟。参数session_id是问诊会话主键last_id初始为 0 表示拉全部历史。注意fetch失败时不要直接弹错误框问诊场景里网络抖动很常见静默重试体验更好。后端对应的poll方法一般是// app/controller/ConsultController.php public function poll() { $sessionId (int)$_GET[session_id]; $lastId (int)$_GET[last_id]; // 校验当前用户是否有权访问该会话 $this-checkSessionAuth($sessionId); $list $this-consultModel-getMessagesAfter($sessionId, $lastId); $this-json([code 0, list $list]); }checkSessionAuth这步不能省。我见过不少源码为了省事直接按session_id查消息结果换个 ID 就能看到别人的问诊记录这是医疗类系统里最不能接受的漏洞。getMessagesAfter用WHERE session_id ? AND id ? ORDER BY id ASC实现走主键索引数据量大了也不慢。3.2 医生排班与可预约时段计算排班模块的难点不在存而在「算」。医生设置的是「每周一、三、五上午 9 点到 12 点」但患者看到的是「今天还有哪几个 15 分钟可以约」。这中间要做时段切分和已约占用排除。// app/model/ScheduleModel.php 生成可预约时段 public function getAvailableSlots($doctorId, $date) { $rule $this-getWeeklyRule($doctorId, date(N, strtotime($date))); // 取当天排班规则 if (!$rule) return []; $slots []; $start strtotime($date . . $rule[start_time]); $end strtotime($date . . $rule[end_time]); $step 15 * 60; // 每个号源 15 分钟 $booked $this-getBookedSlots($doctorId, $date); // 已约时间戳数组 for ($t $start; $t $end; $t $step) { if (!in_array($t, $booked)) { $slots[] date(H:i, $t); } } return $slots; }逻辑是先按星期几取出排班规则把起止时间转成时间戳以 15 分钟为步长遍历跳过已被预约的时段。参数$step决定号源粒度改成30 * 60就是半小时一个号。getBookedSlots返回已占用时间戳数组实际项目里建议直接返回H:i字符串做键比较更直观。提示时段计算一定要用服务器时区别依赖客户端传时间。患者手机时区不对约出来的号会错位。3.3 订单与处方状态流转问诊结束后往往要开处方、生成订单。状态流转最怕「乱跳」比如未支付直接变成已完成。稳妥做法是在模型层定义合法迁移表任何状态变更都走同一个方法。当前状态允许迁移到触发动作待接诊问诊中、已取消医生接诊 / 患者取消问诊中待支付、已结束医生开方 / 结束问诊待支付已支付、已取消支付回调 / 超时取消已支付配送中、已完成药房发货 / 确认收货// app/model/OrderModel.php 状态机校验 private $transitions [ pending [consulting, cancelled], consulting [unpaid, finished], unpaid [paid, cancelled], paid [shipping, done], ]; public function changeStatus($orderId, $to) { $order $this-find($orderId); if (!in_array($to, $this-transitions[$order[status]] ?? [])) { throw new Exception(非法状态流转); } // 更新并写日志 $this-update($orderId, [status $to]); $this-logStatus($orderId, $order[status], $to); }$transitions数组就是白名单changeStatus先查当前状态允许去哪不在名单里直接抛异常。这样即使前端传了奇怪参数后端也拦得住。logStatus记录每次变更出问题时能回溯是谁在什么时候改的。4. 避坑与排查这套源码最容易翻车的五个地方源码能跑不等于能用下面这几条是我实际拆解和部署时踩过的按「现象 → 原因 → 解决」写清楚。4.1 中文乱码从建库到页面全链路排查现象是患者姓名、医生简介显示成问号或方块。原因通常有三层数据库字符集不是utf8mb4、PHP 连接没设charset、HTML 页面没声明meta charsetutf-8。解决要三层一起查建库时用utf8mb4PDO 连接串加charsetutf8mb4页面head里补上 meta 声明。少任何一层都可能乱。4.2 轮询接口 502PHP-FPM 进程被打满现象是问诊页面消息一直转圈Nginx 日志里大量 502。原因是轮询间隔太短、并发一上来 PHP-FPM 子进程不够用。解决分两步先把轮询间隔从 1 秒调到 3 到 5 秒再适当调大pm.max_children。如果问诊量大建议把轮询换成基于消息队列的推送别硬扛。4.3 Shell 脚本权限与路径问题现象是定时清理日志的 Shell 脚本手动跑没问题放进 crontab 就不执行。原因是 crontab 环境变量和登录 shell 不同脚本里用了相对路径或依赖PATH。解决是在脚本开头写绝对路径显式声明PATH并在 crontab 里用bash /full/path/script.sh调用。#!/bin/bash export PATH/usr/local/bin:/usr/bin:/bin LOG_DIR/var/www/telemed/runtime/log find $LOG_DIR -name *.log -mtime 7 -delete-mtime 7表示删除 7 天前的日志-delete直接删不经过rm。路径全部写绝对避免 crontab 找不到。4.4 会话越权换个 ID 就能看别人问诊现象是修改 URL 里的session_id能看到其他患者的问诊记录。原因是接口只按 ID 查没校验当前登录用户。解决是在每个涉及会话、订单的接口里加归属校验WHERE id ? AND (patient_id ? OR doctor_id ?)查不到就返回无权限。4.5 支付回调重复触发导致订单重复现象是同一笔订单被标记多次已支付甚至重复发货。原因是支付平台回调可能重试而代码没做幂等。解决是在changeStatus前先判断当前状态已经是paid就直接返回成功不再重复处理。幂等这层不做后面对账会很痛苦。5. 二次开发与验证把源码改成自己的问诊系统跑通、避坑之后真正要花时间的是按自己业务改。这里给几个我常用的改造方向和验证手法。5.1 接口鉴权与数据隔离的加固原始源码如果用的是 session 鉴权改成 token 更利于前后端分离。可以在登录成功后签发一个带过期时间的 token存 Redis接口层统一校验。数据隔离上所有查询强制带patient_id或doctor_id条件别依赖前端传参。// 统一鉴权中间件思路 public function auth() { $token $_SERVER[HTTP_AUTHORIZATION] ?? ; $uid $this-redis-get(token: . $token); if (!$uid) $this-json([code 401, msg 未登录], 401); return $uid; // 后续业务用这个 uid 做数据隔离 }HTTP_AUTHORIZATION头拿 tokenRedis 查不到就 401。返回的$uid贯穿后续查询保证每个人只能碰自己的数据。5.2 用 Shell 做部署与健康检查Shell 在这套系统里不只是清理日志还能做一键部署和健康检查。我一般会写一个deploy.sh拉代码、装依赖、跑迁移、重启 FPM再写一个health.sh定时探测关键接口。#!/bin/bash # health.sh 探测问诊首页与轮询接口 URLhttp://127.0.0.1/index.php?cconsultapollsession_id1last_id0 CODE$(curl -s -o /dev/null -w %{http_code} $URL) if [ $CODE ! 200 ]; then echo $(date) 健康检查失败状态码 $CODE /var/log/telemed_health.log ficurl -w %{http_code}只取状态码-o /dev/null丢弃响应体。非 200 就写日志配合监控告警。这个脚本放进 crontab 每分钟跑一次能提前发现服务异常。5.3 验证改造是否成功的三个动作改完之后别只看页面能打开。第一用两个不同账号分别登录互相访问对方的session_id确认返回无权限第二模拟支付回调重复发送两次确认订单状态只变一次第三把轮询间隔调大后观察服务器负载确认 502 不再出现。这三步走完才算真正把源码变成了能用的系统。从那以后我每次拿到这类 PHP 问诊源码都会先跑一遍越权测试和幂等测试再谈功能扩展。希望帮到你。本文还有配套的精品资源点击获取