☰
SpringBoot+Vue+MySQL线上医院挂号系统全栈实战解析
2026/10/20 3:21:47 网站建设 项目流程

1. 项目整体定位与功能拆解

1.1 这个系统到底在解决什么

说实话,我最早接触线上挂号系统这类项目,是帮一个朋友所在的社区诊所搭内部预约工具。当时最直观的感受是:患者一大早就得去窗口排队,热门科室的号源五分钟内就被抢空,而真正需要复诊随访的老病号反而抢不到号;医生那边也头疼,出诊时间被大量咨询电话占着,根本没空接真正要紧的病情。

后面我完整跑了一遍这套“SpringBoot后端+Vue前端+MySQL”的线上医院挂号系统,才意识到它真正在解决的,是资源错配问题——医院的人力和号源是固定有限的,患者的时间也是固定有限的,信息不透明才导致两边都难受。这套系统做的其实是把挂号的“场外排队”搬到线上,用数据库来管理号源、用接口来分发号源,让整个过程可追踪、可统计、可调控。

它的目标用户按角色划分很清晰:患者端可以注册登录、浏览科室和医生排班、在线挂号、查看挂号记录;医生端能维护自己的排班出诊表;管理员负责医院基础数据的维护——科室管理、医生管理、号源池管理、挂号记录审核。这一点非常重要,因为很多类似的课设项目喜欢把管理员和用户塞进同一个界面,看起来功能多,实际操作逻辑稀烂。这套系统的设计是彻底的分角色视图,不同的登录身份看到的是完全不同的页面和接口权限。

这个项目适合谁来学习和二次开发?我觉得主要是三类人:一是正在做毕业设计或者课程设计的全栈方向学生,需要一个完整度够、能跑通、代码结构清晰的项目来作为模板;二是刚入行Java后端、想了解SpringBoot+Vue前后端分离完整链路的新人工程师;三是公立医院信息科或者基层医疗机构的技术人员,需要一个开源的本地化挂号系统做参考蓝本,自己基于它改造成内网版本。无论哪种身份,这套系统的工程化程度都足够你学到东西。

1.2 功能模块的完整拼图

我们先把这个系统的功能地图铺开来看,你才能理解后文的数据库设计和接口设计,都是在为这些功能服务的。

整个系统的功能面其实可以切成三大块:

第一块是公共基础服务,包括用户注册、登录、个人资料维护。注意这个系统的用户体系不是单表,而是分成患者用户和管理员,因为两者需要挂载的字段完全不一样。患者表需要年龄、身份证号、联系电话、社保号这类医疗属性字段,管理员表只需要账号、密码、角色标识这种基础信息。很多新手项目喜欢一把梭地把所有用户合在一张表里加个role字段,当时看着方便,等做到排班管理和挂号预约的时候你就知道有多别扭。

第二块是医院资源管理,这是整个系统最核心的部分。科室管理管的是树形结构,比如内科下面还要分心血管内科、消化内科、呼吸内科,这个层级关系要用parent_id来维护;医生管理管的是医生的基本信息、职称、擅长领域,并且要建立医生和科室的多对一关系、医生和排班的排班关系;排班管理管的是医生一周每天的出诊时段,每个时段会产生对应的号源批次。

第三块是挂号业务主线,包括号源查询、挂号下单、支付回调(简化版)、挂号记录管理等。其中最关键的概念是“号源池”——某个医生某个时段最多放出多少号,已挂多少号,剩余多少号。这个在数据库里是一个独立字段还是两张表的设计,直接决定了并发场景下会不会出现超挂的问题。这套系统的做法是,在号源表里同时存总号数、已挂号和剩余号,下单时用乐观锁的方式更新,是一个很适合教学的成熟设计。

1.3 为什么是SpringBoot+Vue+MySQL这套组合

选这套技术栈的人,绝对不是因为市面上SpringBoot最火所以跟风,而是因为它确实能匹配挂号系统这个应用场景的几乎所有诉求。

