基于 AI 辅助的学生成绩管理系统开发实战
学生成绩管理系统开发实战,涵盖需求分析、AI 辅助编码、数据库设计及优化调试。系统基于 Spring Boot 3.0 和 MySQL 8.0,包含用户权限、课程管理、成绩管理等核心模块。通过 AI 工具生成实体类、Mapper、Service 及 Controller 层代码,解决精度丢失、性能瓶颈及修改痕迹等技术问题。最终实现多角色权限控制、成绩自动计算与审核、数据报表导出等功能,提供高效低成本的开发路径。

学生成绩管理系统开发实战,涵盖需求分析、AI 辅助编码、数据库设计及优化调试。系统基于 Spring Boot 3.0 和 MySQL 8.0,包含用户权限、课程管理、成绩管理等核心模块。通过 AI 工具生成实体类、Mapper、Service 及 Controller 层代码,解决精度丢失、性能瓶颈及修改痕迹等技术问题。最终实现多角色权限控制、成绩自动计算与审核、数据报表导出等功能,提供高效低成本的开发路径。

在高校教学管理工作中,学生成绩是教学质量评估、学生学业监测的核心数据,但传统 Excel 表格管理或单机版系统常面临数据同步滞后、权限管控缺失、成绩分析低效等问题。为解决这一痛点,以'学生成绩管理系统'为开发目标,借助 AI 编程工具实现全流程开发,本文将从需求分析到系统落地的关键环节进行梳理,分享 AI 辅助开发的高效实践经验。

系统需满足三类核心用户(教学管理员、教师、学生)的差异化需求,功能拆解如下:
基于需求拆解,系统划分为 5 个联动模块,模块职责与关联关系如下表所示:
| 模块名称 | 核心功能 | 关联模块 |
|---|---|---|
| 用户权限模块 | 账号注册登录、角色权限分配(管理员/教师/学生)、密码重置、账号状态管理 | 所有模块(权限校验) |
| 课程管理模块 | 课程信息维护(新增/编辑/删除课程)、班级 - 课程 - 教师关联、课程学期管理 | 用户权限模块(教师课程权限)、成绩管理模块(课程成绩归属) |
| 成绩管理模块 | 成绩录入与计算、成绩审核与锁定、成绩修改申请与审批、异常成绩标注 | 课程管理模块(课程数据)、用户权限模块(成绩操作权限) |
| 数据统计模块 | 成绩分析(平均分、及格率、分数段)、成绩趋势图表生成、数据报表导出 | 成绩管理模块(成绩数据源)、课程管理模块(课程筛选条件) |
| 消息通知模块 | 成绩录入提醒、成绩审核结果推送、学生成绩异议反馈、系统公告发布 | 成绩管理模块(成绩状态变更)、用户权限模块(接收人匹配) |
打开 IntelliJ IDEA 并启动 AI 编程插件,在'需求输入'界面输入提示词:'开发学生成绩管理系统后端代码,基于 Spring Boot 3.0 和 MySQL 8.0,需包含用户权限(管理员/教师/学生三类角色)、课程管理(课程 CRUD 与师生关联)、成绩管理(成绩录入/计算/审核)三大核心模块,生成实体类、Mapper 接口、Service 层、Controller 层代码,符合 RESTful API 规范,需支持成绩自动计算(按比例汇总)与权限细粒度控制(如教师仅能操作所授课程成绩)。'
提交提示词后,AI 工具约 8 秒生成需求拆解报告,核心内容包括:

