医院智能挂号系统:小程序+Servlet+MySQL与语音科室推荐

发布时间:2026/10/29 12:15:33
医院智能挂号系统:小程序+Servlet+MySQL与语音科室推荐

简介:医院智能挂号系统设计和实现的完整PDF文档,面向医疗信息化研发人员、系统架构师及高校软件工程相关专业师生,针对传统挂号流程效率低、特殊人群操作难等问题给出了系统化解决方案。文档基于实际科研项目,详细阐述手动预约、语音输入预约、图形预约三种挂号方式,并涵盖症状科普、看病笔记、看病流程引导、模拟取号与模拟诊断等配套模块,可用于指导智能医疗系统的设计与开发。资源包仅含1个PDF文件,总大小1.88MB,文档结构完整,附有系统框架、小程序端异步请求、Java项目结构及Servlet类图等关键设计图示。目前已有99人学习浏览,内容既涉及自然语言理解、语音识别、高并发后台等核心技术难点,也包含与医院信息系统集成及移动支付对接的考虑,可为毕业设计、课程项目或医疗信息化改造提供直接参考与代码实现思路。

1. 医院智能挂号系统:不只是把挂号搬到网上

医院智能挂号系统的难点从来不在“挂号”本身,而在面对老人、儿童、视障用户这类特殊群体时,交互怎么设计、语音怎么识别、科室怎么推荐。这篇论文给出的方案是把手动选科、语音输入、图形挂科、症状科普四种方式放进同一个微信小程序里,前端用小程序原生框架,后端走阿里云服务器上的 Java Servlet,数据库落在腾讯云 MySQL。它是三类技术人员的参考范本:做小程序开发的可以看前后端异步通信怎么写,做后端系统设计的可以参考 Servlet 按 mission 参数分发业务类的思路,做智能系统产品的可以借鉴多入口交互如何兜住不同人群。如果你正准备开发类似的挂号、导诊或预约类系统,这篇论文相当于一份完整的需求说明加技术选型清单,直接照着他的框架落地即可。

2. 系统架构拆解:小程序、Servlet、MySQL 三者如何串起来

2.1 前后端分离的轻量方案:为什么选小程序加 Servlet

整个系统分三段:微信小程序负责页面展示和用户交互,Java Servlet 作为后端接口层处理业务逻辑,腾讯云 MySQL 存科室、用户、预约记录。小程序端的架构和 Html5 那一套很像,wxml 加 wxss 控制布局,js 控制事件。后端没有用 Spring Boot 这类重型框架,而是直接落到 Tomcat 上的 JavaEE Servlet,原因很简单——学生项目、功能相对集中,Servlet 足够用,部署也省心,一个 war 包丢进 Tomcat 的 webapps 就能跑。

这种选型给从业者的参考价值在于:不是所有系统都得上微服务,业务边界清楚、并发量可控的场景,Servlet 加单库 MySQL 反而更容易维护。原文里提到一个很关键的设计——小程序发送异步请求时带一个 mission 参数,Servlet 端根据这个参数决定调用哪个业务类。这相当于一个最简版的路由分发器,避免了为每个功能单独开一个 Servlet 的冗余写法。

// 小程序端发送异步请求的核心代码 wx.request({ url: 'http://服务器IP:8080/hospital/WxServlet', data: { mission: 'yuyue', // 业务标识:yuyue=预约,quxiao=取消,paidui=排队 openid: userInfo.openid, // 用户唯一标识,从小程序登录态获取 keshi: selectedDept, // 用户选中的科室名称 yuyueTime: currentTime // 预约时间戳 }, method: 'POST', success(res) { // res.data 是后端返回的 JSON 字符串 // 通过 fastjson 解析后渲染到页面 if (res.data.code === 200) { wx.showToast({ title: '预约成功' }); } } });

这段代码的关键在 mission 参数的设计。所有接口共用同一个 WxServlet,通过 mission 值区分业务类型,好处是前端只需要维护一个请求地址,坏处是 mission 值一旦拼错,后端会找不到对应的业务分支,所以建议在项目里维护一个常量类统一管理 mission 的取值,避免魔法字符串散落在各个页面。openid 从 wx.login 的 code 换出来,是微信生态里识别用户身份的标准做法,后端存用户表时用 openid 做唯一索引,别用自增 id。

2.2 后端 mission 分发机制与业务类划分

