ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于SpringBoot的学生成绩分析系统毕业设计完整实现

基于SpringBoot的学生成绩分析系统毕业设计完整实现 1. 毕业设计真的只是“做功能”吗每年毕业季都能看到大量选题相似的系统学生管理系统、图书馆管理系统、便利店收银系统。你说这些系统有没有用有。但问题是它们已经多到让答辩老师审美疲劳了。如果题目是“基于SpringBoot的学生成绩分析系统”情况就不太一样。它本质上不是一个“增删改查”四件套而是把“成绩管理”和“数据分析”结合起来。在毕设评分标准里数据分析和可视化展示本身就是加分项因为它涉及聚合查询、统计计算、图表渲染、多表关联这些真实业务里高频使用的技术点。这篇文章写给正在选毕设题目、或者已经选了类似题目但不知道从哪下手的同学。我会把这个系统从需求拆解、表结构设计、后端接口实现到前端可视化展示的完整过程讲清楚。文章里的代码可以直接用也可以在此基础上改功能、换 UI作为自己的项目。先给一个判断这个题目的难点不在 CRUD而在成绩统计的 SQL 设计和前后端数据联调。数据库不会写或者图表不会接项目基本就卡死在那。所以这篇文章的重点也会放在这两块。2. 核心需求分析系统的边界到底在哪很多同学拿到题目第一反应是“做个系统”但系统的功能边界是什么往往说不清楚。这是做毕设的大忌。答辩老师最喜欢问的一句话就是“你这个系统的核心功能是什么为什么这么设计”所以需求分析必须写在前面。2.1 系统角色设计学生成绩分析系统通常面向两类用户角色可操作功能说明管理员班级管理、学生管理、课程管理、成绩导入、成绩查询、统计分析系统的核心使用者掌握全部数据权限教师成绩录入、成绩查看、班级成绩分析部分场景下分离也可合并为单一管理员从毕设体量来看建议先做单角色管理员把完整流程跑通再决定是否扩展教师端。单角色系统已经能覆盖课程管理、成绩导入导出、统计分析这些核心场景答辩时也足够支撑你讲清楚设计思路。2.2 核心功能模块一个可落地的成绩分析系统至少需要这几个模块学生管理学生的基本信息维护包括学号、姓名、班级、性别、入学年份。班级管理年级、专业、班级信息的维护用于后续按班级维度做成绩聚合。课程管理课程名称、课程类型必修/选修、学分等信息。成绩管理单条成绩录入、批量导入Excel、成绩修改与删除。批量导入是提升系统实用性的关键功能也是毕设答辩时值得重点展示的部分。成绩分析按课程、按班级、按个人维度统计平均分、最高分、最低分、及格率、优秀率并可视化展示。系统管理管理员登录、密码修改。2.3 非功能需求数据安全方面密码需要加密存储推荐使用 BCrypt操作权限方面未登录用户不能访问系统接口。易用性方面界面尽量保持清爽核心操作三步以内完成重点页面包括登录页、成绩管理页、统计分析页。性能方面在数据量不大几千条成绩的情况下接口响应时间要在毫秒级这个使用 SpringBoot 默认配置就能轻松达到。2.4 为什么选这个题目从毕业设计评分角度看这个题目价值体现在四个层面技术层面覆盖 SpringBoot、MyBatis Plus、MySQL、Vue、ECharts 的主流技术栈具备完整的前后端分离特征。逻辑层面成绩统计涉及 GROUP BY 聚合、多表 JOIN、条件过滤比管理系统的单表增删改查更有含金量。展示层面可视化图表让系统有“效果”答辩现场演示时直观性强。扩展层面后续可以加成绩预测、学科对比、班主任端等工作量可伸缩。3. 技术选型与项目架构别盲目追求全家桶毕设项目最忌讳技术选型堆砌。用不到的技术不要加加了之后你解释不清楚反而变成减分项。这里给出一套稳妥的方案。3.1 后端技术栈技术版本建议用途说明Spring Boot2.7.x 或 3.x项目基础框架推荐使用 Java 8 兼容的 2.7.x避免环境问题MyBatis Plus3.5.x数据访问层框架内置 CRUD 方法减少代码量MySQL8.x / 5.7.x数据库Lombok最新稳定版简化实体类代码EasyExcel3.x成绩批量导入导出Hutool5.x工具类库可选这里要特别说明一个常见误区Spring Boot 版本不是越高越好。如果 JDK 是 8就优先选 Spring Boot 2.7.x如果 JDK 是 17 以上可以用 3.x。版本不匹配会导致启动报错白白浪费时间。这也是很多同学配置环境时最大的坑。3.2 前端技术栈Vue 2 Element UI资料最多遇到问题容易搜到答案适合毕设。Vue 3 Element Plus如果对前端比较熟悉推荐用它技术更新。ECharts图表展示库用于成绩分布的柱状图、饼图、折线图。如果你的目标是快速完成也可以不前后端分离直接用 Thymeleaf 模板引擎渲染页面。但从毕设含金量考虑前后端分离项目更值得做也更容易扩展。3.3 项目整体架构项目采用前后端分离结构src/main/java/com/example/scoreanalysis/ ├── controller/ # 接收前端请求返回数据 ├── service/ # 业务逻辑处理 ├── mapper/ # MyBatis Plus 数据访问接口 ├── entity/ # 数据库实体类 ├── dto/ # 数据传输对象 ├── vo/ # 视图对象返回给前端的数据结构 ├── config/ # 配置类拦截器、跨域等 ├── common/ # 统一返回结果、异常处理 └── utils/ # 工具类层次分明的架构有两个好处一是答题时老师问“你觉得你项目哪里写得好”你能说出结构清晰、职责单一二是后续扩展功能时改动范围可控。3.4 前后端交互流程用户在浏览器点击“班级成绩分析”按钮前端发起 GET /api/score/analysis/class/{classId} 请求后端 Controller 接收请求后调用 Service 层Service 调用 Mapper 执行 SQL 聚合统计结果封装成统一格式返回给前端前端拿到数据后通过 ECharts 渲染柱状图或饼图。这个流程是答辩时必讲的内容最好做到烂熟于心。4. 数据库设计成绩统计表是核心数据库设计是整个项目的地基。表关系梳理不清楚后面写 SQL 就会不停返工。成绩分析系统的核心表有五张。4.1 数据表结构总览表名说明关键字段t_admin管理员表id, username, passwordt_student学生表id, stu_no, name, gender, class_id, enroll_yeart_class班级表id, class_name, grade, majort_course课程表id, course_name, course_type, creditt_score成绩表id, student_id, course_id, score, exam_date成绩表是核心中的核心理论上每个学生每门课一条记录。4.2 核心建表 SQL-- 创建数据库 CREATE DATABASE IF NOT EXISTS score_analysis DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE score_analysis; -- 管理员表 CREATE TABLE t_admin ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录用户名, password VARCHAR(100) NOT NULL COMMENT BCrypt加密后的密码, real_name VARCHAR(50) COMMENT 真实姓名, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 管理员表; -- 班级表 CREATE TABLE t_class ( id BIGINT AUTO_INCREMENT PRIMARY KEY, class_name VARCHAR(50) NOT NULL COMMENT 班级名称如 计算机2101班, grade VARCHAR(20) COMMENT 年级如 2021, major VARCHAR(50) COMMENT 专业如 计算机科学与技术 ) COMMENT 班级表; -- 学生表 CREATE TABLE t_student ( id BIGINT AUTO_INCREMENT PRIMARY KEY, stu_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT DEFAULT 1 COMMENT 1-男 0-女, class_id BIGINT COMMENT 所属班级ID, enroll_year VARCHAR(10) COMMENT 入学年份, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (class_id) REFERENCES t_class(id) ) COMMENT 学生表; -- 课程表 CREATE TABLE t_course ( id BIGINT AUTO_INCREMENT PRIMARY KEY, course_name VARCHAR(50) NOT NULL COMMENT 课程名称, course_type TINYINT DEFAULT 1 COMMENT 1-必修 2-选修, credit DECIMAL(3, 1) DEFAULT 2.0 COMMENT 学分 ) COMMENT 课程表; -- 成绩表 CREATE TABLE t_score ( id BIGINT AUTO_INCREMENT PRIMARY KEY, student_id BIGINT NOT NULL COMMENT 学生ID, course_id BIGINT NOT NULL COMMENT 课程ID, score DECIMAL(5, 2) COMMENT 成绩分数, exam_date VARCHAR(20) COMMENT 考试时间, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (student_id) REFERENCES t_student(id), FOREIGN KEY (course_id) REFERENCES t_course(id), UNIQUE KEY uk_student_course (student_id, course_id) ) COMMENT 成绩表;4.3 表设计的三个关键点唯一索引 uk_student_course避免同一个学生同一门课被录入两次。这个设计在答辩时可以提一下“为什么要加唯一索引”回答是“防止数据重复录入导致统计结果错误”。学生表和班级表用外键关联成绩分析天然需要按班级维度统计没有班级关系分析无从谈起。成绩表使用 DECIMAL(5,2)存储百分制成绩保留两位小数避免浮点数精度问题。4.4 核心统计 SQL 示例在代码实现之前先把最难的部分搞定成绩分析的 SQL。因为后端代码本质上就是对以下 SQL 的封装。以“按课程统计各班平均分”为例SELECT c.class_name, c.course_name, AVG(s.score) AS avg_score, MAX(s.score) AS max_score, MIN(s.score) AS min_score, COUNT(s.id) AS total_count, SUM(CASE WHEN s.score 60 THEN 1 ELSE 0 END) / COUNT(s.id) * 100 AS pass_rate, SUM(CASE WHEN s.score 90 THEN 1 ELSE 0 END) / COUNT(s.id) * 100 AS excellent_rate FROM t_score s JOIN t_student stu ON s.student_id stu.id JOIN t_class c ON stu.class_id c.id JOIN t_course c ON s.course_id c.id WHERE c.id #{courseId} GROUP BY c.id, c.class_name ORDER BY avg_score DESC;这条 SQL 覆盖了 JOIN、GROUP BY、聚合函数、CASE WHEN 条件统计是成绩分析系统的核心能力所在。建议同学们把这条 SQL 彻底看懂因为答辩时老师几乎必然会问“平均分怎么算的、及格率怎么算的”。能说清楚这条 SQL 的逻辑就已经让不少答辩老师对项目产生好印象了。5. 后端核心代码实现从实体类到统计接口环境准备这里不再铺开讲直接给出关键配置。IDE 使用 IntelliJ IDEAJDK 使用 1.8 或 11Maven 3.6MySQL 5.7。Spring Boot 项目可以通过 Spring Initializr 创建也可以直接用 IDEA 新建。5.1 pom.xml 核心依赖dependencies !-- Spring Boot Web 依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis Plus 依赖 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency !-- MySQL 驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- Lombok 依赖 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- EasyExcel 用于成绩批量导入 -- dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.3.2/version /dependency !-- Hutool 工具类 -- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.18/version /dependency /dependencies注意如果 Spring Boot 版本是 2.7.xMyBatis Plus 用 3.5.x 没问题。如果 Spring Boot 是 3.x需要确认 MyBatis Plus 是否提供对应的 starter建议直接用mybatis-plus-spring-boot3-starter。这里不要混用否则会出现启动失败的问题。5.2 统一返回结果类package com.example.scoreanalysis.common; import lombok.Data; Data public class ResultT { private Integer code; // 状态码200 表示成功 private String message; // 提示信息 private T data; // 数据 public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }统一返回结果的目的是让前端在处理响应时逻辑一致不需要对成功和失败的情况分别写不同的解析代码。这也是实际项目中非常基础且必要的封装。5.3 实体类定义以 Student 实体为例package com.example.scoreanalysis.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; Data TableName(t_student) public class Student { TableId(type IdType.AUTO) private Long id; private String stuNo; // 学号 private String name; // 姓名 private Integer gender; // 性别 1-男 0-女 private Long classId; // 班级ID private String enrollYear; // 入学年份 }Score 实体package com.example.scoreanalysis.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.math.BigDecimal; Data TableName(t_score) public class Score { TableId(type IdType.AUTO) private Long id; private Long studentId; // 学生ID private Long courseId; // 课程ID private BigDecimal score; // 成绩 private String examDate; // 考试时间 }使用 Lombok 的 Data 注解自动生成 getter/setter代码量大幅减少。建议同学们理解 Lombok 的作用原理再使用因为答辩时老师可能会问“Data 注解是做什么的”能答出“它在编译阶段生成 getter/setter/toString 等方法”是基本要求。5.4 Mapper 层成绩统计核心 SQLpackage com.example.scoreanalysis.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.scoreanalysis.entity.Score; import com.example.scoreanalysis.vo.CourseAnalysisVO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; Mapper public interface ScoreMapper extends BaseMapperScore { /** * 按课程统计各班级成绩分布 */ Select(SELECT cl.class_name AS className, AVG(s.score) AS avgScore, MAX(s.score) AS maxScore, MIN(s.score) AS minScore, COUNT(s.id) AS totalCount, ROUND(SUM(CASE WHEN s.score 60 THEN 1 ELSE 0 END) / COUNT(s.id) * 100, 2) AS passRate, ROUND(SUM(CASE WHEN s.score 90 THEN 1 ELSE 0 END) / COUNT(s.id) * 100, 2) AS excellentRate FROM t_score s JOIN t_student stu ON s.student_id stu.id JOIN t_class cl ON stu.class_id cl.id WHERE s.course_id #{courseId} GROUP BY cl.id, cl.class_name ORDER BY avgScore DESC) ListCourseAnalysisVO selectCourseAnalysis(Param(courseId) Long courseId); /** * 按课程统计分数段分布 */ Select(SELECT SUM(CASE WHEN score 90 THEN 1 ELSE 0 END) AS excellent, SUM(CASE WHEN score 80 AND score 90 THEN 1 ELSE 0 END) AS good, SUM(CASE WHEN score 70 AND score 80 THEN 1 ELSE 0 END) AS medium, SUM(CASE WHEN score 60 AND score 70 THEN 1 ELSE 0 END) AS pass, SUM(CASE WHEN score 60 THEN 1 ELSE 0 END) AS fail FROM t_score WHERE course_id #{courseId}) ScoreDistributionVO selectScoreDistribution(Param(courseId) Long courseId); }使用注解式 SQL 是毕设项目比较推荐的写法代码量比 XML 文件少逻辑也更集中。如果想挑战自己在 XML 中编写更复杂的动态 SQL也完全可以但要注意配置mybatis-plus.mapper-locations来扫描 XML 文件。5.5 Service 层业务逻辑package com.example.scoreanalysis.service; import com.example.scoreanalysis.mapper.ScoreMapper; import com.example.scoreanalysis.vo.CourseAnalysisVO; import com.example.scoreanalysis.vo.ScoreDistributionVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; Service public class ScoreAnalysisService { Autowired private ScoreMapper scoreMapper; /** * 按课程分析各班级成绩 */ public ListCourseAnalysisVO analyzeCourseByClass(Long courseId) { return scoreMapper.selectCourseAnalysis(courseId); } /** * 按课程分析分数段分布 */ public ScoreDistributionVO analyzeScoreDistribution(Long courseId) { return scoreMapper.selectScoreDistribution(courseId); } }Service 层在这里做了一层薄薄的封装。有人会觉得这种写法“没用”但从项目结构上讲Service 层必须存在因为后续如果要加业务校验、权限控制、事务管理都需要在这一层完成。答辩时回答“为什么 Controller 不直接调用 Mapper”标准答案是“分层架构中Controller 负责接收参数和响应Service 负责业务逻辑组装和事务管理直接调用会导致职责混乱后续维护困难”。5.6 Controller 层接口实现package com.example.scoreanalysis.controller; import com.example.scoreanalysis.common.Result; import com.example.scoreanalysis.service.ScoreAnalysisService; import com.example.scoreanalysis.vo.CourseAnalysisVO; import com.example.scoreanalysis.vo.ScoreDistributionVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(/api/analysis) public class AnalysisController { Autowired private ScoreAnalysisService scoreAnalysisService; /** * 按课程统计各班级成绩 */ GetMapping(/course/{courseId}/class) public ResultListCourseAnalysisVO courseAnalysis(PathVariable Long courseId) { return Result.success(scoreAnalysisService.analyzeCourseByClass(courseId)); } /** * 按课程统计分数段分布 */ GetMapping(/course/{courseId}/distribution) public ResultScoreDistributionVO scoreDistribution(PathVariable Long courseId) { return Result.success(scoreAnalysisService.analyzeScoreDistribution(courseId)); } }V 层建议有课程名称、班级名称等冗余字段方便前端展示。下面给出 VO 的示例package com.example.scoreanalysis.vo; import lombok.Data; import java.math.BigDecimal; Data public class CourseAnalysisVO { private String className; // 班级名称 private BigDecimal avgScore; // 平均分 private BigDecimal maxScore; // 最高分 private BigDecimal minScore; // 最低分 private Integer totalCount; // 参考人数 private BigDecimal passRate; // 及格率 private BigDecimal excellentRate;// 优秀率 }5.7 Excel 成绩批量导入批量导入成绩是提升系统实用性的关键功能。使用 EasyExcel 的监听器模式在读取每一行数据时进行校验并保存到数据库。package com.example.scoreanalysis.listener; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.listener.ReadListener; import com.example.scoreanalysis.entity.Score; import com.example.scoreanalysis.mapper.ScoreMapper; import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; Slf4j public class ScoreImportListener implements ReadListenerScoreImportDTO { private final ScoreMapper scoreMapper; private final Long courseId; private final ListScore cache new ArrayList(); private int successCount 0; private int errorCount 0; public ScoreImportListener(ScoreMapper scoreMapper, Long courseId) { this.scoreMapper scoreMapper; this.courseId courseId; } Override public void invoke(ScoreImportDTO dto, AnalysisContext context) { try { if (dto.getStuNo() null || dto.getScore() null) { errorCount; return; } Score score new Score(); score.setCourseId(courseId); score.setScore(new BigDecimal(dto.getScore().trim())); score.setExamDate(dto.getExamDate()); // 通过学号查找学生ID // 实际项目中应调用 StudentMapper 查询这里简化为通过学号查学生ID再组装 cache.add(score); successCount; } catch (Exception e) { errorCount; log.error(导入数据异常: {}, dto, e); } } Override public void doAfterAllAnalysed(AnalysisContext context) { // 批量保存 cache.forEach(scoreMapper::insert); log.info(导入完成成功 {} 条失败 {} 条, successCount, errorCount); } public int getSuccessCount() { return successCount; } public int getErrorCount() { return errorCount; } }Controller 中接收上传文件的接口PostMapping(/upload) public ResultString uploadScore(MultipartFile file, RequestParam Long courseId) { if (file.isEmpty()) { return Result.error(上传文件不能为空); } try { ScoreImportListener listener new ScoreImportListener(scoreMapper, courseId); EasyExcel.read(file.getInputStream(), ScoreImportDTO.class, listener) .sheet() .headRowNumber(1) .doRead(); return Result.success(导入成功 listener.getSuccessCount() 条失败 listener.getErrorCount() 条); } catch (Exception e) { log.error(成绩导入失败, e); return Result.error(导入失败 e.getMessage()); } }6. 前端页面与 ECharts 可视化展示如果使用 Vue Element UI重点实现两个页面成绩管理页和统计图表页。6.1 成绩分析页 - Vue 代码示例template div classanalysis-container el-card div slotheader span课程成绩分析/span /div el-form inline el-form-item label选择课程 el-select v-modelcourseId placeholder请选择课程 changeloadData el-option v-foritem in courseList :keyitem.id :labelitem.courseName :valueitem.id /el-option /el-select /el-form-item /el-form div idchart stylewidth: 100%; height: 420px;/div /el-card /div /template script import * as echarts from echarts; import { getCourseList } from /api/course; import { getCourseAnalysis } from /api/analysis; export default { data() { return { courseId: null, courseList: [], chart: null, }; }, mounted() { this.loadCourses(); }, methods: { async loadCourses() { const res await getCourseList(); this.courseList res.data.data; }, async loadData() { if (!this.courseId) return; const res await getCourseAnalysis(this.courseId); const data res.data.data; this.renderChart(data); }, renderChart(data) { if (this.chart) { this.chart.dispose(); } this.chart echarts.init(document.getElementById(chart)); const classNames data.map(item item.className); const avgScores data.map(item item.avgScore); const passRates data.map(item item.passRate); this.chart.setOption({ title: { text: 各班级平均分对比, left: center }, tooltip: { trigger: axis }, legend: { data: [平均分, 及格率], bottom: 0 }, xAxis: { type: category, data: classNames }, yAxis: [ { type: value, name: 平均分, min: 0, max: 100 }, { type: value, name: 及格率, min: 0, max: 100 } ], series: [ { name: 平均分, type: bar, data: avgScores, itemStyle: { color: #409EFF } }, { name: 及格率, type: line, yAxisIndex: 1, data: passRates, itemStyle: { color: #67C23A } } ] }); } }, beforeDestroy() { if (this.chart) { this.chart.dispose(); } } }; /script这个页面的核心逻辑是用户选择课程前端请求后端接口拿到统计结果ECharts 渲染柱状图和折线图。柱状图展示平均分折线图展示及格率同一张图里用双 Y 轴展示两类指标效果直观。6.2 ECharts 图表的引入方式在 Vue 项目中引入 ECharts 时注意按需引入以减少打包体积但对于毕设项目直接全量引入import * as echarts from echarts更简单不必过度优化。6.3 前端跨域配置前后端分离开发时前端在 8080 端口后端在 8081 端口跨域是必然出现的问题。在后端添加一个跨域配置类即可解决package com.example.scoreanalysis.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }实际开发中上线部署时更推荐通过 Nginx 做反向代理来规避跨域但毕设阶段直接用后端配置 CORS 是最高效的。7. 环境搭建、启动与运行验证7.1 必要环境与版本建议工具版本建议JDK1.8 或 11Spring Boot 2.7.xMaven3.6 以上MySQL5.7 或 8.0IDEA2021 以上Node.js前端14 以上7.2 数据库初始化在 Navicat 或命令行中创建数据库score_analysis。执行第 4 节中的建表 SQL。插入一条测试管理员数据-- 密码为 admin123 的 BCrypt 加密值 INSERT INTO t_admin (username, password, real_name) VALUES (admin, $2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2, 系统管理员);测试数据建议这样生成插入 2 个班级、每个班级 20 名学生、3 门课程、每个学生每门课各一条成绩记录。这样总计约 120 条成绩数据足够图表展示效果。数据不足会导致图表展示不直观答辩效果差。7.3 后端启动步骤在 IDEA 中打开后端项目等待 Maven 依赖下载完成。修改 application.yml 中的数据库连接密码。server: port: 8081 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/score_analysis?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto运行 ScoreAnalysisApplication.java 的主方法。看到 Spring Boot 启动成功的日志后用浏览器访问http://localhost:8081/api/analysis/course/1/class返回 JSON 数据说明后端正常。7.4 前端启动步骤在 Vue 项目目录执行npm install npm run serve浏览器访问http://localhost:8080登录后进入成绩分析页面选择课程如果能看到柱状图和折线图说明整个链路跑通了。7.5 验证效果的标准验证项预期结果登录验证使用 admin/admin123 能成功登录错误密码登录失败学生管理能新增、修改、删除学生信息列表分页正常成绩录入单条成绩保存成功重复录入同一学生同一课程被拦截Excel 导入选择合法的 Excel 文件能批量导入成绩非法的数据有错误提示课程分析接口返回 JSON 中包含所有班级的统计结果前端图表页面能正常展示柱状图和折线图数据与后端一致8. 常见问题与排查方法问题现象可能原因排查方式解决方案Spring Boot 启动报错JDK 版本与 Spring Boot 版本不匹配启动日志检查异常信息Spring Boot 3.x 需 JDK 17JDK 8 使用 Spring Boot 2.7.x数据库连接失败MySQL 未启动或密码错误使用 Navicat 测试连接检查数据库服务和 application.yml 配置中文乱码数据库字符集不是 utf8mb4查看表结构和连接 URL库表和连接 URL 都改为 utf8mb4接口返回 404请求路径和 Controller 路径不一致检查浏览器请求地址和注解 Path统一使用 RESTful 风格路径外键约束导致删除学生失败该学生存在成绩记录先查看子表数据设计删除策略级联删除或逻辑删除Excel 导入后没有数据EasyExcel 表头映射字段名不匹配对比 DTO 字段和 Excel 列名使用 ExcelProperty 显式指定列名前端跨域报错后端未配置 CORS浏览器 F12 查看控制台错误添加 CorsConfig 配置类图表不显示ECharts 容器高度为 0 或数据为空检查 div 高度和接口返回给容器设置固定高度检查数据格式9. 毕业设计论文写作与答辩建议代码写完了项目跑通了毕设只完成了一半。论文和答辩是另一道重要关卡。这部分往往被低估但恰恰是拉开差距的地方。9.1 论文结构建议论文建议按以下结构组织第一章 绪论研究背景、国内外研究现状、研究意义与主要工作。这一章不要大段引用过时的资料重点写清楚为什么要做成绩分析系统。第二章 需求分析功能性需求、非功能性需求、用例图、可行性分析。第三章 系统设计总体架构设计、功能模块设计、数据库设计、界面设计。第四章 系统实现核心功能模块的展示和关键技术点分析。这里一定要放出核心代码和设计说明不能只贴代码不加文字说明。第五章 系统测试功能测试、性能测试、测试用例和结论。第六章 总结与展望系统存在的不足和下一步优化方向如成绩预测、教学改进建议等。9.2 答辩前必须想明白的问题论文可以慢慢写但答辩时这几个问题一定要准备到位为什么选 SpringBoot 而不是 SSM回答思路SpringBoot 简化了配置内置 Tomcat自动装配机制让开发更高效是当前主流技术栈。成绩及格率是怎么算出来的在哪个环节实现的回答思路SQL 中通过 COUNT 和 CASE WHEN 结合实现数据库层面完成统计后端封装返回。多个学生成绩相同取不取主键唯一索引是怎么解决的回答思路成绩表使用 student_id 和 course_id 联合唯一索引从数据库层面防止重复成绩。如果数据量达到百万级你的统计 SQL 还有效吗回答思路需要按班级、课程建立联合索引必要时使用汇总表或离线计算当前设计满足毕设数据量需求。系统的安全性做了哪些措施回答思路密码 BCrypt 加密、登录拦截器、参数校验、统一异常处理。9.3 评分占比较高的元素根据历年毕业设计评分经验以下几个维度影响较大系统完整性和可运行性占比较大。项目必须能跑起来UI 尽量美观。技术难度和创新点比如数据分析、可视化、批量导入等。论文逻辑性和规范性图表要清晰格式要符合学校要求。答辩现场演示流畅度提前把演示数据准备好不要在答辩现场现导数据。10. 总结与后续学习方向基于 SpringBoot 的学生成绩分析系统是一个很适合本科毕设的题目。它不只是一个管理后台还包含了成绩统计分析这个核心业务场景技术上覆盖了前后端分离、数据库设计、聚合查询、数据可视化等关键能力。如果按照这篇文章的思路完成你最终能得到的效果是一个界面清爽、数据可维护、统计分析有说服力的完整系统。更重要的是你在答辩时能讲清楚每一个设计决策背后的原因而不是背代码。后续如果想继续提升这个项目的含金量可以考虑以下几个方向引入 Redis 缓存热门分析结果提高重复查询的性能。加入学生成绩趋势变化的预测使用简单线性回归不算复杂但很加印象分。增加教师端角色实现不同角色不同权限的精细化控制。将统计结果导出为 Excel 或 PDF 报告也更贴近真实教务系统的使用场景。不过记住一点毕设项目的核心是“完成度”而不是“复杂度”。先把基础功能做得稳定、界面做得整洁、论文写得清楚再考虑扩展。毕竟答辩老师评判的是你是否掌握了系统的技术本质而不是你的功能列表有多长。
返回列表