简介:中医院问诊系统的设计与实现,基于SpringBoot+Vue前后端分离架构完成,压缩包内包含完整可运行源码与数据库脚本,适合计算机类专业学生用于课程设计或毕业设计,也可供需要快速搭建同类业务系统的人员参考。项目采用Java语言、SpringBoot后端框架与Vue前端框架,前端围绕问诊流程、科室挂号、用户管理等常见业务模块展开,后端提供对应接口服务与数据持久化逻辑,整体目录结构清晰,便于二次开发。资源包共857个文件,大小约23.37MB;其中java文件220个对应后端核心代码,vue文件158个覆盖前端主要页面组件,另有63个js脚本、15个css样式文件及大量svg、jpg等静态资源,并包含SQL数据库脚本和bat启动/构建脚本,方便在MySQL 5.7以上环境配合Maven直接部署运行。开发环境推荐JDK1.8与idea或eclipse,压缩包内附install、run、build等批处理命令,可帮助快速完成环境准备、项目启动与打包构建,适合作为课设答辩演示、毕设系统展示或SpringBoot与Vue整合学习的落地样例。当前已有78人学习下载,说明该项目对同类课程设计具有一定参考价值。
1. 一个能当场讲清业务闭环的课设项目:SpringBoot+Vue中医院问诊系统
课设答辩时老师最常问的一句话是:“你这个系统除了能登录,到底干了什么?”基于SpringBoot+Vue的中医院问诊系统,就属于那种能当场讲清业务闭环的项目:患者在线提交主诉、完成预约,医生接诊后按望闻问切录入四诊信息,系统根据辨证结果辅助生成处方,患者端能查看医嘱和用药清单。前后端分离,SpringBoot提供接口,Vue负责页面,源码可运行,适合本科课设和毕设直接起步或二次开发。这套系统的核心难点不在CRUD,而在三件事:问诊记录怎么建模、问诊状态怎么流转、处方数据结构怎么存才合理。想清楚这三个问题,写代码只是体力活。
2. 从挂号到辨证:中医院问诊系统的数据建模与建表脚本
2.1 拆解问诊业务:六个模块把中医流程说清楚
先想清楚业务边界。中医院问诊系统和普通医院挂号系统最大的差别在环节分岔:普通门诊是挂号→看诊→开药,中医问诊里看诊被替换成“四诊采集→辨证分型→治法确立→开方”。很多课设源码在这一块模糊处理,结果答辩时被问“你的辨证结果是怎么来的、存到哪了”就答不上来。
我的做法是把业务拆成六个独立模块:用户(患者/医生/管理员三种角色)、科室与医生、预约问诊、问诊记录(四诊信息)、辨证处方、基础字典。这里字典表是关键——证候分型如肝郁气滞、脾虚湿盛,不能做成用户输入的字符串,要进字典表,这样处方才能和辨证结果真正挂钩。六个模块映射到六张核心基础表:sys_user、medical_doctor、appointment、diagnosis_record、prescription、prescription_item,外围再挂科室表和证候字典表。整个MySQL库的表结构决定了后续所有Controller和页面怎么组织,所以第一步必须把表和字段定死,不要等代码写了一半再回头改表。
2.2 建表脚本:四诊信息和处方明细不能想当然
核心表结构我直接给出建表脚本,其中四诊字段用TEXT存原文、证候用字典code存储这两条设计,是后面接口实现不返工的前提。
-- 用户表:三种角色用role_type区分 CREATE TABLE sys_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录名', password VARCHAR(100) NOT NULL COMMENT 'BCrypt加密后的密码', real_name VARCHAR(30) COMMENT '真实姓名', phone VARCHAR(20), role_type TINYINT NOT NULL COMMENT '0=患者 1=医生 2=管理员', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 问诊记录表:四诊信息用TEXT存原文,辨证结果引用字典code CREATE TABLE diagnosis_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, appointment_id BIGINT NOT NULL COMMENT '关联预约', patient_id BIGINT NOT NULL COMMENT '患者ID', doctor_id BIGINT NOT NULL COMMENT '接诊医生ID', inquiry_text TEXT COMMENT '问诊对话/主诉原文', look_sign TEXT COMMENT '望诊:面色、舌质、舌苔', listen_sign TEXT COMMENT '闻诊:声息、气味', ask_sign TEXT COMMENT '问诊:寒热、汗、二便', pulse_sign TEXT COMMENT '切诊:脉象描述', syndrome_type VARCHAR(30) COMMENT '证候分型,对应字典表code', treatment_principle VARCHAR(255) COMMENT '治法', status TINYINT NOT NULL DEFAULT 0 COMMENT '0=待接诊 1=问诊中 2=已完成 3=已取消', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 处方明细表:一症一方,明细行存草药与克数 CREATE TABLE prescription_item ( id BIGINT AUTO_INCREMENT PRIMARY KEY, prescription_id BIGINT NOT NULL, herb_name VARCHAR(100) NOT NULL COMMENT '中药名', dosage VARCHAR(50) COMMENT '剂量,如10g、3枚', note VARCHAR(255) COMMENT '煎服法备注' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;逻辑说明:问诊主表把四诊拆成四个TEXT字段,而不是塞一个大JSON,理由是答辩时能直接按字段展示“舌苔薄白”“脉弦细”这类原始记录,同时保留inquiry_text存医患对话全文,作为患者端历史可查的主诉原文。syndrome_type存字典表的code而不是中文名,避免同一证候出现“肝郁气滞”和“肝郁气滞型”两种写法,后面做统计时不用清洗数据。每个字段都带COMMENT,课上演示和老师翻数据库表结构时一目了然。
参数说明:status字段就是问诊状态机,0到3四个值,这是全程最容易改崩的地方,后面避坑章节会单独展开。password这里不存明文,用BCrypt加密后再入库,配合Spring Security或jBCrypt工具类都行。角色字段用TINYINT而不是字符串,查询比较更快,也避免大小写问题。
2.3 设计分岔点:辨证放在规则里还是放在人工
很多同学会纠结一个问题:要不要做自动辨证功能?我的建议是课设做到“半自动”就收敛。做法是证候字典表里存一列症状关键词组,选完主诉、舌象、脉象后,后端做一个简单的模糊匹配,返回几个候选证候,由医生确认或修正,最终只提交确认后的syndrome_type。这样既显得有算法逻辑,又不会陷入中医知识库这个无底洞——想靠代码搞定真正的中医辨证,那是甲方几百万项目的事,课设不背这个锅。
| 表名 | 承担职责 | 关键字段 | 与中医流程的对应 |
|---|---|---|---|
| sys_user | 登录与角色 | username, password, role_type | 患者/医生/管理员入口 |
| medical_doctor | 医生执业信息 | user_id, title, department_id | 医生资质展示 |
| appointment | 预约与排期 | patient_id, doctor_id, time_slot, status | 问诊前环节 |
| diagnosis_record | 四诊与辨证 | 四诊TEXT, syndrome_type, status | 问诊核心环节 |
| prescription | 处方头 | record_id, doctor_id, total_amount | 辨证后开方 |
| prescription_item | 处方明细 | prescription_id, herb_name, dosage | 具体用药 |
表设计是答辩的第一道防线。很多源码包拿到手先跑起来再说,但老师往往会直接看数据库脚本,六张表能讲清楚,整个业务链条就站得住。中医问诊的信息化核心就是把“望闻问切”变成可存储、可追溯、可统计的结构化数据,这套建模就是这个思路的直接体现。
3. SpringBoot后端:把问诊流程做成状态机与接口闭环
3.1 项目结构与配置:用最稳的骨架起步
课设项目不要引入花哨组件,SpringBoot 2.7.x + MyBatis-Plus 3.5.x + MySQL 8.0是这几年最稳的组合。项目结构按标准分包:controller、service、mapper、entity、config、common。config里放三个核心Bean:CORS配置、MyBatis-Plus分页插件、JWT拦截器。这个springboot项目结构不要自己发明,照着主流分层写,后面查问题、找资料都容易。
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/tcm_clinic?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai mybatis-plus: configuration: map-underscore-to-camel-case: true逻辑说明:连接串里的useUnicode和characterEncoding控制中文写入,serverTimezone定义时区,避免时间字段差8小时;jackson.date-format统一接口返回的时间格式,前端不用再手动格式化字符串。MyBatis-Plus的map-underscore-to-camel-case配置让数据库的snake_case自动映射到实体的camelCase,少写一堆resultMap。
参数说明:端口可以按演示环境改成8081,但要同步改前端axios的baseURL。管理员初始密码用BCrypt加密后的密文写进初始化脚本,不要放明文,否则老师看application.yml时会觉得你安全意识太弱。
3.2 问诊状态机:Service层把流程卡死
问诊系统最容易翻车的地方是页面交互:患者提交预约后医生还没接诊,患者就开始填写四诊;或者医生A接诊后医生B也能改记录。问题根源是状态机没有在Service层卡住。我定三条规则:待接诊状态只能由医生操作流转为问诊中,同时锁定doctor_id;问诊中状态允许修改四诊和药方,但只能由当前接诊医生操作;只有完成辨证和处方的记录,才能流转为已完成。这三条规则写进同一个原子方法里。
public Boolean updateDiagnosis(DiagnosisRecord record, Long operatorId) { DiagnosisRecord dbRecord = diagnosisRecordMapper.selectById(record.getId()); if (dbRecord == null) { throw new ServiceException("问诊记录不存在"); } Integer status = dbRecord.getStatus(); Long doctorId = dbRecord.getDoctorId(); // 状态 0:只能医生接诊,接诊时写入doctorId并流转到1 if (status == 0) { if (!doctorService.isDoctor(operatorId)) { throw new ServiceException("只有医生可以接诊"); } dbRecord.setDoctorId(operatorId); dbRecord.setStatus(1); diagnosisRecordMapper.updateById(dbRecord); return true; } // 状态 1:锁定当前接诊医生,允许更新四诊、证候、处方 if (status == 1) { if (!doctorId.equals(operatorId)) { throw new ServiceException("该问诊已被其他医生接诊"); } record.setStatus(1); int rows = diagnosisRecordMapper.updateById(record); if (rows == 0) { throw new ServiceException("更新失败,请刷新后重试"); } return true; } throw new ServiceException("当前状态不允许修改"); }逻辑说明:updateById之前先读数据库里当前的状态和doctorId,以数据库值为准,而不是信任前端传过来的对象。这一步能杜绝并发条件下两个医生同时接诊同一条记录的问题。状态0分支只认doctorService.isDoctor(operatorId),如果接口拿前端传的roleType当信任凭据,等于登录功能白做——攻击者改个请求体就能冒充医生。
参数说明:operatorId从哪里来?从JWT令牌里解析,登录后每个请求都在拦截器里拿到userId,放进request属性,Controller再取出传给Service。全程不要用前端传的patientId或doctorId覆盖这个值。这一步是权限链的根基,我在课设答辩时被老师专门追问过。
3.3 JWT登录与拦截器:把权限链讲清楚
登录接口统一走/sys/login,成功后签发JWT,过期时间设30分钟,前端把token存在localStorage。后端拦截器拦截/api/**,放行/api/login和字典查询接口,其余请求校验Authorization头。
public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if ("OPTIONS".equals(request.getMethod())) { return true; // 预检请求放行,否则跨域请求第一次就挂 } String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { throw new AuthException("未登录"); } Long userId = JwtUtil.parseToken(token.replace("Bearer ", "")); request.setAttribute("userId", userId); return true; } }逻辑说明:OPTIONS预检请求必须放行,否则前端axios跨域时会先失败。token解析出的userId放进request属性,后续Controller通过@RequestAttribute("userId")就能拿到当前操作人。这里没有每次查数据库判断登录态是否过期,对课设来说够用;如果系统要真正上线,再加Redis做服务端token续期和管理。
参数说明:JWT工具底层用jjwt或hutool都行,密钥写死在配置里,开发阶段没问题。token存储选localStorage还是cookies,我一般选localStorage,因为Vue路由守卫里判断登录跳转时可以直接读取,不需要额外引入cookie库。这里其实已经暗示下一个章节:前端才是用户直接面对的部分,路由和页面交互必须跟后端的角色模型严格对齐。
4. Vue前端:从路由守卫到问诊对话页的落地
4.1 页面架构与路由权限:先搭好骨架
前端用Vue 2.6 + Element UI + axios,这套组合和SpringBoot后端的适配成本最低。页面路由分成三条线:患者端(预约问诊、问诊记录、我的处方)、医生端(待接诊列表、四诊录入、开方)、管理端(用户管理、科室管理、数据统计)。路由配置里用meta.roles声明谁能访问对应页面。
import Vue from 'vue'; import Router from 'vue-router'; import store from '@/store'; Vue.use(Router); const routes = [ { path: '/login', component: () => import('@/views/Login.vue') }, { path: '/patient', component: () => import('@/layout/PatientLayout.vue'), meta: { roles: ['PATIENT'] }, children: [ { path: 'appointment', component: () => import('@/views/patient/Appointment.vue') }, { path: 'records', component: () => import('@/views/patient/Records.vue') } ] }, { path: '/doctor', component: () => import('@/layout/DoctorLayout.vue'), meta: { roles: ['DOCTOR'] }, children: [ { path: 'pending', component: () => import('@/views/doctor/PendingList.vue') }, { path: 'diagnosis/:id', component: () => import('@/views/doctor/Diagnosis.vue'), props: true } ] } ]; const router = new Router({ mode: 'hash', routes }); router.beforeEach((to, from, next) => { const token = store.state.token; if (to.path === '/login') return next(); if (!token) return next('/login'); const role = store.state.role; if (to.meta.roles && !to.meta.roles.includes(role)) { return next('/403'); } next(); }); export default router;逻辑说明:路由模式用了hash而没选history。原因很朴素:hash模式下前端打包后扔进SpringBoot的static目录,刷新不会404,不需要额外配后端转发,课设交付最省事。vue路由守卫两层筛选:先判断token是否存在,再判断角色是否匹配meta.roles,这样不同角色连页面入口都看不到,而不是进入页面后才弹“无权限”。diagnosis/:id这种带参数路由就是医生从待接诊列表点进来的入口,props: true让页面通过props直接拿到id,比在组件里写this.$route.params更干净。
参数说明:子路由children配合layout组件,实现左侧菜单栏、右侧内容区的后台布局,每个页面不用重复写整体框架。store是Vuex实例,token和role在登录成功后commit进去,刷新页面时再从localStorage恢复,保证路由守卫不会误判。
4.2 axios封装:token注入和错误拦截一步到位
前端每一个请求都要带token,后端拦截器才会放行。用axios拦截器统一处理,而不是在每个请求方法里手动加header,否则几十个接口改到怀疑人生。
import axios from 'axios'; const service = axios.create({ baseURL: process.env.VUE_APP_API || 'http://localhost:8080/api', timeout: 10000 }); service.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); service.interceptors.response.use( response => { const res = response.data; if (res.code !== 0) { Message.error(res.msg || '请求失败'); return Promise.reject(new Error(res.msg)); } return res.data; }, error => { if (error.response && error.response.status === 401) { router.push('/login'); } Message.error('网络异常,请检查后端服务'); return Promise.reject(error); } ); export default service;逻辑说明:请求拦截器负责加token,response拦截器统一解包。后端返回格式约定是{code, msg, data},code为0表示成功,这样前端页面拿到的直接是业务数据而不是再包一层对象。401时自动踢回登录页,不会出现“页面白屏但控制台报错”这种答辩时的尴尬场景。timeout设10秒,防止后端某个慢查询把页面一直挂着转圈。
参数说明:baseURL从环境变量读取,开发环境指向http://localhost:8080/api,打包前改成和后端同域的/api。这样就能配合后面“打包进SpringBoot”的交付方式,生产环境完全不走跨域。另外process.env.VUE_APP_API需要在.env.development和.env.production两个文件里分别定义,这是Vue CLI约定,别直接写死。
4.3 问诊对话页:把四诊录入做成医生顺手的样子
医生接诊后面对的不是一个普通表单,而是一个“对话式问诊”工作台:左侧是患者提交的主诉和症状标签,右侧是四诊录入区,下方有快捷问句栏,中医常见问题(怕冷还是怕热、汗多不多、口渴喜不喜热饮)一键填入。这个页面是整套系统里交互最复杂的地方,也是演示时最能出效果的页面。
<template> <div class="diagnosis-container"> <el-descriptions :model="record" title="患者主诉" :column="1" border> <el-descriptions-item label="主诉">{{ record.inquiryText }}</el-descriptions-item> </el-descriptions> <el-form :model="form" label-width="80px"> <el-input type="textarea" v-model="form.lookSign" placeholder="望诊:面色、舌质、舌苔" /> <el-input type="textarea" v-model="form.pulseSign" placeholder="切诊:脉象描述(如弦细)" /> <el-select v-model="form.syndromeType" placeholder="选择证候分型" filterable> <el-option v-for="item in syndromes" :key="item.code" :label="item.name" :value="item.code" /> </el-select> <el-button type="primary" @click="submitDiagnosis">提交辨证与四诊</el-button> </el-form> </div> </template> <script> export default { data() { return { form: { lookSign: '', pulseSign: '', syndromeType: '' }, syndromes: [] }; }, created() { this.fetchDetail(); this.fetchSyndromes(); }, methods: { fetchDetail() { api.get(`/diagnosis/${this.$route.params.id}`).then(data => { this.record = data; }); }, submitDiagnosis() { api.post('/diagnosis/update', { ...this.form, id: this.$route.params.id }) .then(() => this.$message.success('已保存')); } } }; </script>逻辑说明:el-select的filterable属性让证候分型支持搜索,字段存的是code、展示的是name,和第2章字典表设计对齐。created钩子里并行拉取问诊详情和证候字典,v-model双向绑定的是form对象,提交时把form和id拼成一个对象发给后端update接口,后端会严格走状态机校验当前医生是否匹配。快捷问句栏没有写进代码块里,是一个数组遍历渲染的按钮集合,点击后把问句文本追加到askSign字段,属于纯前端交互,几分钟能加完。
参数说明:textarea的placeholder只是提示,真正医生录入时文本会很长,所以后端字段必须是TEXT而不是varchar(255),这是很多网上下载的源码翻车的高发点。四诊字段我故意没有用动态表格,而是四个独立textarea,答辩演示时直观、截屏也清楚,后端实体类字段能一一对上。
5. 部署与交付避坑:5个让源码“可运行”失效的经典问题
5.1 SpringBoot版本太高导致启动即失败
现象:从网上下载源码包,pom里写的是SpringBoot 3.x,本地装的是JDK 8,一启动就报UnsupportedClassVersionError。
原因:SpringBoot 3.0起强制要求JDK 17,且javax.servlet包改成了jakarta.servlet,老课设里大量import javax.*的代码全部编译不过。这是一个真实的“springboot版本太高”翻车现场,下载源码时版本选择比功能还重要。
解决:课设阶段统一用SpringBoot 2.7.x + JDK 8 + MyBatis-Plus 3.5.x组合。如果源码已经用了3.x,有两条路:降级到2.7并把Jakarta改回javax,或者本地装JDK 17并把javax全部替换成jakarta。我建议走降级路线,因为网上检索到的排错资料里SpringBoot 2.7的答案最多,真遇到问题好搜。改pom后记得mvn clean再重新编译,别增量构建直接跑旧class文件。
5.2 前后端联调跨域:登录成功但业务请求全军覆没
现象:前端页面能打开,输入账号密码点击登录,Network里login接口200,但后续所有业务请求都报CORS error。
原因:前端开发服务器在localhost:8081,后端接口在8080,axios发出的跨域请求在后端没有被允许。最常见漏网之鱼是OPTIONS预检请求没放行,后端只处理了GET和POST,OPTIONS直接返回405。
解决:后端加一个CORS配置类,用WebMvcConfigurer注册跨域映射。注意两个细节:allowCredentials(true)时不能配allowedOrigins(""),必须用allowedOriginPatterns("");allowedMethods要写全POST、GET、PUT、DELETE、OPTIONS,少一个都不行。或者开发期给前端开代理,vue.config.js里devServer.proxy把/api转发到8080。二选一,别都配置导致请求绕来绕去。
5.3 刷新页面就404:路由模式的经典陷阱
现象:前端打包后丢进SpringBoot的static目录,直接访问http://localhost:8080/index.html能打开,点菜单跳转也正常,但按F5刷新当前路由时直接404。
原因:Vue Router开了history模式,前端路由是纯前端状态,后端收到/patient/records路径,找不到对应Controller,就抛出404。hash模式没有这个问题,因为带#号的部分不会发到服务端。
解决:最省事的方法是开局就选hash模式,URL里带#,刷新永远请求index.html。如果坚持history模式,后端要写一个转发Controller,把所有非静态资源路径转发到/index.html,但这步对课设来说纯属增加工作量,不推荐。看别人源码时先看router实例里mode写的是什么,再决定要不要配后端转发。
5.4 时间错乱8小时:问诊时间显示成前一天
现象:患者端问诊记录里的时间比本地时间慢了8小时,或者数据库里存的时间是对的,但接口返回的数据格式带了字母T。
原因:MySQL连接串没配serverTimezone,JVM默认取系统时区,数据库会话取UTC,两边的时区一交错就出现8小时差。jackson序列化时如果没有指定date-format,LocalDateTime会按ISO格式输出成“2024-06-01T10:30:00”。
解决:数据库连接串带上serverTimezone=Asia/Shanghai,SpringBoot配置里加spring.jackson.time-zone=Asia/Shanghai。第3章的application.yml里已经写好,直接抄就行。这个坑特别隐蔽,因为本地测试时有时差正好被系统默认时区掩盖,换一台机器部署就现原形。
5.5 数据库中文乱码与emoji导致保存失败
现象:建表后插入中文变成“???”一串问号,患者主诉里带emoji表情时整条记录保存报错。
原因:表或库的字符集是latin1或utf8。latin1根本不支持中文,utf8在MySQL里最多3个字节,存不了emoji这种4字节字符。很多图形化建表工具默认字符集是utf8,当时能建表,录数据才暴露问题。
解决:建库语句用DEFAULT CHARSET=utf8mb4,连接串加characterEncoding=utf8。注意改库字符集后,已存在的表也要逐个ALTER转换成utf8mb4,否则新老表的关联查询拼接处还是会出现乱码。
6. 交付前最后一步:让源码从“能跑”到“能演示”
角色和数据的预置是演示效果的分水岭。我习惯在系统里预置三个账号:患者zhangsan、医生wangjing、管理员admin,外加两条历史问诊记录和一张已经开好的处方。后端启动时用CommandLineRunner自动初始化,脚本里判断sys_user表记录数大于0就跳过,避免每次重启重复写入演示数据。
@Component public class DataInitializer implements CommandLineRunner { @Autowired private UserService userService; @Override public void run(String... args) throws Exception { if (userService.count() > 0) return; User admin = new User(); admin.setUsername("admin"); admin.setPassword(BCrypt.hashpw("admin123", BCrypt.gensalt())); admin.setRoleType(2); userService.save(admin); User doctor = new User(); doctor.setUsername("wangjing"); doctor.setPassword(BCrypt.hashpw("123456", BCrypt.gensalt())); doctor.setRoleType(1); userService.save(doctor); } }启动完成后,老师的操作路径就是完整演示链:用wangjing登录医生端,从待接诊列表接一条新记录,填写四诊,选择一个证候分型,开一张处方;退出登录,用zhangsan登录患者端,查看这条问诊记录的处方详情。这条链路正好把appointment、diagnosis_record、prescription、prescription_item这四张核心表都走了一遍,演示完就能回答数据库设计问题。
最后再讲一个交付细节:前端打包后把dist目录里的文件复制到SpringBoot的src/main/resources/static下,重新打包成单个jar。这样交付时只有一个jar包加一个sql脚本,老师拷走zip后只要本机有JDK和MySQL,一条命令java -jar就能启动完整系统,不需要同时开两个终端。对应热词就是“vue打包放进springboot中”,这也是这个交付方式在课设场景里最受欢迎的原因。至于“vue项目源码怎么发给别人”这类问题,我通常会把数据库初始化脚本和jar包放在同一层目录,再用一份README写明JDK版本和MySQL密码。
以前我做过一个没有状态机校验的问诊系统,两个医生能同时改同一条问诊记录,答辩时被老师连续追问“你凭什么保证数据不被覆盖”,那次之后我凡是涉及状态流转的操作,都在Service层先把状态和操作人校验写死再放行。这个教训也写进了上面第3章的代码里,你以后自己接项目时,这个习惯能帮你躲开很多“线上数据被改乱”的雷。希望帮到你。
本文还有配套的精品资源,点击获取