后端 WxServlet 收到请求后,第一步不是处理业务,而是解析 mission 参数决定路由方向。原文提到的 MissionSelec 类就是干这件事的。不同业务类各司其职:负责预约的叫 Yuyue 类,负责科室加载的叫 secion 类,负责语音病情分析的是 Diagnosis 类。每个类对应数据库的一张或几张表,类的成员方法对应增删改查操作。

// WxServlet 核心分发逻辑(简化版) protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String mission = request.getParameter("mission"); JSONObject result = new JSONObject(); switch (mission) { case "loadKeshi": // 调用 secion.java 从数据库读取科室列表 List<Dept> deptList = new secion().getAllDepts(); result.put("data", deptList); break; case "yuyue": // 调用 Yuyue 类的预约方法,写入预约表 new Yuyue().addYuyue(request.getParameter("openid"), request.getParameter("keshi")); result.put("code", 200); break; case "quxiao": new Yuyue().cancelYuyue(request.getParameter("openid")); break; } // 用 fastjson 统一打包返回 response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(result.toJSONString()); }

这里有个实践中常踩的坑:Servlet 默认的编码不是 UTF-8,如果不在 doPost 开头设置 request 和 response 的字符编码,中文科室名存进 MySQL 再查出来就是一串问号。原文没提这个细节,但做 Java Web 的人应该都有过这段血泪经历。另外 fastjson 打包返回的字段名要和前端 js 里取数据的字段名严格一致,比如 Java 端叫 data,前端就用 res.data,大小写差一个字母都是 undefined。

2.3 数据库表设计与预约状态的核心约束

系统涉及的核心表有科室表、个人信息表、预约表、排队表。预约表是流转的枢纽,用户在线上预约后记录先在预约表里,点击模拟取号时把记录从预约表搬到排队表,模拟诊断完毕后再从排队表删除并写入历史挂号表。这个设计对做排队叫号类系统很有参考价值——用状态转移代替在一张表里堆字段,每张表只负责一个生命周期阶段,数据更干净,排查问题时也更容易定位。

表名关键字段作用
keshi 科室表id, name, icon存储科室名称与图标,前端宫格渲染的数据源
userinfo 个人信息表openid, name, phone用户身份信息,openid 做唯一键
yuyue 预约表id, openid, keshi, time线上预约记录,取号前存在这里
paidui 排队表id, openid, paihao, status线下排队记录,模拟取号后转入
history 历史挂号表id, openid, keshi, time, status诊断结束后归档,供历史记录页查询

预约表和个人信息表之间通过 openid 关联,不需要外键约束,业务层控制逻辑即可。这里一个值得借鉴的设计是“一次只允许一个有效预约”——用户想换科室必须先把原来的预约取消掉。这种硬约束避免了同一用户重复占号的问题,实现上也很简单,预约前先查该 openid 在预约表里是否有 status 为有效的记录,有则提示先取消。

3. 三大挂号方式的实现:手动、语音、图形背后的共同逻辑

3.1 手动选科:宫格渲染与预约状态的回显逻辑

手动选科是系统的主入口,页面加载时通过 onload 方法向后端请求科室列表,后端从 MySQL 查出数据后用 fastjson 打包成 JSON 数组返回,前端拿到数据后用一个 for 循环渲染成宫格,每行固定三个科室,这是通过 CSS 控制宽度实现的。这一个功能虽然简单,但背后有两处交互细节值得注意:一是已预约用户的回显提醒,二是预约与取消的状态互斥。

// 页面加载时检查用户是否已有预约 Page({ onLoad: function () { let that = this; wx.request({ url: BASE_URL + '/WxServlet', data: { mission: 'checkYuyue', openid: app.globalData.openid }, success(res) { if (res.data.hasYuyue) { that.setData({ hasYuyue: true, yuyueInfo: res.data.yuyueInfo // 已有预约的科室和时间 }); wx.showModal({ title: '提示', content: '您已预约 ' + res.data.yuyueInfo.keshi + ',是否先取消再重新预约?', confirmText: '去取消', success(res) { if (res.confirm) { that.cancelYuyue(); } } }); } } }); } });

这段逻辑防止了一个常见问题:用户重复预约导致数据库里出现多条有效预约记录。很多初做这个功能的人会漏掉 onload 时的状态检查,只在点击预约时校验,结果用户从别的入口进入时依然能重复提交。另一个细节是宫格的每行三个科室,这个约束应该由前端 CSS 控制,而不是后端返回时分组,因为屏幕宽度是前端属性,后端不需要关心渲染细节。

