ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SpringBoot+Vue+MySQL社区医院管理系统:从设计到部署全解析

SpringBoot+Vue+MySQL社区医院管理系统:从设计到部署全解析

SpringBoot+Vue+MySQL这套组合做社区医院管理系统,我太熟了。每年到毕设选题的时候,总有人来问“学长有没有合适的管理系统项目”,十个里面至少六七个最后都落在这类“XX管理系统”上——技术栈经典、业务场景常见、工作量可控,用来做毕业设计或课程设计刚好合适。Java后端配MySQL数据库是很多高校课程教的那个路线,SpringBoot负责接口和业务逻辑,Vue负责页面和交互,两个人各司其职,前后端分离的架构也正好踩中目前企业开发的主流模式。这篇博文我就以“社区医院管理系统”这个具体项目为例,从需求拆解、技术选型、数据库设计到核心业务实现、本地运行与部署,把整套东西掰开了讲清楚,顺带把答辩时老师爱问的那些问题也给你理一遍。

这个系统的业务主线其实不难理解:患者建档挂号,医生按号接诊、写病历、开处方,收费员收费,药房发药扣库存,管理员维护用户、科室、药品等基础数据,最后再用几张统计图表把数据串起来。整个流程是一个完整闭环,从业务上讲得通,从技术上又不会复杂到做不完,非常适合拿来当毕设或者课设。适合的人群也很明确:想用Java全栈完成一个像样项目的在校学生、准备转行Java开发需要简历项目的新手、以及帮学生选题或带项目的人。下面直接上干货。

1. 为什么社区医院这个选题适合做毕设/课设

1.1 业务规模不大不小,拿来练手刚刚好

很多人一开始喜欢挑“三甲医院管理系统”“智慧医疗平台”这种名字听着很高级的题目,三甲医院那个业务复杂程度,光一个医保结算就能让人研究两三个月,再算上预约挂号、分诊叫号、住院管理、检查检验、手术排期,做出来基本就是一个半成品。社区医院的定位就聪明多了:麻雀虽小,五脏俱全。它有三甲医院一样的核心流程——挂号、看诊、开药、收费、发药,但没有医保对接、没有复杂住院管理、没有大型设备对接,业务边界非常清晰。

我说得直白一点,毕设项目的核心目标不是“做出一个上市产品”,而是“证明你掌握了一整套软件开发流程”。社区医院管理系统恰好能覆盖这个目标:需求分析、数据库建模、后端接口开发、前端页面开发、前后端联调、部署演示,每个环节都能在项目里体现。我见过太多人做一个“XX管理系统”结果只有两个页面三个接口,那种项目答辩时一眼就会被看穿。社区医院这个场景天然逼迫你设计出七八张业务表、十几个接口,工作量正好卡在“能独立完成”和“有含金量”之间。

1.2 角色和功能模块怎么拆才合理

社区医院相比大医院,用户角色也比较简单。我给项目定了四种核心角色:

角色职责范围常用功能
管理员维护系统基础数据、管理账号用户管理、科室管理、药品管理、数据统计
医生处理患者就诊业务查看排班、接诊患者、写病历、开处方
收费员处理收费业务挂号收费、处方收费、收费记录查询
药房管理员管理药品和发药药品库存管理、发药登记、库存预警

模块划分上,我个人建议做成下面这样一个清单,照着这个清单做需求文档和功能拆分就很顺:

  • 系统管理模块:登录认证、用户管理、角色管理、密码修改
  • 基础档案模块:患者档案管理、科室维护、药品目录维护
  • 门诊业务模块:医生排班、预约挂号、就诊接诊、病历录入、处方开立
  • 收费药房模块:挂号收费、处方收费、发药出库、库存查询与预警
  • 统计展示模块:挂号量统计、科室营收统计、药品消耗排行

这样做有一个很大的好处:每个模块都有明确的页面和接口归属,分工清楚,写代码的时候不容易乱。而且无论论文里的“系统功能结构图”还是答辩演示,都能直接按照这个框架来讲,逻辑感很强。