先看后端SpringBoot。它最大的价值是低配置化——之前用SSM框架写一个项目,光是Spring的XML配置文件就能堆几十行,现在只需要一个application.yml就能搞定数据源、端口、MyBatis-Plus等核心配置。挂号系统本质上是一个典型的CRUD系统加上业务状态流转,SpringBoot的清晰分层就很合适:Controller层只做参数接收和数据装配,Service层只做业务逻辑和事务控制,Mapper层只做数据持久化操作。这种分层在多人协作开发的时候优势尤其明显,你分到的模块不会跟别人的代码纠缠不清。

再看前端Vue。Vue的渐进式设计对这类后台管理系统特别友好。你不需要一开始就把它当成一个复杂的前端框架来学,只需要把组件拆好、路由配好、状态管理搭好,就能很顺畅地实现整个页面交互。挂号系统涉及大量的表单提交、弹窗确认、表格展示,Vue的双向绑定让表单数据交互的开发效率比操作原生JavaScript高出一个量级。而且Vue生态里的Element-UI组件库几乎成了国内后台管理界面的默认选型——表格、表单、日期选择器、提示消息都是开箱即用。

MySQL更不用说。挂号系统需要强一致性和丰富的事务支持,MySQL在这类关系型数据存储上是绝对稳妥的选择。你可能会问为什么不用Redis做缓存?这个系统的阶段,数据量和并发量根本到不了需要引入缓存中间件的程度,单靠MySQL配合合理的索引和事务隔离级别完全够用。过度设计才是这类项目最容易犯的错误。

2. 数据库设计与核心表结构解析

2.1 数据表设计的第一原则:先把业务主流程跑通

数据库设计是整个系统的地基,地基打歪了,后面改代码的痛苦指数会直线上升。我说说这套系统在表设计上值得关注的地方。

先说总体的表结构拼图,一共八张核心表:管理员表、患者用户表、科室表、医生表、排班表、号源表、挂号订单表、支付流水表(简化结构)。外加几张辅助表或者字段冗余,比如通知记录表。

我第一次看这套表结构的时候,有一个设计印象特别深:它把医生和科室拆成了两张表,通过department_id关联。很多早期设计喜欢直接把科室名字写死在医生表里,短时间看很方便,但要实现科室维度的统计、排班筛选和管理员维护科室层级关系时,就会进退两难——改科室名称要同步所有挂在这个科室下的医生记录,而且根本没法体现科室的多级结构。单独的科室表配合parent_id字段,才能用一条SQL查出一棵科室组织树。

患者表这边,注意它不像一般的商城用户表只存昵称和头像,而是存了身份证号、社保卡号、紧急联系人等医疗场景下才会有实际意义的字段。这提醒了一个通用经验:在设计数据表时,字段不是越少越好,而是必须从业务场景倒推哪些字段在未来的功能扩展中一定会用到。挂号系统后续如果要接在线问诊、电子病历、医保结算,前置的这些身份数据就是地基里的钢筋。

2.2 核心表字段深度拆解

我把整个系统最重要的三张表——排班表、号源表、挂号订单表——的字段设计逻辑展开讲讲。

排班表的字段设计思路是这样的:主键id(自增)、doctor_id(关联医生)、department_id(关联科室,冗余存储是为了查询免联表)、work_date(出诊日期)、time_slot(时段标记,上午/下午/晚间)、status(排班状态:可用/停诊)。关键点在于,同一个医生在同一天不能出现重复的时段排班,这个要在数据库层面用联合唯一索引约束(doctor_id, work_date, time_slot),而不能仅仅依靠前端控制。