3.2 语音输入:WechatSI 转文字加 HanLP 推荐科室

语音挂号是面向老人和视障用户的核心功能,技术链路分两段:前端用微信官方同声传译插件 WechatSI 把语音转成文字,后端用 HanLP 对文字做自然语言处理,匹配出最接近的科室。这个方案巧妙的地方在于没有自己训练语音识别模型,直接复用微信生态的插件能力——在 mp 后台申请插件权限,前端引入插件,长按按钮录音,插件返回识别文本,再把这个文本作为病情描述发送给后端。

HanLP 的接入方式是重点。文章诊断类病情的流程是:读取服务器上保存的字典和语法语义模型,对病情描述做分词、词性标注,再通过推荐算法计算每个科室的匹配度,返回匹配度最高的科室。HanLP 本身支持自定义词典,这个场景下需要准备一份症状词到科室的映射字典,比如“头疼”“头晕”“失眠”映射到神经内科,“咳嗽”“发烧”“咽痛”映射到呼吸内科。

// Diagnosis.java 科室推荐的核心逻辑(简化示例) public String recommendKeshi(String description) { // 加载 HanLP 自定义症状词典 HanLP.Config.CustomDictionaryPath = new String[]{"data/symptom_dict.txt"}; // 对用户描述做分词 List<Term> termList = HanLP.segment(description); // 统计每个科室命中的症状词数量 Map<String, Integer> keshiScore = new HashMap<>(); for (Term term : termList) { String word = term.word; // 从症状-科室映射表中查找该词对应的科室 String keshi = symptomToKeshi.get(word); if (keshi != null) { keshiScore.merge(keshi, 1, Integer::sum); } } // 按得分排序,返回最高分科室 return keshiScore.entrySet().stream() .max(Map.Entry.comparingByValue()) .get().getKey(); }

这段代码里最容易翻车的地方是自定义词典的路径。HanLP 默认从 classpath 根目录加载词典,如果词典文件放在 data 子目录下,路径就得写成 data/symptom_dict.txt,写错路径会导致分词结果完全没命中自定义词。另一个坑是词典的编码格式必须是 UTF-8,用记事本另存的 ANSI 编码会导致乱码词条。推荐算法这里用的是比较朴素的词频统计,实际项目中可以升级成 TF-IDF 或 BM25,原理一样,效果会好不少。

3.3 图形挂科:人体切块与部位到科室的映射

图形挂科是把一张人体图切成头部、胸部、腹部、四肢等区块,用户点击某个部位后,系统列出该部位对应的常见科室,再跳转到手动挂科界面完成预约。这个功能在视觉上看着复杂,实现上其实就是 image 组件的点击区域划分,加上一张部位-科室映射表。

切图有个不那么显然的细节:不能直接用一张整图做背景然后硬编码坐标,因为不同手机屏幕的适配会出问题。原文提到用切图工具把人体图切成多个小块,视觉上通过代码属性让它们衔接美观,这就绕开了坐标适配问题——每个部位是一张独立的小图,用 view 组件并排或叠加摆放,点击事件绑定在每张图上,天然适配不同屏幕。

映射表的设计也要留个心眼。一个部位可能对应多个科室,比如头部既可能是神经内科(头疼),也可能是耳鼻喉科(耳鸣),还可能眼科(视力模糊)。映射表应该设计成一对多结构,而不是简单的字符串字段。这里的推荐思路是:在映射表中给每个科室加权重,比如点击头部时“神经内科”权重最高排第一,但用户仍可以滑动查看其他科室,保留选择余地。

4. 状态流转的设计:预约、取号、诊断全流程如何用一张表驱动

4.1 看病流程图的状态机设计

系统用 iView 的步骤图组件展示看病流程,全局变量 app.globalData.current 控制当前所处步骤。每次操作让 current 增减:线上预约完成进入步骤2,模拟取号进入步骤3,模拟诊断完毕进入步骤4。主页面每次 onshow 时重载这个参数,保证流程图与用户实际操作进度一致。