2. 技术栈选型:SpringBoot + Vue + MySQL这套组合好在哪

2.1 后端为什么用SpringBoot而不是SpringMVC或SSH

很多人纠结后端框架怎么选,我的态度很直接:现在做毕设还去手写SSH(Struts+Spring+Hibernate)属于给自己找罪受,SpringBoot几乎已经是企业级Java开发的事实标准。它最大的价值就是把Spring的繁琐配置全部自动化了,以前要写一堆XML的地方现在一个注解就搞定。内嵌Tomcat的机制也让部署变得简单,打包成jar文件,服务器上有JDK就能跑,不需要单独装Tomcat。

版本方面,我推荐SpringBoot 2.7.x搭配JDK 1.8,这是目前兼容性最稳的组合。SpringBoot 3.x虽然已经发布很久了,但它强制要求JDK17及以上,很多学校的教学环境和实验室电脑还在用JDK8,而且网上搜到的教程、遇到问题时能搜到的解决方案,绝大多数还是围绕2.x的。课设、毕设求的就是一个稳,没必要在这上面冒险。

至于持久层选型,MyBatis Plus是现在的绝对主流,比原生MyBatis少写大量XML,比Spring Data JPA更容易理解和排查问题。BaseMapper直接提供单表的增删改查,分页插件一条配置就能用,代码生成器还能帮我们把实体类、Mapper、Service全部生成出来。这些能力对于快速完成毕设简直是降维打击。我带过很多学生,用MyBatis Plus写CRUD基本就是抄模板,比手写JDBC爽太多。

2.2 前端为什么选Vue + Element UI

前端的技术选型比后端更讲究“投入产出比”。Vue作为渐进式框架,学习曲线相对平缓,一个新手花两三天理解组件、路由、状态管理这些概念,基本就能上手写管理后台。Element UI组件库又专门为后台管理系统优化过,表格、表单、弹窗、分页都是现成的,拖上去就能用。对整个项目来说,前端的开发效率直接决定你能不能在两周内把页面部分写完。

Vue2还是Vue3,我建议大多数做毕设的人选Vue2。别急着喷,这不是说Vue3不好,而是Vue2 + Element UI这套生态已经成熟到了“网上随便一搜全是案例”的程度。你前端遇到任何问题,搜一下基本都有答案。Vue3 + Element Plus当然也可以,但如果你对组合式API不熟练,边学边写可能会拖慢进度。如果你本身对Vue3比较熟,那直接用Vue3也没问题,项目本身不挑版本,业务设计都是一样的。

2.3 前后端分开部署,靠什么连起来

这个项目的前后端分离结构,需要一套清晰的数据交互方案。后端统一返回一个Result对象,结构长这样:

{ "code": 200, "message": "操作成功", "data": {} }

前端所有请求都经过Axios封装,请求拦截器自动带上JWT token,响应拦截器统一处理后端返回的数据。这里有几个关键设计点:

  • 登录接口成功后,后端返回一个token字符串,前端存进localStorage
  • 每次请求,前端在Header里带上Authorization: token值
  • 后端用一个拦截器拦截需要登录的接口,校验token的有效性
  • 如果token失效,后端返回401,前端跳到登录页重新登录

写后端接口的时候,还要给自己定一个规范:路径统一以/api开头,业务接口按模块分组。比如/api/register是挂号相关,/api/prescription是处方相关,/api/drug是药品相关。前端开发的时候通过代理把/api转发到后端的8080端口,这样本地联调完全不会遇到跨域问题。这个规范我在后面第5章还会细说。

3. 数据库设计:关键表和字段一次讲清楚

3.1 用户、医生、患者三类基础档案表

数据库设计是毕设答辩时老师一定会深挖的部分,一个个字段问你“为什么这么设计”都有可能。我按业务模块把表分成几组,咱们一组一组说。

第一组是用户和权限相关的表,至少要有sys_user(用户表)和sys_role(角色表)。sys_user的核心字段要包含:

  • id主键,自增
  • username登录账号,唯一
  • password密码,存BCrypt加密后的密文
  • real_name真实姓名
  • phone手机号
  • role_code角色标识,比如ADMIN、DOCTOR、CASHIER、PHARMACIST
  • status状态,0禁用,1启用
  • create_time创建时间