插件自动跳转至'接口设计'界面,围绕三大核心模块生成接口说明,明确各接口功能定位:
/api/user/login(登录接口)、/api/user/role/assign(权限分配接口);/api/course/add(新增课程)、/api/course/teacher/bind(课程 - 教师绑定接口);/api/score/batch/save(批量录入成绩)、/api/score/calculate(自动计算总成绩接口)、/api/score/audit(成绩审核接口)。
接口设计完成后,插件自动推导数据库表结构(支持手动编辑),核心表结构如下:
CREATE TABLE user_info (
user_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户唯一标识',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
password VARCHAR(255) NOT NULL COMMENT '密码(加密存储)',
user_role ENUM('admin', 'teacher', 'student') NOT NULL COMMENT '用户角色:admin-管理员,teacher-教师,student-学生',
status TINYINT DEFAULT 1 COMMENT '用户状态:1-正常,0-禁用',
create_by VARCHAR(50) COMMENT '创建人',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(50) COMMENT '修改人',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间'
) COMMENT='用户信息表';
CREATE TABLE course_info (
course_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '课程唯一标识',
course_name VARCHAR(100) NOT NULL COMMENT '课程名称',
course_code VARCHAR(50) NOT NULL UNIQUE COMMENT ,
teacher_id COMMENT ,
description TEXT COMMENT ,
credits COMMENT ,
status TINYINT COMMENT ,
create_by () COMMENT ,
create_time DATETIME COMMENT ,
update_by () COMMENT ,
update_time DATETIME COMMENT
) COMMENT;
student_course (
relation_id AUTO_INCREMENT COMMENT ,
student_id COMMENT ,
course_id COMMENT ,
select_time DATETIME COMMENT ,
create_by () COMMENT ,
create_time DATETIME COMMENT
) COMMENT;
score_record (
score_id AUTO_INCREMENT COMMENT ,
student_id COMMENT ,
course_id COMMENT ,
score_type ENUM(, , , ) COMMENT ,
score_value (,) COMMENT ,
score_weight (,) COMMENT ,
is_reviewed TINYINT COMMENT ,
review_time DATETIME COMMENT ,
reviewer () COMMENT ,
create_by () COMMENT ,
create_time DATETIME COMMENT ,
update_by () COMMENT ,
update_time DATETIME COMMENT
) COMMENT;

进入'处理逻辑'界面,插件针对核心接口生成详细业务流程,以'成绩管理'模块为例,关键接口逻辑如下:
接口名称:录入成绩 入参对象属性:studentId(学生 ID,必填)、courseId(课程 ID,必填)、score(成绩分数,必填)、type(成绩类型,必填) 处理逻辑:检查学生是否选课且课程是否存在 返回 RestResult 结果:{"code":"000001","msg":"学生未选该课程或课程不存在","data":…} 处理逻辑:将成绩信息保存到数据库 返回 RestResult 结果:{"code":"000000","msg":"调用成功","data":…}
接口名称:计算并汇总成绩 入参对象属性:studentId(学生 ID,必填)、courseId(课程 ID,必填) 处理逻辑:获取该学生在指定课程下的所有成绩项 返回 RestResult 结果:{"code":"000001","msg":"无相关成绩记录","data":…} 处理逻辑:根据预设比例规则进行加权计算 返回 RestResult 结果:{"code":"000000","msg":"调用成功","data":…}
接口名称:审核成绩 入参对象属性:studentId(学生 ID,必填)、courseId(课程 ID,必填)、status(审核状态,必填) 处理逻辑:确认成绩是否存在且处于待审核状态 返回 RestResult 结果:{"code":"000001","msg":"成绩不存在或已审核","data":…} 处理逻辑:更新成绩的审核状态为通过或拒绝 返回 RestResult 结果:{"code":"000000","msg":"调用成功","data":…}
接口名称:修改成绩 入参对象属性:studentId(学生 ID,必填)、courseId(课程 ID,必填)、newScore(新成绩分数,必填) 处理逻辑:验证成绩是否允许修改(如:是否已审核) 返回 RestResult 结果:{"code":"000001","msg":"成绩不可修改","data":…} 处理逻辑:更新对应的成绩数据 返回 RestResult 结果:{"code":"000000","msg":"调用成功","data":…}
接口名称:查看成绩历史记录 入参对象属性:studentId(学生 ID,必填)、courseId(课程 ID,必填) 处理逻辑:查询该学生在指定课程下所有的成绩变更记录 返回 RestResult 结果:{"code":"000000","msg":"调用成功","data":…}

确认处理逻辑后,点击'生成源码',AI 工具约 3 分钟生成完整工程代码(遵循 MVC 架构),核心代码结构如下:


生成的代码无语法错误,导入 IDE 后可直接启动测试。
生成的 ScoreService 中,总成绩计算使用普通浮点数运算,存在精度丢失问题(如 30% 平时成绩 +70% 期末成绩,结果出现多位小数)。 解决方案:通过 AI 智能会话查询'Java BigDecimal 精确计算成绩比例',获取优化代码,在 ScoreServiceImpl 中使用 BigDecimal 类进行比例计算,示例如下:
BigDecimal usualRatio = new BigDecimal(scoreRule.getUsualRatio()).divide(new BigDecimal(100));
BigDecimal midtermRatio = new BigDecimal(scoreRule.getMidtermRatio()).divide(new BigDecimal(100));
BigDecimal finalRatio = new BigDecimal(scoreRule.getFinalRatio()).divide(new BigDecimal(100));
BigDecimal totalScore = usualScore.multiply(usualRatio).add(midtermScore.multiply(midtermRatio)).add(finalScore.multiply(finalRatio)).setScale(2, BigDecimal.ROUND_HALF_UP);
学生集中查询成绩时,频繁访问 MySQL 数据库,导致接口响应延迟(平均响应时间超 1.5 秒)。
解决方案:借助 AI 智能会话获取'Spring Boot Redis 缓存成绩数据'方案,在 ScoreService 的成绩查询方法上添加 @Cacheable 注解,缓存课程成绩统计结果与学生个人成绩,示例:
@Cacheable(value = "score:student", key = "#studentId + ':' + #courseId")
public ScoreInfo getStudentCourseScore(Long studentId, Long courseId) {
return scoreMapper.selectByStudentAndCourse(studentId, courseId);
}
优化后接口响应时间降至 0.3 秒以内。
教师提交成绩后,若管理员驳回需修改,但生成的代码未记录成绩修改历史,无法追溯变更内容。
解决方案:手动新增 score_history(成绩修改历史表),并通过 AI 生成 ScoreHistory 实体与对应的 Mapper、Service 代码,在 ScoreServiceImpl 的 update 方法中添加历史记录逻辑,每次修改成绩时自动保存旧数据至 score_history 表,实现'每改必留痕'。
系统开发完成后,通过本地部署与多角色实测,实现了'需求全覆盖、操作零门槛、数据高可靠'的开发目标,核心功能代码成果如下:
package com.example.service.impl;
import com.example.demo1.dto.request.*;
import com.example.dto.request.BoundTeacherToCourseRequest;
import com.example.dto.request.CreateCourseRequest;
import com.example.dto.request.DeleteCourseRequest;
import com.example.dto.request.SelectCourseRequest;
import com.example.dto.request.UpdateCourseRequest;
import com.example.dto.response.RestResult;
import com.example.entity.CourseInfo;
import com.example.entity.StudentCourse;
import com.example.entity.UserInfo;
import com.example.repository.CourseRepository;
import com.example.repository.StudentCourseRepository;
import com.example.repository.UserRepository;
import com.example.service.CourseService;
import java.time.LocalDateTime;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 课程服务实现类
*/
@Slf4j
@Service
@Transactional
public class CourseServiceImpl implements CourseService {
@Autowired
private CourseRepository courseRepository;
@Autowired
UserRepository userRepository;
StudentCourseRepository studentCourseRepository;
RestResult<Object> {
(courseRepository.findByCourseName(request.getCourseName()) != ) {
RestResult.error(, );
}
();
BeanUtils.copyProperties(request, courseInfo);
courseInfo.setCreateTime(LocalDateTime.now());
courseInfo.setUpdateTime(LocalDateTime.now());
courseInfo.setStatus(() );
{
courseRepository.save(courseInfo);
RestResult.success(courseInfo);
} (Exception e) {
log.error(, e);
RestResult.error(, );
}
}
RestResult<Object> {
(pageNo == || pageNo <= ) { pageNo = ; }
(pageSize == || pageSize <= ) { pageSize = ; }
PageRequest.of(pageNo - , pageSize);
List<CourseInfo> courses = courseRepository.findAll(pageable).getContent();
Page<CourseInfo> coursePage = <>(courses, pageable, courseRepository.count());
RestResult.success(coursePage);
}
RestResult<Object> {
courseRepository.findById(request.getCourseId()).orElse();
(existingCourse == ) {
RestResult.error(, );
}
(request.getCourseName() != && !request.getCourseName().equals(existingCourse.getCourseName())) {
(courseRepository.findByCourseName(request.getCourseName()) != ) {
RestResult.error(, );
}
}
BeanUtils.copyProperties(request, existingCourse, , );
existingCourse.setUpdateTime(LocalDateTime.now());
{
courseRepository.save(existingCourse);
RestResult.success(existingCourse);
} (Exception e) {
log.error(, e);
RestResult.error(, );
}
}
RestResult<Object> {
courseRepository.findById(request.getCourseId()).orElse();
(existingCourse == ) {
RestResult.error(, );
}
{
studentCourseRepository.deleteByCourseId(request.getCourseId());
courseRepository.deleteById(request.getCourseId());
RestResult.success();
} (Exception e) {
log.error(, e);
RestResult.error(, );
}
}
RestResult<Object> {
userRepository.findById(request.getTeacherId()).orElse();
courseRepository.findById(request.getCourseId()).orElse();
(teacher == || course == ) {
RestResult.error(, );
}
course.setTeacherId(request.getTeacherId());
course.setUpdateTime(LocalDateTime.now());
{
courseRepository.save(course);
RestResult.success();
} (Exception e) {
log.error(, e);
RestResult.error(, );
}
}
RestResult<Object> {
userRepository.findById(request.getStudentId()).orElse();
courseRepository.findById(request.getCourseId()).orElse();
(student == || course == ) {
RestResult.error(, );
}
studentCourseRepository.findByStudentIdAndCourseId(request.getStudentId(), request.getCourseId());
(existingRecord != ) {
RestResult.error(, );
}
();
record.setStudentId(request.getStudentId());
record.setCourseId(request.getCourseId());
record.setCreateTime(LocalDateTime.now());
{
studentCourseRepository.save(record);
RestResult.success();
} (Exception e) {
log.error(, e);
RestResult.error(, );
}
}
}
package com.example.service.impl;
import com.example.dto.request.ScoreModifyRequest;
import com.example.dto.request.ScoreRecordRequest;
import com.example.dto.request.ScoreReviewRequest;
import com.example.entity.CourseInfo;
import com.example.entity.ScoreRecord;
import com.example.entity.StudentCourse;
import com.example.repository.CourseRepository;
import com.example.repository.ScoreRepository;
import com.example.repository.StudentCourseRepository;
import com.example.service.ScoreService;
import com.example.util.RestResult;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 成绩服务实现类
*/
@Slf4j
@Service
@Transactional
public class ScoreServiceImpl implements ScoreService {
@Autowired
private ScoreRepository scoreRepository;
@Autowired
private CourseRepository courseRepository;
@Autowired
private StudentCourseRepository studentCourseRepository;
/**
* 录入成绩
*
* @param request 入参对象
* @return RestResult 结果
*/
@Override
public RestResult recordScore(ScoreRecordRequest request) {
(!checkStudentSelectCourse(request.getStudentId(), request.getCourseId())) {
RestResult.error();
}
{
();
scoreRecord.setStudentId(request.getStudentId());
scoreRecord.setCourseId(request.getCourseId());
scoreRecord.setScoreType(request.getType());
scoreRecord.setScoreValue(BigDecimal.valueOf(request.getScore()));
scoreRecord.setIsReviewed();
scoreRecord.setCreateTime(LocalDateTime.now());
scoreRecord.setUpdateTime(LocalDateTime.now());
scoreRepository.save(scoreRecord);
RestResult.success();
} (Exception e) {
log.error(, e);
RestResult.error();
}
}
RestResult {
List<ScoreRecord> records = scoreRepository.findByStudentIdAndCourseIdOrderByCreateTimeAsc(studentId, courseId);
(records.isEmpty()) {
RestResult.error();
}
;
(ScoreRecord record : records) {
(record.getIsReviewed() == ) {
totalScore += record.getScoreValue().doubleValue() * record.getScoreWeight().doubleValue();
}
}
RestResult.success(totalScore);
}
RestResult {
scoreRepository.findLatestByStudentIdAndCourseId(request.getStudentId(), request.getCourseId());
(latestRecord == || latestRecord.getIsReviewed() == ) {
RestResult.error();
}
{
latestRecord.setIsReviewed(request.getStatus() ? : );
latestRecord.setReviewer();
latestRecord.setReviewTime(LocalDateTime.now());
latestRecord.setUpdateTime(LocalDateTime.now());
scoreRepository.save(latestRecord);
RestResult.success();
} (Exception e) {
log.error(, e);
RestResult.error();
}
}
RestResult {
scoreRepository.findLatestByStudentIdAndCourseId(request.getStudentId(), request.getCourseId());
(latestRecord == ) {
RestResult.error();
}
(latestRecord.getIsReviewed() == ) {
RestResult.error();
}
{
latestRecord.setScoreValue(BigDecimal.valueOf(request.getNewScore()));
latestRecord.setUpdateTime(LocalDateTime.now());
scoreRepository.save(latestRecord);
RestResult.success();
} (Exception e) {
log.error(, e);
RestResult.error();
}
}
RestResult {
List<ScoreRecord> records = scoreRepository.findByStudentIdAndCourseIdOrderByCreateTimeAsc(studentId, courseId);
RestResult.success(records);
}
{
courseRepository.findById(courseId).orElse();
(course == || course.getStatus() != ) {
;
}
studentCourseRepository.findOneByStudentIdAndCourseId(studentId, courseId);
studentCourse != ;
}
}
package com.example.service.impl;
import com.example.demo1.entity.UserRole;
import com.example.dto.request.LoginRequest;
import com.example.dto.request.RegisterRequest;
import com.example.dto.request.RoleAssignRequest;
import com.example.dto.response.RestResult;
import com.example.entity.UserInfo;
import com.example.repository.UserRepository;
import com.example.service.UserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.security.Key;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* 用户业务逻辑实现类
*/
@Slf4j
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
// JWT 密钥,实际应用中应从配置文件读取
@Value("${jwt.secret}")
private String secretKey;
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public RestResult<?> register(RegisterRequest request) {
log.info(, request.getUsername());
(StringUtils.hasText(request.getUsername()) && StringUtils.hasText(request.getPassword())) {
(userRepository.findByUsername(request.getUsername()).isPresent()) {
RestResult.fail(, );
}
{
();
userInfo.setUsername(request.getUsername());
userInfo.setPassword(encryptPassword(request.getPassword()));
userInfo.setUserRole(request.getRole());
userInfo.setCreateTime(java.time.LocalDateTime.now());
userInfo.setUpdateTime(java.time.LocalDateTime.now());
userRepository.save(userInfo);
log.info(, request.getUsername());
Map<String, Object> data = <>();
data.put(, userInfo.getUserId());
data.put(, userInfo.getUsername());
data.put(, userInfo.getUserRole());
RestResult.success(data);
} (Exception e) {
log.error(, e);
RestResult.fail(, );
}
} {
RestResult.fail(, );
}
}
RestResult<?> login(LoginRequest request) {
log.info(, request.getUsername());
(!StringUtils.hasText(request.getUsername()) || !StringUtils.hasText(request.getPassword())) {
RestResult.fail(, );
}
{
userRepository.findByUsername(request.getUsername()).orElse();
(user == || !validatePassword(request.getPassword(), user.getPassword())) {
RestResult.fail(, );
}
generateToken(user);
Map<String, Object> data = <>();
data.put(, token);
data.put(, user.getUsername());
data.put(, user.getUserRole());
RestResult.success(data);
} (Exception e) {
log.error(, e);
RestResult.fail(, );
}
}
RestResult<?> validateToken(String token) {
log.info();
{
Jwts.parserBuilder()
.setSigningKey(secretKey.getBytes())
.build()
.parseClaimsJws(token)
.getBody();
claims.getSubject();
(String) claims.get();
UserRole.valueOf(roleStr.toUpperCase());
Map<String, Object> data = <>();
data.put(, username);
data.put(, role);
RestResult.success(data);
} (Exception e) {
log.warn(, e);
RestResult.fail(, );
}
}
RestResult<?> assignRole(RoleAssignRequest request) {
log.info(, request.getUserId(), request.getRole());
userRepository.findById(request.getUserId()).orElse();
(user == ) {
RestResult.fail(, );
}
{
user.setUserRole(request.getRole());
user.setUpdateTime(java.time.LocalDateTime.now());
userRepository.save(user);
Map<String, Object> data = <>();
data.put(, user.getUserId());
data.put(, user.getUsername());
data.put(, user.getUserRole());
RestResult.success(data);
} (Exception e) {
log.error(, e);
RestResult.fail(, );
}
}
String {
rawPassword;
}
{
rawPassword.equals(encodedPassword);
}
String {
();
(now.getTime() + );
(secretKey.getBytes(), SignatureAlgorithm.HS512.getJcaName());
Jwts.builder()
.setSubject(user.getUsername())
.claim(, user.getUserRole().toString())
.setIssuedAt( ())
.setExpiration(expiryDate)
.signWith(key, SignatureAlgorithm.HS512)
.compact();
}
}
该学生成绩管理系统的落地,不仅解决了传统管理模式的痛点,更具备三大核心价值:
此外,借助 AI 辅助的开发模式,相比传统手动编码,整体开发周期大幅缩短,且生成的代码符合行业规范,后期维护成本降低,为高校毕业设计、中小型教学管理系统开发提供了高效、低成本的实现路径。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online