
先说结论这就是一套典型的 Spring Boot Vue 前后端分离项目市面上很多“健康管理系统”课程设计和毕业设计都是从这套架子改出来的。你拿到的交付物一般包括源码、数据库脚本.sql和配套文档功能跑通不难难的是你能不能把技术选型、表设计、接口逻辑讲清楚。这篇文章我会按“整体定位 - 数据库设计 - 后端接口 - 前端工程 - 报错排查”的顺序把这类系统从零到一拆开揉碎顺便把最容易踩的坑全部标出来。不管你是要做课程设计、毕业设计还是想拿一个前后端分离项目去面试照着这套思路走基本不会翻车。1. 项目定位与技术选型先搞明白这套系统到底在做什么1.1 项目形态、功能边界与适用人群健康管理系统本质上做的是“健康数据的采集、存储、分析和反馈”。用户注册登录后维护一份基础健康档案然后按天录入身高、体重、血压、心率、血糖这些指标系统根据录入数据计算 BMI、判断风险等级再用图表展示趋势最后给出健康建议或提醒。功能边界一般控制在 8 到 10 个模块以内不要一上来就搞智能诊断、AI 推荐那是给自己挖坑。常见的模块划分是登录注册、个人健康档案、健康记录录入、记录列表查询、健康趋势统计、运动记录、饮食记录、健康建议、个人中心。管理员端可以再做一个用户管理、健康建议管理用于答辩时展示系统完整性。这个项目适合三类人第一数据库课程设计或 Java Web 课程设计的同学交付物天然包含源码、数据库脚本、文档正好满足作业要求第二准备毕业设计的学生前后端分离的技术栈足够写在论文里第三刚学完 Java 基础和 Vue 基础、想做一个真正全栈项目的入门开发者。对第三类人来说这个项目最大的价值不是功能多牛而是让你把“前端发请求 - 后端接请求 - 查库 - 返回数据 - 前端渲染”这条链路完整走一遍。1.2 为什么是 Spring Boot Vue而不是 JSP 或 SSM先聊后端。早期课程设计喜欢用 JSP Servlet或者 SSMSpring SpringMVC MyBatis配合 JSP 页面。JSP 最大的问题在于前后端耦合太严重HTML、Java 代码、数据库查询结果全塞在一个文件里改个样式都能把后端代码改崩。SSM 虽然分层清晰但要配置的东西太多光 spring.xml、springmvc.xml、mybatis-config.xml 三个文件就够新手折腾大半天。Spring Boot 把这些繁琐配置全干了。内嵌 Tomcat、自动装配、starter 依赖管理一个 main 方法就能启动整个后端服务。对课程设计和简历项目来说开发效率是第一位的Spring Boot 可以让你的精力集中在业务代码上而不是配置文件上。前端选 Vue 而不是 React核心原因是上手成本。Vue 的模板语法和普通 HTML 非常接近中文文档和社区资料也明显更友好配合 Element UI 这类组件库表格、表单、弹窗、分页这些后台管理页面常用组件可以直接拿来用短期内就能做出界面体面的前端页面。React 当然也很好但它的 Hooks 心智模型、JSX 转换、状态管理方案对刚接触前端工程化的同学来说学习曲线会更陡。数据库方面MySQL 是这类项目的大众选择开源免费、资料多、Navicat/DBeaver 等图形化客户端支持完善后面连数据库、导数据、备份都很方便。我把这套组合和传统方案做个对比方便你答辩时说明为什么这么选对比维度Spring Boot VueSSM JSPJSP Servlet开发效率高前后端并行开发中需要大量 XML 配置低代码耦合严重前后端分离天然分离接口联调清晰分离不彻底不分离部署复杂度后端 jar 包 前端静态文件需要打包 war 部署到外部 Tomcat同左简历/答辩价值面试常见技术栈可深挖点多偏传统亮点少基本属于过时方案代码可维护性高组件化和模块化清晰中差这个组合还有一个隐性优势就是它和 Java 面试八股文高度重合。Spring IOC/AOP、Spring MVC 拦截器、JWT 鉴权、RESTful API、MyBatis-Plus 分页原理、Vue 生命周期、路由守卫、Axios 拦截器这些全是高频面试题。做完这个项目你能串起来的面试知识点非常多这一点后面第五章会展开讲。2. 数据库设计从一开始就别给自己挖坑数据库是这类系统的地基。很多同学拿到源码后第一件事就是跑前端结果前端一起来登录接口直接报错一查是数据库没导进去或者表结构对不上。所以我把数据库设计放在第二章这也是配套交付物里 .sql 脚本的核心内容。2.1 核心表拆解与业务关系健康管理系统的核心是用户围绕用户把所有业务数据串起来。一条比较清晰的主线是用户表存账号信息健康档案表存用户的静态基础资料健康记录表存动态监测指标运动记录和饮食记录表存行为数据健康建议表存系统或管理员生成的建议内容。我用一张表把这 6 张核心表的关系整理出来表名作用核心字段与用户表关系sys_user用户账号信息username、password、phone、real_name主表health_profile健康档案相对静态gender、birthday、height、medical_history一对一health_record健康测量记录动态height、weight、血压、心率、血糖、BMI、risk_level一对多exercise_record运动记录exercise_type、duration、calories一对多diet_record饮食记录meal_type、food_name、calorie一对多health_advice健康建议user_id、advice_content、advice_type一对多之所以把静态档案和动态记录分开建表核心原因是数据变化频率完全不同。档案里的性别、出生日期几乎不变而健康记录每天甚至每半天就要新增一条。分表之后查询趋势图时只需要扫描记录表不需要反复关联档案表如果后期要做慢病管理、历史数据占比分析记录表也可以独立统计。答辩时只要你能说出“静态数据与动态数据分离”这个设计理由评委基本不会再追问这张表为什么存在。2.2 建表 SQL 与字段设计要点直接给出一份可以落地的 SQL。重点关注字段类型选择这是课程设计答辩的高频问题。CREATE TABLE sys_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(32) NOT NULL COMMENT 登录名, password VARCHAR(128) NOT NULL COMMENT 密码(MD5或BCrypt加密), real_name VARCHAR(32) DEFAULT NULL COMMENT 真实姓名, gender TINYINT DEFAULT 0 COMMENT 性别 0未知 1男 2女, birthday DATE DEFAULT NULL COMMENT 出生日期, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE health_record ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 记录ID, user_id BIGINT NOT NULL COMMENT 关联用户ID, height DECIMAL(5,2) DEFAULT NULL COMMENT 身高单位cm, weight DECIMAL(5,2) DEFAULT NULL COMMENT 体重单位kg, systolic_pressure INT DEFAULT NULL COMMENT 收缩压单位mmHg, diastolic_pressure INT DEFAULT NULL COMMENT 舒张压单位mmHg, heart_rate INT DEFAULT NULL COMMENT 心率单位次/分, blood_sugar DECIMAL(4,2) DEFAULT NULL COMMENT 空腹血糖单位mmol/L, bmi DECIMAL(4,2) DEFAULT NULL COMMENT BMI指数, risk_level VARCHAR(16) DEFAULT NULL COMMENT 风险等级正常/偏高/偏低/异常, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, record_date DATE NOT NULL COMMENT 测量日期, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), KEY idx_user_date (user_id, record_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健康测量记录表;几个字段设计的细节拿出来单独说。第一身高体重这类数值用 DECIMAL 而不是 FLOAT。FLOAT 是浮点数存储有精度问题算 BMI 时容易出现 0.1 的误差DECIMAL 是定点数可以精确到指定小数位DECIMAL(5,2) 表示最大 999.99对身高厘米和体重千克完全够用。第二性别、状态这类只有几个取值的字段用 TINYINT不要用 VARCHAR。TINYINT 占 1 个字节VARCHAR 至少占 1 个字符的长度而且用数字取值为以后扩展留下了余地。比如性别现在只有 1 男 2 女以后要加一个“保密”TINYINT 直接插入新值就行。第三时间字段区分 DATE 和 DATETIME。birthday 只需要年月日用 DATEcreate_time 需要精确到时分秒用 DATETIME。CREATE_TIME 设置 DEFAULT CURRENT_TIMESTAMP插入时不需要手动赋值这个细节在文档里写清楚展示时会显得很专业。第四user 表的 password 字段长度不要只留 32 位。如果后期用 BCrypt 加密长度是 60 位所以直接用 VARCHAR(128) 最稳妥。课程设计阶段很多同学用 MD5 加密32 位够用但一旦要升级加密方案字段长度不够就很尴尬。再说一句字段命名。避免用 desc、order、group 这类 SQL 保留字做字段名也别用驼峰命名法。数据库字段统一用下划线命名Java 实体类用驼峰命名MyBatis-Plus 默认开启驼峰映射两边会自动对应省掉一堆配置。2.3 索引、外键与测试数据准备health_record 表里的联合索引idx_user_date (user_id, record_date)是最需要解释的点。系统的核心查询场景是“查某个用户在某段时间内的健康记录”这个查询的 WHERE 条件就是user_id ? AND record_date BETWEEN ? AND ?联合索引正好命中这个场景。课程设计的数据量虽然小但把这个索引设计讲出来能直接体现你懂索引的原理。外键方面我建议课程设计阶段把外键加上。虽然实际企业开发中很多项目会刻意不用物理外键但对课程设计来说物理外键能直观展示表之间的关联关系ER 图画起来也顺理成章加分作用大于性能影响。如果要做逻辑外键也至少要保证在 service 层写清楚关联逻辑不要变成一张张孤立的表。最后强调一下测试数据。一份好的 .sql 脚本必须包含初始化数据和测试数据至少要有一个管理员账号、两个测试用户每个用户下面有近一个月、按天分布的健康记录这样打开前端页面时折线图、表格、统计卡片才有内容可看。很多同学只在表里插了一条数据图表画出来是一条直线答辩演示效果非常差。我建议每个测试用户至少插入 20 到 30 条记录数值要有波动比如血压有几天偏高、有几天正常这样风险判断和健康建议功能才有展示场景。3. 后端 Java 接口从登录鉴权到指标计算后端是这套系统的核心也是文档里接口说明部分的主角。拿到源码后不要急着改业务先把工程结构和接口组织方式理清楚。3.1 工程结构与依赖选型一个标准的 Spring Boot 工程包结构一般是这样的com.example.health ├── controller # 接口层 ├── service # 业务逻辑层 │ └── impl ├── mapper # MyBatis-Plus 数据访问层 ├── entity # 数据库实体类 ├── config # 配置类如拦截器、跨域 ├── common # 通用返回结果、全局异常 └── utils # JWT 工具类等依赖方面pom.xml 里核心依赖就这几个dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdcom.auth0/groupId artifactIdjava-jwt/artifactId version4.4.0/version /dependency为什么选 MyBatis-Plus 而不是原生 MyBatis因为这类系统 80% 的数据库操作是单表 CRUDMyBatis-Plus 的 BaseMapper 直接把 insert、update、selectById、selectPage 这些方法全封装好了你不用写 XML 文件。配合它的 LambdaQueryWrapper动态条件查询写起来非常简洁。但有一个前提你答辩或者面试时一定要能说清 MyBatis-Plus 和 MyBatis 的区别不能光会说“它方便”。核心是MyBatis-Plus 在 MyBatis 基础上封装了通用 Mapper底层仍然是 MyBatis 的 SQL 会话机制分页插件本质上是拦截器在执行 SQL 前自动拼接 limit 语句。spring-boot-starter-web 是标配内嵌 Tomcat、Spring MVC、Jackson 序列化全都集成了。Java JWT 这个库用来生成和校验 token比手写 Base64 拼 token 要安全得多。3.2 基于 JWT 的登录鉴权这类系统的登录流程现在主流方案是 JWT不用 Session。原因很直接前后端分离的架构下前端可能部署在 8080 端口后端在 9090 端口Session 默认绑定在服务端跨域请求还需要专门处理 Cookie、CORS 认证模式很麻烦。JWT 把用户信息加密后放在 token 里前端每次请求在请求头带上 token后端拦截器校验 token 是否合法即可服务端不需要存储任何状态。核心代码是 JWT 工具类和拦截器。这里给出一个简化可用的拦截器演示public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行跨域预检请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { token token.substring(7); } try { // 使用与登录时一致的密钥校验 JWTVerifier verifier JWT.require(Algorithm.HMAC256(health-system-secret)).build(); verifier.verify(token); return true; } catch (Exception e) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\message\:\登录已过期请重新登录\}); return false; } } }然后在配置类里注册拦截器并放行登录接口Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JwtInterceptor()) .addPathPatterns(/api/**) .excludePathPatterns(/api/auth/login, /api/auth/register); } }有几个细节要留意。第一拦截器只拦截 /api/** 路径登录注册接口要放行否则用户根本没登录就被 401 挡在门外。第二前端发请求时需要手动在 axios 拦截器里加上Authorization: Bearer token这个请求头。第三JWT 密钥不要直接写在代码里课程设计阶段虽然问题不大但如果要放到简历项目里至少要用配置文件或环境变量管理密钥。这里顺带对应一个常见的 Java 面试点Spring MVC 拦截器是基于 HandlerInterceptor 接口实现的底层依赖 Spring AOP 机制但和 Spring AOP 又不一样。AOP 可以拦截任意 Spring Bean 方法拦截器只能拦截 Controller 方法。答辩被问到两者区别时能说出这个层次就很加分。3.3 健康记录新增与指标计算逻辑健康记录新增是整个系统的核心接口前后端联调、图表展示、风险判断都围绕它展开。接口设计一般是 POST /api/health/record请求体是 JSON{ userId: 1, height: 170.5, weight: 70.2, systolicPressure: 130, diastolicPressure: 85, heartRate: 78, bloodSugar: 5.6, recordDate: 2025-12-01, remark: 早晨空腹测量 }注意接口参数里没有 bmi 和 riskLevel这两个字段由后端根据前端传上来的值计算绝不能让前端直接传。否则用户传一个 bmi: 1图表数据就直接被污染了。BMI 计算逻辑很简单public BigDecimal calcBmi(BigDecimal height, BigDecimal weight) { if (height null || weight null || height.compareTo(BigDecimal.ZERO) 0) { return null; } // 身高从cm转为m BigDecimal h height.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP); // BMI 体重(kg) / 身高(m)^2保留两位小数 return weight.divide(h.multiply(h), 2, RoundingMode.HALF_UP); }风险等级判断我一般这么写public String calcRisk(Integer systolic, Integer diastolic, Integer heartRate) { String risk 正常; if (systolic ! null systolic 140) { risk 偏高; } if (diastolic ! null diastolic 90) { risk 偏高; } if (systolic ! null systolic 90) { risk 偏低; } if (heartRate ! null (heartRate 100 || heartRate 60)) { risk 异常; } return risk; }这里反复强调一点用 BigDecimal 做计算尤其是除法一定要指定精度和舍入规则。new BigDecimal(70.2).divide(...)如果不传精度会直接报 ArithmeticException。另一个常见坑是数据库里的 DECIMAL 字段映射到 Java 后是 BigDecimal如果你用 Double 接精度会再次丢失所以实体类字段直接用 BigDecimal不要图省事用 Double。3.4 统一返回结构和全局异常做完接口后需要把返回格式统一起来。一个标准的统一返回结构长这样{ code: 200, message: 操作成功, data: {} }对应的 Java 类可以这样写public class ResultT { private Integer code; private String message; private T data; public static T ResultT ok(T data) { ResultT r new Result(); r.setCode(200); r.setMessage(操作成功); r.setData(data); return r; } public static T ResultT error(String message) { ResultT r new Result(); r.setCode(500); r.setMessage(message); return r; } }为什么一定要这样做因为前后端分离开发时前端需要根据 code 判断接口是否成功而不是根据 HTTP 状态码。这样设计后Axios 拦截器只需要判断 code 是否为 200统一弹错误提示后端也只需要在业务出错时return Result.error(xxx)各司其职。全局异常处理是很多同学会忽略的环节。用 RestControllerAdvice 加 ExceptionHandler 把 RuntimeException、参数校验异常、空指针异常统一捕获返回 Result 格式的错误信息这样接口即使报错也不会把整个堆栈直接暴露给前端。加了这一层你的文档里就能写“项目具备完善的异常处理机制”又是一个加分项。再说回“数据库增删改查”这件事。表面上看这个系统就是典型的 CRUD 项目但课程设计和面试要的从来不是“你会写 insert 和 select”而是你在做新增时有没有校验参数唯一性做删除时有没有考虑关联数据做查询时有没有支持分页和条件组合。比如删除用户时他的健康记录表、运动记录表都关联着 user_id不做处理就会出现脏数据。MyBatis-Plus 的 LambdaQueryWrapper 可以让你快速实现按条件删除LambdaQueryWrapperHealthRecord wrapper new LambdaQueryWrapper(); wrapper.eq(HealthRecord::getUserId, userId); healthRecordMapper.delete(wrapper);这一段逻辑放进删除用户的 service 方法里业务闭环就完整了。这样的细节课程设计文档里每写一个答辩的时候就能多讲五分钟。4. Vue 前端页面规划与工程化实操前端部分的工作量不比后端少。拿到源码后最容易出问题的环节集中在路由配置、Axios 封装、图表渲染和打包部署。我把这几个核心点按实际操作顺序过一遍。4.1 路由规划与登录守卫先规划页面结构。健康管理系统一般包含登录页、注册页、主布局侧边栏 顶栏 内容区以及若干业务页面。Vue Router 的配置大致如下import { createRouter, createWebHistory } from vue-router const routes [ { path: /login, component: () import(/views/Login.vue) }, { path: /register, component: () import(/views/Register.vue) }, { path: /, component: () import(/layouts/Layout.vue), redirect: /dashboard, children: [ { path: dashboard, component: () import(/views/Dashboard.vue) }, { path: profile, component: () import(/views/Profile.vue) }, { path: record, component: () import(/views/RecordList.vue) }, { path: record/add, component: () import(/views/RecordAdd.vue) }, { path: record/:id, component: () import(/views/RecordDetail.vue) }, { path: exercise, component: () import(/views/ExerciseRecord.vue) }, { path: diet, component: () import(/views/DietRecord.vue) }, { path: statistics, component: () import(/views/Statistics.vue) } ] } ] const router createRouter({ history: createWebHistory(), routes })如果你用的 Vue2路由模式写mode: history通配符是path: *Vue3 写:pathMatch(.*)*。Vue2 和 Vue3 的路由配置差异是前端环境配置的高频报错点后面章节会专门说。路由传参是另一个容易踩坑的地方。从记录列表跳转详情页建议用record/:id这种动态路径传参因为它依赖路径本身刷新页面后参数依然保留。如果使用this.$route.params.id跳转传参刷新后就可能丢失这是 Vue 路由面试中高频出现的问题。还有 query 方式适合传非敏感的可读参数比如列表筛选条件。区分好 params 和 query 的使用场景前端代码会干净很多。路由守卫是登录校验的最后一层防线。后端虽然有拦截器但前端总不能等请求 401 了才跳登录页。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path ! /login !token) { next(/login) } else { next() } })页面级守卫和后端拦截器双管齐下才是完整的安全闭环。4.2 axios 封装、组件拆分与图表可视化axios 一定要封装不要在每个页面里直接axios.get(...)。原因很简单几十个页面里如果每个都手动拼请求头等到 token 过期要统一跳转时你得改几十个文件。封装成一个 service 文件后所有逻辑收敛到一处。import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: /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( resp { const res resp.data if (res.code ! 200) { Message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, err { if (err.response err.response.status 401) { localStorage.removeItem(token) router.push(/login) } Message.error(err.response?.data?.message || 网络异常) return Promise.reject(err) } )这里用了 baseURL /api配合 Nginx 反向代理或者 Vue 脚手架里的 proxy 配置可以很好地解决开发环境的跨域问题。开发时在 vue.config.js 里配module.exports { devServer: { proxy: { /api: { target: http://localhost:9090, changeOrigin: true } } } }这样前端请求 /api/health/record 时开发服务器会自动转发到后端的 9090 端口本地开发几乎不会遇到跨域 CORS 问题。页面组件拆分方面建议按功能拆而不是按文件类型拆。比如把健康记录页面拆成 HealthRecordForm、HealthRecordList、HealthRecordChart 三个组件每个组件只负责一件事。组件通信用 props 和自定义事件跨组件共享数据用 PiniaVue3或 VuexVue2。课程设计阶段不建议过度设计搞十几个全局 store 反而是负担。图表可视化一般用 ECharts。健康趋势图是这类系统的门面把最近 30 天的血压或体重数据用折线图展示出来视觉效果非常直观。ECharts 在 Vue 里的用法很简单先npm install echarts然后在组件 mounted 里初始化实例import * as echarts from echarts const chartDom document.getElementById(healthChart) const myChart echarts.init(chartDom) myChart.setOption({ xAxis: { type: category, data: dates }, yAxis: { type: value }, series: [{ type: line, data: values, smooth: true }] })这里要特别注意ECharts 实例在组件销毁时需要调用myChart.dispose()否则切页面时内存会持续累积。另一个高频坑是图表容器 div 没有设置高度ECharts 渲染出来是一个空白区域。给容器一个固定的 height比如 300px就不会出问题。如果系统里有健康宣教视频模块Vue 播放 m3u8 流的方案我也顺带提一句一般用 vue-video-player 配 hls.js 就能解决不要把 mp4 和 m3u8 混为一谈它们底层协议完全不同。4.3 打包部署与布局异常排查前端开发完成后npm run build会生成 dist 静态文件目录。把 dist 里的文件扔到 Nginx 的 html 目录再配一个反向代理到后端整个项目就能上线访问。部署阶段最经典的两个坑对应热搜里“vue 打包后 布局异常”和页面刷新 404 的问题。第一个坑打包后图片、JS、CSS 全部加载不出来页面白屏。原因通常是 vite.config.js 里的 base 写死了/而项目部署在某个子路径下。解决方法是把 base 改成相对路径export default defineConfig({ base: ./ })Vue CLI 项目则对应publicPath: ./。第二个坑部署后访问首页没问题但刷新二级页面直接 404。这是因为 Vue Router 用了 history 模式刷新 /record/1 时Nginx 找不到这个路径对应的真实文件。解决方法是让 Nginx 对不存在的路径都返回 index.htmlserver { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:9090/api/; } }加上 try_files 这一行Vue 路由才能在刷新时正常回到入口文件再由前端路由接管。如果不想折腾 Nginx也可以把 Vue Router 的 history 模式改成 hash 模式URL 会多一个#但对功能没有影响。课程设计和简单演示场景用 hash 模式省心很多。5. 环境搭建、高频报错对照与答辩经验最后这部分我把从零开始跑通一个 Java Vue 项目需要的环境、最常见的报错和课程设计/面试经验一次说清楚。5.1 环境准备与启动命令先从 JDK 说起。后端是 Spring BootJDK 8 或 11 都行建议双版本都装好部分老机器默认 JDK8新版 Spring Boot 3 要求 JDK17。装完 JDK 一定要配置 JAVA_HOME 环境变量然后在命令行验证java -version mvn -version如果 java 命令能识别、mvn 提示不是内部或外部命令说明 Maven 还没装或者没配环境变量。Maven 装好后建议把 settings.xml 里的镜像改成阿里云不然首次下载依赖能磨掉你一下午耐心。数据库方面MySQL 5.7 和 8.0 都常见。注意 MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver连接 URL 建议加上时区参数spring.datasource.urljdbc:mysql://localhost:3306/health_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.password你的密码前端环境Node.js 版本是最大的变量。Vue2 项目搭配 Node 16 比较稳Vue3 项目可以上 Node 18 或 20。安装 Node 时会自动带上 npm但 npm 默认源在国外建议先换成国内镜像npm config set registry https://registry.npmmirror.com然后安装依赖并启动前端npm install npm run serve后端启动更直接mvn spring-boot:run启动顺序一般先 MySQL再后端最后前端。后端起来后先用接口测试工具Apifox/Postman测一下登录接口确认数据库连接没问题再开前端页面联调这样能少踩很多“前后端都报错但不知道先查谁”的坑。5.2 高频报错对照表我把这类项目最常见的报错按“现象 - 原因 - 解决”整理一张速查表你可以把它截图存下来报错现象可能原因解决方式后端启动报 Port 8080 was already in use8080 端口被占用改server.port9090或杀掉占用进程启动报 ClassNotFoundException: com.mysql.cj.jdbc.Drivermysql-connector-java 版本太低或没引入使用 MySQL 8 驱动或检查 pom 依赖连接数据库报 Access denied for user账号密码错误或权限不足用 Navicat 测试连接检查 root 密码与授权操作数据库报 time zone 错误MySQL 时区未配置连接 URL 加serverTimezoneAsia/Shanghainpm install 报 ERESOLVE unable to resolve dependency treeNode 版本与依赖冲突降低 Node 版本或加--legacy-peer-deps安装输入 vue 提示不是内部或外部命令Vue CLI 未全局安装或未执行 npm install使用npx vue或重新npm install -g vue/cli前端请求接口报 CORS error前后端跨域后端配 CorsConfig或前端 devServer 配 proxy接口报 404 或 405路径或请求方法不匹配用 Postman 单独验证后端接口检查 Controller 映射打包后页面空白publicPath/base 配置错误Vite 配base: ./Vue CLI 配publicPath: ./刷新页面 404history 模式未处理配 Nginx try_files或改用 hash 模式这十条基本覆盖了课程设计里 90% 的环境类报错。遇到报错先不要慌按这个表格逐条排除多数问题十分钟内能定位。5.3 让文档和答辩更有说服力的经验之谈配套文档是这类项目交付物里最容易被忽略、又最能拉开差距的部分。一个合格的健康管理系统文档至少要包含六块内容需求概述系统解决什么问题、技术架构为什么选 Spring Boot Vue、数据库设计ER 图和表结构说明、核心接口说明路径、参数、返回结构、部署文档环境准备和启动步骤、测试用例演示用的账号和数据。写文档的时候有一个原则功能照实写设计亮点往深里写。比如“健康记录接口会根据指标自动计算 BMI 和风险等级”这就是功能“为了防止前端伪造数据BMI 和风险等级由后端计算而非前端传入”这就是设计亮点后者才是能体现你思考深度的内容。答辩演示顺序也很重要。我建议按这个流程走注册新用户 - 登录 - 完善健康档案 - 连续录入几天的健康记录 - 查看趋势图和风险等级 - 展示管理员端如果有- 演示错误处理比如重复注册、密码错误。整个过程控制在五分钟以内把最顺的路径走完不要在中途临时测试不熟悉的流程。最后说面试。如果你打算把这套系统写进简历一定要提前准备好这几个问题为什么用 JWT 不用 Session无状态、跨域友好、服务端不存储JWT 有哪些缺点无法主动过期、token 太长解决方案是加 Redis 黑名单或缩短有效期MyBatis-Plus 分页是怎么实现的拦截器在 SQL 后拼接 limit如果用户量变大数据库层面怎么优化加索引、读写分离、分表分库集成 Redis 可以做哪些事缓存 token、缓存健康趋势数据降低数据库压力这五个问题答好了这套系统的简历价值才能完全体现出来。别只会说“我做了个健康管理系统”要说“我独立设计并实现了一个基于 Spring Boot Vue 的前后端分离系统负责健康数据的录入、指标计算、趋势分析和鉴权模块”然后等着面试官往你的技术深度里挖。按照这套思路从数据库设计、后端接口再到前端页面完整走一遍我个人体会是收获最大的其实不是会用了某个框架而是把“一个需求如何落地成一个系统”这条链路想明白了。最后再分享一个很不起眼但很加分的小技巧不管是课程设计还是面试展示先把 SQL 脚本在一台干净数据库上从头执行一遍再按“注册 - 录入 - 查询 - 分析”的顺序完整演示一遍这个流程能提前暴露大量你以为没问题、实际一跑就崩的细节。拿到源码和文档也不要直接交差自己把表结构和接口画一遍能讲清楚逻辑才是真正掌握了一个项目。