这里要提醒一句,不要把数据库表和业务表混在一个表里。我们用一个role_code字段区分角色就够了,不需要真的把用户、医生、收费员拆成三张表,否则登录认证会写得非常别扭。医生表单独建一张doctor表,存医生的专业职称(主任医师、主治医师之类)、所属科室、擅长领域、个人简介,用user_id和用户表关联。这样做的核心价值是:账号信息关注“能不能登录”,业务信息关注“这个医生擅长什么”,两者职责分离。

第二组是患者档案表。patient表重点字段:

  • id_card身份证号,设置唯一约束
  • gender性别
  • birth_date出生日期
  • phone联系电话
  • address联系地址
  • allergy_history过敏史,这个字段很重要,医生开药时一定会看
  • remark备注

患者不直接关联用户表,因为患者通常是前台登记建档的,不需要登录系统。这个设计在答辩时容易被问到,提前想好怎么解释:患者是业务对象,用户是系统账号,两者维度不同。

3.2 业务核心表:排班、挂号、处方、药品、收费

第一张核心业务表是schedule(排班表),记录医生在某一天某个时段是否出诊。字段包括doctor_id、dept_id(科室)、schedule_date(日期)、time_slot(上午/下午)、total_count(总号数)、remaining_count(剩余号数)、fee(挂号费)。这里必须要有remaining_count,因为挂号业务的实质就是“消耗号源”,后续在代码里要用乐观锁的方式保证不超卖。

第二张是registration(挂号表),字段包括patient_id、doctor_id、schedule_id、reg_date、time_slot、serial_no(序号)、status(挂号状态)、fee、create_time。挂号表的状态设计是整个门诊流程的关键:

  • 1 待就诊
  • 2 就诊中
  • 3 已完成
  • 0 已取消

第三组是处方和药品。prescription(处方主表)记录doctor_id、patient_id、total_amount、status(1未收费,2已收费待发药,3已发药,4已取消);prescription_item(处方明细表)记录drug_id、quantity、price、amount、usage_notes(用法用量,比如“一日两次,一次一粒”);drug(药品表)记录name、specification(规格)、manufacturer(生产厂家)、unit(单位)、price(零售价)、stock(库存)、warning_stock(库存预警线)、category(药品分类)。

第四组是收费记录表charge_record,字段包括charge_type(收费类型:挂号费/处方费)、biz_id(关联的业务单号,挂号ID或处方ID)、amount(金额)、pay_method(支付方式:现金/微信/支付宝)、operator_id(操作人)、create_time。

数据库设计里还有一个通用原则必须强调:所有金额字段一律用decimal(10,2),严禁用float或double,二进制浮点数存金额会有精度误差,做结算时一分钱对不上就是个大事故。状态字段建议用tinyint加注释,不要用字符串,既省空间又直观。

3.3 几张表之间的关系怎么梳理

表和表之间其实就是业务关系。抽掉业务外壳来看:

  • 医生和排班是一对多:一个医生有多条排班记录
  • 排班和挂号是一对多:一个排班对应多个挂号的患者
  • 患者和挂号是一对多:一个患者可以多次挂号
  • 患者和处方是一对多:一次就诊可开一个处方
  • 处方和处方明细是一对多:一个处方包含多个药品条目
  • 药品和处方明细是多对多:一个药品出现在多个处方里,一个处方包含多个药品

我建议在写代码的时候使用“逻辑外键”,也就是只维护关联字段,不一定要在数据库层面建物理外键约束。MyBatis Plus的关联查询本来就是靠代码控制的,物理外键反而会给初始化数据和测试带来麻烦。但要记得给常用查询字段加索引,比如registration表的patient_id、schedule_id,prescription表的patient_id,drug表的name。数据量小的时候感觉不到索引的价值,答辩时能说出来“我在高频查询字段上建了索引”本身就是加分项。