// 全局步骤状态管理 App({ globalData: { current: 1, // 1=选择科室, 2=预约成功, 3=已取号排队, 4=诊断完毕 openid: null, yuyueInfo: null }, onLaunch() { // 从缓存恢复登录态 let openid = wx.getStorageSync('openid'); if (openid) { this.globalData.openid = openid; wx.setStorageSync('current', this.globalData.current); } } }); // 模拟取号时更新步骤 function simulateQuHao() { app.globalData.current = 3; wx.setStorageSync('current', 3); // 调用后端把预约记录转移到排队表 wx.request({ url: BASE_URL + '/WxServlet', data: { mission: 'quhao', openid: app.globalData.openid }, success(res) { if (res.data.paihaoNum) { that.setData({ paihaoNum: res.data.paihaoNum }); } } }); }

这里的一个实战经验:步骤状态必须持久化,不能只存在内存里。小程序进程可能被系统回收,用户切后台再回来,内存里的 globalData 可能已经丢失。最简单的方式是同步到 Storage,每次启动时读取,每次变更时写入。后端也可以存一个状态字段,但前端本地持久化能保证页面渲染不错乱,后端状态则用于保证数据一致性。

4.2 模拟取号的数据迁移操作

模拟取号是连接线上预约和线下排队的桥梁,核心操作是把用户预约记录从预约表转移到排队表,同时删除预约表中的原记录,并且返回一个排队号码。这个操作用到了事务,必须保证迁移和删除同时成功或同时失败,否则会出现用户既在预约表中又在排队表中的重复状态。

-- 模拟取号的数据迁移操作(在业务类中通过事务控制) -- Step1: 从预约表查询用户的有效预约 SELECT * FROM yuyue WHERE openid = #{openid} AND status = 1; -- Step2: 插入排队记录并生成排队号 INSERT INTO paidui (openid, keshi, paihao, status) VALUES (#{openid}, #{keshi}, #{maxPaihao + 1}, 1); -- Step3: 删除预约表中的原记录 DELETE FROM yuyue WHERE id = #{yuyueId};

三个步骤必须放在同一个事务里。另外,排队号的生成不能简单地用当前最大排队号加一,因为高并发下两个请求同时读到同一个最大值,会生成重复排队号。常见做法是用数据库的自增主键做排队号,或者用 Redis 的 INCR 命令生成递增序列。这个系统因为是模拟场景,用户量很小,用 max 加一问题不大,但如果真要做线上系统,这一处必须改。

4.3 模拟诊断完毕与历史记录归档

模拟诊断完毕是这个流程的收尾动作,用户完成取号后点击按钮,后端把用户从排队表移除,同时将这次挂号信息写入历史表。这样设计的好处是业务表始终保持轻量——排队表只有当前在排队的人,历史表记录所有已完成的挂号,查询历史时单独查归档表,不会因数据量增长拖慢主流程查询。

// 模拟诊断完毕的归档操作 public void finishDiagnose(String openid) { // 开启事务 Connection conn = getConnection(); conn.setAutoCommit(false); try { // 查询排队信息 String sql = "SELECT * FROM paidui WHERE openid=? AND status=1"; PaiduiRecord record = query(sql, openid); // 1. 写入历史表 String insertHistory = "INSERT INTO history " + "(openid, keshi, paihao, finish_time) VALUES (?, ?, ?, NOW())"; execute(conn, insertHistory, openid, record.getKeshi(), record.getPaihao()); // 2. 删除排队记录 String deletePaidui = "DELETE FROM paidui WHERE id=?"; execute(conn, deletePaidui, record.getId()); // 3. 提交事务 conn.commit(); } catch (Exception e) { conn.rollback(); throw e; // 需要保证归档与删除的原子性 } }

这段逻辑里有个容易被忽视的点:归档操作必须记录完成时间,历史记录页面需要按时间倒序展示。如果历史表里没有时间字段,用户看到的多条记录无法区分先后顺序,体验会打折扣。另外,历史表和预约表的结构几乎一样,很多人会直接复用预约表加一个 status 字段来区分是否完成,但这样会让主表越来越臃肿,建议还是按状态拆表,归档后记录只读,不再参与业务流转。

5. 避坑指南:从论文到线上系统最容易翻车的六个细节

5.1 一次只能预约一个科室的限制

现象:用户预约了神经内科后,又想预约呼吸内科,系统直接提示失败,没有给出取消入口。

原因:后端在预约接口做了硬校验——预约表中存在该 openid 的有效记录时直接返回错误码,但前端没有在错误码返回时引导用户跳转到取消预约页面。

