SpringBoot+Vue中医院问诊系统:从数据库设计到部署的完整毕设指南

发布时间:2026/10/7 5:33:08
SpringBoot+Vue中医院问诊系统:从数据库设计到部署的完整毕设指南 简介面向高校课程设计与毕业设计场景采用SpringBoot与Vue进行前后端分离开发的中医院问诊系统是一套可直接运行的完整项目。项目基于JDK1.8、MySQL5.7及以上版本环境搭建覆盖患者端问诊、医生端接诊等基础业务能帮助学习者理解从前端页面到后端接口再到数据库访问的完整请求链路。资源包共857个文件大小约23.37MB以Java、Vue、JavaScript、CSS等源码文件为主同时也包含SQL数据库脚本、Windows下的一键安装与启动批处理、以及项目工程配置文件导入开发工具后即可围绕源码进行二次修改。目前已有78人学习下载适合作为课设或毕设的参考项目尤其适合需要快速理解SpringBoot与Vue整合方式的中级Java学习者。包内附带前端编译产物和若干辅助文件目录结构接近真实业务系统结合源码与配置逐层阅读可以较快掌握项目启动流程、分层写法与常见排错切入点对后续自行扩展模块也有一定帮助。1. 为什么课设/毕设选「SpringBootVue 中医院问诊系统」它到底解决什么问题每年课设或毕设选题季中医院问诊系统都是常客但绝大多数人拿到的要么是老旧 SSH 架构要么是纯前端假数据答辩时一被问“数据库怎么设计的”“并发挂号怎么处理”就卡壳。这套基于 SpringBootVue 的中医院问诊系统核心价值在于它是“可运行、能演示、有完整业务闭环”的前后端分离工程患者能注册登录、按科室挂医生号医生能拉取待诊队列、写电子病历、开中药处方管理员能管科室、管药品、管号源。技术栈是当前就业市场最主流的 SpringBoot Vue 组合不是冷门框架答辩时也容易讲清楚。这套系统的受众很明确正在做课设或毕设的计算机相关专业学生以及想快速搭一套“像回事”的管理系统来练手或二次开发的初级工程师。它不涉及复杂的分布式中间件也不需要高并发架构重点在于把 RESTful API、JWT 认证、RBAC 权限、表关系设计、前后端联调这一整套“毕业设计必考知识点”走通。下面从工程结构、业务表设计、核心接口、前端页面到部署避坑一层层拆开讲。2. 用 SpringBootVue 搭中医院问诊系统项目结构、技术选型与启动顺序2.1 前后端分离的工程骨架目录怎么分代码放哪里拿到一个“源码可运行”的 SpringBootVue 工程第一件事不是双击运行而是先把目录结构认清楚。常见的做法是后端一个文件夹、前端一个文件夹根目录放 README 和数据库初始化脚本。以这套系统的典型布局为例hospital-tcm/ ├── backend/ # SpringBoot 后端工程 │ ├── src/main/java/com/example/hospital/ │ │ ├── controller/ # 控制层暴露 REST 接口 │ │ ├── service/ # 业务逻辑层 │ │ ├── mapper/ # MyBatis-Plus 数据访问层 │ │ ├── entity/ # 数据库实体类 │ │ ├── config/ # 跨域、JWT、MyBatis-Plus 配置 │ │ ├── common/ # 统一返回结果、异常处理、工具类 │ │ └── HospitalApplication.java # 启动类 │ ├── src/main/resources/ │ │ ├── application.yml # 数据源、端口、JWT 密钥配置 │ │ └── mapper/ # XML 文件复杂 SQL 时使用 │ └── pom.xml ├── frontend/ # Vue 前端工程 │ ├── src/ │ │ ├── api/ # 按模块封装的 API 请求 │ │ ├── router/ # 路由配置含动态路由和守卫 │ │ ├── store/ # Pinia 或 Vuex 状态管理 │ │ ├── views/ # 页面组件 │ │ ├── components/ # 公共组件 │ │ └── utils/request.js # axios 实例与拦截器 │ ├── package.json │ └── vite.config.js # 开发代理配置 └── sql/ └── hospital_tcm.sql # 数据库初始化脚本这个结构本身就有“毕设答辩价值”目录分层的理由是控制层只做参数接收和响应封装业务逻辑下沉到 service数据访问通过 MyBatis-Plus 的 BaseMapper 减少手写 SQL。前端按 api、router、store、views 拆分是为了让页面组件不直接写 axios所有请求统一走 request.js 拦截器方便加 token 和统一错误提示。我一般会建议在 README 里补一张图说明请求流转路径浏览器 → Vue Router → views → api/request.js → SpringBoot Controller → Service → Mapper → MySQL答辩讲这张图比背概念管用得多。2.2 版本选型与启动顺序为什么推荐 SpringBoot 2.7 Vue 3 MyBatis-Plus技术选型这一块很多新手上来就追新结果踩了版本不兼容的坑。SpringBoot 3.x 要求 JDK 17如果学校的评测环境还是 JDK 8运行直接报错Vue 3 配 Vite 开发时要求 Node 14但部分国产浏览器内核版本低页面加载会白屏。做课设毕设稳定压倒一切我一般用这套组合后端SpringBoot 2.7.18 JDK 8 MyBatis-Plus 3.5.x MySQL 5.7或 8.0 前端Vue 3.2 Vite 4 Element Plus 2.4 Pinia 2 vue-router 4选 SpringBoot 2.7 而不是 3.x 的原因2.7 是最后一个原生支持 JDK 8 的大版本资料最多遇到问题百度就能搜到而且和 MyBatis-Plus 3.5 配合不需要额外适配。Vue 3 Element Plus 是当前前端的主流组合组件风格适合做管理系统如果之前学过 Vue 2 也不用慌Vue 3 的组合式 API 上手成本不高。MyBatis-Plus 能省掉大量单表 CRUD 的 XML 配置问诊系统里挂号单、病历、处方这些表的增删改查几乎全是单表操作用它最省力。启动顺序是固定的先启动 MySQL 并导入 sql/hospital_tcm.sql再启动后端 SpringBoot默认端口 8080最后启动前端npm install npm run devVite 默认端口 5173。开发环境里前端通过 Vite 代理把/api转发到后端所以不需要手动处理跨域。后端启动成功的标志是控制台打印 Tomcat started on port 8080前端启动成功的标志是终端提示 Local: http://localhost:5173。2.3 配置文件的三个必调参数数据源、JWT 密钥、文件上传大小配置是整个系统跑不跑得起来的命门。application.yml 里有三个参数是每次换环境都要动的而且踩坑概率极高。下面这段配置是我基于常见做法整理的最小模板照着改就能用server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/hospital_tcm?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 20MB max-request-size: 20MB mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 jwt: secret: hospital-tcm-secret-key-2024-please-change expire: 604800 # 7 天单位秒逻辑说明数据源 URL 里的serverTimezoneAsia/Shanghai是必填的MySQL 8.x 驱动器不指定时区会报Cannot create PoolableConnectionFactorycharacterEncodingutf8保证问诊单、药品名称里的中文不乱码。MyBatis-Plus 的map-underscore-to-camel-case把数据库的下划线字段自动映射为实体的驼峰属性比如real_name对应realName不用手写 resultMap逻辑删除配置让挂号单、处方记录不物理删除只打标答辩时可以说“这是为了保留医疗操作审计轨迹”。JWT 密钥和过期时间是前后端约定的重点密钥过期或者临时改了前端已登录用户会全部 401排查时最容易忽视。3. 问诊业务的数据基石八张核心表与权限模型设计3.1 数据表总览从用户到处方一张图看懂业务流转中医院问诊系统的业务绕不开这几个核心对象人患者、医生、管理员、科室、排班、挂号、问诊、处方、药品。把这几个对象落成数据表整个系统的骨架就有了。常见设计是八张表每张表的职责如下表名核心字段作用sys_userid, username, password, real_name, role, phone统一登录账号角色区分患者/医生/管理员tcm_doctorid, user_id, dept_id, title, introduction, reg_fee医生扩展信息关联科室与挂号费tcm_deptid, dept_name, intro科室表如内科、针灸科tcm_scheduleid, doctor_id, dept_id, work_date, am_pm, total, remain排班表控制号源数量tcm_registerid, patient_id, schedule_id, doctor_id, status, create_time挂号单状态流转待就诊→已就诊→已取消tcm_medical_recordid, patient_id, doctor_id, register_id, symptom, diagnosis, advice问诊记录核心病历信息tcm_prescriptionid, record_id, doctor_id, total_price, create_time处方主表关联药品明细tcm_prescription_itemid, prescription_id, drug_id, dosage, quantity处方明细支持一症多药这张表设计最关键的关联链条是患者先看排班 → 选号挂号生成 tcm_register → 医生接诊后写 tcm_medical_record → 根据诊断开 tcm_prescription → 处方下挂多条 tcm_prescription_item 指向具体药品。这个链路能支撑“我的挂号”“我的病历”“我的处方”这三大查询页面答辩时顺着链条讲就很有逻辑性。3.2 权限模型为什么用 RBAC 而不是给用户表的 role 字段写死很多毕设项目图省事直接给 sys_user 一个 role 字段登录后根据字符串判断权限。这套系统如果也这么干会有一个显而易见的短板假如要扩展一个“药师”角色用来审核处方就得改前端每个判断用户角色的地方后端每个 Controller 里的逻辑分支也得加。用 RBAC 模型加角色只需要在数据库插两条关联记录。常见做法是加两张中间表CREATE TABLE sys_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_name VARCHAR(50) NOT NULL, role_key VARCHAR(50) NOT NULL UNIQUE COMMENT 角色标识如 patient/doctor/admin ); CREATE TABLE sys_user_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, role_id BIGINT NOT NULL, UNIQUE KEY uk_user_role (user_id, role_id) );实际开发中为了控制工作量可以简化为 sys_user 表保留 role 字段用于前端菜单显隐同时用 Spring Security 的注解做接口级权限控制。给一个最小可用的后端权限校验写法RestController RequestMapping(/api/doctor) public class DoctorWorkbenchController { GetMapping(/pending-list) PreAuthorize(hasRole(DOCTOR)) public Result getPendingList(RequestParam Long doctorId) { // 只允许医生角色访问待问诊列表 return Result.success(registerService.getPendingList(doctorId)); } }逻辑说明PreAuthorize(hasRole(DOCTOR))会在进入 Controller 方法之前检查当前登录用户的角色没有 DOCTOR 角色直接返回 403。前端配合 vue-router 的路由守卫根据登录用户角色动态生成菜单这样后端是数据安全底线前端是体验优化。如果做毕设觉得 Spring Security 配置太繁琐可以拿一个简单的 JWT 拦截器替代但答辩时老师大概率会问“如何防止患者接口访问医生接口”用 Spring Security 注解回答会更扎实。3.3 问诊记录表设计中医辨证字段怎么存才能让答辩加分中医问诊和西医最大的区别在“辨证分型”和“治法方药”病历里不能只有“发热、咳嗽”这种症状描述。设计 tcm_medical_record 表时建议加几个中医特色字段这也是和普通“医院挂号系统”拉开差距的地方答辩时容易拿高分CREATE TABLE tcm_medical_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, register_id BIGINT NOT NULL, patient_id BIGINT NOT NULL, doctor_id BIGINT NOT NULL, symptom VARCHAR(500) COMMENT 主诉患者自述, tongue TEXT COMMENT 舌象舌质、舌苔, pulse TEXT COMMENT 脉象如弦脉、滑脉, syndrome_type VARCHAR(100) COMMENT 辨证分型如风寒束表证、肝郁气滞证, treatment_principle VARCHAR(200) COMMENT 治法如疏肝理气、活血化瘀, advice VARCHAR(500) COMMENT 医嘱, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );这几句话讲清楚就行舌象和脉象是中医四诊里的核心信息源拿TEXT字段存是因为医生可能输入长文本syndrome_type是辨证论治的核心输出对应到处方里的方剂与药品treatment_principle和syndrome_type做到一一对应比如“风寒束表证”对应“辛温解表”。表结构里一旦出现证型和治法老师会认为你对业务做了深入研究这比多写几个 CRUD 接口有价值得多。4. 把问诊全流程跑通从挂号到开方的接口设计与 Vue 前端落地4.1 挂号接口的设计与实现防超挂与号源扣减挂号是问诊系统的入口也是并发场景最容易出问题的地方。毕设阶段不需要引入 Redis 分布式锁但至少要做数据库层面的扣减保护。挂号接口的核心逻辑是先查排班的剩余号源大于 0 才允许生成挂号单同时把 remain 字段减一。常见做法是使用乐观锁Service public class RegisterServiceImpl implements RegisterService { Override Transactional(rollbackFor Exception.class) public RegisterResult createRegister(RegisterRequest request) { // 1. 查排班带上版本号做乐观锁 TcmSchedule schedule scheduleMapper.selectById(request.getScheduleId()); if (schedule null || schedule.getRemain() 0) { throw new BusinessException(号源已满或排班不存在); } // 2. 更新号源注意 SQL 条件是 remain 0 int updated scheduleMapper.deductRemain(schedule.getId(), schedule.getVersion()); if (updated 0) { throw new BusinessException(该时段号源已被抢完请选择其他时段); } // 3. 生成挂号单 TcmRegister register new TcmRegister(); register.setPatientId(request.getPatientId()); register.setDoctorId(schedule.getDoctorId()); register.setScheduleId(schedule.getId()); register.setStatus(0); // 0-待就诊 1-已就诊 2-已取消 registerMapper.insert(register); return new RegisterResult(register.getId(), schedule.getRegFee()); } }对应的 Mapper 方法Update(UPDATE tcm_schedule SET remain remain - 1, version version 1 WHERE id #{id} AND version #{version} AND remain 0) int deductRemain(Param(id) Long id, Param(version) Integer version);逻辑说明扣减号源时不使用remain - 1的普通 UPDATE而是加version #{version}条件保证两个用户同时抢最后一个号时只有一个能更新成功另一个 updated 为 0 直接提示“号源已满”。Transactional保证扣号源和插入挂号单要么都成功要么都回滚不会出现“号扣了但挂号单没生成”的数据不一致。参数上唯一需要前端配合的是scheduleId前端要从排班列表里把选中项的 id 带上。4.2 Vue 3 前端登录态管理与 API 封装axios 拦截器里的两个关键逻辑前端项目源码拿到手后第一个要改的文件永远是src/utils/request.js它是所有请求的咽喉。一套可运行系统的 axios 封装必须处理两个问题请求头发 token响应里统一剥开 Result 壳子。下面是这套系统常见的最小封装import axios from axios import { ElMessage } from element-plus import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器从 localStorage 取 token 放到请求头 request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer ${token} } return config }) // 响应拦截器统一处理错误码与 401 跳登录 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.msg || 请求失败) return Promise.reject(new Error(res.msg)) } return res.data }, error { if (error.response?.status 401) { localStorage.removeItem(token) localStorage.removeItem(userInfo) router.push(/login) ElMessage.warning(登录已过期请重新登录) } else { ElMessage.error(网络异常请稍后重试) } return Promise.reject(error) } ) export default request逻辑说明baseURL: /api配合 Vite 代理开发环境请求/api/doctor/pending-list会被转发到http://localhost:8080/api/doctor/pending-list浏览器控制台不会出现跨域报错。后端统一返回{ code: 200, data: ..., msg: success }结构拦截器里直接剥出data返回页面调用不需要每处都写.data.data。401 处理是这套系统的“后悔药”token 过期一次全系统自动踢回登录页不用每个页面手动判断。4.3 问诊工作台页面从挂号队列到病历填写表单的组件化思路医生端的问诊工作台是这套系统的核心页面。页面布局一般分三块左侧待诊队列中间患者信息与病历表单右侧已开处方预览。用 Vue 3 组合式 API 来写数据流非常清晰template div classworkbench el-aside width280px el-card shadownever template #header待诊队列/template div v-foritem in pendingList :keyitem.id classpatient-item clickselectPatient(item) span{{ item.patientName }}/span el-tag sizesmall{{ item.registerNo }}/el-tag /div /el-card /el-aside el-main el-form :modelmedicalForm label-width100px el-form-item label主诉 el-input v-modelmedicalForm.symptom typetextarea :rows3 / /el-form-item el-form-item label舌象 el-input v-modelmedicalForm.tongue placeholder如舌红苔薄黄 / /el-form-item el-form-item label脉象 el-input v-modelmedicalForm.pulse placeholder如弦滑数 / /el-form-item el-form-item label辨证分型 el-select v-modelmedicalForm.syndromeType allow-create filterable el-option v-fort in syndromeTypes :keyt :labelt :valuet / /el-select /el-form-item el-button typeprimary clicksubmitRecord提交病历并开方/el-button /el-form /el-main /div /template script setup import { ref, onMounted } from vue import request from /utils/request const pendingList ref([]) const medicalForm ref({ registerId: null, patientId: null, symptom: , tongue: , pulse: , syndromeType: }) const loadPendingList async () { const data await request.get(/doctor/pending-list, { params: { doctorId: JSON.parse(localStorage.getItem(userInfo)).id } }) pendingList.value data } const selectPatient (item) { medicalForm.value.registerId item.registerId medicalForm.value.patientId item.patientId } const submitRecord async () { await request.post(/medical-record/create, medicalForm.value) ElMessage.success(病历已提交) loadPendingList() } onMounted(loadPendingList) /script这个组件演示了问诊工作台最基本的交互逻辑点击待诊队列左侧条目选中患者表单里录入中医四诊信息提交后刷新队列。syndromeType用allow-create的 Select 组件医生可以选预设证型也可以手动输入新证型这是考虑到中医证型自由度高、预设清单不可能穷尽而做的妥协设计。页面没有放处方明细编辑是因为处方开立一般跳转到独立页面或弹出抽屉组件避免一个页面过长影响答辩演示效果。5. 源码可运行的关键一步编译打包、Nginx 部署与常见问题避坑5.1 从开发环境到生产环境后端打包与前端构建的参数调整拿到源码后如果只在开发环境跑通还不够毕设演示时经常要在教室机器上用打包后的版本演示。后端打包用 Maven前端打包用 Vite但有两处参数必须改否则部署上去全是坑。后端打包前打开application.yml把数据库连接改成演示机器的实际地址然后把 JWT 密钥固定下来不要用随机生成的值。打包命令mvn clean package -DskipTests打包产物在backend/target/hospital-tcm-0.0.1-SNAPSHOT.jar。启动时用java -jar hospital-tcm.jar --spring.profiles.activeprod可以让生产配置生效前提是 resources 下有application-prod.yml。前端打包前要改vite.config.js里的代理路径改成后端实际地址再执行构建npm run build产物在frontend/dist/下生成的是纯静态文件 index.html assets 目录。这时候有一个毕设常见的问题前端项目源码怎么发给别人——直接发 dist 目录是不够的接收方如果要二次开发得拿到完整 src 源码如果只是运行演示把 dist 扔给 Nginx 或后端静态资源目录即可。我一般建议交付时把整个 frontend 目录含 node_modules 可选和打包说明一起发这样别人能自己npm run dev也能npm run build。5.2 用 Nginx 托管前端并转发 API 请求location 前缀匹配的配置要点前后端分离部署最经典的方案是 Nginx 托管前端静态文件同时转发/api到后端服务。这段配置几乎是毕设答辩现场必备直接抄着改即可server { listen 80; server_name localhost; root /usr/share/nginx/html/dist; index index.html; # 前端路由 history 模式支持 location / { try_files $uri $uri/ /index.html; } # API 请求转发到后端 location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这里的核心是try_files $uri $uri/ /index.html。Vue 3 默认使用 createWebHistory 的路由模式刷新/patient/records页面时若没有这条规则Nginx 会返回 404加上后所有未知路径都回退到 index.html由前端路由自己解析。proxy_pass http://127.0.0.1:8080/api/;末尾的斜杠代表把/api前缀原样传给后端例如浏览器请求/api/doctor/pending-list后端收到的是/api/doctor/pending-list。如果去掉斜杠写成不带路径的形式转发时会把/api去掉后端接口就得调整前缀这是最容易翻车的地方。5.3 避坑专章中医院问诊系统跑不起来最常见的 5 个问题问题 1后端启动直接报 Failed to configure a DataSource 错误。现象是控制台提示无法连接数据库或找不到数据源。原因基本是application.yml里的 MySQL 地址、账号、密码不对或者 MySQL 服务没启动更隐蔽的是 MySQL 8.0 以上需要额外引入spring-boot-starter的驱动匹配版本。解决先确认 MySQL 服务在mysql -uroot -p能登录再看pom.xml里mysql-connector-j版本是否为 8.0 及以上最后核对 yml 缩进——Spring Boot 配置文件对缩进极其敏感tab 和空格混用会直接解析失败。问题 2前端 npm run dev 启动正常但浏览器白屏控制台报 Cannot read properties of undefined。现象是新拉下来的代码执行npm install后启动Element Plus 组件全部不渲染。原因多数是依赖版本不一致比如 package.json 里 Element Plus 版本是 2.4但 npm 自动安装了 2.5部分组件 API 有破坏性改动。解决删除node_modules和package-lock.json然后npm install --legacy-peer-deps重新安装如果还不行就手动把 package.json 里 Element Plus 版本改成和源码标注的一致。问题 3登录接口返回 401但数据库里账号密码明明是对的。现象是拿初始化 SQL 里给的 admin 账号登录前端报“用户名或密码错误”。原因通常是初始化 SQL 里存的密码是 BCrypt 加密串和 application.yml 里 JWT 密钥或加密盐不匹配或者用户表的密码字段在 MyBatis-Plus 查询时被逻辑删除过滤掉了。解决先看 SQL 文件里的 INSERT 语句密码字段是不是$2a$开头的 BCrypt 格式不是的话重新用后端提供的工具类生成密文再排查 sys_user 表是否有deleted字段且默认值为 1导致查询带上了deleted1条件把所有账号都过滤了。问题 4开发环境下前端访问 /api 接口报跨域错误。现象是浏览器控制台显示 Access-Control-Allow-Origin 缺失明明在 SpringBoot 里写了 CorsFilter。原因很可能是 Vite 代理没有生效或者后端配置类被 SpringBoot 的自动配置覆盖。解决先在vite.config.js里确认server.proxy写了对/api的代理且 target 指向http://localhost:8080如果用了后端 CORS 配置常见写法是Configuration类里注册 CorsFilter Bean注意allowedOriginPatterns不要写成allowedOrigins(*)后者在携带凭证时会被浏览器拒绝。问题 5按医生查看待诊队列查不到数据但数据库里挂号单明明存在。现象是前端选中科室或医生后列表为空。原因通常是查询参数没对齐挂号单表里存的 doctor_id 是医生的用户 ID 还是医生扩展表tcm_doctor的 ID前后端没约定一致。解决在数据库执行一条 SQL 验证关联关系例如SELECT * FROM tcm_register r JOIN tcm_doctor d ON r.doctor_id d.user_id看是否匹配匹配不上就检查后端代码里 pending-list 接口条件用的字段名要么统一用 user_id要么在排班表新增时同时冗余一个 doctor_id。6. 往上再走一步给问诊系统加模拟数据、验证流程与答辩演示技巧6.1 注入一套完整的演示数据写一个 CommandLineRunner 自动填充答辩前最怕的是演示时发现没有号源可挂或没有病历可看临时手工往数据库插数据既不专业又容易出错。更稳妥的做法是在后端启动时用一个数据初始化组件自动灌入演示数据参考 SpringBoot 的ApplicationRunner接口Component public class DataInitializer implements ApplicationRunner { Override Transactional public void run(ApplicationArguments args) { // 1. 检查是否存在管理员账号不存在则创建 if (userMapper.selectCount(new LambdaQueryWrapperSysUser() .eq(SysUser::getUsername, admin)) 0) { SysUser admin new SysUser(); admin.setUsername(admin); admin.setPassword(new BCryptPasswordEncoder().encode(123456)); admin.setRole(ADMIN); admin.setRealName(系统管理员); userMapper.insert(admin); } // 2. 检查是否有演示科室没有则插入内科、针灸科、骨伤科 // 3. 检查是否有演示医生账号和对应排班数据 // 4. 插入一个演示患者账号patient01 / 123456 } }这套初始化逻辑有讲究不能无条件插数据否则每次启动都会重复生成要用selectCount判断存在性密码一律用 BCrypt 加密存储不能存明文演示排班的日期要动态生成比如LocalDate.now().plusDays(1)加上上午/下午两个时段这样无论哪天演示都有可挂的号不会因为数据过期而“无号可挂”。代码里注释要把每一步的作用点出来答辩时老师问“数据从哪来”直接说“启动时自动初始化”比自己手动拼接 INSERT 语句要显眼很多。6.2 验收自查清单从注册到出处的全流程验证源码是否能运行不是后端启动就算也不只是前端页面能打开就算。我给自己定过一套验收流程建议拿到源码后先按步骤走一遍步骤操作预期结果1导入 SQL启动后端与前端浏览器访问 5173 端口出现登录页2用 admin / 123456 登录跳转到管理员后台能看到科室管理菜单3用演示患者账号登录能看到挂号页面按科室筛选医生点击挂号成功4用医生账号登录待诊队列出现刚才挂号的卡片点击后能填写病历与处方5切回患者账号在“问诊记录”里能看到医生写的病历与处方药品6退出登录再进入后台直接访问路由会被重定向到登录页token 鉴权生效这套流程覆盖了系统最核心的三角色闭环管理员维护基础数据患者挂号医生接诊开方。验收时如果第 3 步报“号源已满”检查初始化排班的 remain 字段如果第 5 步的处方药品明细是空的检查前端提交处方时有没有把 prescriptionItem 数组一起传或者后端接收参数时没有加RequestBody。逐项核对完才能放心地把它作为可运行的交付物。6.3 答辩演示的三个现场技巧弹窗、路由守卫和错误提示的加分用法最后说点实操层面的“血泪经验”。第一演示时不要全屏展示代码编辑器把浏览器窗口固定在 1366 宽度避免页面上 Element Plus 组件挤压变形第二给前端路由加一个全局前置守卫已经是标配但演示时可以手动清掉 localStorage 里的 token 再刷新页面让评委看到自动踢回登录页的效果这一个操作能省下很多关于“安全怎么做的”追问第三后端接口被非法访问时评委可能会顺手输错 URL 或者在控制台改 role 字段用 Spring Security 的 403 返回加前端统一 ElMessage 提示“无权访问”比一张白页体面得多。如果时间有富余还可以在管理端加一个问诊统计页面按日统计挂号量与处方销售额用一个简单的SELECT DATE(create_time), COUNT(*) FROM tcm_register GROUP BY DATE(create_time)再配上 ECharts 柱状图。这不算大改造但对答辩的“工作量感”提升非常明显——它展示的不只是 CRUD还有数据分析意识。我带学生做这套系统时经常说源码能跑只是及格线能把一个流程讲成完整故事才是加分项。从挂号到病历到处方这条链路本身就是中医院问诊系统区别于普通图书管理系统的核心抓住它演示就成功了一半。希望帮到你。本文还有配套的精品资源点击获取