SpringBoot+Vue课程管理系统源码(含数据库脚本、管理员账号及完整部署文档)

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:提供一套可直接运行的课程管理全栈项目,后端用SpringBoot开发,前端基于Vue.js搭配Element UI实现响应式界面,前后端完全分离。包含完整的后端服务模块(RainngCourseBE)、前端工程(RainngCourseFE)、MySQL建库建表SQL脚本、详细启动说明文档以及技术截图PDF。默认管理员账号为admin,密码123456,导入SQL后即可启动后端服务和前端项目。功能覆盖课程信息增删改查、教师与学生基础信息维护、班级排课逻辑、用户角色权限控制等典型教务场景。代码结构清晰,无冗余文件,保留.gitignore和.idea等开发配置但不影响打包部署;node_modules未上传,需npm install自行安装依赖。适合Java或Vue初学者实战练习,也适合作为高校计算机类专业毕业设计参考模板,支持快速二次开发与功能扩展。

1. 项目概述:这不是一个“Demo”,而是一套能真正跑起来的教务管理骨架

我带过三届毕业设计,每年都有学生卡在“系统搭不起来”这一步——不是不会写代码,而是被环境配置、前后端联调、数据库初始化这些琐事拖垮了信心。直到去年我把这套课程管理系统从零重构了一遍,才真正理解什么叫“开箱即用”。它不是那种只有一张登录页、点进去就404的“教学演示项目”,而是一个从数据库建表到角色权限落地、从班级排课逻辑到前端响应式渲染,全部走通闭环的真实业务骨架。核心关键词——课程管理、Vue前端、SpringBoot后端、Element UI、数据库脚本——每一个都不是摆设:课程增删改查支持富文本描述和封面图上传;教师信息管理带职称、入职年份、所授课程关联;学生档案包含学号、专业、班级归属及选课记录;排课模块不是简单的时间段填空,而是基于教室容量、教师空闲时段、课程周学时约束的三层校验逻辑。默认账号admin/123456不是为了偷懒,而是经过完整权限测试后的最小可行凭证——你导入SQL后启动后端(mvn spring-boot:run),再执行npm run serve,浏览器打开localhost:8080,就能看到左侧菜单栏里“课程管理”“教师管理”“排课计划”“权限设置”四个主模块全部可点击、可操作、有数据反馈。它不追求炫酷动画或微服务架构,但每个接口都带Swagger文档,每张表都有字段注释,每个Vue组件都按功能拆分(比如CourseList.vue只负责列表展示与搜索,CourseForm.vue专注表单验证与提交),这种结构对初学者极其友好:你想学后端,就盯着RainngCourseBE/src/main/java/com/rainng/course/controller下的Controller层看请求怎么进来、Service层怎么处理业务、Mapper层怎么映射SQL;你想练前端,就打开RainngCourseFE/src/views/course目录,观察Element UI的el-table如何绑定分页数据、el-dialog怎么控制表单弹窗、el-date-picker怎样格式化学期起止时间。更关键的是,它没把开发环境当累赘——.idea目录保留了Maven依赖索引和编码配置,node_modules虽未上传,但package.json里所有依赖版本都锁定(包括vue@2.7.16、element-ui@2.15.14、axios@1.6.7),你执行npm install绝不会出现“版本冲突导致表格错位”的尴尬。这不是一个让你截图交差的玩具,而是一块能让你亲手打磨出毕业设计成品的坯料。

2. 整体架构设计与技术选型逻辑:为什么是这套组合,而不是其他方案?

2.1 前后端分离的底层动因:不是跟风,而是为降低学习门槛

很多初学者一上来就想搞“SpringBoot+Vue全家桶”,结果卡在跨域、代理配置、打包路径上三天。这套系统坚持前后端完全分离,根本原因在于把“让代码跑起来”这个最脆弱的环节彻底解耦。后端只暴露RESTful API(如GET /api/course?page=1&size=10),前端通过axios统一拦截请求头、处理token、捕获401跳转登录页——这意味着你完全可以先不启动前端,用Postman直接调后端接口,验证课程数据能否正确返回JSON;也可以先不碰Java,只改前端路由和组件,用Mock.js模拟后端响应,把界面交互逻辑跑通。这种分离不是技术炫技,而是把“环境问题”和“业务问题”物理隔离:当你发现课程列表空白时,第一反应是查浏览器Network面板看API是否返回200,而不是怀疑pom.xml里某个starter版本不对。我刻意没采用Nginx反向代理做开发期联调,因为那会引入新的配置变量(location规则、proxy_pass路径),初学者容易陷入“到底是前端路径写错还是Nginx转发失败”的死循环。取而代之的是Vue CLI的devServer代理机制,在vue.config.js里写:

devServer: {
  proxy: {
    '/api': {
      target: 'http://localhost:8081', // SpringBoot默认端口
      changeOrigin: true,
      pathRewrite: { '^/api': '' }
    }
  }
}

这段代码的精妙之处在于:前端发请求时写/api/course,开发服务器自动把/api前缀去掉,转发到http://localhost:8081/course,且changeOrigin: true解决了浏览器同源策略报错。你不需要懂HTTP协议细节,只要记住“前端所有接口地址加/api前缀,后端Controller的@RequestMapping别写/api就行”。

2.2 SpringBoot后端选型:为什么不用Spring Cloud或MyBatis-Plus?

看到RainngCourseBE目录下只有spring-boot-starter-webspring-boot-starter-data-jpaspring-boot-starter-security三个核心starter,可能有人疑惑:现在都2024年了,为啥不用MyBatis-Plus的CRUD封装?答案很实在——避免抽象层遮蔽SQL本质。初学者如果直接用courseMapper.selectPage(),很容易忽略分页查询背后的LIMIT ? OFFSET ?原理,更不会意识到COUNT(*)统计总数时若没加索引会导致全表扫描。这套系统坚持手写JPQL(Java Persistence Query Language):

@Query("SELECT c FROM Course c WHERE c.name LIKE %?1% AND c.status = ?2")
Page<Course> findByNameAndStatus(String name, Integer status, Pageable pageable);

好处是:你一眼能看出查询条件对应SQL的WHERE子句,Pageable参数明确告诉你分页由JPA自动生成LIMIT/OFFSET,而COUNT语句也由框架自动生成。更重要的是,所有实体类(Course.javaTeacher.java)都用@Table(name = "t_course")显式指定表名,字段用@Column(name = "course_name")标注列名,杜绝了“驼峰转下划线”这种隐式转换带来的调试困惑。至于没上Spring Cloud,是因为单体架构已足够承载教务系统负载——全校5000师生,课程表并发访问峰值也就几百QPS,强行拆微服务反而增加注册中心、网关、熔断等学习成本。Security模块也没用OAuth2,而是基于Session的传统认证:UsernamePasswordAuthenticationFilter拦截登录请求,UserDetailsService加载用户角色,@PreAuthorize("hasRole('ADMIN')")注解控制接口权限。这种方案的好处是,你能在SecurityConfig.java里清晰看到http.authorizeRequests()链式配置,理解“/login”放行、“/api/”需要认证、“/api/admin/”需ADMIN角色的具体含义,而不是被OAuth2的Authorization Code Flow绕晕。

2.3 Vue前端与Element UI的务实选择:放弃Composition API,拥抱Options API

RainngCourseFE用的是Vue 2.7(兼容Vue 3 Composition API语法糖但保持Options API主体),而非Vue 3 + Vite。原因很朴素:高校教材和主流教程仍以Vue 2为蓝本,学生查资料时不会因语法差异卡壳。比如课程列表组件CourseList.vue,它的data选项定义状态:

data() {
  return {
    tableData: [],
    pagination: {
      currentPage: 1,
      pageSize: 10,
      total: 0
    },
    loading: false
  }
}

methods里写获取数据方法:

async fetchCourses() {
  this.loading = true;
  try {
    const res = await this.$http.get('/api/course', {
      params: {
        page: this.pagination.currentPage,
        size: this.pagination.pageSize
      }
    });
    this.tableData = res.data.content;
    this.pagination.total = res.data.totalElements;
  } finally {
    this.loading = false;
  }
}

这种写法的好处是:生命周期钩子(mounted)、计算属性(computed)、监听器(watch)全部集中在一个对象里,学生调试时console.log(this.tableData)能立刻看到当前组件实例的所有状态,不像Composition API需要在setup里return一堆ref,调试时得翻reactive对象层层展开。Element UI的选择同样务实:它的el-table自带分页、排序、筛选,el-form提供rules校验规则和validate方法,el-dialog控制显隐状态——这些不是“必须用”的功能,而是把重复劳动封装成可配置项。比如课程表单的必填校验:

rules: {
  name: [{ required: true, message: '课程名称不能为空', trigger: 'blur' }],
  credit: [{ type: 'number', message: '学分必须为数字', trigger: 'blur' }]
}

你只需改message提示文字,不用自己写正则判断输入框是否为空。更重要的是,Element UI的样式是CSS-in-JS实现,所有组件样式都在node_modules/element-ui/lib/theme-chalk里,你修改主题色只需覆盖变量(如$--color-primary: #409EFF),不会像Ant Design那样需要webpack重编译主题包。

2.4 数据库脚本的设计哲学:不追求范式完美,专注业务可读性

database.sql脚本里t_course表有teacher_id外键,但t_teacher表没有冗余的course_count字段;t_class_schedule排课表直接存course_idteacher_idroom_idweek_daysession_startsession_end六个字段,而不是拆成“课程-教师关联表”+“排课时段表”。这种设计不是偷懒,而是让SQL查询直白到小学生都能看懂。比如查某教师本周课表:

SELECT 
  c.name AS course_name,
  r.room_number,
  cs.week_day,
  cs.session_start,
  cs.session_end
FROM t_class_schedule cs
JOIN t_course c ON cs.course_id = c.id
JOIN t_room r ON cs.room_id = r.id
WHERE cs.teacher_id = ? AND cs.week_day BETWEEN 1 AND 5;

如果拆成多层关联,这条SQL会变成四表JOIN,初学者光是理清表关系就要半小时。脚本里所有字段都加了中文注释:

CREATE TABLE `t_course` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(100) NOT NULL COMMENT '课程名称',
  `credit` int(11) NOT NULL DEFAULT '2' COMMENT '学分',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-启用,0-停用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程基本信息表';

这种注释不是形式主义,而是当你在IDEA里鼠标悬停字段时,能立刻看到“状态:1-启用,0-停用”,不用翻文档猜数字含义。更关键的是,脚本末尾的初始化数据:

INSERT INTO `t_user` VALUES (1,'admin','e10adc3949ba59abbe56e057f20f883e','ADMIN');
INSERT INTO `t_teacher` VALUES (1,'张教授','教授','2010-03-15',1);

密码e10adc3949ba59abbe56e057f20f883e是MD5(“123456”),这是故意为之——让你明白密码不是明文存储,且能通过DigestUtils.md5DigestAsHex("123456".getBytes())在Java里复现。这种设计让数据库不再是个黑盒,而是业务逻辑的具象化表达。

3. 核心模块实现详解:从数据库建表到前端交互的完整链路

3.1 数据库初始化:三步走,避开90%的部署失败

很多人导入SQL失败,根本原因不是SQL语法错,而是MySQL版本和字符集不匹配。这套系统的database.sql开头明确声明:

SET NAMES utf8mb4;
CREATE DATABASE IF NOT EXISTS rainng_course DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE rainng_course;

这意味着你必须用MySQL 5.7.8+(支持utf8mb4)且创建数据库时指定字符集。实操步骤如下:

第一步:确认MySQL版本
在命令行执行:

mysql --version

若显示mysql Ver 8.0.33Ver 5.7.42,则符合要求;若为5.6.x,需升级。升级不是重装,而是下载对应平台的安装包(Windows用MSI,macOS用DMG),安装时勾选“Upgrade existing installation”。

第二步:创建数据库并导入
不要用Navicat右键“运行SQL文件”,而要用命令行确保字符集生效:

# 登录MySQL
mysql -u root -p
# 执行建库语句(复制database.sql开头的CREATE DATABASE部分)
CREATE DATABASE IF NOT EXISTS rainng_course DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 退出MySQL
EXIT;
# 导入SQL(关键!指定字符集)
mysql -u root -p --default-character-set=utf8mb4 rainng_course < database.sql

这里--default-character-set=utf8mb4是救命参数——它强制MySQL客户端用utf8mb4解析SQL文件,否则中文注释里的“教授”二字会被识别为乱码,导致建表失败。

第三步:验证数据完整性
导入后别急着启动,先检查关键表数据:

USE rainng_course;
SELECT COUNT(*) FROM t_user; -- 应返回1(admin用户)
SELECT COUNT(*) FROM t_teacher; -- 应返回1(张教授)
SELECT * FROM t_course LIMIT 1; -- 查看字段值是否正常

t_user表为空,说明密码加密字段e10adc3949ba59abbe56e057f20f883e被截断(旧版MySQL varchar(32)不够存MD5),此时需手动修改t_userpassword字段长度:

ALTER TABLE t_user MODIFY COLUMN password VARCHAR(32);

提示:如果使用Docker运行MySQL,务必在docker-compose.yml中挂载配置文件:
yaml services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root volumes: - ./my.cnf:/etc/mysql/conf.d/my.cnf
my.cnf内容为:
ini [client] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci

3.2 后端服务启动:绕过IDEA的坑,用Maven命令行保命

RainngCourseBEpom.xmlspring-boot-maven-plugin配置了<executable>true</executable>,这意味着你可以直接用java -jar运行,但初学者常犯两个错误:一是用IDEA的绿色三角形按钮启动(会忽略application.yml里的spring.profiles.active=dev),二是没配好MySQL连接池。正确的启动流程是:

第一步:修改数据库配置
打开RainngCourseBE/src/main/resources/application-dev.yml,找到:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/rainng_course?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
    username: root
    password: root

password: root改成你MySQL的实际密码(不是SQL文件里的admin密码)。注意URL里的serverTimezone=GMT%2B8不能删——这是解决“Caused by: java.sql.SQLException: The server time zone value ‘XXX’ is unrecognized”错误的关键。

第二步:清理并构建
不要点IDEA的“Rebuild Project”,而要打开终端执行:

cd RainngCourseBE
mvn clean package -DskipTests

-DskipTests跳过单元测试(初学者的测试用例常因H2数据库配置失败),clean确保旧class文件被清除。生成的jar包在target/rainng-course-be-1.0.jar

第三步:命令行启动

java -jar target/rainng-course-be-1.0.jar --spring.profiles.active=dev

看到控制台输出Started RainngCourseBeApplication in X.XXX seconds即成功。此时访问http://localhost:8081/swagger-ui.html,能看到所有API文档——这是比写代码更重要的环节:点开CourseControllerGET /api/course,点击“Try it out”,再点“Execute”,如果返回JSON数据,证明后端已活。

注意:若报错java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException,说明你用了JDK 11+(JAXB被移除)。解决方案是在pom.xml添加:
xml <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency>

3.3 前端工程启动:npm install的隐藏陷阱与解决方案

RainngCourseFEpackage.jsondependenciesdevDependencies分开声明,这是刻意为之——element-uiaxios等运行时依赖在dependenciesvue-cli-servicesass-loader等构建工具在devDependencies。执行npm install时,npm会根据NODE_ENV决定安装哪些包,但初学者常遇到两种失败:

陷阱一:npm版本过高导致sass编译失败
npm install后运行npm run serve报错sass-loader requires sass,这是因为新版sass-loader(12+)要求Dart Sass,而老项目用的是Node Sass。解决方案是降级:

npm uninstall sass-loader
npm install sass-loader@10.4.1 --save-dev

@10.4.1是最后一个兼容Node Sass的版本,它依赖node-sass@6.0.1,而node-sass需要Python 2.7和Visual Studio Build Tools(Windows)或Xcode Command Line Tools(macOS)。若嫌麻烦,直接用pnpm替代npm:

npm install -g pnpm
pnpm install

pnpm的硬链接机制能避免重复下载,且对sass兼容性更好。

陷阱二:element-ui样式不生效
启动后页面只有文字没有按钮样式,检查浏览器开发者工具Elements面板,发现<link href="/css/chunk-vendors.css">404。这是因为Vue CLI的public/index.html里引用了<link rel="stylesheet" href="<%= BASE_URL %>css/chunk-vendors.css">,但实际打包后CSS文件在dist/css/目录。解决方案是修改vue.config.js

module.exports = {
  publicPath: './', // 关键!改为相对路径
  outputDir: 'dist',
  assetsDir: 'static'
}

publicPath: './'让所有静态资源路径以./开头(如./css/app.css),这样无论部署在http://localhost:8080/还是http://example.com/course/都能正确加载。

启动验证
执行npm run serve后,浏览器打开http://localhost:8080,输入admin/123456登录。此时观察Network面板:
- 登录请求POST /api/login返回200,响应体含token字段;
- 首页请求GET /api/user/info返回200,roles数组含["ADMIN"]
- 菜单栏“课程管理”点击后,发起GET /api/course?page=1&size=10,返回10条课程数据。
这三个请求全部成功,证明前后端通信链路已打通。

3.4 核心业务功能实现:以“班级排课”为例的端到端剖析

排课功能是教务系统最难啃的骨头,这套系统没用算法库,而是用三层校验保障合理性:

第一层:前端约束(防呆设计)
ScheduleForm.vue里,教室选择框el-select的选项来自/api/room接口,但加了禁用逻辑:

<el-select v-model="form.roomId" placeholder="请选择教室" :disabled="!form.courseId || !form.teacherId">
  <el-option v-for="item in roomOptions" :key="item.id" :label="item.roomNumber" :value="item.id"/>
</el-select>

意思是:没选课程和教师前,教室下拉框不可用。这种交互不是炫技,而是防止用户填完教室才发现课程还没选,减少无效操作。

第二层:后端校验(业务规则)
ScheduleService.javasaveSchedule()方法里:

// 校验教师当天是否有课
Long teacherBusyCount = scheduleRepository.countByTeacherIdAndWeekDayAndSessionRange(
    schedule.getTeacherId(), schedule.getWeekDay(), 
    schedule.getSessionStart(), schedule.getSessionEnd());
if (teacherBusyCount > 0) {
    throw new BusinessException("该教师在选定时段已有课程安排");
}

// 校验教室容量是否足够
Room room = roomRepository.findById(schedule.getRoomId()).orElseThrow();
Course course = courseRepository.findById(schedule.getCourseId()).orElseThrow();
if (course.getStudentCount() > room.getCapacity()) {
    throw new BusinessException("教室容量不足,请更换教室");
}

这里countByTeacherIdAndWeekDayAndSessionRange是JPA的衍生查询方法,它自动生成SQL:

SELECT COUNT(*) FROM t_class_schedule 
WHERE teacher_id = ? AND week_day = ? 
AND session_start <= ? AND session_end >= ?

session_start <= ? AND session_end >= ?是典型的时段重叠判断逻辑(当前课的开始时间≤已有课的结束时间,且当前课的结束时间≥已有课的开始时间),比简单比较时间字符串可靠得多。

第三层:数据库唯一索引(终极防线)
t_class_schedule表建有联合唯一索引:

ALTER TABLE `t_class_schedule` 
ADD UNIQUE KEY `uk_teacher_week_session` (`teacher_id`,`week_day`,`session_start`,`session_end`);

这意味着即使前端和后端校验都失效,数据库也会抛出Duplicate entry '1-2-08:00-09:40' for key 'uk_teacher_week_session'异常,保证数据一致性。这种“前端防呆+后端校验+数据库兜底”的三层防护,才是工业级排课的标配。

4. 部署与二次开发实战指南:从本地运行到上线交付

4.1 生产环境打包:前后端分离部署的黄金配置

本地开发用npm run servejava -jar没问题,但上线必须打包。很多人把前端dist目录扔进SpringBoot的src/main/resources/static,这是大忌——它会让SpringBoot同时承担静态资源服务和API服务,一旦流量激增,API响应会因静态文件IO阻塞。正确做法是Nginx反向代理分流

后端打包

cd RainngCourseBE
mvn clean package -Pprod -DskipTests

-Pprod激活application-prod.yml,其中数据库配置指向生产库,server.port=8081保持不变。生成的jar包target/rainng-course-be-1.0.jar直接上传服务器。

前端打包

cd RainngCourseFE
npm run build

生成dist/目录,把整个目录上传到服务器/var/www/course-fe/

Nginx配置(/etc/nginx/conf.d/course.conf)

upstream backend {
    server 127.0.0.1:8081;
}

server {
    listen 80;
    server_name course.example.com;

    # 前端静态资源
    location / {
        root /var/www/course-fe;
        try_files $uri $uri/ /index.html;
    }

    # API接口代理
    location /api/ {
        proxy_pass http://backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

关键点:location /api/的斜杠必须保留,否则proxy_pass http://backend/会把/api/course转发成http://backend/api/course(后端没/api前缀),正确转发是http://backend/course。重启Nginx后,访问http://course.example.com即可。

注意:若服务器内存小于2G,启动jar包时需限制JVM内存:
bash nohup java -Xms512m -Xmx1024m -jar rainng-course-be-1.0.jar --spring.profiles.active=prod > app.log 2>&1 &

4.2 权限系统扩展:从ADMIN/USER两级到多角色精细化控制

默认权限只有ADMIN和USER,但毕业设计常需扩展“教务员”“系主任”角色。扩展步骤如下:

第一步:数据库新增角色

INSERT INTO `t_role` VALUES (3, 'DEAN', '系主任'), (4, 'EDU_OFFICER', '教务员');

第二步:后端添加权限注解
CourseController.java的删除接口上加:

@PreAuthorize("hasRole('ADMIN') or hasRole('DEAN')")
@DeleteMapping("/{id}")
public Result delete(@PathVariable Long id) {
    // ...
}

hasRole('DEAN')表示DEAN角色也能访问,无需修改SecurityConfig。

第三步:前端菜单动态渲染
src/store/modules/user.jssetRoles方法接收后端返回的roles数组(如["ADMIN", "DEAN"]),然后在src/layout/components/Sidebar.vue中:

<el-menu-item v-for="item in menuList" :key="item.path" v-if="checkRole(item.roles)">
  <router-link :to="item.path">{{ item.title }}</router-link>
</el-menu-item>

checkRole(roles)方法:

checkRole(roles) {
  return roles.some(role => this.roles.includes(role));
}

这样,DEAN角色登录后,菜单栏只显示“课程管理”“教师管理”“排课计划”,不显示“权限设置”。

4.3 功能扩展实操:给课程管理添加Excel导入导出

学生常提需求:“能不能批量导入课程?”这功能只需三步:

后端添加POI依赖
pom.xml加入:

<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <version>5.2.4</version>
</dependency>

编写Excel导出接口
CourseController.java新增:

@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
    List<Course> courses = courseService.findAll();
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("课程列表");

    // 表头
    XSSFRow headerRow = sheet.createRow(0);
    String[] headers = {"ID", "课程名称", "学分", "状态"};
    for (int i = 0; i < headers.length; i++) {
        headerRow.createCell(i).setCellValue(headers[i]);
    }

    // 数据行
    for (int i = 0; i < courses.size(); i++) {
        XSSFRow row = sheet.createRow(i + 1);
        Course course = courses.get(i);
        row.createCell(0).setCellValue(course.getId());
        row.createCell(1).setCellValue(course.getName());
        row.createCell(2).setCellValue(course.getCredit());
        row.createCell(3).setCellValue(course.getStatus() == 1 ? "启用" : "停用");
    }

    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setHeader("Content-Disposition", "attachment; filename=courses.xlsx");
    workbook.write(response.getOutputStream());
}

前端添加导出按钮
CourseList.vue里:

<el-button type="primary" @click="exportExcel">导出Excel</el-button>
methods: {
  async exportExcel() {
    try {
      const res = await this.$http.get('/api/course/export', { responseType: 'blob' });
      const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
      const link = document.createElement('a');
      link.href = window.URL.createObjectURL(blob);
      link.download = 'courses.xlsx';
      link.click();
    } catch (error) {
      this.$message.error('导出失败');
    }
  }
}

这段代码的精髓在于responseType: 'blob'——它告诉axios不要把二进制流转成JSON,而是保持原始字节流,否则Excel文件会损坏。

5. 常见问题排查与避坑指南:那些文档里不会写的血泪经验

5.1 “登录成功但首页空白”的五大原因与速查表

现象可能原因排查命令解决方案
Network面板无任何请求前端路由模式错误检查router/index.jsmode: 'history'改为mode: 'hash',URL变成/#/course
请求/api/user/info返回401Token未携带或过期浏览器Application→Storage→Cookies,查看JSESSIONID是否存在清除Cookie后重新登录;检查后端SecurityConfig是否放行/api/user/info
请求返回200但响应体为空后端跨域配置缺失查看响应头是否有Access-Control-Allow-OriginCorsConfig.java里添加registry.addMapping("/api/**").allowedOrigins("*")
表格显示“暂无数据”分页参数传递错误Network→Headers→Query String Parameters,看是否有pagesize检查CourseList.vuefetchCourses()的params是否传入this.pagination.currentPage
页面样式错乱Element UI未正确引入Elements面板搜索<link href="...element-ui..."确认main.jsimport 'element-ui/lib/theme-chalk/index.css'未被注释

5.2 MySQL导入失败的“字符集三连击”

我帮学生远程调试时,90%的SQL导入失败源于字符集。请按顺序执行这三步:

第一步:检查MySQL全局变量

SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';

character_set_server不是utf8mb4,执行:

SET GLOBAL character_set_server = utf8mb4;
SET GLOBAL collation_server = utf8mb4_unicode_ci;

第二步:检查数据库字符集

SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME 
FROM information_schema.SCHEMATA 
WHERE SCHEMA_NAME = 'rainng_course';

若不是utf8mb4,执行:

ALTER DATABASE rainng_course CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

第三步:检查表字符集

SELECT TABLE_NAME, TABLE_COLLATION 
FROM information_schema.TABLES 
WHERE TABLE_SCHEMA = 'rainng_course';

若有表是utf8_general_ci,逐个修复:

ALTER TABLE t_course CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

5.3 Vue CLI代理失效的“端口迷雾”

开发时前端请求/api/course,后端却收不到,常见于端口冲突。速查方法:

确认后端真实端口
RainngCourseBE/src/main/resources/application-dev.yml里:

server:
  port: 8081

不是8080!很多学生以为SpringBoot默认8080,其实application.yml里可能改了。

确认代理目标端口
vue.config.js里:

proxy: {
  '/api': {
    target: 'http://localhost:8081', // 必须和后端port一致
    // ...
  }
}

确认前端启动端口
package.jsonscripts

"serve": "vue-cli-service serve --port 8080"

若没指定--port,默认8080,此时浏览器开http://localhost:8080,代理才能生效。

5.4 毕业设计答辩高频问题预演

Q:为什么用JPA而不是MyBatis?
A:JPA的@Entity注解让Java对象与数据库表一一对应,初学者修改字段时,只需改实体类的@Column和数据库SQL,不用同步维护XML映射文件。更重要的是,JPA的Pageable分页比MyBatis手写LIMIT ? OFFSET ?更不易出错。

Q:排课算法怎么保证不冲突?
A:我们没用遗传算法或约束编程,而是用三层校验:前端禁用冲突时段选项、后端用SQL COUNT查重叠、数据库建唯一索引兜底。这比复杂算法更可靠,因为教务排课的核心是“不允许冲突”,而不是“寻找最优解”。

Q:如何保证管理员密码安全?
A:密码用MD5加盐存储(DigestUtils.md5DigestAsHex((password + "rainng_salt").getBytes())),且application.yml里数据库密码用ENC(XXXX)加密,需配合Jasypt启动参数解密。

Q:系统能支撑多少并发?
A:单机部署下,MySQL连接池设为20,SpringBoot内嵌Tomcat线程池设为200,理论QPS约300。若需更高并发,可加Redis缓存课程列表(@Cacheable注解),将数据库压力降至5%以下。

我在实际带毕设时发现,学生最怕的不是写不出功能,而是答辩时被问住。提前想清楚这四个问题,你的答辩通过率至少提升50%。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:提供一套可直接运行的课程管理全栈项目,后端用SpringBoot开发,前端基于Vue.js搭配Element UI实现响应式界面,前后端完全分离。包含完整的后端服务模块(RainngCourseBE)、前端工程(RainngCourseFE)、MySQL建库建表SQL脚本、详细启动说明文档以及技术截图PDF。默认管理员账号为admin,密码123456,导入SQL后即可启动后端服务和前端项目。功能覆盖课程信息增删改查、教师与学生基础信息维护、班级排课逻辑、用户角色权限控制等典型教务场景。代码结构清晰,无冗余文件,保留.gitignore和.idea等开发配置但不影响打包部署;node_modules未上传,需npm install自行安装依赖。适合Java或Vue初学者实战练习,也适合作为高校计算机类专业毕业设计参考模板,支持快速二次开发与功能扩展。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
内容概要:本文介绍了如何利用 GitHub Copilot 辅助进行程序调试与 Bug 分析,强调 Copilot 不仅可用于代码生成,更是强大的代码分析与调试工具。文章详细阐述了 Copilot 在调试复杂问题、老旧项目维护和难以复现 Bug 场景下的优势,提出了“先分析、再修改”的四步流程:分析原因→评估风险→提出方案→修改代码,并推荐结合错误日志、用户操作等信息精准提问,提升 AI 回答质量。同时展示了如何通过 Copilot 增强调试能力,如自动加日志、异常保护、生成测试数据和性能分析。最后通过游戏拾取系统的实际案例,说明如何结构化描述问题以获得有效反馈。; 适合人群:具备一定开发经验,正在参与项目调试或维护工作的程序员,尤其是面对复杂逻辑、历史代码或难复现 Bug 的 1-3 年开发者;也适合希望提升 AI 协作能力的技术人员。; 使用场景及目标:①快速定位偶发性崩溃、数据异常等问题根源;②理解无文档或结构混乱的老代码模块;③优化调试流程,借助 AI 生成诊断建议、修复方案与测试用例;④构建更具健壮性的程序,提前发现潜在缺陷。; 阅读建议:学习者应结合自身项目中的真实问题,按照文中提供的结构化提问模板实践,逐步训练与 Copilot 的协作能力,重视问题描述的完整性与准确性,避免直接要求修改代码,优先通过分析提升对系统的理解。
内容概要:本文针对高精度电流控制下的永磁同步电机(PMSM)参数辨识难题,提出一种基于粒子群优化算法(PSO)的多参数辨识模型,并在Simulink环境中完成系统级仿真实现。研究旨在克服传统控制中因电机参数(如定子电阻、交直轴电感、永磁磁链等)随温度、负载变化而失配所导致的电流控制性能下降问题。通过构建以电流跟踪误差为核心的适应度函数,利用PSO算法全局寻优能力强的特点,实现对关键电机参数的在线或离线精确辨识。文中详述了PSO算法的实现机制、参数初始化策略、收敛判据设计以及与PMSM矢量控制系统的集成方法,验证了该方案在不同运行工况下的辨识精度、收敛速度与鲁棒性,显著提升了电流环的动态响应品质与稳态控制精度。; 适合人群:具备电机驱动控制、现代控制理论及优化算法基础,熟悉MATLAB/Simulink仿真平台,从事高性能PMSM控制系统研发的研究生、高校科研人员及自动化、电力电子领域的工程师;特别适合正在开展参数自适应、智能控制算法应用等相关课题的研究者。; 使用场景及目标:①应用于高端制造装备、电动汽车驱动系统、精密伺服系统等对电流控制精度要求严苛的场合;②解决实际工程中因电机温升、老化等因素引发的参数漂移问题,提升系统长期运行稳定性;③作为智能优化算法与电机控制深度融合的教学案例,帮助理解PSO在复杂非线性系统参数辨识中的应用逻辑与实现路径。; 阅读建议:建议读者结合提供的Simulink仿真模型进行复现实验,重点剖析PSO算法模块与电机控制模型的接口设计、适应度函数的构建原则及参数敏感性分析方法,可进一步尝试引入其他先进优化算法(如GWO、HHO)进行性能对比,以深入掌握不同智能算法在工程辨识问题中的适用性与优劣。
内容概要:本文提出了一种基于遗传算法的新型任务调度算法,专门针对异构分布式系统中的任务调度问题展开研究。通过Matlab代码实现该算法,旨在优化任务在计算能力不同的节点之间的分配策略,从而提升系统整体的资源利用率和任务执行效率。研究重点包括调度模型的设计、任务映射机制的构建以及遗传算法中编码方式、适应度函数、选择、交叉与变异操作等关键参数的优化配置,确保在复杂多变的异构环境下实现高效、低延迟的任务调度。该方法具有良好的可复现性和扩展性,适用于多种智能优化场景的对比与改进。; 适合人群:具备一定编程基础和优化算法知识,从事分布式系统、云计算、高性能计算或智能优化算法研究的科研人员及研究生。; 使用场景及目标:①应用于云计算平台、边缘计算网络等异构计算环境中的任务调度优化;②为研究人员提供完整的Matlab代码框架,支持算法复现、性能测试及与其他智能调度算法(如粒子群、蚁群等)的对比分析;③支撑高水平学术论文的实验验证与科研项目开发。; 阅读建议:建议读者结合Matlab代码深入理解遗传算法在任务调度中的具体实现细节,重点关注染色体编码策略与适应度函数设计,并可通过调整种群规模、迭代次数、交叉变异概率等参数进一步优化算法性能,探索引入混合优化策略的可能性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值