4. 核心业务链路怎么实现:挂号、开方、发药全流程

4.1 登录认证与权限控制:JWT + 拦截器足够用

很多人在登录模块纠结要不要用Spring Security。我直接给结论:不用。Spring Security那套过滤器链和配置对于毕设来说太沉重了,理解成本高,调试也麻烦。用JWT加一个HandlerInterceptor就能实现全部需求,而且逻辑清晰,答辩时也好讲。

登录接口的核心逻辑其实只有三步:查用户、验密码、发token。代码写出来大概是这个意思:

@PostMapping("/login") public Result login(@RequestBody LoginDTO dto) { User user = userMapper.findByUsername(dto.getUsername()); if (user == null) { return Result.error("用户名不存在"); } if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) { return Result.error("密码错误"); } if (user.getStatus() == 0) { return Result.error("账号已被禁用"); } String token = JwtUtil.createToken(user.getId(), user.getRoleCode()); return Result.success(token); }

注意这里的passwordEncoder.matches(),密码不是明文比对。注册或初始化账号时用BCryptPasswordEncoder对密码加密入库,登录时把用户输入的明文和数据库里的密文做匹配。BCrypt每次加密都会生成不同的密文,所以哪怕两个用户密码一样,密文也不一样,这能防止彩虹表攻击。这个点答辩时被问到的概率极高,一定要能解释清楚。

JWT的token里只放必要信息(用户ID和角色),不放密码等敏感信息。后端拦截器在请求进入Controller之前校验token:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("Authorization"); if (token == null || !JwtUtil.verify(token)) { response.setStatus(401); return false; } // 解析出用户ID放入request,后续业务直接取 Integer userId = JwtUtil.getUserId(token); request.setAttribute("userId", userId); return true; }

前端配合在Axios请求拦截器里自动带上token:

axios.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = token } return config })

再配合Vue Router的路由守卫,没登录就跳登录页,登录了但访问无权限页面就提示,这一套下来权限控制就算完整了。

4.2 挂号扣号:用事务和乐观锁解决并发超卖

挂号业务是整个项目里最有“技术含量”的一个点,也是答辩时最容易被追问的部分。很多人都能写出“先查询剩余号数,大于0就插入挂号记录”,但这里有个隐藏问题:如果两个人同时挂号,都查到了剩余号数是1,然后同时插入记录,结果就变成两个人都挂上了,号源成了负数。

正确的做法是让“扣减号源”这一个操作成为不可拆分的原子操作。我用一个带条件的更新语句来实现:

@Transactional public Registration createRegistration(RegisterDTO dto) { // 1. 乐观锁扣减号源 int rows = scheduleMapper.decreaseRemaining(dto.getScheduleId()); if (rows == 0) { throw new BizException("该时段号源已满,请选择其他时段"); } // 2. 生成挂号记录 Registration reg = new Registration(); reg.setPatientId(dto.getPatientId()); reg.setScheduleId(dto.getScheduleId()); reg.setDoctorId(dto.getDoctorId()); reg.setStatus(1); reg.setCreateTime(new Date()); registrationMapper.insert(reg); return reg; }

对应的SQL是:

UPDATE schedule SET remaining_count = remaining_count - 1 WHERE id = #{scheduleId} AND remaining_count > 0

这个语句最关键的地方在AND remaining_count > 0。数据库会锁住这一行,保证同时只有一个请求能成功把remaining_count扣到非负数。affected rows如果返回0,就说明号源已经没了,直接提示用户就行。别看这只是一行SQL,它其实就是乐观锁思想在库存业务里的落地。

为什么这个方法要加@Transactional?因为“扣号源”和“插入挂号记录”是两个操作,要么都成功,要么都失败。如果扣了号源但插入挂号记录失败,用户会莫名其妙丢一个号;如果插入了记录但没扣号源,下一个用户就会被超卖影响。事务保证了数据一致性。这个思维模式非常重要,后面所有涉及多表写入的接口都要沿用。

4.3 医生开方与药房发药:状态机让数据流转清晰

医生接诊的逻辑是:确认患者挂号记录状态从“待就诊”变为“就诊中”,然后写病历(可以选择单独建一张medical_record表,也可以简化成在挂号记录里加一个诊断字段),再选药品生成处方。提交处方时,主表和明细表在一个事务里写入:

@Transactional public void createPrescription(PrescriptionDTO dto) { // 1. 插入处方主表,状态默认为“未收费” Prescription presc = new Prescription(); presc.setPatientId(dto.getPatientId()); presc.setDoctorId(dto.getDoctorId()); presc.setTotalAmount(dto.getTotalAmount()); presc.setStatus(1); prescriptionMapper.insert(presc); // 2. 循环插入明细 for (PrescriptionItemDTO item : dto.getItems()) { PrescriptionItem detail = new PrescriptionItem(); detail.setPrescriptionId(presc.getId()); detail.setDrugId(item.getDrugId()); detail.setQuantity(item.getQuantity()); detail.setPrice(item.getPrice()); detail.setAmount(item.getPrice() * item.getQuantity()); detail.setUsageNotes(item.getUsageNotes()); prescriptionItemMapper.insert(detail); } }

处方状态的设计我前面已经列过:1未收费 → 2已收费待发药 → 3已发药。为什么要加“收费后待发药”这个中间状态?因为收费和发药很可能是两个人在不同时间操作的。收费是确认“患者付了钱”,发药是确认“药已给出”,状态区分开,哪个环节出了问题都能一眼定位。

发药扣库存有个关键顺序问题:必须在收费成功之后扣库存,不能在医生开方时就扣。原因是患者可能开了药但不来缴费,如果你在开方时就扣库存,会造成大量库存被无效冻结。收费成功后再扣,患者是确定要买这批药的,库存扣除才有意义。扣库存的接口同样要用“库存充足才更新”的条件UPDATE,和挂号扣号源是同一个套路。

5. 本地环境搭建与前后端联调实操

5.1 先搭好开发环境:版本搭配决定后续是否踩坑

环境版本这件事,我见过太多人栽跟头。打开IDEA、安装MySQL、拉取代码、npm install、启动,每一步都可能有版本问题。我直接把推荐版本整理成表格:

组件推荐版本备注
JDK1.8与SpringBoot 2.x兼容最好
Maven3.6+管理后端依赖
MySQL5.7或8.0本教程针对8.0,5.7注意驱动差异
SpringBoot2.7.x稳定,教程多
Node.js14-16太新的Node版本可能导致Vue CLI编译报错
Vue CLI4.x或5.x对应Vue2项目
IDEA任意较新版本Ultimate版自带SpringBoot插件

如果用的是MySQL 8.0,JDBC驱动要选com.mysql.cj.jdbc.Driver,连接URL里建议带上参数useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true。前两个参数保证中文不乱码,serverTimezone解决时差8小时问题,allowPublicKeyRetrieval=true解决MySQL 8的认证失败问题。这些不是可有可无的配置,少了真的会报错。

5.2 后端项目从导入到跑通完整步骤

拿到一份SpringBoot源码后,启动流程其实很固定:

  1. 用IDEA以Maven项目方式导入,等待依赖下载完成
  2. 在MySQL中新建数据库,比如community_hospital,导入项目里自带的sql脚本
  3. 修改application.yml里的数据库连接信息(URL、用户名、密码)
  4. 直接运行主类CommunityHospitalApplication,观察控制台日志
  5. 看到“Tomcat started on port(s): 8080”就说明启动成功

一个很容易踩的坑是数据库字符集。建库的时候记得指定字符集:

CREATE DATABASE community_hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

utf8mb4比utf8多了对emoji和更多特殊字符的支持,也能存正常的生僻字。如果已经是utf8也没什么大问题,但新建库还是建议直接用utf8mb4。

启动后建议给自己配一个接口文档工具。如果项目里已经集成了knife4j或Swagger,直接访问对应地址就能看到所有接口的文档,前端联调时对照文档开发非常高效。即便项目没集成,临时加一个也很简单,一个依赖加一个注解配置的事,值得花这个时间。

5.3 前端项目启动与联调配置

前端拿到代码后,目录下打开终端,执行:

npm install npm run serve

npm install如果特别慢或者报错,大概率是镜像源的问题。国内环境下先切换镜像再装:

npm config set registry https://registry.npmmirror.com

前端启动起来后默认会在http://localhost:8081或其它端口。注意,前后端端口不要撞成同一个。后端的application.yml里如果配了server.port=8080,前端开发服务器就用8081。同时,前端项目的vue.config.js里配置代理,把所有/api开头的请求转发到后端:

module.exports = { devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

这个配置的价值在于:前端代码里请求路径写作/api/login,浏览器访问的是http://localhost:8081/api/login,开发服务器收到后自动转发给http://localhost:8080/api/login。因为浏览器看到的请求始终是同源的,所以根本不会触发CORS跨域问题。很多同学在本地联调时被跨域折磨到崩溃,就是因为没用代理而直接在前端写死http://localhost:8080。

5.4 部署到服务器:把项目真正“交出去”

毕设如果能现场演示部署到服务器上的版本,观感会提升很多,而且这本身也是答辩加分项。后端打包很简单:

mvn clean package -DskipTests

生成的target目录下会有community-hospital-0.0.1.jar文件,在服务器上执行:

nohup java -jar community-hospital-0.0.1.jar > app.log 2>&1 &

前端打包生成静态文件:

npm run build

dist目录下的文件上传到服务器的Nginx静态目录,再配置一下反向代理:

server { listen 80; server_name your-domain.com; location / { root /var/www/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }

这里try_files $uri $uri/ /index.html这行非常关键。Vue用history路由时,用户访问/register其实没有真实文件,Nginx默认会返回404,加上这行配置就能让所有路径都回退到index.html,由前端路由接管。

数据库也要迁移:本地MySQL导出sql文件,服务器上导入。这里提醒一句,生产环境不要再把数据库密码裸写在application.yml里,至少要用环境变量去引用,这是我一直强调的习惯。

6. 毕设/课设专属:问题排查清单和答辩高能问答

6.1 本地运行时最常见的几个报错与解法

我做这个项目过程中帮人排查过上百次启动问题,整理出一张高频报错速查表,遇到问题时直接对号入座:

报错现象根本原因解决方案
前端请求/api/login报404Vue代理没配或后端context-path不一致核对vue.config.js代理路径,确认后端接口前缀
前端请求报CORS error前端直接访问了后端地址用代理方式访问,或后端加CORS配置
连接数据库报Public Key Retrieval is not allowedMySQL 8的认证机制问题URL加allowPublicKeyRetrieval=true
报错Access denied for user 'root'@'localhost'数据库账号密码配置错误核对application.yml里的username/password
前端启动后页面白屏Vue编译报错或入口文件路径问题打开浏览器控制台看报错,检查main.js
npm install报Node版本过高错误Node版本与Vue CLI不兼容用Node 14或16版本重装依赖
刷新页面404history路由没有fallback改用hash模式或配置Nginx try_files
接口返回401token没传到后端或token过期检查Axios拦截器,确认请求头带了Authorization
时间字段差8小时MySQL连接时区未设置URL加serverTimezone=Asia/Shanghai
前端调用接口后一直弹登录页响应拦截器把业务错误当成未登录检查拦截器判断逻辑,只看HTTP 401状态码

排查问题的思路也很重要:先看浏览器Network面板里请求是否发出、返回什么状态码和响应体,再看后端控制台有没有报错堆栈。大多数前后端联调问题都能在这一步定位。不要在没确认问题出在前端还是后端的情况下瞎改配置,越改越乱。

6.2 演示的时候按什么顺序讲才显得逻辑清晰

答辩演示不是把所有功能点一遍,而是要有叙事逻辑。我强烈建议你按业务闭环来演示,让评委跟着业务走而不是跟着功能菜单走。

推荐的演示顺序:

  1. 登录系统,展示三种不同角色进入系统后的不同菜单(证明权限控制生效)
  2. 管理员进入基础数据维护,展示添加患者档案、维护药品目录(证明基础功能完整)
  3. 医生排班,创建一个明天的出诊排班(对应schedule表)
  4. 模拟患者挂号,此时能看到号源减一(演示核心业务)
  5. 医生账号接诊,打开待就诊列表,写诊断、开处方(演示处方生成)
  6. 收费员账号收费,处方状态从“未收费”变为“待发药”(演示状态流转)
  7. 药房账号发药,药品库存减少(演示库存联动)
  8. 最后打开统计报表,展示刚才的业务数据出现在图表里(演示数据综合)

这样一套下来,评委能很直观地看到数据在系统里流转,每一步操作都有上一步做铺垫。演示过程中最好准备一套完整的测试数据:几个患者、几种药品、几个医生、几条挂号记录,不要让评委看到一个空荡荡的系统。我见过太多人现场临时录入“测试用户”“测试药品”,体验很差。

6.3 答辩高频问题和加分点提前准备

结合我带项目的经验,评委最爱问的问题基本围绕四个方面:为什么选这个技术、业务怎么实现的、数据库为什么这么设计、项目有什么不足。下面这些问题提前准备好回答思路:

问:为什么用SpringBoot而不用SSH?
答:SpringBoot简化配置、内嵌Tomcat、生态好、上手快,是当下企业主流选择,能证明我接触的是目前行业主流的开发方式。

问:JWT和Session有什么区别?为什么选JWT?
答:Session存储在服务器内存,需要维护会话状态;JWT是自包含的无状态token,适合前后端分离和横向扩展。JWT的缺点是无法主动失效,所以到期时间不能设置太长。

问:密码是怎么存储的?
答:BCrypt加盐哈希存储,就算数据库泄露,也不能直接拿到明文密码。

问:挂号并发超卖怎么解决?
答:用带条件UPDATE扣减号源,remaining_count > 0保证不超卖,配合事务保证扣号和插入记录的一致性。

问:如果客户反馈系统很慢,你会怎么优化?
答:先从SQL和索引入手,查询频繁的表加索引;引入Redis缓存热点数据;静态资源走Nginx缓存;接口层面做限流。这个问题能答出这几点就说明有工程思维了。

问:你觉得项目最不满意的地方是什么?
答:比如当前没有使用Redis存储验证码、药品库存扣减没有用消息队列削峰,后面可以优化。重点不是真的贬低自己,而是展示你对技术边界有认知。

除了上述问题,还可以主动加一些亮点代码:全局异常处理器统一处理业务异常、参数校验用@Valid注解、逻辑删除代替物理删除、操作日志通过AOP切面记录。这些点每加一个都能让代码“看起来”更像企业级项目,答辩时主动介绍,评委印象分直接拉高。

最后再说几句实在话

这个项目我带过不少同学完整跑通,我自己最大的体会是:代码量其实不是最值钱的部分,值钱的是你能把每个设计决策背后的“为什么”讲清楚。很多人下载源码跑通后就去答辩,结果老师问一句“你这个排班扣号怎么做的”就哑了,那等于白做。所以拿到源码之后,强烈建议自己手动把核心代码敲一遍,哪怕一边抄一边理解,也比直接复制粘贴然后装作会了强得多。

最后分享一个实用小技巧:正式答辩之前,把数据库清空重新初始化一遍,按我上面说的演示顺序完整走两遍流程,确保数据和页面状态都是干净的。中途如果发现哪一步操作报错,提前修掉,别把修复问题的过程留到答辩现场直播。答辩现场演示项目的流畅程度,很多时候比项目本身的功能复杂度更影响成绩。祝各位都能顺利过关。

按着这套思路去跑通一个SpringBoot + Vue + MySQL的社区医院管理系统,并不难,难的是过程中真的把每一张表、每一个接口、每一次状态流转琢磨明白。项目本身只是一个载体,你从里面带走的那套“怎么分析业务、怎么设计系统、怎么排查问题”的能力,才是真正值钱的东西。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进