尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Spring Boot+Vue运动健康管理系统毕业设计全栈开发指南

Spring Boot+Vue运动健康管理系统毕业设计全栈开发指南 运动健康管理系统是计算机专业毕业设计的热门选题之一它结合了数据管理、用户交互和业务逻辑能很好地展示学生的综合开发能力。很多同学在选题后面对从零开始的代码编写、数据库设计和系统集成常常感到无从下手最终要么项目功能单薄要么逻辑混乱难以通过答辩。本文将围绕一个典型的运动健康管理系统从需求分析、技术选型、数据库设计、前后端实现到部署演示提供一个清晰、完整、可直接用于毕业答辩的解决方案。这个方案基于主流的 Spring Boot Vue.js 技术栈采用前后端分离架构涵盖了用户管理、运动数据记录、健康指标分析、数据可视化等核心功能。即使你之前对某个技术栈不熟悉也可以按照本文的步骤一步步搭建出一个结构清晰、功能完备、界面美观的毕业设计项目。我们将重点解释每个环节的设计思路和实现细节确保你不仅能“跑起来”更能“讲明白”从容应对答辩老师的提问。1. 理解运动健康管理系统的核心需求与架构在动手编码之前必须先明确系统要解决什么问题以及如何组织代码结构。一个合格的毕业设计项目其需求应当来源于实际场景功能模块之间应具备清晰的逻辑关系。1.1 系统核心业务场景分析运动健康管理系统主要服务于有日常运动习惯或健康管理需求的个人用户。其核心业务流可以概括为用户记录运动数据 - 系统处理并存储数据 - 系统分析数据并生成报告 - 用户查看报告以指导后续运动。基于此我们可以拆解出以下几个核心功能模块用户管理模块负责用户的注册、登录、个人信息维护。这是系统的基础。运动记录模块允许用户手动录入或通过接口上传单次运动的详细信息如运动类型跑步、骑行、游泳、时长、距离、消耗卡路里、平均心率等。健康数据模块用户可以定期录入或连接设备同步身体指标如体重、身高、BMI、血压、睡眠时长等。数据统计与分析模块这是系统的“大脑”。它需要聚合用户的运动与健康数据进行统计分析例如计算周/月总运动时长、消耗趋势、体重变化曲线等。可视化报表模块将分析结果以图表如折线图、柱状图、饼图的形式直观展示给用户。系统管理模块可选用于提升项目复杂度如果设计为多角色系统如普通用户、管理员则需要管理员角色来管理所有用户、查看全局数据统计等。1.2 前后端分离的技术选型与项目结构为了体现现代Web开发的主流实践我们采用前后端分离架构。后端提供RESTful API接口前端通过HTTP请求调用这些接口两者独立开发、部署。后端技术栈 (Spring Boot)框架Spring Boot 2.7.x。它简化了Spring应用的初始搭建和开发过程是Java领域构建REST API的事实标准。数据持久层MyBatis-Plus。它在MyBatis基础上进行了增强提供了通用的CRUD操作能极大减少基础SQL的编写。数据库MySQL 8.0。关系型数据库适合存储结构化的用户和运动数据。权限控制Spring Security JWT (JSON Web Token)。用于实现用户认证登录和授权接口访问控制。工具库Lombok简化Java Bean编写、HutoolJava工具类库。前端技术栈 (Vue.js)框架Vue 3 Composition API。当前主流的前端框架生态丰富学习曲线平缓。构建工具Vite。新一代前端构建工具启动和热更新速度极快。UI组件库Element Plus。基于Vue 3的桌面端组件库能快速搭建出美观的界面。图表库ECharts。百度开源的图表库功能强大文档齐全非常适合数据可视化。HTTP客户端Axios。用于发起HTTP请求调用后端API。路由管理Vue Router。管理前端页面路由。状态管理PiniaVuex的替代品更简洁。用于管理跨组件的用户登录状态等。项目目录结构预览一个清晰的项目结构是良好开发的开端。以下是建议的目录结构sport-health-system/ ├── backend/ # 后端Spring Boot项目 │ ├── src/main/java/com/sporthealth/ │ │ ├── controller/ # 控制器层接收请求返回响应 │ │ ├── entity/ # 实体类与数据库表对应 │ │ ├── mapper/ # MyBatis-Plus的Mapper接口 │ │ ├── service/ # 业务逻辑层接口 │ │ ├── service/impl/ # 业务逻辑层实现 │ │ ├── config/ # 配置类如MyBatis-Plus、Spring Security │ │ ├── utils/ # 工具类如JWT工具 │ │ └── SportHealthApplication.java # 启动类 │ └── src/main/resources/ │ ├── application.yml # 主配置文件 │ └── mapper/ # MyBatis XML映射文件如需 ├── frontend/ # 前端Vue项目 │ ├── public/ # 静态资源 │ └── src/ │ ├── api/ # 封装所有后端API请求 │ ├── assets/ # 图片、样式等资源 │ ├── components/ # 可复用组件 │ ├── router/ # 路由配置 │ ├── stores/ # Pinia状态管理 │ ├── utils/ # 前端工具函数 │ ├── views/ # 页面组件 │ └── App.vue, main.js └── database/ # 数据库SQL脚本 └── init.sql # 建表及初始化数据脚本2. 环境准备与数据库设计在开始编写业务代码前需要搭建好基础的开发环境并设计出合理的数据库表结构。这是项目稳定的基石。2.1 开发环境与工具清单请确保你的本地开发环境已安装以下工具并建议使用指定或更高版本。工具/环境推荐版本作用说明验证安装命令JDK1.8 或 11/17Java运行环境java -versionMaven3.6Java项目构建与依赖管理mvn -vNode.js16.x 或 18.xJavaScript运行时npm包管理器node -v,npm -vMySQL8.0.x数据库服务器mysql --versionIDEIntelliJ IDEA / VS Code后端/前端开发IDE-浏览器Chrome / Edge前端调试-注意Spring Boot 2.7.x 与 JDK 8、11、17 兼容性较好。Vue 3 对 Node.js 版本有要求建议使用 LTS 版本。2.2 核心数据库表设计根据1.1节分析的业务模块我们设计五张核心表。设计时需考虑字段类型、长度、是否为空、默认值、索引以及表间关系。1. 用户表 (sys_user)存储系统用户的基本信息。CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL COMMENT 用户名用于登录, password varchar(255) NOT NULL COMMENT 加密后的密码, nickname varchar(50) DEFAULT NULL COMMENT 用户昵称, avatar varchar(500) DEFAULT NULL COMMENT 头像URL, email varchar(100) DEFAULT NULL COMMENT 邮箱, phone varchar(20) DEFAULT NULL COMMENT 手机号, gender tinyint DEFAULT 0 COMMENT 性别0未知1男2女, birthday date DEFAULT NULL COMMENT 生日, height decimal(5,2) DEFAULT NULL COMMENT 身高(cm), weight decimal(5,2) DEFAULT NULL COMMENT 体重(kg), create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表;2. 运动记录表 (sport_record)记录用户每一次的运动详情。CREATE TABLE sport_record ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, user_id bigint NOT NULL COMMENT 用户ID, sport_type varchar(20) NOT NULL COMMENT 运动类型如running, cycling, swimming, start_time datetime NOT NULL COMMENT 运动开始时间, duration int NOT NULL COMMENT 运动时长分钟, distance decimal(8,2) DEFAULT NULL COMMENT 运动距离公里, avg_heart_rate int DEFAULT NULL COMMENT 平均心率次/分钟, calories int DEFAULT NULL COMMENT 消耗卡路里大卡, remark varchar(500) DEFAULT NULL COMMENT 备注, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_start_time (start_time), CONSTRAINT fk_record_user FOREIGN KEY (user_id) REFERENCES sys_user (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT运动记录表;3. 健康数据表 (health_data)记录用户某一时刻的健康指标。CREATE TABLE health_data ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, user_id bigint NOT NULL COMMENT 用户ID, record_date date NOT NULL COMMENT 记录日期, weight decimal(5,2) DEFAULT NULL COMMENT 体重(kg), blood_pressure_high int DEFAULT NULL COMMENT 血压高压, blood_pressure_low int DEFAULT NULL COMMENT 血压低压, heart_rate int DEFAULT NULL COMMENT 静息心率, sleep_hours decimal(3,1) DEFAULT NULL COMMENT 睡眠时长(小时), remark varchar(500) DEFAULT NULL COMMENT 备注, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_user_date (user_id, record_date), -- 同一用户同一天只能有一条记录 CONSTRAINT fk_health_user FOREIGN KEY (user_id) REFERENCES sys_user (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健康数据表;4. 运动类型字典表 (dict_sport_type)维护系统支持的运动类型便于前端下拉框选择和数据统计分类。CREATE TABLE dict_sport_type ( code varchar(20) NOT NULL COMMENT 类型编码, name varchar(50) NOT NULL COMMENT 类型名称, icon varchar(100) DEFAULT NULL COMMENT 图标, calorie_factor decimal(5,2) DEFAULT NULL COMMENT 卡路里计算系数, sort int DEFAULT 0 COMMENT 排序, PRIMARY KEY (code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT运动类型字典表;可以初始化一些数据INSERT INTO dict_sport_type VALUES (running, 跑步, el-icon-position, 1.05, 1), (cycling, 骑行, el-icon-bicycle, 0.65, 2);设计要点说明外键约束sport_record和health_data表通过user_id关联sys_user表并设置ON DELETE CASCADE确保用户删除时其相关记录也被删除保持数据一致性。唯一约束health_data表的uk_user_date约束保证了用户每天最多记录一次健康数据避免重复。索引为user_id和start_time等查询频繁的字段添加索引能显著提升大数据量下的查询性能。字典表使用字典表管理运动类型使系统更易扩展。当需要新增运动类型时只需插入一条记录无需修改代码。3. 后端Spring Boot项目实现我们将从创建项目开始逐步实现用户认证、运动记录CRUD、数据统计等核心API。3.1 初始化项目与基础配置使用 Spring Initializr 或 IDE 创建 Spring Boot 项目选择依赖Spring Web,MyBatis Framework,MySQL Driver,Lombok。创建完成后手动在pom.xml中添加 MyBatis-Plus 和 JWT 相关依赖。!-- pom.xml 部分依赖 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.16/version /dependency配置application.yml文件设置数据库连接、MyBatis-Plus 和 JWT 密钥。# application.yml server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sport_health_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL开发时使用 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名如果启用 logic-delete-value: 1 logic-not-delete-value: 0 jwt: secret: yourSuperSecretKeyHereAtLeast32BytesLong # JWT加密密钥务必修改并保密 expire: 604800 # Token过期时间秒默认7天3.2 实现JWT认证与用户登录接口首先创建用户实体类User并使用 MyBatis-Plus 的注解。// entity/User.java package com.sporthealth.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDate; import java.time.LocalDateTime; Data TableName(sys_user) public class User { TableId(type IdType.AUTO) private Long id; private String username; private String password; // 存储加密后的密码 private String nickname; private String avatar; private String email; private String phone; private Integer gender; private LocalDate birthday; private BigDecimal height; private BigDecimal weight; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; }创建对应的UserMapper接口继承 MyBatis-Plus 的BaseMapper。// mapper/UserMapper.java package com.sporthealth.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.sporthealth.entity.User; import org.apache.ibatis.annotations.Mapper; Mapper public interface UserMapper extends BaseMapperUser { }创建JwtUtil工具类用于生成和解析 JWT Token。// utils/JwtUtil.java package com.sporthealth.utils; import io.jsonwebtoken.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Date; Component public class JwtUtil { Value(${jwt.secret}) private String secret; Value(${jwt.expire}) private Long expire; public String generateToken(String username) { Date now new Date(); Date expiryDate new Date(now.getTime() expire * 1000); return Jwts.builder() .setSubject(username) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public String getUsernameFromToken(String token) { Claims claims Jwts.parserBuilder() .setSigningKey(secret) .build() .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public boolean validateToken(String token) { try { Jwts.parserBuilder().setSigningKey(secret).build().parseClaimsJws(token); return true; } catch (JwtException | IllegalArgumentException e) { return false; } } }创建登录请求的DTOData Transfer Object、统一返回结果类然后实现AuthController。// dto/LoginRequest.java package com.sporthealth.dto; import lombok.Data; import javax.validation.constraints.NotBlank; Data public class LoginRequest { NotBlank(message 用户名不能为空) private String username; NotBlank(message 密码不能为空) private String password; }// controller/AuthController.java package com.sporthealth.controller; import com.sporthealth.dto.LoginRequest; import com.sporthealth.entity.User; import com.sporthealth.service.UserService; import com.sporthealth.utils.JwtUtil; import com.sporthealth.common.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; RestController RequestMapping(/api/auth) public class AuthController { Autowired private UserService userService; Autowired private JwtUtil jwtUtil; PostMapping(/login) public Result login(Valid RequestBody LoginRequest request) { // 1. 根据用户名查询用户 User user userService.findByUsername(request.getUsername()); if (user null) { return Result.error(用户名或密码错误); } // 2. 验证密码 (实际项目中密码应加密存储这里简化) // 假设存储的是明文实际应用务必使用BCryptPasswordEncoder等加密 if (!user.getPassword().equals(request.getPassword())) { return Result.error(用户名或密码错误); } // 3. 生成JWT Token String token jwtUtil.generateToken(user.getUsername()); // 4. 返回Token和用户基本信息不包含密码 user.setPassword(null); return Result.success(登录成功).data(token, token).data(user, user); } PostMapping(/register) public Result register(Valid RequestBody User user) { // 检查用户名是否已存在 if (userService.findByUsername(user.getUsername()) ! null) { return Result.error(用户名已存在); } // 密码加密存储示例实际需用加密器 // user.setPassword(passwordEncoder.encode(user.getPassword())); userService.save(user); return Result.success(注册成功); } }3.3 实现运动记录与健康数据的CRUD接口以运动记录为例创建实体类、Mapper、Service和Controller。MyBatis-Plus 能极大简化基础CRUD代码。// entity/SportRecord.java package com.sporthealth.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; Data TableName(sport_record) public class SportRecord { TableId(type IdType.AUTO) private Long id; private Long userId; private String sportType; private LocalDateTime startTime; private Integer duration; // 分钟 private BigDecimal distance; // 公里 private Integer avgHeartRate; private Integer calories; private String remark; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; }// service/impl/SportRecordServiceImpl.java package com.sporthealth.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.sporthealth.entity.SportRecord; import com.sporthealth.mapper.SportRecordMapper; import com.sporthealth.service.SportRecordService; import org.springframework.stereotype.Service; import java.time.LocalDate; import java.util.List; Service public class SportRecordServiceImpl extends ServiceImplSportRecordMapper, SportRecord implements SportRecordService { Override public ListSportRecord getRecordsByUserAndDate(Long userId, LocalDate date) { LambdaQueryWrapperSportRecord wrapper new LambdaQueryWrapper(); wrapper.eq(SportRecord::getUserId, userId) .ge(SportRecord::getStartTime, date.atStartOfDay()) .lt(SportRecord::getStartTime, date.plusDays(1).atStartOfDay()) .orderByDesc(SportRecord::getStartTime); return this.list(wrapper); } }// controller/SportRecordController.java package com.sporthealth.controller; import com.sporthealth.entity.SportRecord; import com.sporthealth.service.SportRecordService; import com.sporthealth.common.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.time.LocalDate; import java.util.List; RestController RequestMapping(/api/sport-record) public class SportRecordController { Autowired private SportRecordService sportRecordService; Autowired private JwtUtil jwtUtil; // 从Token中获取当前用户ID的辅助方法 private Long getCurrentUserId(HttpServletRequest request) { String token request.getHeader(Authorization).substring(7); // 去掉Bearer String username jwtUtil.getUsernameFromToken(token); // 根据username查询用户ID这里简化实际可从Token或缓存中获取 // 假设有一个根据用户名查ID的服务 return 1L; // 示例ID } PostMapping public Result addRecord(RequestBody SportRecord record, HttpServletRequest request) { record.setUserId(getCurrentUserId(request)); sportRecordService.save(record); return Result.success(添加成功); } GetMapping(/list) public Result listRecords(RequestParam(required false) String date, HttpServletRequest request) { Long userId getCurrentUserId(request); LocalDate queryDate (date ! null) ? LocalDate.parse(date) : LocalDate.now(); ListSportRecord records sportRecordService.getRecordsByUserAndDate(userId, queryDate); return Result.success(records); } PutMapping(/{id}) public Result updateRecord(PathVariable Long id, RequestBody SportRecord record) { record.setId(id); sportRecordService.updateById(record); return Result.success(更新成功); } DeleteMapping(/{id}) public Result deleteRecord(PathVariable Long id) { sportRecordService.removeById(id); return Result.success(删除成功); } }健康数据模块的实现逻辑与此类似。至此后端的核心API已经搭建完成可以通过Postman等工具进行测试。4. 前端Vue项目与可视化实现前端项目负责用户交互将后端API提供的数据以友好的形式展示出来并通过图表进行可视化。4.1 初始化Vue项目与配置路由使用Vite快速创建Vue项目并安装所需依赖。npm create vuelatest sport-health-frontend # 根据提示选择Vue Router, Pinia, ESLint cd sport-health-frontend npm install element-plus axios echarts npm install --save-dev element-plus/icons-vue配置路由 (router/index.js)定义登录页、主页、运动记录页、健康数据页、统计报表页等。// router/index.js import { createRouter, createWebHistory } from vue-router import Login from ../views/Login.vue import Layout from ../layouts/Layout.vue import SportRecord from ../views/SportRecord.vue import HealthData from ../views/HealthData.vue import Dashboard from ../views/Dashboard.vue const routes [ { path: /login, name: Login, component: Login, meta: { requiresAuth: false } }, { path: /, component: Layout, meta: { requiresAuth: true }, children: [ { path: , redirect: /dashboard }, { path: dashboard, name: Dashboard, component: Dashboard }, { path: sport-record, name: SportRecord, component: SportRecord }, { path: health-data, name: HealthData, component: HealthData } ] } ] const router createRouter({ history: createWebHistory(), routes }) // 路由守卫检查用户是否已登录Token是否存在 router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } }) export default router4.2 封装Axios请求与用户状态管理创建统一的Axios实例并配置请求/响应拦截器自动在请求头中添加JWT Token。// utils/request.js import axios from axios import { ElMessage } from element-plus import router from ../router const service axios.create({ baseURL: http://localhost:8080/api, // 后端API地址 timeout: 10000 }) // 请求拦截器 service.interceptors.request.use( config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }, error { return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response { const res response.data if (res.code 200) { return res.data // 返回后端Result中的data字段 } else { ElMessage.error(res.msg || 请求失败) return Promise.reject(new Error(res.msg || Error)) } }, error { if (error.response error.response.status 401) { ElMessage.error(登录已过期请重新登录) localStorage.removeItem(token) router.push(/login) } else { ElMessage.error(error.message || 网络错误) } return Promise.reject(error) } ) export default service使用Pinia管理用户登录状态。// stores/user.js import { defineStore } from pinia import { ref } from vue import { login as loginApi } from ../api/auth export const useUserStore defineStore(user, () { const token ref(localStorage.getItem(token) || ) const userInfo ref(JSON.parse(localStorage.getItem(userInfo)) || null) const login async (loginForm) { const res await loginApi(loginForm) token.value res.token userInfo.value res.user localStorage.setItem(token, res.token) localStorage.setItem(userInfo, JSON.stringify(res.user)) } const logout () { token.value userInfo.value null localStorage.removeItem(token) localStorage.removeItem(userInfo) } return { token, userInfo, login, logout } })4.3 实现核心页面与ECharts图表集成以数据看板页 (Dashboard.vue) 为例展示如何集成ECharts。!-- views/Dashboard.vue -- template div classdashboard h2运动健康看板/h2 el-row :gutter20 el-col :span12 div classchart-container h3本周运动时长趋势/h3 div refsportChartRef stylewidth: 100%; height: 400px;/div /div /el-col el-col :span12 div classchart-container h3运动类型分布/h3 div reftypeChartRef stylewidth: 100%; height: 400px;/div /div /el-col /el-row !-- 其他统计卡片 -- /div /template script setup import { ref, onMounted } from vue import * as echarts from echarts import { getSportTrend, getSportTypeDistribution } from ../api/dashboard const sportChartRef ref(null) const typeChartRef ref(null) onMounted(async () { // 获取数据 const trendData await getSportTrend() const typeData await getSportTypeDistribution() // 初始化运动趋势折线图 const sportChart echarts.init(sportChartRef.value) const trendOption { xAxis: { type: category, data: trendData.dates }, yAxis: { type: value, name: 时长(分钟) }, series: [{ data: trendData.durations, type: line, smooth: true }], tooltip: { trigger: axis } } sportChart.setOption(trendOption) // 初始化运动类型饼图 const typeChart echarts.init(typeChartRef.value) const typeOption { tooltip: { trigger: item }, legend: { orient: vertical, left: left }, series: [{ type: pie, radius: 50%, data: typeData.map(item ({ name: item.name, value: item.count })), emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: rgba(0, 0, 0, 0.5) } } }] } typeChart.setOption(typeOption) // 响应窗口大小变化 window.addEventListener(resize, () { sportChart.resize() typeChart.resize() }) }) /script对应的后端需要提供/api/dashboard/trend和/api/dashboard/type-distribution接口通过SQL聚合查询返回图表所需数据。5. 系统联调、部署与答辩准备前后端分别开发完成后需要进行联调测试并打包部署最终准备答辩材料。5.1 前后端联调与常见问题排查启动后端Spring Boot应用和前端Vue开发服务器。# 后端 cd backend mvn spring-boot:run # 前端 cd frontend npm run dev访问http://localhost:5173(Vite默认端口) 即可看到前端页面。联调时最常见的问题是跨域CORS和接口路径错误。跨域问题后端需要配置CORS允许前端域名的请求。// config/WebMvcConfig.java Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) // 生产环境应指定具体前端地址 .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }接口404或500错误检查网络请求使用浏览器开发者工具的Network面板查看请求URL、方法、请求头、响应状态码和响应体。检查后端日志Spring Boot控制台会打印详细的错误堆栈信息是排查问题的关键。核对参数确认前端传递的参数名、类型与后端接口定义是否一致。数据库连接检查application.yml中的数据库配置、MySQL服务是否启动、数据库和表是否存在。5.2 项目打包与简易部署后端打包使用Maven打包为可执行的JAR文件。cd backend mvn clean package -DskipTests # 生成的JAR文件在 target/ 目录下如 sport-health-backend-0.0.1-SNAPSHOT.jar # 运行 java -jar target/sport-health-backend-0.0.1-SNAPSHOT.jar前端打包使用Vite构建生产环境静态文件。cd frontend npm run build # 生成的静态文件在 dist/ 目录下部署方式开发演示可以同时运行后端JAR和将前端dist目录下的文件放到Nginx或Spring Boot的static目录下。简易部署将前端dist目录内的所有文件复制到后端项目的src/main/resources/static目录下然后打包。这样访问http://localhost:8080就能看到前端页面API请求发往同一域名避免了跨域。这是毕业设计演示最方便的方式。生产部署需分别部署后端云服务器JDK和前端Nginx并配置域名、HTTPS等。5.3 毕业设计答辩要点与项目亮点提炼完成代码开发后更重要的是如何向答辩老师清晰展示你的工作。1. 答辩陈述结构项目概述简要说明项目背景、目标用户、解决的核心问题。技术选型解释为什么选择Spring Boot和Vue.js阐述前后端分离的优势。系统设计展示系统架构图、功能模块图、数据库ER图。核心功能演示现场登录系统演示用户注册、记录运动、查看图表等核心流程。这是答辩的重点。难点与解决方案阐述你遇到的主要技术难点如JWT认证、ECharts集成、跨域处理及如何解决。总结与展望总结项目完成情况说明可以继续优化的方向如接入智能手环API、增加社交功能、引入机器学习分析等。2. 项目亮点供答辩时强调技术栈主流且完整使用了当前企业级开发的主流技术组合。前后端分离架构体现了清晰的工程化思想。RESTful API设计接口规范易于理解和使用。数据可视化使用ECharts将枯燥的数据转化为直观图表提升了项目表现力。用户认证与安全实现了基于JWT的认证机制保证了接口安全。代码结构清晰遵循了MVC或类似分层模式代码可读性和可维护性好。3. 准备问答提前思考老师可能问的问题为什么运动记录表和健康数据表要分开设计JWT和Session认证有什么区别各有什么优劣如果用户量很大数据库查询变慢可以如何优化答为常用查询字段加索引、分库分表、引入缓存如Redis前端图表是如何与后端数据联动的你这个项目有什么实际应用价值按照以上步骤完成你将拥有一个功能完整、结构清晰、技术栈主流的运动健康管理系统。它不仅能够顺利运行更能让你在答辩中清晰地阐述设计思路与技术细节从而获得理想的成绩。
返回列表