号源表是挂号系统的精髓所在,也是最容易设计出问题的一张表。这套系统的字段是:id、schedule_id(关联排班表)、total_number(总号数)、appointed_number(已挂号数)、remain_number(剩余号数)、version(版本号,用于乐观锁)。其中有几个设计巧思值得你细品:

  • 为什么不在排班表里直接放号总数,而是单独用一张号源表?因为一个排班周期(比如一上午)可能需要动态调整号源池。单独一张表可以让医生或管理员增量补充号源,不影响排班主数据。
  • 为什么同时保留appointed_number和remain_number,而不是只存一个剩余数?因为业务上有时需要看“已挂彩”的进度,比如出诊时段过半了还有多少患者没来,这个信息对医生做时间预判很有用。冗余一个字段,查询时就不用临时做减法。
  • 为什么要有version字段?这是专门为高并发场景下的超卖问题准备的——两个患者同时请求挂同一个号的最后一个名额,如果没有版本控制,两个请求都可能读到剩余号数为1,然后各自把自己的订单插入成功,这就是典型的超卖。乐观锁的机制很简单:UPDATE 号源表 SET appointed_number = appointed_number + 1, remain_number = remain_number - 1, version = version + 1 WHERE id = ? AND version = ?,如果影响行数为0,说明已经被别人抢先,本次请求失败并提示“号源被抢完了”。

挂号订单表的字段就相对直白:order_no(业务订单号,唯一)、patient_id(患者)、schedule_id、doctor_id(冗余)、department_id(冗余)、visit_date(冗余)、time_slot(冗余)、status(订单状态:待支付/已支付/已取消/已退号/已完成)、create_time。注意到大量字段冗余没有?这在订单表设计中是完全正确的选择——订单是业务快照,查询时如果还要不断关联排班表、医生表、科室表才能展示给用户看,数据库的压力会成倍增加。用户查询历史挂号的经历非常高频,而排班表和医生表的改动又很频繁,如果订单表不是快照结构,用户的就医历史记录就会因为医生排班的变动而错乱。

2.3 数据一致性的三个关键保护区

这颗系统的数据库设计在一致性方面有几个保护和取舍,值得拿出来单独说。

第一个保护区是号源更新和订单插入必须在一个事务里。挂号的原子操作是:先扣减号源,再插入订单,二者之间不能有中间状态。如果先插入订单再扣号源,扣减失败会导致订单无号可用;如果先扣号源再插入订单,插入失败会导致号源无端减少。这套系统的做法是在Service层标注@Transactional,保证两件事要么同时成功,要么同时回滚。

第二个是订单状态不能随意跳转。一个挂号订单的合法流转路径是:待支付 -> 已支付 -> 已完成;待支付 -> 已取消;已支付 -> 已退号。代码里最好用一个状态机枚举类来控制状态流转,而不是让开发人员随意setStatus()。有次我在另一个项目里,因为状态流转没控制好,用户取消挂号和支付回调同时触发,订单状态直接被覆盖成完成状态,钱退了号却还在,导致一个号源被重复占用,这个问题排查了整整半天。

第三个是数据库隔离级别和锁的选择。默认的MySQL隔离级别是可重复读,在挂号的“扣减号源”这条更新操作上,必须使用乐观锁版本号机制(或者悲观锁SELECT FOR UPDATE)来防止并发下的覆盖更新。两个事务同时对同一条号源记录做更新,如果没有锁控制,后提交的事务会覆盖先提交的结果,导致实际挂出的号超过号源上限。这是挂号系统在并发场景下最容易翻车的点。

3. 后端核心模块的实现逻辑

3.1 接口架构和统一返回格式

这套后端采用标准的Controller-Service-Mapper三层架构,这个架构本身不新鲜,但它在细节上的处理有值得学习的地方。

接口风格是RESTful的。举几个实际的接口例子:

  • POST /api/auth/register —— 用户注册
  • POST /api/auth/login —— 登录,返回JWT令牌
  • GET /api/departments/tree —— 科室树,用于前端展示层级结构
  • GET /api/doctors/department/{deptId} —— 按科室查医生列表
  • GET /api/schedules/doctor/{doctorId} —— 按医生查排班计划
  • GET /api/schedules/available?doctorId=&date= —— 查询某医生某天还剩多少号
  • POST /api/appointments —— 提交挂号订单
  • PUT /api/appointments/{orderNo}/cancel —— 取消挂号

每一层接口都必须返回统一的JSON格式。这套系统的做法是Result对象封装:code(200成功,401未登录,500系统异常)、message(给前端提示用的人话)、data(业务数据)。这个习惯我强烈建议你在自己的项目里也固定下来——如果有的接口直接返回裸数据,有的接口返回封装对象,前端对接的时候就得为每个接口单独写异常处理逻辑,联调效率会大打折扣。