解决:前端收到“已有有效预约”的错误码后,弹出提示框,提供“去取消”按钮,点击后跳转到历史预约页面先执行取消操作。后端校验保留,前端引导补齐,两层配合才能形成闭环。实际开发中建议后端返回的错误码要包含一个 action 字段,告诉前端下一步该做什么。

5.2 预约人数和排队号码的数据不一致

现象:页面上显示的预约人数还是预约前的数字,用户点击模拟取号后拿到排队号码,但页面上的人数没有刷新。

原因:模拟取号成功后,前端只处理了排队号码的回显,没有重新请求接口刷新预约人数和科室状态。

解决:在模拟取号的 success 回调里,除了显示排队号,再发一个请求拉取最新的科室预约数据,或者让后端在一次返回里把排队号和最新人数一起打包返回,减少一次网络往返。这种问题在多模块联调时很常见,解决方法就是每次状态变更后,刷新所有依赖该状态的 UI 数据。

5.3 中文乱码问题

现象:科室名称从数据库查出来再到前端显示,变成了一串问号。

原因:三层传输中有一层字符编码不一致。MySQL 表结构是 utf8 编码,但 JDBC 连接串没有指定 characterEncoding,或者 Servlet 的 response 没有设置 UTF-8。

解决:JDBC 连接串加上 characterEncoding=utf-8,Servlet 的 request 和 response 都设置 setCharacterEncoding("UTF-8"),同时 MySQL 建表时统一用 utf8mb4——注意 utf8mb4 和 utf8 的区别,utf8mb4 才能完整支持 emoji 和生僻字。字符编码这个问题往往不是难在解决,而是难在排查,所以建议在项目初始化时就把编码配置统一列好,别等出了问题再满项目找。

5.4 语音识别准入门槛与实际效果落差

现象:使用 WechatSI 插件做语音转文字,识别结果和用户实际说的内容偏差很大,尤其是带方言口音的用户。

原因:WechatSI 是通用语音识别,对普通话识别率高,但对方言支持有限,而且“头疼”“脑壳痛”这类同义表达在不同的地区差异巨大。

解决:在 HanLP 的症状词典里把方言词和普通话表达做同义映射,比如“脑壳痛”“头痛”“脑袋疼”都映射到同一个症状词,再对应到神经内科。词典的维护不是一次性工作,上线后要持续收集用户的真实表达,定期补充词条。另外,识别结果返回后可以在前端做一次二次确认,把识别出的文字显示给用户看,用户点击确认后再发送给后端,避免误识别直接产生错误的科室推荐。

5.5 小程序真机预览与开发者工具差异

现象:开发者在工具里跑得好好的,真机预览时某些页面空白或样式错乱。

原因:开发者工具的模拟器和真机渲染引擎不完全一致,部分 CSS 属性和组件在真机上表现不同。最常见的是 flex 布局的兼容问题,以及部分组件如 picker 在工具里弹层正常,真机却定位错乱。

解决:养成真机预览的习惯,尤其是涉及布局和交互的功能,不能只依赖开发者工具。出现差异时优先检查 CSS 兼容性,用开发者工具的“机型模拟”多切换几种机型测试。另外,请求的域名必须是 HTTPS 且在微信公众平台后台配置过合法域名,开发者工具可以关掉校验,真机不行,这个问题会直接导致所有请求失败,页面数据加载不出来。

5.6 HanLP 词典加载失败导致推荐接口报错

现象:语音输入后,后端报错“词典加载失败”或“找不到文件 data/symptom_dict.txt”。

原因:路径配置错误或文件缺失。HanLP 默认从 classpath 根目录查找,打包成 war 包后文件路径可能和本地开发不一致。

解决:将自定义词典放在项目的 resources 目录下,打包后会自动进入 classpath,路径写成 classpath:data/symptom_dict.txt。调试阶段可以通过 HanLP.Config.ShowTermNature 开启分词过程日志,直接看到词典是否加载成功。这里给个排查建议:先写一个最简单的 main 方法测试 HanLP 分词,确认词典加载没问题,再接入 Servlet 业务逻辑,缩小排查范围。这一步能帮你分清是 HanLP 本身的问题还是业务代码的问题。

6. 把它做成能上线的系统:四个值得改造的点和一条验证路径

6.1 从单 for 循环到科室分组渲染

