)
本文还有配套的精品资源点击获取简介提供一套可直接运行的课程管理全栈项目后端用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里所有依赖版本都锁定包括vue2.7.16、element-ui2.15.14、axios1.6.7你执行npm install绝不会出现“版本冲突导致表格错位”的尴尬。这不是一个让你截图交差的玩具而是一块能让你亲手打磨出毕业设计成品的坯料。2. 整体架构设计与技术选型逻辑为什么是这套组合而不是其他方案2.1 前后端分离的底层动因不是跟风而是为降低学习门槛很多初学者一上来就想搞“SpringBootVue全家桶”结果卡在跨域、代理配置、打包路径上三天。这套系统坚持前后端完全分离根本原因在于把“让代码跑起来”这个最脆弱的环节彻底解耦。后端只暴露RESTful API如GET /api/course?page1size10前端通过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-web、spring-boot-starter-data-jpa、spring-boot-starter-security三个核心starter可能有人疑惑现在都2024年了为啥不用MyBatis-Plus的CRUD封装答案很实在——避免抽象层遮蔽SQL本质。初学者如果直接用courseMapper.selectPage()很容易忽略分页查询背后的LIMIT ? OFFSET ?原理更不会意识到COUNT(*)统计总数时若没加索引会导致全表扫描。这套系统坚持手写JPQLJava Persistence Query LanguageQuery(SELECT c FROM Course c WHERE c.name LIKE %?1% AND c.status ?2) PageCourse findByNameAndStatus(String name, Integer status, Pageable pageable);好处是你一眼能看出查询条件对应SQL的WHERE子句Pageable参数明确告诉你分页由JPA自动生成LIMIT/OFFSET而COUNT语句也由框架自动生成。更重要的是所有实体类Course.java、Teacher.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 APIRainngCourseFE用的是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_id、teacher_id、room_id、week_day、session_start、session_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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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.33或Ver 5.7.42则符合要求若为5.6.x需升级。升级不是重装而是下载对应平台的安装包Windows用MSImacOS用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-setutf8mb4 rainng_course database.sql这里--default-character-setutf8mb4是救命参数——它强制MySQL客户端用utf8mb4解析SQL文件否则中文注释里的“教授”二字会被识别为乱码导致建表失败。第三步验证数据完整性导入后别急着启动先检查关键表数据USE rainng_course; SELECT COUNT(*) FROM t_user; -- 应返回1admin用户 SELECT COUNT(*) FROM t_teacher; -- 应返回1张教授 SELECT * FROM t_course LIMIT 1; -- 查看字段值是否正常若t_user表为空说明密码加密字段e10adc3949ba59abbe56e057f20f883e被截断旧版MySQL varchar(32)不够存MD5此时需手动修改t_user表password字段长度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.cnfmy.cnf内容为ini [client] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci3.2 后端服务启动绕过IDEA的坑用Maven命令行保命RainngCourseBE的pom.xml里spring-boot-maven-plugin配置了executabletrue/executable这意味着你可以直接用java -jar运行但初学者常犯两个错误一是用IDEA的绿色三角形按钮启动会忽略application.yml里的spring.profiles.activedev二是没配好MySQL连接池。正确的启动流程是第一步修改数据库配置打开RainngCourseBE/src/main/resources/application-dev.yml找到spring: datasource: url: jdbc:mysql://localhost:3306/rainng_course?useUnicodetruecharacterEncodingutf8serverTimezoneGMT%2B8 username: root password: root把password: root改成你MySQL的实际密码不是SQL文件里的admin密码。注意URL里的serverTimezoneGMT%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.activedev看到控制台输出Started RainngCourseBeApplication in X.XXX seconds即成功。此时访问http://localhost:8081/swagger-ui.html能看到所有API文档——这是比写代码更重要的环节点开CourseController的GET /api/course点击“Try it out”再点“Execute”如果返回JSON数据证明后端已活。注意若报错java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException说明你用了JDK 11JAXB被移除。解决方案是在pom.xml添加xml dependency groupIdjavax.xml.bind/groupId artifactIdjaxb-api/artifactId version2.3.1/version /dependency3.3 前端工程启动npm install的隐藏陷阱与解决方案RainngCourseFE的package.json里dependencies和devDependencies分开声明这是刻意为之——element-ui、axios等运行时依赖在dependenciesvue-cli-service、sass-loader等构建工具在devDependencies。执行npm install时npm会根据NODE_ENV决定安装哪些包但初学者常遇到两种失败陷阱一npm版本过高导致sass编译失败npm install后运行npm run serve报错sass-loader requires sass这是因为新版sass-loader12要求Dart Sass而老项目用的是Node Sass。解决方案是降级npm uninstall sass-loader npm install sass-loader10.4.1 --save-dev10.4.1是最后一个兼容Node Sass的版本它依赖node-sass6.0.1而node-sass需要Python 2.7和Visual Studio Build ToolsWindows或Xcode Command Line ToolsmacOS。若嫌麻烦直接用pnpm替代npmnpm install -g pnpm pnpm installpnpm的硬链接机制能避免重复下载且对sass兼容性更好。陷阱二element-ui样式不生效启动后页面只有文字没有按钮样式检查浏览器开发者工具Elements面板发现link href/css/chunk-vendors.css404。这是因为Vue CLI的public/index.html里引用了link relstylesheet href% BASE_URL %css/chunk-vendors.css但实际打包后CSS文件在dist/css/目录。解决方案是修改vue.config.jsmodule.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返回200roles数组含[ADMIN]- 菜单栏“课程管理”点击后发起GET /api/course?page1size10返回10条课程数据。这三个请求全部成功证明前后端通信链路已打通。3.4 核心业务功能实现以“班级排课”为例的端到端剖析排课功能是教务系统最难啃的骨头这套系统没用算法库而是用三层校验保障合理性第一层前端约束防呆设计ScheduleForm.vue里教室选择框el-select的选项来自/api/room接口但加了禁用逻辑el-select v-modelform.roomId placeholder请选择教室 :disabled!form.courseId || !form.teacherId el-option v-foritem in roomOptions :keyitem.id :labelitem.roomNumber :valueitem.id/ /el-select意思是没选课程和教师前教室下拉框不可用。这种交互不是炫技而是防止用户填完教室才发现课程还没选减少无效操作。第二层后端校验业务规则ScheduleService.java的saveSchedule()方法里// 校验教师当天是否有课 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的衍生查询方法它自动生成SQLSELECT 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 serve和java -jar没问题但上线必须打包。很多人把前端dist目录扔进SpringBoot的src/main/resources/static这是大忌——它会让SpringBoot同时承担静态资源服务和API服务一旦流量激增API响应会因静态文件IO阻塞。正确做法是Nginx反向代理分流后端打包cd RainngCourseBE mvn clean package -Pprod -DskipTests-Pprod激活application-prod.yml其中数据库配置指向生产库server.port8081保持不变。生成的jar包target/rainng-course-be-1.0.jar直接上传服务器。前端打包cd RainngCourseFE npm run build生成dist/目录把整个目录上传到服务器/var/www/course-fe/。Nginx配置/etc/nginx/conf.d/course.confupstream 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.activeprod app.log 21 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.js里setRoles方法接收后端返回的roles数组如[ADMIN, DEAN]然后在src/layout/components/Sidebar.vue中el-menu-item v-foritem in menuList :keyitem.path v-ifcheckRole(item.roles) router-link :toitem.path{{ item.title }}/router-link /el-menu-itemcheckRole(roles)方法checkRole(roles) { return roles.some(role this.roles.includes(role)); }这样DEAN角色登录后菜单栏只显示“课程管理”“教师管理”“排课计划”不显示“权限设置”。4.3 功能扩展实操给课程管理添加Excel导入导出学生常提需求“能不能批量导入课程”这功能只需三步后端添加POI依赖pom.xml加入dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.4/version /dependency编写Excel导出接口CourseController.java新增GetMapping(/export) public void export(HttpServletResponse response) throws IOException { ListCourse 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; filenamecourses.xlsx); workbook.write(response.getOutputStream()); }前端添加导出按钮CourseList.vue里el-button typeprimary clickexportExcel导出Excel/el-buttonmethods: { 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.js里mode: history改为mode: hashURL变成/#/course请求/api/user/info返回401Token未携带或过期浏览器Application→Storage→Cookies查看JSESSIONID是否存在清除Cookie后重新登录检查后端SecurityConfig是否放行/api/user/info请求返回200但响应体为空后端跨域配置缺失查看响应头是否有Access-Control-Allow-Origin在CorsConfig.java里添加registry.addMapping(/api/**).allowedOrigins(*)表格显示“暂无数据”分页参数传递错误Network→Headers→Query String Parameters看是否有page和size检查CourseList.vue里fetchCourses()的params是否传入this.pagination.currentPage页面样式错乱Element UI未正确引入Elements面板搜索link href...element-ui...确认main.js里import 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.json里scriptsserve: vue-cli-service serve --port 8080若没指定--port默认8080此时浏览器开http://localhost:8080代理才能生效。5.4 毕业设计答辩高频问题预演Q为什么用JPA而不是MyBatisAJPA的Entity注解让Java对象与数据库表一一对应初学者修改字段时只需改实体类的Column和数据库SQL不用同步维护XML映射文件。更重要的是JPA的Pageable分页比MyBatis手写LIMIT ? OFFSET ?更不易出错。Q排课算法怎么保证不冲突A我们没用遗传算法或约束编程而是用三层校验前端禁用冲突时段选项、后端用SQLCOUNT查重叠、数据库建唯一索引兜底。这比复杂算法更可靠因为教务排课的核心是“不允许冲突”而不是“寻找最优解”。Q如何保证管理员密码安全A密码用MD5加盐存储DigestUtils.md5DigestAsHex((password rainng_salt).getBytes())且application.yml里数据库密码用ENC(XXXX)加密需配合Jasypt启动参数解密。Q系统能支撑多少并发A单机部署下MySQL连接池设为20SpringBoot内嵌Tomcat线程池设为200理论QPS约300。若需更高并发可加Redis缓存课程列表Cacheable注解将数据库压力降至5%以下。我在实际带毕设时发现学生最怕的不是写不出功能而是答辩时被问住。提前想清楚这四个问题你的答辩通过率至少提升50%。本文还有配套的精品资源点击获取简介提供一套可直接运行的课程管理全栈项目后端用SpringBoot开发前端基于Vue.js搭配Element UI实现响应式界面前后端完全分离。包含完整的后端服务模块RainngCourseBE、前端工程RainngCourseFE、MySQL建库建表SQL脚本、详细启动说明文档以及技术截图PDF。默认管理员账号为admin密码123456导入SQL后即可启动后端服务和前端项目。功能覆盖课程信息增删改查、教师与学生基础信息维护、班级排课逻辑、用户角色权限控制等典型教务场景。代码结构清晰无冗余文件保留.gitignore和.idea等开发配置但不影响打包部署node_modules未上传需npm install自行安装依赖。适合Java或Vue初学者实战练习也适合作为高校计算机类专业毕业设计参考模板支持快速二次开发与功能扩展。本文还有配套的精品资源点击获取