3.2 JWT认证与拦截器的完整链路

整个系统的权限控制链路是:登录 -> 签发JWT -> 前端存储 -> 请求携带 -> 后端校验 -> 角色路由。

登录逻辑比较常规:患者通过手机号+密码登录,账号密码用BCrypt加密存储,登录成功后签发JWT。JWT里携带用户的ID、角色、过期时间,用密钥签名。注意这个系统把JWT的密钥放在了application.yml里,并且通过加密工具进行了处理,不至于裸奔在配置文件里——我见过太多项目直接把密钥明文写在配置里然后上传到公开仓库,这个安全隐患非常致命。

真正的关键点在拦截器上。这套系统实现了一个JWT拦截器,里面重写了三个核心方法:

  • preHandle:检查请求头Authorization里的Token,解析校验通过后把用户信息放入ThreadLocal,以便Controller层直接从上下文获取当前登录用户,而不用每个接口都从参数里取userId。
  • 一旦校验失败,立即返回401状态码和信息,不让请求进入到Controller层。

更细的设计是角色权限校验。挂号系统中的一些接口是面向医生或管理员的,比如医生维护排班、管理员管理科室。拦截器在校验JWT合法性之后,还要用subject里的角色信息做二次验证:这个接口是否需要指定角色?如果是,再校验当前用户的角色是否匹配。这种双重校验机制才是信息管理系统权限控制的正确姿势。

3.3 挂号和退号的核心业务逻辑

整个后端业务里最复杂、最值得展开讲的是挂号和退号的两段逻辑,这两段也是面试的时候最容易被追问的业务闭环。

挂号的业务时序是这样的:

  1. 前端提交挂号的请求,包含排班ID和患者ID。
  2. 后端校验排班是否还存在、是否已停诊。
  3. 调用号源表的乐观锁扣减,更新appointed_number和remain_number。
  4. 扣减成功则生成订单,初始化状态为待支付。
  5. 支付流程简化处理后,支付成功回调将状态改为已支付。
  6. 整个流程用@Transactional包裹,任何一步失败都会整体回滚。

我在做这个模块的时候踩过一个坑:当时只实现了挂号和扣号源的逻辑,没考虑排班到日期后,如果预约的患者没来就诊,剩余号怎么处理的问题。后来在系统里加了一个定时任务,用于每天凌晨处理超过就诊日期且仍处于待支付的订单——自动取消并将号源回收到号源池。实际部署过挂号系统的朋友会知道,就诊违约和未支付订单是每天都会出现的高频场景,没有一个兜底的自动清理机制,号源池就会越积越乱,最终影响真正想挂号的用户。

退号逻辑则涉及资金流水的概念。已支付的订单要退号时,订单状态变为已退号,号源相应地回补。注意这里的回补同样需要乐观锁机制:剩余号数加回时,version号同步递增。如果这一步处理不当,会出现用户退号成功但号源仍未恢复的情况,导致后来的患者明明看到有余号却始终挂不上。

4. 前端Vue的实现与联调细节

4.1 前端工程结构和页面路由设计

Vue前端这块,这套系统用的是Vue 2.x语法配合Vue Router和Vuex,外加Element UI组件库。这套组合的历史地位相当稳固——大量已有的中后台管理系统都是这个技术底座,如果你未来进入国内互联网公司做后端系统开发,大概率也会接触同样思路的前端代码。

前端工程的结构划分很清晰:dist目录放构建产物,public目录放静态资源,src目录下面按api、assets、router、store、views、components拆分子目录。

路由设计上有几个细节值得说道。整个系统的页面分为公共页和登录后页面两大类:登录页、注册页是公共页,不需要携带Token即可访问;而首页、科室页、挂号页、订单中心等需要登录认证。路由守卫的实现方式是在Vue Router的beforeEach钩子中判断目标路由是否需要登录,如果有Token则放行,否则重定向到登录页。注意这里只做简单的Token存在性校验即可——真正的Token有效性校验还是得靠后端拦截器判断,前端做路由守卫只是为了提升用户体验,不要把它当成安全措施。

