跳到主要内容
基于 Spring Boot 的学生成绩管理系统设计与实现 | 极客日志
Java AI java
基于 Spring Boot 的学生成绩管理系统设计与实现 基于 Spring Boot 和 MySQL 构建学生成绩管理系统,涵盖用户权限、课程管理及成绩统计模块。通过 AI 辅助生成核心代码,解决精度丢失与性能瓶颈问题,采用 Redis 缓存优化查询效率,并结合 RBAC 模型保障数据安全。系统支持多角色操作与数据追溯,适用于高校教学管理场景。
GopherDev 发布于 2026/3/28 更新于 2026/4/23 1 浏览前言
在高校教学管理工作中,学生成绩是教学质量评估、学生学业监测的核心数据。传统 Excel 表格管理或单机版系统常面临数据同步滞后、权限管控缺失、成绩分析低效等问题。为解决这一痛点,以'学生成绩管理系统'为开发目标,借助 AI 编程助手工具实现全流程开发,本文将从需求分析到系统落地的关键环节进行梳理,分享辅助开发的高效实践经验。
一、需求分析与规划
1. 功能需求
系统需满足三类核心用户(教学管理员、教师、学生)的差异化需求,功能拆解如下:
教学管理员端 :用户管理(新增教师/学生账号、分配班级与课程权限)、课程管理(创建课程、关联授课教师)、成绩模板配置(设置成绩构成比例,如平时成绩占 30%、期末成绩占 70%)、成绩数据导出(按班级/课程/学期生成 Excel 报表)、系统日志查看(跟踪成绩修改、账号操作记录);
教师端 :成绩录入(按学生名单批量或单个录入平时、期中、期末成绩,系统自动计算总成绩)、成绩审核(提交总成绩前预览核对,提交后锁定不可修改)、成绩分析(查看所授课程的平均分、及格率、分数段分布图表)、学生成绩反馈(回复学生的成绩疑问,标注异常成绩说明);
学生端 :成绩查询(按学期/课程查看个人各项成绩及总成绩)、成绩趋势分析(查看同一课程历年成绩对比或个人多学期成绩变化)、成绩异议申请(对疑问成绩提交申诉,跟踪处理进度)、成绩单下载(下载经管理员审核后的官方成绩证明)。
2. 核心模块
基于需求拆解,系统划分为 5 个联动模块,模块职责与关联关系如下表所示:
模块名称 核心功能 关联模块 用户权限模块 账号注册登录、角色权限分配(管理员/教师/学生)、密码重置、账号状态管理 所有模块(权限校验) 课程管理模块 课程信息维护(新增/编辑/删除课程)、班级 - 课程 - 教师关联、课程学期管理 用户权限模块(教师课程权限)、成绩管理模块(课程成绩归属) 成绩管理模块 成绩录入与计算、成绩审核与锁定、成绩修改申请与审批、异常成绩标注 课程管理模块(课程数据)、用户权限模块(成绩操作权限) 数据统计模块 成绩分析(平均分、及格率、分数段)、成绩趋势图表生成、数据报表导出 成绩管理模块(成绩数据源)、课程管理模块(课程筛选条件) 消息通知模块 成绩录入提醒、成绩审核结果推送、学生成绩异议反馈、系统公告发布 成绩管理模块(成绩状态变更)、用户权限模块(接收人匹配)
二、AI 辅助开发实录
1. 步骤 1:输入提示词(明确开发需求)
打开 IntelliJ IDEA 并集成 AI 编程插件,在'需求输入'界面输入提示词:'开发学生成绩管理系统后端代码,基于 Spring Boot 3.0 和 MySQL 8.0,需包含用户权限(管理员/教师/学生三类角色)、课程管理(课程 CRUD 与师生关联)、成绩管理(成绩录入/计算/审核)三大核心模块,生成实体类、Mapper 接口、Service 层、Controller 层代码,符合 RESTful API 规范,需支持成绩自动计算(按比例汇总)与权限细粒度控制(如教师仅能操作所授课程成绩)。'
2. 步骤 2:AI 理解需求(需求拆解与确认)
提交提示词后,AI 助手约 8 秒生成需求拆解报告,核心内容包括:
需求匹配度分析 :明确系统为教学场景下的成绩管理类系统,核心诉求是'数据安全 + 效率提升',需重点解决权限隔离与成绩自动化处理,与教育领域常见管理系统需求高度适配;
模块拆解确认 :将需求细化为用户权限模块(含角色权限矩阵设计,如管理员全权限、教师仅课程内权限)、课程管理模块(含班级 - 课程 - 教师关联逻辑)、成绩管理模块(含成绩计算规则配置、审核流程设计)。
3. 步骤 3:设计接口(RESTful API 规划)
插件自动跳转至'接口设计'界面,围绕三大核心模块生成接口说明,明确各接口功能定位:
用户权限接口 :负责账号认证(登录生成 JWT 令牌)、权限校验(接口访问时验证角色权限)、用户信息维护,例如 (登录接口)、 (权限分配接口);
/api/user/login
/api/user/role/assign
课程管理接口 :实现课程信息 CRUD 与关联管理,例如 /api/course/add(新增课程)、/api/course/teacher/bind(课程 - 教师绑定接口);
成绩管理接口 :覆盖成绩录入、计算、审核全流程,例如 /api/score/batch/save(批量录入成绩)、/api/score/calculate(自动计算总成绩接口)、/api/score/audit(成绩审核接口)。
4. 步骤 4:表结构设计(数据库表生成) 接口设计完成后,插件自动推导数据库表结构(支持手动编辑),核心表结构如下:
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 BIGINT COMMENT '授课教师 ID' ,
description TEXT COMMENT '课程描述' ,
credits INT DEFAULT 0 COMMENT '学分' ,
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 student_course (
relation_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '师生关联关系唯一标识' ,
student_id BIGINT NOT NULL COMMENT '学生用户 ID' ,
course_id BIGINT NOT NULL COMMENT '课程 ID' ,
select_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '选课时间' ,
create_by VARCHAR (50 ) COMMENT '创建人' ,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) COMMENT= '学生选课关系表' ;
CREATE TABLE score_record (
score_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '成绩记录唯一标识' ,
student_id BIGINT NOT NULL COMMENT '学生用户 ID' ,
course_id BIGINT NOT NULL COMMENT '课程 ID' ,
score_type ENUM('exam' , 'homework' , 'project' , 'quiz' ) NOT NULL COMMENT '成绩类型:exam-考试,homework-作业,project-项目,quiz-小测' ,
score_value DECIMAL (5 ,1 ) NOT NULL COMMENT '成绩数值' ,
score_weight DECIMAL (3 ,2 ) DEFAULT 1.00 COMMENT '该成绩在总评中的权重比例' ,
is_reviewed TINYINT DEFAULT 0 COMMENT '是否已审核:0-未审核,1-已审核' ,
review_time DATETIME COMMENT '审核时间' ,
reviewer VARCHAR (50 ) COMMENT '审核人' ,
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= '成绩记录表' ;
5. 步骤 5:处理逻辑(接口实现思路生成) 进入'处理逻辑'界面,插件针对核心接口生成详细业务流程,以'成绩管理'模块为例,关键接口逻辑如下:
接口名称:录入成绩
入参对象属性: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":…}
6. 步骤 6:生成源码(完整工程代码输出) 确认处理逻辑后,点击'生成源码',AI 助手约 3 分钟生成完整工程代码(遵循 MVC 架构),核心代码结构如下:
配置类 :src/main/java/com/example/config/CorsConfig(跨域配置)、RedisConfig(Redis 缓存配置)、SecurityConfig(权限安全配置);
Controller 层 :src/main/java/com/example/controller/UserController.java、CourseController.java、ScoreController.java(接口映射与参数接收);
DTO 类 :src/main/java/com/example/dto/request/ScoreBatchSaveRequest.java(批量成绩录入请求类)、ScoreAuditRequest.java(成绩审核请求类),src/main/java/com/example/dto/response/ScoreStatisticResponse.java(成绩统计响应类);
实体类 :src/main/java/com/example/entity/UserInfo.java、CourseInfo.java、ScoreInfo.java、ScoreRule.java(映射数据库表);
Service 层 :src/main/java/com/example/service/UserService.java、CourseService.java、ScoreService.java(接口定义)及对应的 impl 实现类(业务逻辑实现);
启动类 :src/main/java/com/example/ScoreManagementApplication.java(项目入口)。
生成的代码无语法错误,导入 IDE 后可直接启动测试。
三、优化与调试心得
1. 遇到的问题及解决方案
(1)问题 1:成绩计算精度丢失 生成的 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);
(2)问题 2:高频成绩查询性能低 学生集中查询成绩时,频繁访问 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);
}
(3)问题 3:成绩修改无痕迹 教师提交成绩后,若管理员驳回需修改,但生成的代码未记录成绩修改历史,无法追溯变更内容。
解决方案 :手动新增 score_history(成绩修改历史表),并通过 AI 生成 ScoreHistory 实体与对应的 Mapper、Service 代码,在 ScoreServiceImpl 的 update 方法中添加历史记录逻辑,每次修改成绩时自动保存旧数据至 score_history 表,实现'每改必留痕'。
四、系统成果展示与应用价值
1. 功能成果展示 系统开发完成后,通过本地部署与多角色实测,实现了'需求全覆盖、操作零门槛、数据高可靠'的开发目标,核心功能代码成果如下:
package com.example.service.impl;
import com.example.dto.request.*;
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
private UserRepository userRepository;
@Autowired
private StudentCourseRepository studentCourseRepository;
@Override
public RestResult<Object> createCourse (CreateCourseRequest request) {
if (courseRepository.findByCourseName(request.getCourseName()) != null ) {
return RestResult.error("000001" , "课程名称已存在" );
}
CourseInfo courseInfo = new CourseInfo ();
BeanUtils.copyProperties(request, courseInfo);
courseInfo.setCreateTime(LocalDateTime.now());
courseInfo.setUpdateTime(LocalDateTime.now());
courseInfo.setStatus((byte ) 1 );
try {
courseRepository.save(courseInfo);
return RestResult.success(courseInfo);
} catch (Exception e) {
log.error("创建课程失败" , e);
return RestResult.error("999999" , "系统错误" );
}
}
@Override
public RestResult<Object> queryCourseList (Integer pageNo, Integer pageSize, String keyword) {
if (pageNo == null || pageNo <= 0 ) { pageNo = 1 ; }
if (pageSize == null || pageSize <= 0 ) { pageSize = 10 ; }
Pageable pageable = PageRequest.of(pageNo - 1 , pageSize);
List<CourseInfo> courses = courseRepository.findAll(pageable).getContent();
Page<CourseInfo> coursePage = new PageImpl <>(courses, pageable, courseRepository.count());
return RestResult.success(coursePage);
}
@Override
public RestResult<Object> updateCourse (UpdateCourseRequest request) {
CourseInfo existingCourse = courseRepository.findById(request.getCourseId()).orElse(null );
if (existingCourse == null ) {
return RestResult.error("000001" , "课程不存在" );
}
if (request.getCourseName() != null && !request.getCourseName().equals(existingCourse.getCourseName())) {
if (courseRepository.findByCourseName(request.getCourseName()) != null ) {
return RestResult.error("000001" , "课程名称已存在" );
}
}
BeanUtils.copyProperties(request, existingCourse, "courseId" , "createTime" );
existingCourse.setUpdateTime(LocalDateTime.now());
try {
courseRepository.save(existingCourse);
return RestResult.success(existingCourse);
} catch (Exception e) {
log.error("更新课程失败" , e);
return RestResult.error("999999" , "系统错误" );
}
}
@Override
public RestResult<Object> deleteCourse (DeleteCourseRequest request) {
CourseInfo existingCourse = courseRepository.findById(request.getCourseId()).orElse(null );
if (existingCourse == null ) {
return RestResult.error("000001" , "课程不存在" );
}
try {
studentCourseRepository.deleteByCourseId(request.getCourseId());
courseRepository.deleteById(request.getCourseId());
return RestResult.success(null );
} catch (Exception e) {
log.error("删除课程失败" , e);
return RestResult.error("999999" , "系统错误" );
}
}
@Override
public RestResult<Object> boundTeacherToCourse (BoundTeacherToCourseRequest request) {
UserInfo teacher = userRepository.findById(request.getTeacherId()).orElse(null );
CourseInfo course = courseRepository.findById(request.getCourseId()).orElse(null );
if (teacher == null || course == null ) {
return RestResult.error("000001" , "教师或课程不存在" );
}
course.setTeacherId(request.getTeacherId());
course.setUpdateTime(LocalDateTime.now());
try {
courseRepository.save(course);
return RestResult.success(null );
} catch (Exception e) {
log.error("绑定教师到课程失败" , e);
return RestResult.error("999999" , "系统错误" );
}
}
@Override
public RestResult<Object> selectCourse (SelectCourseRequest request) {
UserInfo student = userRepository.findById(request.getStudentId()).orElse(null );
CourseInfo course = courseRepository.findById(request.getCourseId()).orElse(null );
if (student == null || course == null ) {
return RestResult.error("000001" , "学生或课程不存在" );
}
StudentCourse existingRecord = studentCourseRepository.findByStudentIdAndCourseId(request.getStudentId(), request.getCourseId());
if (existingRecord != null ) {
return RestResult.error("000001" , "该学生已选过此课程" );
}
StudentCourse record = new StudentCourse ();
record.setStudentId(request.getStudentId());
record.setCourseId(request.getCourseId());
record.setCreateTime(LocalDateTime.now());
try {
studentCourseRepository.save(record);
return RestResult.success(null );
} catch (Exception e) {
log.error("学生选课失败" , e);
return RestResult.error("999999" , "系统错误" );
}
}
}
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;
@Override
public RestResult recordScore (ScoreRecordRequest request) {
if (!checkStudentSelectCourse(request.getStudentId(), request.getCourseId())) {
return RestResult.error("学生未选该课程或课程不存在" );
}
try {
ScoreRecord scoreRecord = new ScoreRecord ();
scoreRecord.setStudentId(request.getStudentId());
scoreRecord.setCourseId(request.getCourseId());
scoreRecord.setScoreType(request.getType());
scoreRecord.setScoreValue(BigDecimal.valueOf(request.getScore()));
scoreRecord.setIsReviewed(0 );
scoreRecord.setCreateTime(LocalDateTime.now());
scoreRecord.setUpdateTime(LocalDateTime.now());
scoreRepository.save(scoreRecord);
return RestResult.success("录入成功" );
} catch (Exception e) {
log.error("录入成绩失败:" , e);
return RestResult.error("系统异常,请稍后再试" );
}
}
@Override
public RestResult calculateScore (Long studentId, Long courseId) {
List<ScoreRecord> records = scoreRepository.findByStudentIdAndCourseIdOrderByCreateTimeAsc(studentId, courseId);
if (records.isEmpty()) {
return RestResult.error("无相关成绩记录" );
}
double totalScore = 0.0 ;
for (ScoreRecord record : records) {
if (record.getIsReviewed() == 1 ) {
totalScore += record.getScoreValue().doubleValue() * record.getScoreWeight().doubleValue();
}
}
return RestResult.success(totalScore);
}
@Override
public RestResult reviewScore (ScoreReviewRequest request) {
ScoreRecord latestRecord = scoreRepository.findLatestByStudentIdAndCourseId(request.getStudentId(), request.getCourseId());
if (latestRecord == null || latestRecord.getIsReviewed() == 1 ) {
return RestResult.error("成绩不存在或已审核" );
}
try {
latestRecord.setIsReviewed(request.getStatus() ? 1 : 0 );
latestRecord.setReviewer("system" );
latestRecord.setReviewTime(LocalDateTime.now());
latestRecord.setUpdateTime(LocalDateTime.now());
scoreRepository.save(latestRecord);
return RestResult.success("审核完成" );
} catch (Exception e) {
log.error("审核成绩失败:" , e);
return RestResult.error("系统异常,请稍后再试" );
}
}
@Override
public RestResult modifyScore (ScoreModifyRequest request) {
ScoreRecord latestRecord = scoreRepository.findLatestByStudentIdAndCourseId(request.getStudentId(), request.getCourseId());
if (latestRecord == null ) {
return RestResult.error("成绩不存在" );
}
if (latestRecord.getIsReviewed() == 1 ) {
return RestResult.error("成绩不可修改" );
}
try {
latestRecord.setScoreValue(BigDecimal.valueOf(request.getNewScore()));
latestRecord.setUpdateTime(LocalDateTime.now());
scoreRepository.save(latestRecord);
return RestResult.success("修改成功" );
} catch (Exception e) {
log.error("修改成绩失败:" , e);
return RestResult.error("系统异常,请稍后再试" );
}
}
@Override
public RestResult getScoreHistory (Long studentId, Long courseId) {
List<ScoreRecord> records = scoreRepository.findByStudentIdAndCourseIdOrderByCreateTimeAsc(studentId, courseId);
return RestResult.success(records);
}
private boolean checkStudentSelectCourse (Long studentId, Long courseId) {
CourseInfo course = courseRepository.findById(courseId).orElse(null );
if (course == null || course.getStatus() != 1 ) {
return false ;
}
StudentCourse studentCourse = studentCourseRepository.findOneByStudentIdAndCourseId(studentId, courseId);
return studentCourse != null ;
}
}
package com.example.service.impl;
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;
@Value("${jwt.secret}")
private String secretKey;
public UserServiceImpl (UserRepository userRepository) {
this .userRepository = userRepository;
}
@Override
public RestResult<?> register(RegisterRequest request) {
log.info("开始进行用户注册:{}" , request.getUsername());
if (StringUtils.hasText(request.getUsername()) && StringUtils.hasText(request.getPassword())) {
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
return RestResult.fail("000001" , "用户名已存在" );
}
try {
UserInfo userInfo = new UserInfo ();
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 = new HashMap <>();
data.put("userId" , userInfo.getUserId());
data.put("username" , userInfo.getUsername());
data.put("role" , userInfo.getUserRole());
return RestResult.success(data);
} catch (Exception e) {
log.error("用户注册失败" , e);
return RestResult.fail("999999" , "服务器内部错误" );
}
} else {
return RestResult.fail("000002" , "用户名或密码不能为空" );
}
}
@Override
public RestResult<?> login(LoginRequest request) {
log.info("开始用户登录验证:{}" , request.getUsername());
if (!StringUtils.hasText(request.getUsername()) || !StringUtils.hasText(request.getPassword())) {
return RestResult.fail("000002" , "用户名或密码不能为空" );
}
try {
UserInfo user = userRepository.findByUsername(request.getUsername()).orElse(null );
if (user == null || !validatePassword(request.getPassword(), user.getPassword())) {
return RestResult.fail("000001" , "用户名或密码错误" );
}
String token = generateToken(user);
Map<String, Object> data = new HashMap <>();
data.put("token" , token);
data.put("username" , user.getUsername());
data.put("role" , user.getUserRole());
return RestResult.success(data);
} catch (Exception e) {
log.error("用户登录异常" , e);
return RestResult.fail("999999" , "服务器内部错误" );
}
}
@Override
public RestResult<?> validateToken(String token) {
log.info("开始校验 Token 有效性" );
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(secretKey.getBytes())
.build()
.parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
String roleStr = (String) claims.get("role" );
Map<String, Object> data = new HashMap <>();
data.put("username" , username);
data.put("role" , roleStr);
return RestResult.success(data);
} catch (Exception e) {
log.warn("Token 无效或已过期" , e);
return RestResult.fail("000001" , "Token 无效或已过期" );
}
}
@Override
public RestResult<?> assignRole(RoleAssignRequest request) {
log.info("开始为用户分配角色:userId={}, role={}" , request.getUserId(), request.getRole());
UserInfo user = userRepository.findById(request.getUserId()).orElse(null );
if (user == null ) {
return RestResult.fail("000001" , "用户不存在" );
}
try {
user.setUserRole(request.getRole());
user.setUpdateTime(java.time.LocalDateTime.now());
userRepository.save(user);
Map<String, Object> data = new HashMap <>();
data.put("userId" , user.getUserId());
data.put("username" , user.getUsername());
data.put("role" , user.getUserRole());
return RestResult.success(data);
} catch (Exception e) {
log.error("更新用户角色失败" , e);
return RestResult.fail("999999" , "服务器内部错误" );
}
}
private String encryptPassword (String rawPassword) {
return rawPassword;
}
private boolean validatePassword (String rawPassword, String encodedPassword) {
return rawPassword.equals(encodedPassword);
}
private String generateToken (UserInfo user) {
Date now = new Date ();
Date expiryDate = new Date (now.getTime() + 86400000 );
Key key = new SecretKeySpec (secretKey.getBytes(), SignatureAlgorithm.HS512.getJcaName());
return Jwts.builder()
.setSubject(user.getUsername())
.claim("role" , user.getUserRole().toString())
.setIssuedAt(new Date ())
.setExpiration(expiryDate)
.signWith(key, SignatureAlgorithm.HS512)
.compact();
}
}
2. 应用价值与延伸 该学生成绩管理系统的落地,不仅解决了传统管理模式的痛点,更具备三大核心价值:
效率提升 :将教师成绩录入、管理员数据汇总的时间成本降低 70%,释放教学管理人力投入核心教学工作;
数据安全 :基于 RBAC 权限模型与操作日志追溯,实现'谁操作、谁负责',成绩修改记录永久留存,杜绝信息泄露与数据篡改风险;
可扩展性 :系统预留'对接教务系统''接入人脸识别签到数据'等接口,未来可扩展'成绩与考勤关联分析''个性化学习推荐'等功能,适配高校教学管理的长期发展需求。
此外,借助 AI 辅助的开发模式,相比传统手动编码,整体开发周期显著缩短,且生成的代码符合行业规范,后期维护成本降低,为高校毕业设计、中小型教学管理系统开发提供了高效、低成本的实现路径。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online