原文明确提了一个待改进点:科室列表没有分组,不管内科外科儿科都用一个 for 循环平铺渲染。小科室少看不出问题,三甲医院动辄几十个科室,用户滑动查找的成本极高。改造思路是给科室表加一个 parent_id 字段,大类为 0,小类指向父级 id,前端先渲染大类,点击后再加载该大类下的子科室,用二级联动替代平铺列表。这样做并不复杂,后端多一个按 parentId 查询的接口,前端用两个数组分别存大类和小类,交互上参考市面上的地图选点组件。

6.2 从模拟排队到实时排队状态推送

这个系统里的取号和诊断都是模拟操作,做演示没问题,真实场景需要的是实时性。用户在线上预约后,到医院现场用叫号机扫码取号,排队号码和前方等待人数要实时可见。改造路径有两条:第一条是短轮询,前端每隔 10 秒调一次接口查排队状态,简单但浪费请求;第二条是 WebSocket 长连接,后端把排队状态变化推给对应 openid 的小程序端。微信小程序的 WebSocket 接口叫 wx.connectSocket,服务器端可以用 Tomcat 的 WebSocket 支持或单独部署一个推送服务。对于这套基于 Servlet 的系统,建议先用短轮询,逻辑改动小,上线跑通后再换长连接。

6.3 从规则推荐到基于历史挂号的协同过滤

原文提到了一个思路但没展开:根据用户历史挂号记录做科室推荐。比如一个用户每次都挂呼吸内科,再来挂号时可以在页面上优先展示呼吸内科。实现上分两层:轻量版是统计该用户历史挂号中出现次数最多的科室,直接排序靠前;进阶版是算科室之间的关联性——挂了五官科的用户有大概率也挂过呼吸内科,用协同过滤的思路计算“看了A科室的人还看了B科室”,把关联科室推荐给新用户。数据量不大时用 SQL 直接算就行,用户多了再考虑离线计算。

6.4 让云笔记本成为真正的电子病历卡

原文里的看病笔记目前只做了记录功能,也就是一个本地备忘。要变成电子病历卡,需要加上时间和随访两个维度:每次挂号时自动生成一条时间戳,用户可以在时间戳下补充当次诊断结果和用药信息;复查时系统提示“您上次是 3 个月前看的呼吸内科,建议复查”。这里的关键是数据结构——每条笔记要挂上挂号记录的 id,否则笔记和看病历史是割裂的。我用过不少医院的 App,做得差的病历卡就是两个模块各玩各的,就诊记录归就诊记录,备忘录归备忘录,根本没法形成时间线。

6.5 完整验证路径:从项目启动到真机走通全流程

拿到这套源码或文档后,不要东看一眼西看一眼,按下面的顺序验证一次:

  1. 准备环境:JDK 8、Tomcat 8.5、MySQL 5.7、微信开发者工具
  2. 导入数据库脚本,确认科室表和用户表里有测试数据
  3. 后端项目打成 war 包丢进 Tomcat 的 webapps,启动 Tomcat
  4. 浏览器访问 Servlet 接口地址,确认能返回 JSON 数据
  5. 微信开发者工具导入小程序端代码,把请求地址改成服务器局域网 IP
  6. 完整走一遍流程:登录 → 手动选科 → 预约 → 模拟取号 → 模拟诊断完毕 → 查历史记录
  7. 语音输入功能测试:长按录音,确认识别文字正确,后端返回科室推荐

我自己的习惯是,拿到一个新的前后端联调项目,先写一个最简的测试页面把登录态跑通,再做业务功能验证。因为登录态是整个系统的前置条件,登录有问题后面全是白搭。语音识别和 HanLP 推荐这两个模块单独写测试类验证,不要在页面上调试,那样你分不清是语音没转出来还是 HanLP 匹配错了还是前端渲染挂了。

最后说一句:这类智能挂号系统的核心从来不是某个单点技术有多深,而是手动、语音、图形三种入口如何自然地服务于不同人群,以及预约、排队、诊断这条状态链怎么保持数据一致。建议你先把状态流转的数据表关系彻底吃透,再动手改代码,顺序反了容易被细节带偏。希望这篇拆解能让你在复现时少走几个弯路,有问题可以对照原文的参考文献目录按图索骥,那里面的资料都是可以直接落地的。

本文还有配套的精品资源,点击获取