页面之间的跳转关系也设计得很符合医院业务习惯:用户登录进入首页 -> 选择科室 -> 查看该科室医生列表 -> 查看某个医生的排班日历 -> 选择某个时间段的号源 -> 确认下单 -> 支付 -> 在我的预约中查看结果。整条主流程最多8个页面就能完成,没有多余的分支环节。这种线性清晰的流程设计,对于老年患者这类对互联网产品不熟悉的用户群体尤其重要。

4.2 API请求封装与状态管理

前端这块有个细节特别值得新手学习:对axios的封装处理。

这套系统的做法是在src/api目录下建一个request.js工具模块,对axios做二次封装。核心处理逻辑包括:设置基础URL(指向后端接口根路径)、配置请求拦截器(自动在Header里携带JWT Token)、配置响应拦截器(对后端统一返回结构做解包处理,code=200才返回data给业务代码,遇到401则自动跳转到登录页)。

我看过太多学生的前端代码,fetch请求直接写在页面组件里,每个页面都复制一段处理异常的方法。当时能跑,等页面上到十个以上,头疼就开始了——后端接口路径改了,你要全项目替换字符串;登录过期了,你要在每个页面里手动判断错误码;加一个统一的请求头,你得挨个页面去改。把请求统一封装的收益,在项目规模变大后会越来越明显。

Vuex状态管理在本系统里的职责分配是这样的:用户登录后的基本信息和Token存放在Vuex的user模块中,同时配合vuex-persistedstate把关键状态持久化到localStorage,这样用户刷新浏览器后不用重新登录。挂号时的临时选择状态(选中的科室、医生、排班)放在sessionStorage里,因为这类页面跳转的参数不适合塞进URL——毕竟暴露在地址栏里既不安全也不美观。

4.3 前后端联调和跨域问题处理

前后端分离开发必然遇到的第一个技术问题就是跨域。前端跑在8080端口,后端跑在8081端口,浏览器的同源策略会直接拦截所有后端的接口响应。这套系统的解决方式是后端配置CorsFilter,允许跨域请求并指定允许的请求头、请求方法和携带凭证的规则。

