workerman仅负责后台调度与websocket推送,语音播报必须由终端设备(如浏览器web audio api或物理呼叫器)执行,严禁后端调用声卡。

Workerman 本身不直接支持音频播放或硬件声卡控制,医院叫号系统的「声音播报」必须依赖外部服务或终端设备,Workerman 只适合做后台调度、状态管理、WebSocket 推送和排队逻辑——这是架构前提,绕不开。
用 Worker + WebServer 承接叫号 Web 管理端和大屏展示
门诊叫号系统需要实时刷新窗口号、患者姓名、状态(正在呼叫/已过号/已就诊),前端不能靠轮询。Workerman 的 WebServer 配合 Worker 实例可稳定支撑百级并发的 HTML/JS 页面服务;关键是要把 Worker 实例和 TextServer(或 GatewayWorker)分离部署,避免 Web 请求阻塞消息广播。
- 管理端(护士站)通过 POST 提交叫号指令,由
WebServer接收并写入共享内存(如pcntl_shmop)或 Redis,再通知所有在线窗口屏更新 - 大屏和窗口屏用 WebSocket 连接
GatewayWorker,收到指令后触发本地 JS 播放音频(注意:音频文件需提前加载到浏览器缓存,否则首次播放有延迟) - 别在
onMessage回调里执行sleep()或文件读取,会卡住整个进程;音频路径应由前端决定,后端只推{ "window": "A03", "name": "张三", "type": "call" }这类轻量结构
用 Timer::add 实现候诊队列自动叫号与超时判定
真实场景中,护士不会每叫一个号都点一次鼠标。需要定时器驱动队列流转:比如“当前窗口空闲 → 自动从该科室队列取下一位 → 广播 → 启动 90 秒倒计时 → 超时未应答则标记为过号”。Timer::add 是唯一可靠方式,但要注意它运行在主进程,不能做耗时操作。
- 队列数据必须存在 Redis 或 MySQL 中,
Timer回调只负责查、改状态、发 WebSocket 消息,绝不执行file_get_contents()或 shell 命令 - 多个窗口共用一个科室队列时,用 Redis
lpop+setnx保证原子性,避免重复叫号 - 倒计时逻辑不要放在 PHP 层做,前端收到
"status":"calling"后自行启动 JSsetTimeout;后端只需在超时后发一条{"window":"A03","status":"timeout"}更新状态
声音播报必须交给终端侧,Workerman 不碰音频设备
Linux 服务器默认无音频子系统,exec('aplay') 或 shell_exec('say') 在 Docker 容器或无桌面环境必然失败;即使成功,多进程并发调用声卡也会冲突报错 Device or resource busy。所有语音合成和播放必须下沉到终端设备。
- 窗口屏用 Chrome 浏览器 + Web Audio API 播放预置 MP3(如 “请 A03 号张三到 1 号诊室”),MP3 文件按模板生成并存于 Nginx 静态目录
- 若需 TTS(文字转语音),用前端 Web Speech API(Chrome 支持较好),或调用公网 TTS 接口(如阿里云语音合成),返回 base64 音频再播放;严禁后端生成语音文件再推送
- 有物理呼叫器(带喇叭的 LED 设备)?让它连上局域网,Workerman 用
socket_client发 UDP 包(如"CMD:PLAY,A03"),由嵌入式固件解析执行——这才是工业级做法
最易被忽略的一点:叫号音量必须可独立调节。每个窗口屏的音量是物理属性,不能由后端统一控制。系统设计之初就要约定,音量调节按钮只作用于本机浏览器或外接设备,Workerman 只管“播什么”,不管“多大声”。











