本文目录导读:

在PHP项目中实现在线问诊功能是一个复杂的系统工程,涉及前端交互、后端业务逻辑、数据安全、实时通信以及医疗合规等多个方面,下面我会为你提供一个从基础到进阶的设计思路和实现步骤。
核心功能模块
一个完整的在线问诊系统通常包含以下模块:
-
用户端
- 图文咨询:发送文字消息、图片(如病历、检查单)。
- 语音/视频咨询:实时音视频通话。
- 问诊记录:查看历史对话、处方、医生建议。
- 支付:支付咨询费。
-
医生端
- 接诊面板:查看待接诊、进行中的患者列表。
- 回复系统:输入文字、发送图片、发送语音、发起视频。
- 开具处方/诊断:填写诊断结果、开具电子处方(药品、用法用量)。
- 患者管理:查看历史患者,回顾历史病历。
-
管理后台
- 医生管理:审核医生资质、排班、上下线。
- 问诊监控:查看实时问诊状态,处理投诉。
- 数据统计:问诊量、收入、用户满意度等。
技术架构选择
关键决策:实时通信方案
这是在线问诊的核心,PHP 是阻塞式的,不适合直接处理长时间的双向连接(如WebSocket),因此你需要选择以下方案之一:
-
方案A:采用第三方服务(推荐)
- 优点:开发成本低,稳定性高,自带合规证书。
- 流行选项:
- 腾讯云IM(即时通信):集成简单,支持文字、图片、语音、视频。
- 声网(Agora):专注音视频领域,延迟极低。
- 环信、融云:成熟的IM/PaaS平台。
- 工作流程:
- 你的PHP后端生成通信所需的 Token/AppKey。
- 前端(H5/App)使用SDK直接连接第三方服务器进行实时通信。
- PHP后端通过第三方提供的回调或REST API 获取聊天记录、通话时长等数据并存储到自己的数据库。
-
方案B:自建WebSocket服务 + Swoole/Workerman
- 原理:PHP 7/8配合Swoole扩展或Workerman框架,常驻内存,性能接近Go/Java。
- 优点:完全控制数据,长期来看成本可控。
- 缺点:开发复杂度高,需要对Swoole/Workerman有深入了解,需要处理高并发下的连接管理。
- 实现:PHP后端作为WebSocket服务器,前端建立WebSocket连接,PHP负责转发消息、存储聊天记录。
存储与业务逻辑
- PHP框架:Laravel、ThinkPHP、Symfony 等。
- 数据库:
- MySQL:存储用户信息、订单、病历、药品库。
- Redis:缓存医生在线状态、未读消息数、会话锁。
- 文件存储:阿里云OSS、腾讯云COS 或 本地分布式存储。不要存图片到数据库。
数据库表设计 (核心表)
-- 问诊订单表
CREATE TABLE `consultation_orders` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_sn` varchar(32) NOT NULL COMMENT '订单号',
`user_id` bigint unsigned NOT NULL COMMENT '患者ID',
`doctor_id` bigint unsigned NOT NULL COMMENT '医生ID',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '状态: 0待支付, 1待接诊, 2进行中, 3已完成, 4已取消',
`type` tinyint NOT NULL COMMENT '类型: 1图文, 2语音, 3视频',
`amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '金额',
`start_time` datetime DEFAULT NULL COMMENT '接诊时间',
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_doctor_id` (`doctor_id`),
KEY `idx_status` (`status`)
);
-- 消息记录表 (如果自建IM)
CREATE TABLE `chat_messages` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` bigint unsigned NOT NULL COMMENT '会话ID',
`sender_type` tinyint NOT NULL COMMENT '发送者类型: 1患者, 2医生',
`msg_type` tinyint NOT NULL COMMENT '消息类型: 1文本, 2图片, 3语音, 4视频',
`content` text COMMENT '文本内容 或 文件URL',
`duration` int DEFAULT '0' COMMENT '音频/视频时长(秒)',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_order_id` (`order_id`)
);
-- 处方表
CREATE TABLE `prescriptions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_id` bigint unsigned NOT NULL,
`doctor_id` bigint unsigned NOT NULL,
`diagnosis` text COMMENT '诊断结果',
`advice` text COMMENT '医师建议',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- 处方明细表
CREATE TABLE `prescription_items` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`prescription_id` bigint unsigned NOT NULL,
`drug_name` varchar(100) NOT NULL COMMENT '药品名称',
`specification` varchar(50) COMMENT '规格',
`dosage` varchar(50) COMMENT '用量',
`frequency` varchar(50) COMMENT '频次',
`days` tinyint COMMENT '天数',
`quantity` int COMMENT '数量',
PRIMARY KEY (`id`)
);
核心流程实现 (以“图文咨询 + 第三方IM”为例)
创建问诊订单
// ConsultationController.php (Laravel 示例)
public function createOrder(Request $request) {
// 1. 验证参数
$userId = auth()->id();
$doctorId = $request->input('doctor_id');
$type = $request->input('type', 1); // 1=图文
// 2. 检查医生在线状态 (从Redis获取)
$doctorOnline = Redis::get('doctor:online:'.$doctorId);
if (!$doctorOnline) {
return response()->json(['error' => '医生当前不在线'], 400);
}
// 3. 生成订单(状态为待支付)
$order = ConsultationOrder::create([
'order_sn' => generateOrderSn(),
'user_id' => $userId,
'doctor_id' => $doctorId,
'type' => $type,
'status' => 0, // 待支付
'amount' => $doctor->consultation_fee,
]);
// 4. 生成支付凭证 (调用支付网关)
// ...
return response()->json(['order' => $order, 'payment_url' => $payUrl]);
}
支付成功后,分配会话
// PaymentController.php (支付回调)
public function paymentCallback(Request $request) {
// 1. 验证签名
// 2. 更新订单状态为 "待接诊"
$order = ConsultationOrder::where('order_sn', $request->order_sn)->first();
$order->status = 1;
$order->save();
// 3. 通过第三方IM创建会话 (例如使用腾讯云IM)
// 调用腾讯云IM API:创建一个新的单聊会话
// 参数:from_account = 系统管理员,to_account = 医生ID,会话组 = 患者ID
$imService->createConversation($order->user_id, $order->doctor_id);
// 4. 通知医生有新订单 (通过WebSocket 或 推送)
event(new NewConsultationEvent($order));
// 5. 生成通信Token
$userSig = $imService->genUserSig($order->user_id);
$doctorSig = $imService->genUserSig($order->doctor_id);
// 返回给前端
}
前端接入第三方IM
// 前端 (JS SDK 示例)
// 1. 初始化 SDK
let tim = TIM.create({
SDKAppID: YOUR_SDK_APP_ID
});
// 2. 登录 (用户点击开始咨询时)
tim.login({
userID: userId,
userSig: userSig // 从PHP后端获取
});
// 3. 获取会话列表
let conversation = tim.getConversationList();
// 4. 发送消息 (用户输入并点击发送)
let message = tim.createTextMessage({
to: doctorUserId,
conversationType: TIM.TYPES.CONV_C2C,
payload: { text: '医生您好,我最近经常头痛...' }
});
tim.sendMessage(message);
// 5. 接收消息 (监听事件)
tim.on(TIM.EVENT.MESSAGE_RECEIVED, function(event) {
// 新消息到达,更新聊天界面
});
医生开具处方
// DoctorPrescriptionController.php
public function createPrescription(Request $request, $orderId) {
$order = ConsultationOrder::findOrFail($orderId);
// 验证医生身份
if ($order->doctor_id != auth()->id()) {
abort(403);
}
// 创建处方主表
$prescription = Prescription::create([
'order_id' => $orderId,
'doctor_id' => auth()->id(),
'diagnosis' => $request->input('diagnosis'),
'advice' => $request->input('advice'),
]);
// 创建处方明细
foreach ($request->input('drugs') as $drug) {
$prescription->items()->create($drug);
}
// 更新订单状态为 "已完成"
$order->status = 3; // 已完成
$order->end_time = now();
$order->save();
// 通知患者 (推送)
return response()->json(['prescription' => $prescription->load('items')]);
}
必须注意的关键问题
-
数据安全与合规
- 问诊数据是敏感医疗数据。必须使用HTTPS 传输。
- 数据库中的问诊记录、处方、聊天记录等应做加密存储(字段级加密或全库加密)。
- 严格遵守当地医疗数据保护法规(如中国的《互联网诊疗管理办法》),问诊记录需保留至少15年。
-
医生资质核验
- 上线前必须强制医生上传《医师执业证》、《医师资格证》。
- 后台有严格的审核流程,确保“人证合一”。
-
责任划分
- 清晰的用户协议,界定“在线问诊仅提供健康咨询,不能替代线下诊疗”。
- 处方药需要更强的审核机制(如医生电子签名)。
-
性能优化
- 冷热数据分离:将最近1个月的问诊记录放在MySQL热库,历史数据定期迁移到归档库或ES。
- 缓存:医生排班、在线状态、常见问答等缓存到Redis。
-
应急处理
- 断线重连:音视频或IM服务需要实现牢固的重连机制。
- 超时未接诊:如果医生5分钟未接诊,系统自动取消订单并全额退款。
总结与推荐
-
学习成本最低、最稳定的方案:PHP(Laravel)+ 第三方IM/音视频服务(腾讯云IM/声网)。
- PHP专注于处理订单、用户、支付、处方这些业务逻辑。
- 实时通信交给专业的第三方平台,你只需要几个API调用就搞定了。
-
如果想自己秀肌肉或成本极致压缩:PHP(Laravel) + Workerman/Swoole + WebSocket。
- 你自己实现消息的路由、持久化、推送。
- 注意:这会消耗大量时间和Bug调试,除非你的团队有Swoole专家,否则不推荐自建IM。
额外建议:不要试图在一个Query里查所有聊天记录,分页加载,一定要做好图片、语音文件的CDN加速,否则医生打开患者发的检查单图片会非常慢。
如果需要更具体的代码片段或某个模块的深入讲解,可以告诉我你卡在哪一步。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播