资讯动态

基于 Spring Boot + Vue3 的教学管理信息系统设计与实现

发布时间:2026/10/26 9:12:07 来源:尧图企业网站定制
温馨提示本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系名片一、项目背景与意义随着高校和培训机构教学规模的不断扩大课程安排、学生选课、成绩录入、教师授课等业务数据量快速增长传统依赖 Excel 表格和线下纸质流程的管理方式逐渐暴露出数据分散、统计困难、权限混乱、信息不同步等问题。教学管理信息系统通过统一的数据模型和在线协作流程把教务人员、教师和学生三类角色纳入同一平台能够显著提升教学管理效率和数据准确性。本文基于Spring Boot Vue3前后端分离架构设计并实现一套教学管理信息系统覆盖用户与权限管理、课程管理、选课管理、成绩管理、公告管理等核心模块并给出关键代码实现与部署方案适合作为课程设计、毕业设计或企业级后台系统的参考实践。二、系统需求分析2.1 角色划分角色核心职责典型功能管理员维护基础数据与系统配置用户管理、角色权限、课程审核、公告发布教师承担课程教学与成绩评定查看授课课程、录入成绩、查看学生名单学生参与选课与查询成绩在线选课、退课、查询课表与成绩2.2 核心功能需求用户与权限管理支持用户注册、登录、角色分配基于 JWT 实现无状态鉴权基于角色进行接口级权限控制。课程管理管理员维护课程信息课程编号、名称、学分、授课教师、容量、上课时间与地点支持分页查询与条件检索。选课管理学生在线选课与退课系统需校验课程容量、时间冲突与重复选课。成绩管理教师按课程录入学生成绩学生可查询个人成绩与绩点。公告管理管理员发布教学通知首页展示最新公告。2.3 非功能需求接口响应时间在常规数据量下控制在 500ms 以内。关键业务选课、成绩录入需保证数据一致性使用数据库事务控制。系统需具备良好的可扩展性新增业务模块时不影响既有功能。三、技术选型与整体架构3.1 技术栈层次技术选型说明前端框架Vue3 Vite组合式 API构建速度快UI 组件库Element Plus后台管理系统常用组件状态管理Pinia管理登录态与用户信息路由Vue Router 4动态路由与权限守卫HTTP 客户端Axios统一请求封装与拦截器后端框架Spring Boot 3.x快速构建 RESTful 服务持久层MyBatis-Plus简化 CRUD 与分页数据库MySQL 8.0关系型数据存储鉴权Spring Security JWT无状态认证与授权缓存Redis存储 Token 黑名单与热点数据3.2 系统架构flowchart LR A[Vue3 前端 SPA] --|Axios / JSON| B[Spring Boot 应用] B -- C[Controller 层] C -- D[Service 层] D -- E[Mapper 层 MyBatis-Plus] E -- F[(MySQL 8.0)] D -- G[(Redis)] B -- H[Spring Security JWT]前后端通过 RESTful 接口通信前端负责页面渲染与交互后端负责业务逻辑与数据持久化职责边界清晰便于独立开发与部署。四、数据库设计系统核心表包括用户表、角色表、课程表、选课记录表、成绩表和公告表。以下给出关键表结构。CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录账号, password VARCHAR(100) NOT NULL COMMENT BCrypt 加密密码, real_name VARCHAR(50) NOT NULL COMMENT 真实姓名, role VARCHAR(20) NOT NULL COMMENT ADMIN/TEACHER/STUDENT, status TINYINT DEFAULT 1 COMMENT 1 启用 0 禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_no VARCHAR(30) NOT NULL UNIQUE COMMENT 课程编号, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, credit DECIMAL(3,1) NOT NULL COMMENT 学分, teacher_id BIGINT NOT NULL COMMENT 授课教师, capacity INT NOT NULL COMMENT 容量, selected INT DEFAULT 0 COMMENT 已选人数, class_time VARCHAR(50) COMMENT 上课时间, location VARCHAR(100) COMMENT 上课地点, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE course_selection ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, course_id BIGINT NOT NULL, select_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_course (student_id, course_id) ); CREATE TABLE score ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, course_id BIGINT NOT NULL, score DECIMAL(5,2) COMMENT 百分制成绩, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_student_course (student_id, course_id) );其中course_selection与score表通过唯一索引保证同一学生对同一课程不会重复选课或重复录入成绩从数据库层面兜底业务一致性。五、后端核心实现5.1 项目结构teaching-admin ├── src/main/java/com/example/teaching │ ├── common # 统一响应、异常处理、工具类 │ ├── config # Security、MyBatis-Plus、Redis 配置 │ ├── controller # 接口层 │ ├── service # 业务层 │ ├── mapper # 持久层 │ ├── entity # 实体类 │ └── dto # 请求/响应对象 └── src/main/resources ├── application.yml └── mapper # XML 映射文件5.2 统一响应与全局异常处理Data public class ResultT { private Integer code; private String message; private T data; public static lt;Tgt; Resultlt;Tgt; success(T data) { Resultlt;Tgt; r new Resultlt;gt;(); r.setCode(200); r.setMessage(success); r.setData(data); return r; } public static lt;Tgt; Resultlt;Tgt; fail(String message) { Resultlt;Tgt; r new Resultlt;gt;(); r.setCode(500); r.setMessage(message); return r; } }RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(BusinessException.class) public ResultVoid handleBusiness(BusinessException e) { return Result.fail(e.getMessage()); } ExceptionHandler(Exception.class) public ResultVoid handleException(Exception e) { log.error(系统异常, e); return Result.fail(系统繁忙请稍后重试); } }5.3 JWT 鉴权Component public class JwtUtil { Value(${jwt.secret}) private String secret; Value(${jwt.expire}) private long expire; public String generateToken(Long userId, String username, String role) { Date now new Date(); return Jwts.builder() .setSubject(username) .claim(userId, userId) .claim(role, role) .setIssuedAt(now) .setExpiration(new Date(now.getTime() expire)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } public Claims parseToken(String token) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } }在SecurityConfig中注册JwtAuthenticationFilter从请求头Authorization中解析 Token将用户身份写入SecurityContext并通过PreAuthorize(hasRole(ADMIN))实现接口级权限控制。5.4 选课业务实现Service RequiredArgsConstructor public class CourseSelectionServiceImpl implements CourseSelectionService { private final CourseMapper courseMapper; private final CourseSelectionMapper selectionMapper; Override Transactional(rollbackFor Exception.class) public void selectCourse(Long studentId, Long courseId) { Course course courseMapper.selectById(courseId); if (course null) { throw new BusinessException(课程不存在); } if (course.getSelected() gt; course.getCapacity()) { throw new BusinessException(课程已满选课失败); } Long count selectionMapper.selectCount(new LambdaQueryWrapperlt;CourseSelectiongt;() .eq(CourseSelection::getStudentId, studentId) .eq(CourseSelection::getCourseId, courseId)); if (count gt; 0) { throw new BusinessException(请勿重复选课); } CourseSelection selection new CourseSelection(); selection.setStudentId(studentId); selection.setCourseId(courseId); selectionMapper.insert(selection); // 乐观更新已选人数避免并发超卖 int rows courseMapper.increaseSelected(courseId); if (rows 0) { throw new BusinessException(选课人数更新失败请重试); } } }Update(UPDATE course SET selected selected 1 WHERE id #{courseId} AND selected capacity) int increaseSelected(Param(courseId) Long courseId);通过selected capacity作为更新条件把容量校验下沉到 SQL 层配合事务与唯一索引可有效避免高并发下的课程超卖问题。5.5 成绩录入接口RestController RequestMapping(/api/scores) RequiredArgsConstructor public class ScoreController { private final ScoreService scoreService; PreAuthorize(hasRole(TEACHER)) PostMapping public Resultlt;Voidgt; saveScore(RequestBody Valid ScoreDTO dto) { scoreService.saveOrUpdateScore(dto); return Result.success(null); } GetMapping(/my) public Resultlt;Listlt;ScoreVOgt;gt; myScores(AuthenticationPrincipal LoginUser user) { return Result.success(scoreService.listByStudentId(user.getUserId())); } }六、前端核心实现6.1 请求封装import axios from axios import { ElMessage } from element-plus import { useUserStore } from /stores/user import router from /router const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) request.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response?.status 401) { useUserStore().logout() router.push(/login) } ElMessage.error(error.message || 网络异常) return Promise.reject(error) } ) export default request6.2 动态路由与权限守卫import { createRouter, createWebHistory } from vue-router import { useUserStore } from /stores/user const routes [ { path: /login, component: () import(/views/Login.vue) }, { path: /, component: () import(/layout/Index.vue), children: [ { path: course, component: () import(/views/course/CourseList.vue), meta: { roles: [ADMIN, TEACHER] } }, { path: select, component: () import(/views/course/SelectCourse.vue), meta: { roles: [STUDENT] } }, { path: score, component: () import(/views/score/ScoreList.vue) } ] } ] const router createRouter({ history: createWebHistory(), routes }) router.beforeEach((to, from, next) { const userStore useUserStore() if (to.path ! /login !userStore.token) { return next(/login) } const roles to.meta.roles if (roles !roles.includes(userStore.role)) { return next(/403) } next() }) export default router6.3 选课页面示例template el-table :datacourseList v-loadingloading el-table-column propcourseNo label课程编号 width140 / el-table-column propcourseName label课程名称 / el-table-column propteacherName label授课教师 width120 / el-table-column propcredit label学分 width80 / el-table-column label容量 width120 template #default{ row } {{ row.selected }} / {{ row.capacity }} /template /el-table-column el-table-column label操作 width120 template #default{ row } el-button typeprimary sizesmall :disabledrow.selected row.capacity clickhandleSelect(row) 选课/el-button /template /el-table-column /el-table /template script setup import { ref, onMounted } from vue import { ElMessage } from element-plus import request from /api/request const courseList ref([]) const loading ref(false) const loadCourses async () { loading.value true try { courseList.value await request.get(/api/courses) } finally { loading.value false } } const handleSelect async (row) { await request.post(/api/selections, { courseId: row.id }) ElMessage.success(选课成功) loadCourses() } onMounted(loadCourses) /script七、系统部署与运行7.1 后端启动server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/teaching_admin?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver data: redis: host: localhost port: 6379 jwt: secret: your_jwt_secret_key_at_least_32_chars expire: 864000007.2 前端启动npm install npm run dev7.3 生产构建# 前端打包产物输出到 dist 目录 npm run build 后端打包 mvn clean package -DskipTests java -jar target/teaching-admin-1.0.0.jar生产环境建议使用 Nginx 托管前端静态资源并将/api反向代理到后端服务同时开启 Gzip 压缩与静态资源缓存。八、关键问题与优化建议并发选课超卖采用条件更新加唯一索引双重保障必要时可引入 Redis 预扣减库存。权限粒度当前按角色控制后续可扩展为基于菜单与按钮的细粒度权限模型。接口性能课程列表等高频查询可加入 Redis 缓存并配合分页避免全表扫描。数据安全密码使用 BCrypt 加密存储敏感接口增加操作日志记录。可观测性接入 Spring Boot Actuator 与日志采集便于线上问题排查。九、总结本文基于 Spring Boot 3 Vue3 前后端分离架构完成了一套教学管理信息系统的需求分析、数据库设计、后端鉴权与业务实现、前端路由与页面开发并针对选课并发、权限控制和性能优化给出了实践建议。该方案结构清晰、扩展性好可直接作为课程设计或毕业设计的基础工程也可根据实际业务继续扩展排课、考勤、教学评价等模块。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