ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot医院预约挂号系统开发实战:架构设计、并发防超卖与部署

SpringBoot医院预约挂号系统开发实战:架构设计、并发防超卖与部署 1. 医院预约痛点与这套SpringBoot系统实际要解决的问题先说个我当年的感受为什么计算机毕业设计题目里医疗类项目永远是大热门因为围绕它是能稀里哗啦列出一堆需求的——预约挂号、医生排班、电子病历、药品管理、收费结算、统计分析……功能点足够多逻辑足够复杂做出来也容易向评委展示你的系统确实在解决现实问题。但问题恰恰出在这里。我见过太多做医疗项目的同学一上来就朝大而全这个方向冲结果到了中期答辩的时候排班逻辑还没想明白、预约并发压根没考虑项目直接烂尾。所以如果你正在考虑做这个题目第一件事不是打开IDEA写登录注册而是先想明白一件事你的系统到底要在哪个环节上真正解决用户的痛点。拿医院场景来说痛点其实非常集中。线下挂号的体验大家都懂早上五六点到窗口排队、专家号放出来几分钟就没了、挂错科室还得来回折腾。一个互联网便民服务系统核心价值应该落在把线下的不确定变成线上的确定上——患者打开手机就能看到今天的号源情况、选定时间段、在线支付、到点直接去诊室医生可以提前维护自己的排班避免像以前那样靠护士手工登记。预约管理平台的本质是一个确定性调度系统而SpringBoot恰好是搭建这种系统的很顺手的技术底座。这个平台适合谁来参考我建议这几类人重点关注第一正在做SpringBoot毕业设计、需要一套完整可复现架构的同学第二想从会写CRUD过渡到会设计业务系统的Java初学者第三哪怕你不是做医疗方向这个项目里的排班模型、预约防并发、角色权限控制抽出来放在会议室预约、场馆预订、培训报名这些场景里完全可以复用。我当时的方案做的是三层角色体系患者端微信小程序风格的前端页面、医生端排班和接诊记录管理、管理员端基础数据配置和运营统计前后端分离后端就是SpringBoot单体应用加MySQL。今天这篇文章会把整条链路拆开讲清楚从架构选型到数据库设计再到并发控制和部署复盘你完全可以照着这份思路复现一版属于自己的系统。2. 技术选型逻辑与项目分层结构先别急着写代码技术选型这个环节很多同学上来就是SpringBoot选最新版、前端随便套一个模板、数据库表想到哪写到哪。等到开发中期问题集中爆发某个依赖和SpringBoot版本不兼容、前端请求跨域、MyBatis-Plus和分页插件打架……这些都是选型阶段欠的债开发阶段还。2.1 SpringBoot版本怎么选为什么我用2.7.x而不是3.x打开Spring Initializr默认给的SpringBoot版本可能是3.x甚至更新的版本。如果你直接用它搭项目后面大概率会遇到这些坑3.x基于Jakarta EE很多老教程里的javax.servlet包要全改成jakarta.servlet部分第三方starter还没适配比如某些短信SDKMyBatis-Plus也得用专门的3.5.x以上版本才能兼容。所以我的建议非常直接毕业设计项目用SpringBoot 2.7.x系列别追新。我自己用的是2.7.18这个版本稳定性好网上资料最齐全遇到的任何报错几乎都能搜到解决方案。不是说你不能用3.x而是在毕设有限的周期里把精力花在业务逻辑上远比花在环境适配上有价值。配套的技术栈我列个表出来都是经过实测稳妥的组合组件选型用途说明版本建议后端框架SpringBoot主业务接口开发2.7.18持久层框架MyBatis-Plus单表CRUD不用写SQL复杂查询手写XML3.5.5数据库MySQL核心业务数据存储5.7或8.0均可权限框架Spring Security JWT登录认证与接口鉴权Spring Security 5.7接口文档Knife4j自动生成在线接口文档答辩演示利器4.x前端框架Vue3 Element Plus后台管理界面开发Vue 3.4 Element Plus 2.x构建工具Maven项目依赖管理和打包3.8.x2.2 后端分层Controller到底该不该写业务逻辑我见过不少同学的代码风格是所有逻辑全堆在Controller里——查数据、判断状态、写返回结果全部混在一起一个方法上百行。这种代码跑起来没问题但答辩的时候面试老师翻几下源码就皱眉头了。标准的做法是四层结构Controller层只做参数接收和数据响应把请求转发给Service层Service层业务逻辑的核心位置比如排班冲突检测、预约名额校验都在这里做Mapper层DAO层数据库操作继承BaseMapper复杂查询用XMLEntity层数据库表对应的实体对象我项目里的包结构大致长这样你可以直接参考com.medical.platform ├── controller // 接口入口如 PatientController, DoctorController ├── service // 业务接口 impl实现类 ├── mapper // MyBatis-Plus 数据访问 ├── entity // 数据库实体映射 ├── dto / vo // 前端接收参数 / 前端展示数据 ├── config // 跨域配置、Security配置、Knife4j配置 ├── utils // JWT工具、日期工具、统一返回封装 └── exception // 全局异常处理器我把dto和vo单独拆开的体会是千万别把前端的请求参数直接绑定到Entity上否则你会被字段校验搞得焦头烂额还会造成不必要的数据库字段暴露。举个例子用户注册时前端传的是username、password但数据库用户表里还有role、createTime直接绑定就会出现越权赋值的安全漏洞。2.3 前后端联调时最多踩的跨域与统一返回结构前后端分离开发的时候前端的端口通常是5173或者8080后端跑在8081一旦前端发起AJAX请求就会触发跨域问题。解决办法在SpringBoot里加一个WebMvcConfigurer配置重写addCorsMappings方法允许指定路径跨域放行就行。这个代码非常简单网上一搜一大堆但我建议你在config包里专门建一个CorsConfig类而不是用CrossOrigin注解一个接口一个接口地加因为那样太零散了。和跨域配套的还有一个很关键的设计——统一返回结构。我定义了一个Result类包含三个字段code状态码、message提示信息、data业务数据。所有接口返回的都统一是这个结构。这样做的好处是在前端可以用同一套拦截逻辑处理响应比如code不等于200就全局弹错误提示不用每个接口单独处理。这个细节在做前端联调的时候体验差距非常大。3. 核心业务模块拆解预约、排班、挂号三条主线医疗系统的业务复杂度比普通管理系统高一个档次因为涉及到状态流转。我开发时发现其实整个系统就是围绕三条主线展开的管理端配置基础数据、医生端维护排班、患者端预约挂号。3.1 管理端科室、医生、号源三者的绑定机制很多同学做这个系统时会忽略一个关键设计科室、医生、号源这三者不是三个孤立表而是一套层层绑定的关系。内科下面挂5个医生医生A在2025年6月10日上午有排班这个排班决定了他当天上午有几个号源、每个号源几点到几点。我先说管理端的核心功能。管理员登录后主要负责维护基础数据科室管理新增科室、科室简介、医生管理录入医生信息绑定所属科室和职称、排班管理查看全院排班情况必要时人工干预。这些功能本质上是后面所有预约流程的数据源头——没有科室和医生数据患者端就什么都看不到。还有一点比较容易遗漏医生的账号往往也是管理员在医生管理模块里创建的。我做的方案是管理员录入医生信息时系统自动生成初始账号手机号和初始密码医生首次登录后强制修改密码。这样免去了手动到数据库里insert记录的尴尬。3.2 医生端排班自动生成规则与接诊记录闭环医生端的核心功能是排班维护和患者接诊记录。这里我踩过一个大坑最初我把排班做成了医生手动选择某天某个时间段是否出诊然后数据存成一个开启排班状态的布尔字段。结果测试的时候发现预约完成后医生无法区分号源是否已经有人约走因为排班只记录了当天出不出诊没记录筛出诊的细分时段里还剩几个号。后来我把排班模型改成了两个层次排班日期表doctor_schedule和号源表schedule_slot。排班日期表记录某医生在2025-06-10上午出诊总号源20个号源表根据规则自动生成20条具体记录每条记录一个时间段状态为可预约/已被预约。这样患者在预约时实际是锁定一条具体的号源而不是给排班日期表减1并发控制才能真正落实。医生的接诊流程我建议做一个今日待诊患者列表。患者预约成功后医生端能按日期看到当天预约了他的哪些患者、就诊序号是多少、病历主诉是什么。接诊完成后可以勾选就诊完成。这个闭环虽然逻辑简单但是答辩时的加分项因为它体现出了平台不仅仅是预约工具还能辅助医生进行日常接诊管理。3.3 患者端从选科室到拿号整个流程的状态变化患者端的流程设计是整个系统的门面。我做的是一个仿小程序的页面风格核心路径是这样的患者注册/登录维护个人基本信息首页按科室分类浏览科室列表进入科室详情查看科室下所有医生进入医生详情选择要预约的日期系统自动列出该医生当天的号源时间段选中一个可预约的时间段确认预约预约成功后在我的预约里查看预约记录和就诊二维码实际可以直接显示预约状态这里必须想清楚预约单的状态流转设计。我的方案用了五个状态待就诊、已完成、已取消、爽约、已过期。状态之间的流转规则很明确待就诊的状态下患者可以取消预约状态变为已取消同时释放号源供其他患者预约医生点击就诊完成待就诊变为已完成预约日期已过但患者没有到诊待就诊自动或定时变为爽约或者已过期二者可以二选一不必都保留这些状态管理我发现用后端的一个StatusEnum配合状态流转Service来做最清晰不要用散落在各文件里的if-else判断否则后期加需求的时候你根本不知道哪里需要改。3.4 管理员统计面板与运营数据展示统计功能我不建议你一开始就花大力气做炫酷图表。先保证几个核心指标的数据能查出来再谈图表——今日预约总数、各科室预约量排行、医生接诊量统计、患者爽约率。我当时的时间不够没有在前端集成ECharts图表库而是做了一个简单的数字卡片统计页展示今日预约数、累计注册患者数、在线医生数。答辩的时候老师基本不会纠结图表是否炫酷更看重你的统计口径是否清晰。4. 数据库表设计核心业务表怎么用最小集合支撑整个系统数据库设计这个环节我看了很多同学交上来的设计文档最大的问题就是表多到离谱却互相不用。我最初的数据库设计文档里甚至写过20多张表后来真正落地的只有10张核心表其他的纯属画饼。这里的经验是表的数量要跟着业务流程走不跟着想象力走。4.1 核心表结构与字段设计要点我列出10张真正贯穿整个系统的表这已经是能够支撑完整业务闭环的最小集合表名核心字段职责说明userid, username, password, real_name, phone, role, avatar统一用户表通过role区分患者/医生/管理员避免建三张用户表departmentid, dept_name, dept_intro, status科室表管理端维护doctor_infoid, user_id, dept_id, title, introduction, good_at医生详情表扩展User表中医生的专业信息scheduleid, doctor_id, dept_id, work_date, period, total, available排班日期表记录某天某时段的出诊和号源总量schedule_slotid, schedule_id, start_time, end_time, status号源时间段表具体到几点几分到几点几分reservationid, patient_id, doctor_id, slot_id, schedule_id, visit_date, status, create_time预约订单表整个系统的核心流水记录medical_recordid, patient_id, doctor_id, diagnosis, prescription, create_time就诊记录表医生接诊后填写announcementid, title, content, create_time系统公告管理端发布feedbackid, user_id, content, status患者反馈管理端查看operation_logid, user_id, action, detail, create_time操作日志表记录关键操作行为有几个地方需要特别说明。第一个是user表用角色区分三种人群。为什么不分三张表因为Spring Security做登录认证时只需要一张用户表查账号密码如果拆成三张表登录逻辑就得先判断这个账号是医生还是患者再决定去哪个表里查非常别扭。统一用户表加上role字段登录时一次性查出来后续的权限控制交给Spring Security的角色判断机制开发效率高很多。第二个是schedule_slot号源表的设计。有人可能会问我直接用schedule表里一个available字段做减法不就得了为什么还要单独建一张号源表因为只有单独把时间细分到具体的号源记录防重复预约才能从我后面讲的数据库行锁来入手。如果只是做字段减法你得自己写更新前先查一次判断大于0再更新的逻辑这一步在高并发下极容易超卖。4.2 设计索引和约束时容易被忽视的细节光把表建出来远远不够索引设计才是数据库性能的分水岭。我做压测的时候发现预约提交特别慢用EXPLAIN看了执行计划发现每次预约时查询reservation表的全表扫描要几毫秒看似不多但高并发场景会被放大成为瓶颈。接下来的处理很简单给外键字段加上普通索引即可。需要加索引的字段组合推荐reservation表的patient_id visit_date组合索引用于查询患者某日期范围的预约记录、slot_id唯一索引从数据库层防止同一个号源被重复预约这是最后一道安全锁、schedule_slot表的schedule_id索引查询某排班下的所有号源时用、schedule表的doctor_id work_date索引医生排班查询用。我重点说下slot_id唯一索引的意义虽然我在Service层已经通过状态更新为已预约影响行数来判断是否并发冲突但数据库的唯一索引是兜底方案。哪怕代码逻辑有漏洞、两条请求同时通过了Service层判断数据库层的唯一约束也会挡下第二条写入直接报Duplicate Entry异常。日志记录下这个异常服务不会崩溃用户会看到该号源已被约走的友好提示这三层防护配合在一起才是稳妥的方案。4.3 统一用户表vs拆分三张表的最终结论开发时我一直在这两种方案间犹豫最终结论上面也提到了统一用户表。但要注意统一user表之后医生信息并不应该全部塞到user表里。医生的职称、擅长领域、简介这些专业属性应该单独放doctor_info表通过user_id关联。同理患者的身份证号、病历号这类信息如果有特殊需求建议也单独拆表而不是把用户表变成一个什么都装的大仓库。这样做的好处是User信息是通用的但医疗专业字段可以按角色定制后续哪怕加一个护理人员角色也不需要动user表的基本结构。5. 预约秒级响应的并发控制防超卖才是系统的灵魂如果让我用一个词概括这个项目里最有技术含量的部分我会毫不犹豫地说并发控制。原因很简单几乎所有评委老师看到预约挂号系统这个题目时第一个想到的提问就是同一个医生最后一个号两个患者同时提交预约你系统怎么保证不超卖5.1 乐观锁思路update语句自带判断更新行数我的方案结合实际做了折中处理。先说第一层——数据库行锁乐观方案。每次预约的时候不是先查号源是否可预约再执行插入而是直接执行一条带条件的更新SQLUPDATE schedule_slot SET status 1 WHERE id #{slotId} AND status 0;这条SQL的妙处在于它同时做了两件事判断当前状态是否为可预约以及将状态改为已被预约。MySQL默认行级锁机制会保证同一条记录在同一时刻只有一个事务能更新成功所以两个患者同时提交时只有一个能拿到更新了1行的结果即AffectedRows 1另一个人拿到的是AffectedRows 0。这就是数据库层面的防超卖核心逻辑。拿到更新后的AffectedRows之后服务端判断如果AffectedRows 1说明号源已经锁定成功继续执行创建预约记录的插入操作如果等于0直接返回该号源已被预约请选择其他时间段。5.2 为什么第一版会被超卖AffectedRows被忽略的坑哪怕方案已经这么标准我第一版代码里还是出现了超卖问题。问题出在哪我把AffectedRows的判断放在了if里但没有把更新逻辑和插入逻辑放在同一个事务中。于是发生了这样的情况线程A执行更新AffectedRows1线程B也执行更新AffectedRows0B被拦截。这看起来没问题但A在更新成功之后、插入预约记录之前事务还没提交B的更新虽然AffectedRows0但对A没有任何阻塞。如果A的插入失败了事务回滚但号源状态已经改了……不对回滚会把更新也回滚掉所以问题不在这里。实际出问题的地方更隐蔽我用的是MyBatis-Plus的updateById它默认只根据主键更新字段不会带AND status 0这个条件。所以两个线程同时调updateById可能都会成功执行一个把状态改成1另一个把状态改成1覆盖写AffectedRows都是1结果就是超卖。后来我改成写XML自定义SQL再加上Transactional事务注解才算稳定。这个坑提醒了我一个关键原则并发控制的SQL必须显式写出条件不要依赖ORM框架的默认行为。5.3 事务边界设计与GlobelException兜底事务边界的正确姿势是开启一个事务事务里包含锁定号源和创建预约记录两步。任何一步失败整个事务回滚号源状态恢复成可预约用户得到提示。我在ReservationService.createOrder()这个方法上加了Transactional(rollbackFor Exception.class)rollbackFor指定为Exception.class是因为Spring默认只回滚RuntimeException如果你在方法里抛的是自定义的业务异常我习惯继承RuntimeException默认也能回滚所以这个设置更多是防御性的。还有一个必须预告的点数据库唯一索引会兜住最后一层极限并发。我在reservation表给slot_id字段实际是slot_id visit_date设置了唯一索引。哪怕Service层逻辑出错、两个事务同时通过了AffectedRows判断第二个插入会直接抛出DuplicateKeyException。我会在全局异常处理器里捕获这个异常转成用户友好的提示。RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(DuplicateKeyException.class) public Result handleDuplicateKeyException(DuplicateKeyException e) { return Result.error(该时间段已被预约请选择其他时段); } }全局异常处理不只是为了这个场景它还能统一处理参数校验异常、业务异常、系统异常避免用户直接看到一坨500报错堆栈。答辩演示时如果你能主动演示两个浏览器同时抢最后一个号一个成功一个提示繁忙这个场景项目说服力会强很多。6. 权限控制与JWT登录认证三种角色怎么做到各看各的数据身份认证和权限控制是第二个容易被答辩老师追问的点。我先说明一下Spring Security在这里的角色——做登录认证和接口访问控制而JWT负责无状态的会话保持。6.1 JWT登录认证的完整链路流程大概是这样用户输入账号密码请求打到/api/auth/login后端用authenticationManager.authenticate()做校验成功之后用JWT工具类生成一个token返回给前端。前端把token存到localStorage之后每次请求都在Authorization头里带上Bearer token。后端这边我写了一个JwtAuthenticationFilter继承OncePerRequestFilter拦截所有请求解析token、校验签名和过期时间、把用户名取出来加载用户的权限信息放到SecurityContext里。这样后面的接口鉴权就能用了。这个链路如果纯靠背代码大概率会在细节上翻车。我推荐先理解三个核心问题JWT里存什么只存用户ID和用户名不要存密码等敏感信息。token本身只是携带信息的签名凭证不是保险箱。token过期了怎么办最简单的方案是过期时间设置长一些比如7天前端每次请求发现401就跳转登录页。复杂一点的方案是刷新的refresh_token但毕设项目里没有太大必要。Spring Security的密码加密必须用BCrypt。直接用MD5存储密码答辩时老师会觉得你不专业。Spring Security自带BCryptPasswordEncoder每次加密的密文都不相同因为会掺入随机盐安全等级完全不同。6.2 角色权限控制PreAuthorize的正确用法我用的是注解式权限控制。在Controller的方法上加PreAuthorize(hasRole(ADMIN))意思是只有ADMIN角色才能调用这个接口。这个方法非常直观前端路由也能做一层角色控制但后端必须留一道闸门。在配置类里我开启EnableGlobalMethodSecurity(prePostEnabled true)然后在接口上做声明。有三条最常用的规则PreAuthorize(hasRole(ADMIN))管理员专属接口比如维护科室PreAuthorize(hasAnyRole(ADMIN,DOCTOR))管理员和医生都能调的接口比如排班管理PreAuthorize(hasRole(PATIENT))患者接口比如创建预约除了角色维度数据维度也需要控制。比如患者登录后只能查到自己创建的预约记录、医生只能查到自己名下的排班这在查询条件里直接加上登录者的ID即可。如果你想在注解层面实现只能操作自己的数据就得自己扩展Spring Security的权限判断逻辑毕设阶段不推荐复杂度高很多在Service层直接判断就足够了。6.3 密码加密和登录校验的常见翻车现场这一块我要郑重提醒一下当年我自己就翻过车最初为了方便用户注册时密码直接用明文存数据库想着毕设而已。后来被同学提醒了一句老师会看数据库的我才意识到这简直是送分题变送命题。换成BCryptPasswordEncoder之后简洁又安全注册的时候调用encode()登录的时候调用matches()代码只要改两行难度几乎为零体验是完全不同层面的。另一个常见翻车点是前端密码传输。如果用户在前端输入密码后用HTTP明文传到后端那就算后端做了BCrypt也白搭——抓包就能抓到真实密码。实际的正式系统要用HTTPS开发阶段没法申请证书可以先在局域网里跑但至少要有意识浏览器控制台里看到的payload密码要尽量脱敏。可以在前端先把密码做一次SHA256摘要后端再对摘要做BCrypt。这个双保险虽然毕设可能用不到但答辩你一说出来档次就上去了。7. 前端页面结构、交互链路与接口对接说完后端我来快速过一遍前端的搭建思路。这也是很多同学头疼的点——前后端分离的开发模式看着简单真把页面串起来总是问题不断。7.1 页面路由设计与角色视图分离我的前端用的Vue3 Element Plus Vue Router Pinia。整体按角色拆分为三个入口患者端路由首页科室列表、医生列表页、医生详情页含号源选择、个人中心我的预约/我的病历/个人信息医生端路由工作台今日待诊患者列表、排班管理查看排班/维护排班、个人信息管理端路由科室管理、医生管理、排班总览、公告管理、反馈管理、统计面板前端的路由守卫在登录后判断用户角色然后重定向到对应首页。比如患者登录后默认跳转/patient/home医生登录后跳转/doctor/workbench。这个设计一个是用户体验好另一个是后端接口只做权限控制的策略下前端也无脑暴露不存在的入口。关于前端代码一个能传递给新手的建议是把接口请求封装到一个统一模块里。我配置了axios的baseURL和请求拦截器每次请求自动带上token响应统一做code ! 200的错误提示。这个封装做完后写页面组件时会轻松很多。7.2 最影响开发效率的两个联调细节第一个是Mock数据。前端同学在等后端接口时用mock数据先渲染页面是常态。但我的建议是前后端同时开发时尽量让前端先按约定的JSON结构做假数据接口写好后再替换这个模式能帮双方把接口字段定义提前对齐减少后期返工。第二个是接口命名规范。后端Controller的路径前缀要统一我采用/api/xxx作为前缀再从业务域细分/api/auth/**登录注册、/api/patient/**患者端、/api/doctor/**医生端、/api/admin/**管理端。再加上Knife4j自动生成的接口文档前端完全不需要反复问后端这个接口传什么参数自己查文档就能对接。这串流程在答辩的时候给老师演示一遍在线文档自动生成至少是半个加分项。7.3 状态展示与用户提示的细节设计预约流程里的状态展示是最容易做出体验感的细节。我的做法是预约按钮的可用性直接由号源状态的status决定。status0显示可预约按钮可点status1显示已约满按钮置灰。用户点击预约成功后原按钮立刻变置灰并显示我已预约这需要前端在拿到成功后刷新号源列表数据。另外要把等待响应时用户的操作心态考虑进去。提交预约按钮在请求发出后要加loading状态防止用户连点两次导致重复提交后端虽然会拦截但前端主动防止体验更好。这些都是小细节但整套体验下来和能跑就行的差距就拉开了。8. 从Startup到答辩演示一套稳过验收的清单8.1 后端打包部署的具体操作后端项目开发完成后我用Maven打包成jar包在服务器上用java -jar方式跑的。重点说几个打包过程中经常遇到的问题第一application.yml里数据库连接配置一定要用环境变量或者写死成服务器的地址不要用localhost。我最初本地联调时全用localhost部署到服务器后忘记改结果接口一连就报连接拒绝。用环境变量的话打包时不用改代码启动命令里带--spring.datasource.urlxxx覆盖即可。第二静态资源前端打包后的dist文件可以单独部署在Nginx上也可以直接把前端的dist目录里的文件拷贝到后端resources/static目录下统一由SpringBoot提供访问。毕设答辩、演示这种场景前后端一起打包成单jar包其实是最省事的方案——一启动就是一个完整的系统不用扯Nginx配置。当然如果你要展示后端能力还是Nginx加jar包分开部署更专业。两种方案我分别踩过各有优劣关键看你的演示环境。第三如果是演示环境只有一台电脑直接mvn clean package -DskipTests打包然后java -jar medical-platform.jar跑起来浏览器访问http://localhost:8081即可。首次启动前建议执行一个初始化SQL脚本把管理员账号admin/admin123、测试医生账号、测试患者账号都提前insert好演示时直接登录即可。8.2 功能测试的关键用例特别是并发场景怎么演示测试报告是毕设文档里必须交的但很多同学到最后都是随便写写。我给一份实际有价值的验收用例清单覆盖核心业务场景测试项操作步骤预期结果手机号注册前端注册页输入未注册手机号密码注册成功返回token自动登录重复手机号注册再次注册相同手机号提示该手机号已注册科室列表展示患者登录后进入首页按科室卡片展示所有科室医生详情页排班展示点击科室下某医生展示该医生未来7天的排班和今日号源余量正常预约选择一个可预约号源确认预约预约成功号源变已约满我的预约里出现记录重复预约同一号源用两个账号先后预约同一时段前者成功后者提示该时段已被约取消预约在待就诊状态点取消状态变已取消同时号源释放为可预约医生查看今日待诊医生登录进入工作台展示今天预约该医生的患者列表医生填写就诊记录点击某一患者完成接诊预约状态变为已完成生成一条病历记录权限控制患者token访问管理员接口返回403无权访问并发场景的演示正常演示环境一般没有特别大的流量压测工具我的实操经验是开两个浏览器窗口用两个不同的患者账号同时对一个号源发起预约请求。但纯手工操作很难做到完全同时——实际可以写一个简单的JMeter脚本或者Postman集合同时发两个请求。后端日志里能看到一个成功一个失败的记录把这个日志截图放进测试报告里非常有说服力。8.3 答辩时最容易被打断的3个问题提前做好准备答辩老师见过的毕业设计千篇一律每个人都会做一个商城、一个管理系统、一个预约平台。为了不被问穿提前把下面三个问题准备好问题一为什么你的系统需要预约号源表直接在排班表里扣库存不行吗答设计号源表是为了保证时段级的精确控制和并发安全。如果只是排班表里做一个total和available的减法两个用户同时下单时大概率出现读取到同一个available值然后都更新成available-1超卖难以避免。每个号源对应一条数据库记录每个预约请求对应一条UPDATE ... WHERE status 0语句数据库行锁天然挡住了冲突。这个回答能同时展示你对数据库模型和并发控制的理解。问题二你的JWT放在header里如何防止被窃取答项目实际部署在局域网内风险相对可控。同时我做了两层保障第一前端用axios拦截器统一在Authorization头携带token第二后端对异常JWT在全局异常处理器统一拦截返回401。生产环境进一步防范的话可以使用HTTPS传输防止中间人抓包、设置合理的token过期时间降低被盗用后的有效期、前端不把token存localStorage而是存内存避免XSS脚本读取。问题三如果医院有一百个医生同时放号你的系统会不会崩答单体SpringBoot应用在这个规模下完全没问题。100个医生并发放号本质是100条不同记录的更新MySQL行级锁天然支持这种场景。如果到了几千家医院、每天几十万预约的体量当前架构肯定要演进为微服务、消息队列削峰、Redis缓存。但作为毕业设计我会强调单体架构已经覆盖了系统的核心业务场景和边界情况。这三个回答里最重要的是把为什么这样设计说清楚老师通常不会追问超出你能力范围的细节但会通过你对自己设计逻辑的熟悉程度来评估这个毕业设计是不是你自己做的。9. 复盘一下我做这个项目最花时间的三件事整个项目从需求分析到答辩通过前后花了大概7周。复盘下来最花时间的三件事第一排班模型从字段减法改造成号源表行级锁的过程前前后后折腾了将近两周中间还推翻重来过一次第二前端联调时因为接口字段不一致反复返工后来用了Knife4j文档统一返回结构才好一些第三权限控制踩坑Spring Security的过滤器链配置说复杂不复杂但对新手来说为什么我的接口全被拦截了为什么放行了还是不能访问这类问题特别消耗时间。如果你时间紧我建议把重心放在这个顺序上先把登录、排班、预约、状态流转这条核心链路跑通这是系统的骨架再补医生接诊、病历记录这是完整的业务闭环最后才是公告、反馈、统计这些锦上添花的功能。别一上来就把公告管理、角色权限这些外围功能全部搭好然后发现核心预约流程还有bug那种返工是最痛苦的。还有一个小技巧想分享给各位项目里的每一个核心业务模块都建议单独写一个Markdown文档记录设计思路。比如排班模块为什么要用号源表、预约状态机的流转规则、并发控制的三层防线记录下来之后最后写毕业设计文档时基本可以整段迁移根本不愁字数不够。而且答辩前复习时拿这个文档临时扫一遍心里会踏实很多。医疗系统这个题目无论做预约、挂号还是便民服务底层的核心逻辑都是资源调度和状态管理。把排班看成一个资源池把预约看成资源锁定操作把就诊看成状态流转的终点——想明白了这一层你收获的就不仅是一个数据库CRUD系统而是一套可以迁移到任意预订类业务场景的系统设计能力。这个收获在后续无论是工作中还是在别的项目中都能反复用上。
返回列表