引言
在教育信息化深度推进的背景下,学校对成绩数据的管理需求已从 “简单存储” 转向 “智能分析”—— 传统手工统计不仅耗时耗力,更无法快速挖掘成绩背后的教学问题(如班级薄弱学科、学生成绩波动趋势)。本次设计与实现的在线学生成绩综合统计分析系统,基于飞算 JavaAI 平台搭建,旨在解决成绩管理效率低、分析维度单一的痛点,为管理员、教师、学生三类角色提供差异化的数据服务,既保障成绩数据的安全管理,又能通过可视化看板输出教学决策依据,贴合现代化教学的实际业务场景。
技术栈
- 后端:采用Spring Boot 3.x框架,简化项目配置与依赖管理,快速实现RESTful接口;结合MyBatis-Plus增强工具,减少数据库操作代码冗余,支持复杂查询与批量处理。
- 数据库:使用MySQL 8.0,存储学生信息、成绩数据、课程信息等核心数据,其支持的复合索引与事务特性,能满足10万+条成绩记录的高效存储与安全读写。
- 前端可视化:集成ECharts图表库与Vue.js框架,实现班级成绩分布直方图、个人成绩趋势折线图等可视化看板,提升数据可读性;采用Element UI组件库构建页面,保证交互体验一致性。
- 开发工具:依赖IntelliJ IDEA 2024.3.2作为开发环境,搭配飞算JavaAI插件实现“自然语言转代码”,大幅缩短开发周期。
一.需求分析与规划
功能需求
系统需覆盖三类角色的核心业务场景,具体功能如下:
- 管理员:成绩Excel批量导入(支持5000条/次)、课程信息增删改查、用户角色权限配置、全校成绩统计报表导出(PDF/Excel格式)。
- 教师:单条成绩录入与修改、所带班级成绩分布分析(按分数段统计)、学生成绩波动预警(分数下降超20分自动提示)、学科平均分排名查询。
- 学生:个人成绩明细查询、单科年级排名查看、学期成绩趋势图展示、薄弱学科(低于班级平均分10分)标注。
核心模块
按“高内聚、低耦合”原则,将系统划分为三大核心模块,模块间通过接口交互,便于后续扩展:
- 用户权限模块:管理用户注册、登录、角色分配,基于RBAC模型控制数据访问权限。
- 成绩管理模块:负责成绩的录入、导入、修改与存储,核心是保证成绩数据的准确性与安全性。
- 统计分析模块:实现成绩的多维度分析(班级、个人、分数段),输出可视化结果与统计报表。
技术选型
综合考虑开发效率、系统性能与教育场景适配性,技术栈选择逻辑如下:
- 后端框架:Spring Boot 3.x(轻量化、社区支持丰富)+ MyBatis-Plus(简化SQL操作,支持批量插入)。
- 数据库:MySQL 8.0(开源免费、支持大规模数据存储,兼容教育行业预算需求)。
- 可视化:ECharts(开源图表库,支持多类型图表,适配成绩分析场景)。
- 开发工具:飞算JavaAI(通过自然语言生成代码,降低重复编码工作量)。
从该图可清晰看到系统四大模块的功能拆解与技术支撑关系,为后续开发明确了执行路径。
二.环境准备
1. 下载IntelliJ IDEA
选择IntelliJ IDEA作为开发环境,其对Java项目的兼容性与插件生态更适配飞算JavaAI工具:
- 进入IntelliJ IDEA官网,根据操作系统选择“Windows 64-bit”版本(本文以Windows为例)。
- 点击“Download”下载安装包,等待约5-10分钟(视网络速度而定)。
2. 安装IntelliJ IDEA
按照安装向导逐步操作,重点配置如下:
- 选择安装路径(建议非C盘,如D:\Program Files\JetBrains\IntelliJ IDEA 2024.3.2)。
- 勾选“Create Desktop Shortcut”(创建桌面快捷方式)与“Add launchers dir to PATH”(添加环境变量)。
- 点击“Install”开始安装,完成后勾选“Run IntelliJ IDEA”启动软件。
启动后界面如下,选择“New Project”进入项目创建流程:
3. 安装飞算JavaAI插件
飞算JavaAI插件是实现“自然语言转代码”的核心工具,安装步骤如下:
- 打开IDEA,点击顶部菜单栏“File → Settings → Plugins”,进入插件市场。
- 在搜索框输入“飞算”,找到“Feisuan JavaAI”插件,点击“Install”下载(需等待1-2分钟)。
- 下载完成后,点击“Restart IDE”重启IDEA,插件生效。重启后左侧工具栏会出现“飞算JavaAI”图标,即为安装成功:
4. 登录飞算JavaAI
需登录飞算账号绑定项目,才能使用代码生成功能:
- 点击左侧“飞算JavaAI”图标,打开插件面板,点击“登录”按钮。
- 在弹出的登录窗口中,输入飞算开发者中心账号(无账号需先到飞算官网注册),点击“登录”。
- 登录成功后,插件面板会显示“已绑定项目”,选择本次开发的项目(项目ID:EDU-2025-09),完成环境对接:
三.模块设计与编码
1. 飞算JavaAI生成基础模块
通过飞算插件输入自然语言需求,自动生成项目骨架与核心代码,步骤如下:
- 在飞算插件面板的“需求编辑器”中,输入以下指令:
“生成在线学生成绩综合统计分析系统基础模块,包含:学生实体(学号、姓名、班级、入学年份)、成绩实体(成绩ID、关联学号、课程编码、分数、考试时间、考试类型)、课程实体(课程编码、课程名称、学分);实现用户登录/注册、成绩录入/导入、班级平均分统计、个人排名查询功能;技术栈:Spring Boot 3.x + MyBatis-Plus + MySQL 8.0。” - 点击“提交需求”,按照指引一步步进行;
- 最后生成的项目结构如下,核心包与类已自动创建:
2. 核心代码展示
entity包下实体类示例
Student.java
(学生实体)
package com.feisuan.edu.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
/**
* <p>
* 学生实体类:映射student表
* </p>
* @author feisuan-javaai
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("student") // 关联数据库表名
public class Student {
/**
* 学号:主键,自增
*/
@TableId(type = IdType.AUTO)
private Long studentNo;
/**
* 学生姓名:非空
*/
private String studentName;
/**
* 班级ID:格式如2024-01(2024级1班)
*/
private String classId;
/**
* 入学年份:如2024
*/
private Integer enrollmentYear;
/**
* 创建时间:自动填充
*/
private LocalDate createTime;
}
Score.java
(成绩实体)
package com.feisuan.edu.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* <p>
* 成绩实体类:映射score表
* </p>
* @author feisuan-javaai
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("score")
public class Score {
/**
* 成绩ID:主键,自增
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 关联学号:外键,关联student表的studentNo
*/
private Long studentNo;
/**
* 课程编码:关联course表的courseCode
*/
private String courseCode;
/**
* 分数:保留2位小数,范围0-100
*/
private BigDecimal score;
/**
* 考试时间:如2024-06-20
*/
private LocalDate examDate;
/**
* 考试类型:期中/期末/月考
*/
private String examType;
}
dto包下数据传输对象示例
ScoreAddDTO.java
(成绩录入请求DTO)
package com.feisuan.edu.dto;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* <p>
* 成绩录入请求DTO:接收前端传入的成绩数据,含参数校验
* </p>
* @author feisuan-javaai
*/
@Data
public class ScoreAddDTO {
/**
* 关联学号:必填
*/
@NotNull(message = "学号不能为空")
private Long studentNo;
/**
* 课程编码:必填
*/
@NotBlank(message = "课程编码不能为空")
private String courseCode;
/**
* 分数:必填,0-100分
*/
@NotNull(message = "分数不能为空")
@DecimalMin(value = "0.00", message = "分数不能低于0分")
@DecimalMax(value = "100.00", message = "分数不能高于100分")
private BigDecimal score;
/**
* 考试时间:必填
*/
@NotNull(message = "考试时间不能为空")
private LocalDate examDate;
/**
* 考试类型:必填,只能是期中/期末/月考
*/
@NotBlank(message = "考试类型不能为空")
private String examType;
}
StudentRankQueryDTO.java
(个人排名查询DTO)
package com.feisuan.edu.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* <p>
* 个人排名查询DTO:接收前端查询参数
* </p>
* @author feisuan-javaai
*/
@Data
public class StudentRankQueryDTO {
/**
* 学号:必填
*/
@NotNull(message = "学号不能为空")
private Long studentNo;
/**
* 课程编码:必填
*/
@NotBlank(message = "课程编码不能为空")
private String courseCode;
/**
* 学期:必填,格式如2024-2025-1
*/
@NotBlank(message = "学期不能为空")
private String term;
}
vo包下视图对象示例
StudentRankVO.java
(个人排名返回VO)
package com.feisuan.edu.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* <p>
* 个人排名返回VO:封装前端需要的排名数据,隐藏数据库字段细节
* </p>
* @author feisuan-javaai
*/
@Data
public class StudentRankVO {
/**
* 学号
*/
private Long studentNo;
/**
* 学生姓名
*/
private String studentName;
/**
* 课程名称
*/
private String courseName;
/**
* 分数
*/
private BigDecimal score;
/**
* 年级排名
*/
private Integer rank;
/**
* 年级总人数
*/
private Integer gradeTotal;
/**
* 排名百分比:如30.50%
*/
private String rankPercentage;
}
mapper包下数据访问接口示例
ScoreMapper.java
package com.feisuan.edu.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.feisuan.edu.entity.Score;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* <p>
* 成绩Mapper接口:继承MyBatis-Plus的BaseMapper,自带CRUD方法
* </p>
* @author feisuan-javaai
*/
@Mapper
public interface ScoreMapper extends BaseMapper<Score> {
/**
* 自定义查询:按班级、课程、学期查询成绩列表
*/
@Select("SELECT * FROM score s JOIN student st ON s.student_no = st.student_no " +
"WHERE st.class_id = #{classId} AND s.course_code = #{courseCode} AND s.term = #{term}")
List<Score> selectByClassAndCourse(String classId, String courseCode, String term);
/**
* 自定义查询:统计年级内某课程分数高于目标分数的人数
*/
@Select("SELECT COUNT(*) FROM score WHERE course_code = #{courseCode} AND term = #{term} AND score > #{targetScore}")
Long countHigherScore(String courseCode, String term, BigDecimal targetScore);
}
service包下接口及实现类示例
ScoreService.java
(成绩服务接口)
package com.feisuan.edu.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.feisuan.edu.dto.ScoreAddDTO;
import com.feisuan.edu.dto.StudentRankQueryDTO;
import com.feisuan.edu.entity.Score;
import com.feisuan.edu.vo.ScoreDistributionVO;
import com.feisuan.edu.vo.StudentRankVO;
/**
* <p>
* 成绩服务接口:定义成绩相关业务逻辑
* </p>
* @author feisuan-javaai
*/
public interface ScoreService extends IService<Score> {
/**
* 录入成绩
* @param scoreAddDTO 成绩录入参数
* @return 录入成功的成绩实体
*/
Score addScore(ScoreAddDTO scoreAddDTO);
/**
* 查询个人年级排名
* @param queryDTO 排名查询参数
* @return 个人排名信息VO
*/
StudentRankVO getStudentGradeRank(StudentRankQueryDTO queryDTO);
/**
* 统计班级成绩分布(按分数段)
* @param classId 班级ID
* @param courseCode 课程编码
* @param term 学期
* @return 成绩分布VO
*/
ScoreDistributionVO getClassScoreDistribution(String classId, String courseCode, String term);
}
ScoreServiceImpl.java
(成绩服务实现类)
package com.feisuan.edu.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feisuan.edu.dto.ScoreAddDTO;
import com.feisuan.edu.dto.StudentRankQueryDTO;
import com.feisuan.edu.entity.Course;
import com.feisuan.edu.entity.Score;
import com.feisuan.edu.entity.Student;
import com.feisuan.edu.mapper.CourseMapper;
import com.feisuan.edu.mapper.ScoreMapper;
import com.feisuan.edu.mapper.StudentMapper;
import com.feisuan.edu.service.ScoreService;
import com.feisuan.edu.vo.ScoreDistributionVO;
import com.feisuan.edu.vo.StudentRankVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* <p>
* 成绩服务实现类:实现成绩相关业务逻辑
* </p>
* @author feisuan-javaai
*/
@Slf4j
@Service
@RequiredArgsConstructor // 构造器注入,替代@Autowired
public class ScoreServiceImpl extends ServiceImpl<ScoreMapper, Score> implements ScoreService {
private final ScoreMapper scoreMapper;
private final StudentMapper studentMapper;
private final CourseMapper courseMapper;
@Override
@Transactional // 事务控制:确保成绩录入失败时回滚
public Score addScore(ScoreAddDTO scoreAddDTO) {
log.info("开始录入成绩:学号={}, 课程编码={}", scoreAddDTO.getStudentNo(), scoreAddDTO.getCourseCode());
// 1. 校验学生与课程是否存在
Student student = studentMapper.selectById(scoreAddDTO.getStudentNo());
Course course = courseMapper.selectById(scoreAddDTO.getCourseCode());
if (student == null) {
throw new RuntimeException("学号不存在:" + scoreAddDTO.getStudentNo());
}
if (course == null) {
throw new RuntimeException("课程编码不存在:" + scoreAddDTO.getCourseCode());
}
// 2. 转换DTO为实体
Score score = new Score();
BeanUtils.copyProperties(scoreAddDTO, score);
// 补充学期(从考试时间推导,如2024-06属于2023-2024-2学期)
String term = getTermByExamDate(scoreAddDTO.getExamDate());
score.setTerm(term);
// 3. 保存成绩
save(score);
log.info("成绩录入成功:成绩ID={}", score.getId());
return score;
}
@Override
public StudentRankVO getStudentGradeRank(StudentRankQueryDTO queryDTO) {
log.info("查询个人排名:学号={}, 课程编码={}, 学期={}",
queryDTO.getStudentNo(), queryDTO.getCourseCode(), queryDTO.getTerm());
// 1. 查询学生、课程与成绩信息
Student student = studentMapper.selectById(queryDTO.getStudentNo());
Course course = courseMapper.selectById(queryDTO.getCourseCode());
Score score = lambdaQuery()
.eq(Score::getStudentNo, queryDTO.getStudentNo())
.eq(Score::getCourseCode, queryDTO.getCourseCode())
.eq(Score::getTerm, queryDTO.getTerm())
.one();
if (score == null) {
throw new RuntimeException("未查询到该学生的成绩记录");
}
// 2. 统计排名(高于当前分数的人数+1)
Long higherCount = scoreMapper.countHigherScore(
queryDTO.getCourseCode(), queryDTO.getTerm(), score.getScore()
);
Integer rank = higherCount.intValue() + 1;
// 3. 统计年级总人数
Integer gradeTotal = lambdaQuery()
.eq(Score::getCourseCode, queryDTO.getCourseCode())
.eq(Score::getTerm, queryDTO.getTerm())
.list()
.size();
// 4. 封装VO返回
StudentRankVO rankVO = new StudentRankVO();
rankVO.setStudentNo(student.getStudentNo());
rankVO.setStudentName(student.getStudentName());
rankVO.setCourseName(course.getCourseName());
rankVO.setScore(score.getScore());
rankVO.setRank(rank);
rankVO.setGradeTotal(gradeTotal);
// 计算排名百分比(保留2位小数)
String rankPercentage = String.format("%.2f%%", (double) rank / gradeTotal * 100);
rankVO.setRankPercentage(rankPercentage);
return rankVO;
}
@Override
public ScoreDistributionVO getClassScoreDistribution(String classId, String courseCode, String term) {
// 1. 查询班级成绩列表
List<Score> scoreList = scoreMapper.selectByClassAndCourse(classId, courseCode, term);
// 2. 按分数段分组统计
Map<String, Long> distributionMap = scoreList.stream()
.collect(Collectors.groupingBy(
s -> {
double scoreValue = s.getScore().doubleValue();
if (scoreValue < 60) return "0-59";
else if (scoreValue < 70) return "60-69";
else if (scoreValue < 80) return "70-79";
else if (scoreValue < 90) return "80-89";
else return "90-100";
},
Collectors.counting()
));
// 3. 封装VO
ScoreDistributionVO distributionVO = new ScoreDistributionVO();
distributionVO.setClassId(classId);
distributionVO.setCourseCode(courseCode);
distributionVO.setTerm(term);
distributionVO.setSegment0_59(distributionMap.getOrDefault("0-59", 0L));
distributionVO.setSegment60_69(distributionMap.getOrDefault("60-69", 0L));
distributionVO.setSegment70_79(distributionMap.getOrDefault("70-79", 0L));
distributionVO.setSegment80_89(distributionMap.getOrDefault("80-89", 0L));
distributionVO.setSegment90_100(distributionMap.getOrDefault("90-100", 0L));
return distributionVO;
}
/**
* 辅助方法:根据考试时间推导学期
*/
private String getTermByExamDate(java.time.LocalDate examDate) {
int year = examDate.getYear();
int month = examDate.getMonthValue();
if (month >= 9 && month <= 12) {
return year + "-" + (year + 1) + "-1"; // 9-12月属于下学年第一学期
} else {
return (year - 1) + "-" + year + "-2"; // 1-8月属于本学年第二学期
}
}
}
3.网页展示
登录进去之后,我们可以看到很详细的页面:
四.自我感想
在开发在线学生成绩综合统计分析系统的过程中,我对“教育信息化”的理解从“技术落地”转向了“业务适配”。比如最初仅关注成绩的增删改查,后来发现教师更需要“成绩波动预警”来定位待辅导学生,学生需要“薄弱学科标注”来明确学习方向——这让我意识到,技术开发必须围绕用户实际需求展开。
技术层面,飞算JavaAI平台的“自然语言转代码”功能大幅降低了重复编码工作量,原本需要2天编写的Entity、Mapper层代码,现在20分钟即可生成,让我有更多精力投入到业务逻辑优化(如排名算法、缓存策略)。同时,通过解决“数据重复导入”“可视化加载慢”等问题,我对数据库索引优化、Redis缓存使用的理解也更加深入,不再是单纯“会用”,而是“能用好”。
这次开发也让我明白,一个合格的系统不仅要“功能能用”,更要“体验好用”——比如为成绩录入添加参数校验,避免教师输入错误分数;为统计报表提供多格式导出,方便管理员汇报使用。这些细节虽小,却直接影响用户对系统的认可程度。
总结
本次在线学生成绩综合统计分析系统的设计与实现,完整覆盖了“需求分析→环境搭建→模块开发→问题优化”的全流程,成功构建了支持多角色、多维度分析的成绩管理系统。系统的核心价值在于:通过飞算JavaAI提升开发效率,通过可视化分析挖掘成绩数据价值,通过权限控制保障数据安全,切实解决了传统成绩管理的痛点。
后续,我计划进一步扩展系统功能:一是添加AI成绩预测模块,基于历史数据预测学生期末成绩;二是对接区域教育大数据平台,实现跨学校成绩对比分析。