具体的配置逻辑是设置allowedOrigins为前端的访问地址(开发环境是http://localhost:8080),allowedMethods为GET/POST/PUT/DELETE/OPTIONS,allowedHeaders设置为Authorization和Content-Type,并设置allowCredentials为false或true。这里有一条血泪教训:如果你用allowCredentials(true),allowedOrigins就不能用通配符*,必须写具体地址,否则某些浏览器版本会直接抛异常拒绝请求。我在调试一个老项目时,就是被这个组合问题坑了两天才意识到。

联调时最大的坑还不是跨域,而是数据格式约定。后端的日期类型在Java里默认序列化后返回"2025-06-15T08:30:00.000+00:00"这种UTC格式,而前端Element UI的日期选择器默认期望"yyyy-MM-dd HH:mm:ss"这种本地格式。如果不统一,前端会显示一串看不懂的字符,或者直接解析报错。解决办法是后端在application.yml里配置全局的Jackson日期格式化,指定为GMT+8时区并设置pattern,而不是在每一个DTO字段上加@JsonFormat注解——后者虽然也行,但维护成本要高一个数量级。

5. 环境准备与启动上线全流程

5.1 本地环境要求清单

很多人拿这个项目跑不起来,不是代码问题,是环境问题。我给一份标准的本地运行环境清单:

  • JDK 1.8+(建议8或11,最好别用17+,有些老版本依赖对高版本JDK兼容性有隐患)
  • Maven 3.6+(用于依赖管理,配置阿里云镜像加速更省时间)
  • MySQL 5.7+或8.0+(数据库,注意编码设置为utf8mb4,避免中文乱码)
  • Node.js 14+(前端构建环境,npm第6或7版本都行)
  • IDE选型随意:后端用IDEA、Eclipse都可以;前端用VS Code比较轻量

关于版本有一个很实际的经验建议:不要盲目追求最新版本。SpringBoot 2.x和Vue 2.x在这个项目场景下已经足够了,不要为了“尝鲜”用SpringBoot 3.x——那会带来javax到jakarta包名迁移的问题,很多第三方库和旧代码不兼容,平白无故给自己增加踩坑成本。

5.2 数据库初始化和配置文件修改

这个项目提供了一个init.sql或者直接在resources目录下的db文件夹里放建表语句和初始数据。实际操作时,你需要做的核心步骤如下:

第一步,创建数据库实例。执行CREATE DATABASE hospital_挂号系统名 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,注意库名不要用连字符,MySQL里库名带连字符每次都要加反引号,极其折磨人。

第二步,导入sql脚本。使用mysql命令行或者Navicat客户端导入项目提供的数据库脚本。脚本里一般包含:建库语句、建表语句、初始数据(科室数据、超级管理员账号、演示医生账号)。如果你是新手,建议不要直接在Navicat里运行整个脚本,分段执行并且注意看到“Query OK”字样再执行下一段——这样如果遇到报错你能快速定位是哪个环节出了问题。

第三步,修改后端数据源配置。打开src/main/resources/application.yml,将数据库的URL、用户名、密码改成你自己的本地配置。重点注意URL里一定要带useSSL=false和serverTimezone=Asia/Shanghai这两个参数,否则可能会出现连接警告和日期偏差8小时的问题。

第四步,修改前端接口地址。前端项目中src/api/request.js里配置的基础URL默认指向http://localhost:8081,如果你的后端端口改了,对应的前端配置也要同步更新。

5.3 启动后端和前端的具体操作

后端启动相对简单。在IDEA中打开项目根目录,等待Maven自动下载依赖,找到启动类(一般是项目名+Application.java),右键直接运行即可。看见Tomcat started on port(s): 8081字样就说明启动成功。

前端启动要分两步。先在项目根目录执行npm install安装依赖,这一步可能会遇到一些老依赖的兼容性警告,只要不出现fatal error基本可以忽略。依赖安装成功后执行npm run serve启动开发服务器,默认在localhost:8080端口。

一个非常常见的启动坑是端口占用:8080被之前启动过的前端进程占着,或者8081被占用,又或者MySQL的3306端口被占用。解决方法是命令行执行netstat -ano | findstr :8080之类的命令,找到占用进程的PID,去任务管理器强制结束,或者直接修改application.yml和vue.config.js里的端口配置。

6. 常见问题与排查技巧实录

6.1 典型报错速查表

我把这段时间跑这个系统时遇到的真实报错按频率整理成了一份速查表,你在实操过程中如果遇到报错可以按图索骥。

报错场景典型报错信息直接原因排查方向
后端启动失败Table 'hospital.schedule' doesn't exist数据库没有完整导入SQL脚本检查建表语句是否执行成功
前端npm install卡住ERR! code ETIMEDOUT网络原因下载依赖超时切换npm镜像源为国内源
登录后页面空白控制台报TypeError: Cannot read property 'xxx' of undefined后端返回的数据结构和前端expect的不一致用Postman调用接口看真实返回格式
请求接口报401JWT token expiredToken过期失效检查前端是否配置自动带Token,后端过期时间是否过短
跨域报错CORS policy: No 'Access-Control-Allow-Origin'后端没有正确配置CorsFilter检查allowedOrigins是否包含前端地址
挂号时提示号源不足更新失败,剩余号数未减乐观锁version冲突检查Service层代码是否在循环里更新,以及事务是否生效
列表页日期显示成时间戳"1718457600000"Java返回的日期类型被序列化成时间戳检查application.yml里的Jackson配置

这里的每一行背后都是真实场景,我挑几个展开讲讲。

6.2 血泪教训:三个最隐蔽的坑

第一个坑,MySQL连接池时区问题导致的日期错乱。早期项目里的jdbcURL没有配置serverTimezone,导致从数据库里查出来的就诊日期会比正常日期少8个小时。排查过程极其痛苦:明明数据库表里的值是2025-06-15,前端查出来却变成2025-06-14T16:00:00.000Z,除非你同时打开数据库和前端页面比对,否则根本发现不了。本质原因是MySQL的UTC时间和本地的GMT+8时间在时区上存在差异。解决办法就是在JDBC连接串里显式加上serverTimezone=Asia/Shanghai。这个配置虽然不起眼,但它能避免很多让人摸不着头脑的“灵异现象”。

第二个坑,Element UI日期选择器和后端接收参数的格式不匹配。前端选中的日期格式是"2025-06-15",但后端实体类的字段类型是Date,Spring接收到字符串后需要按某种默认格式解析;如果接受不到,直接抛400错误。问题的根源在于String转Date的过程依赖默认格式,而默认格式在不同的地区设置下并不一致。最终的解决方式是:在实体类日期字段上用@JsonFormat统一声明输入输出格式,然后再在application.yml里配一套全局默认日期格式作为兜底。前端表单提交时注意,把日期选择器输出的Date类型转成后端的预期格式之后,再放在请求体里,尽量做到“前端输出什么,后端就接收什么”。

第三个坑,JWT拦截器拦截了登录接口本身,导致整个系统无法登录。有个版本的拦截器逻辑写的是统一拦截所有/api/开头的请求,结果把登录接口也拦了,每次登录都会报401。排查到最后,看到拦截器配置代码才发现,忽略列表里没有把登录和注册接口排除掉。这个问题的解决方案很机械但容易漏:把无需认证的接口路径(登录、注册、浏览科室列表、搜索医生等)加到排除列表中,其余的请求才进行token验证。这个逻辑在配置类里用excludePathPatterns方法指定,每一行都要写清楚,别学有些人的写法,只排除了一个登录接口,结果前端死活调不到注册接口。

6.3 部署上线时额外的三条建议

本地跑通只是最基础的一步。如果想把系统真正上线部署,还有几条补充建议,都是我实践里反复验证过有效的方法。

第一,数据库连接信息不要写死在代码里。至少把数据库的用户名、密码通过环境变量注入,或者放到单独的配置中心里管理。我见过一个上线不到一周的项目,因为开发者把数据库密码明文写在application.yml里并且直接推到代码仓库,导致整库被恶意脚本删掉数据。这个教训非常惨痛,而且完全可以避免。

第二,前端的构建产物要用Nginx提供服务。通过npm run build生成的dist目录,放到Nginx的html目录下,配置一个server块,将/api/路径转发到后端服务。生产环境的跨域处理方式就不靠CorsFilter放行了,而是通过Nginx的反向代理——浏览器只访问Nginx的域名,Nginx把API请求转发给后端,不存在跨域问题。这条方案比CORS通配符更安全、更规范。

第三,添加简单的操作日志模块。一个可追溯的挂号系统,所有关键操作(挂号、退号、取消、停诊)一定要有日志记录,包括操作人、操作时间、操作内容、请求IP。这个功能听起来不起眼,但在真正运营时是排查问题的第一入口。比如患者说他挂过号但没收到通知,你只要查一下日志,就能立刻确认:他到底挂没挂上?是支付环节断了,还是通知渠道出了问题?

我个人在实际操作中的体会是:这类全栈项目真正难的地方,从来不是某个技术点有多高深,而是当你把前端、后端、数据库、部署环境串起来的时候,那份“把每个环节都打磨到可用状态”的耐性。这套挂号系统虽然从技术栈上讲不算复杂,但它的完整度、规范度、业务逻辑的严谨度,在同类型的开源项目里是少见的,值得你下载下来一遍遍跑、一行行读。最后再分享一个小技巧:跑这类项目时,建议你准备一个接口测试工具(比如Postman或Apifox),把每个接口的请求参数整理成一套模板。这样做的好处是,前端页面出问题的时候,你能用工具先确认到底是后端接口本身的问题,还是前端调用方式的问题,少走很多弯路。这个习惯,不管以后你做后端还是搞全栈,都能省下大把时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询