简介这是一套基于SpringBoot后端与Vue前端构建的多端教务管理系统源码同时兼容微信小程序运行环境面向教育培训机构的技术开发者与二次开发学习者用于解决多校区教务协同、招生财务与在线教学等业务场景的数字化管理问题。压缩包共359个文件约6.98MB其中Java源码260个构成核心业务逻辑另有27张PNG与1张JPG图形资源、4个XML与2个YML配置文件、properties属性文件及md说明文档等目录结构清晰便于按模块检索与二次开发。系统支持跨区域多校区运营功能覆盖课程排期、学员档案、财务稽核、招生渠道、团体报名分销以及直播录播教学并提供PC管理后台、家长小程序与微信小程序管理端三种访问方式。目前已有70人学习下载适合希望研究多端教务系统架构、借鉴模块化设计或进行功能裁剪复用的开发者参考。1. 多端教务系统为什么值得用 SpringBoot Vue 重做一遍很多学校或培训机构的教务系统PC 端还在跑十年前的老框架小程序端干脆没有排课、查成绩、请假全靠群里吼。这套「基于 SpringBoot 与 Vue 的多端教务管理系统源码」要解决的就是一套后端同时喂饱 PC 管理后台和微信小程序两个前端。后端用 SpringBoot 提供 REST 接口PC 端用 Vue 做管理界面小程序端复用同一套接口做学生和教师入口。适合谁适合手里有教务需求、想拿一套能跑通的源码做二次开发的团队也适合想练「一套后端多端复用」这种工程结构的开发者。下面按「先立结构、再动手、最后避坑」的顺序拆开讲。2. 多端教务系统的技术选型与工程结构怎么定2.1 为什么后端选 SpringBoot 而不是别的教务系统的核心是数据一致性和接口稳定性不是高并发秒杀。SpringBoot 的自动配置、起步依赖、内嵌容器让一个教务后端能在半小时内跑起来这对中小团队来说比什么都实在。常见做法是用 SpringBoot 2.7.x 或 3.x配 MyBatis-Plus 做 ORMMySQL 8 做存储Redis 做 token 缓存和验证码。选 MyBatis-Plus 而不是 JPA是因为教务系统里大量「按班级查、按学期查、按教师查」的条件查询手写 SQL 更可控。这里有个选型边界要说清楚如果你的教务系统要对接学校已有的统一身份认证CAS 或 OAuth2SpringBoot 的 Security 模块能直接接但配置量不小。我一般会先把认证抽成一个独立 filter别和业务代码搅在一起不然后面换认证方式会翻车。2.2 一套后端怎么同时服务 PC 和微信小程序关键在接口层做「端无关」。后端只暴露/api/**的 REST 接口返回 JSONPC 端和小程序端各自封装请求。区别只在两处一是登录态PC 用 JWT 放 header小程序用wx.login拿 code 换 openid 再签发 JWT二是权限PC 管理员能调所有接口小程序学生只能调自己相关的接口。// 统一返回结构两端共用 public class ResultT { private int code; // 200 成功401 未登录403 无权限 private String msg; private T data; // 省略 getter/setter } // 登录接口PC 和小程序走不同分支但返回同一结构 PostMapping(/api/login) public ResultLoginVO login(RequestBody LoginDTO dto) { if (MINI.equals(dto.getClientType())) { // 小程序code 换 openid String openid wxService.code2Openid(dto.getCode()); return Result.ok(authService.loginByOpenid(openid)); } // PC账号密码 return Result.ok(authService.loginByPassword(dto.getUsername(), dto.getPassword())); }逻辑说明clientType字段区分端避免写两套登录接口。参数说明code是小程序wx.login返回的临时凭证五分钟内有效必须后端换 openid不能前端直接拿。Result统一结构让两端的前端拦截器能写一套。2.3 工程目录怎么分才不乱我一般按「模块 分层」来分别一上来就微服务教务系统单体足够。edu-system/ ├── edu-common/ // 工具类、统一返回、异常 ├── edu-model/ // 实体、DTO、VO ├── edu-mapper/ // MyBatis-Plus mapper ├── edu-service/ // 业务逻辑 ├── edu-web/ // Controller 启动类 └── edu-mini/ // 小程序端独立仓库或子目录PC 端 Vue 项目单独一个仓库用vue-cli或vite建通过axios调/api。小程序端用原生或 uniapp同样调/api。三个仓库共享一份接口文档推荐用 Knife4j 自动生成别手写文档改接口必忘。提示跨域问题在开发阶段用 SpringBoot 的CorsFilter解决生产环境用 Nginx 反代别在代码里写死CrossOrigin。3. 从零跑通后端SpringBoot 教务接口的最小实现3.1 环境准备与依赖版本怎么锁先把版本锁死这是血泪经验。SpringBoot 版本太高比如 3.2会导致部分老依赖不兼容我一般用 2.7.18 这个长期维护版。JDK 用 8 或 11MySQL 8.0Redis 6。!-- pom.xml 关键依赖 -- parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency /dependencies参数说明mybatis-plus3.5.x 和 SpringBoot 2.7 兼容性最好别升到 3.5.5有分页插件行为变化。mysql-connector-java8.0.33 是 8.x 里稳定的一个版本。3.2 教务核心表怎么设计教务系统绕不开这几张表学生、教师、课程、班级、选课、成绩、排课。别一上来搞几十张表先把核心七张建好。CREATE TABLE course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL COMMENT 课程名, teacher_id BIGINT NOT NULL COMMENT 授课教师, credit DECIMAL(3,1) DEFAULT 0 COMMENT 学分, semester VARCHAR(16) NOT NULL COMMENT 学期如2024-1, max_students INT DEFAULT 60, INDEX idx_semester (semester) ) COMMENT 课程表; 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_stu_course (student_id, course_id) ) COMMENT 选课表;逻辑说明course_selection上的唯一索引是防重复选课的第一道闸别只靠代码判断并发下会漏。semester加索引是因为查课表几乎都按学期过滤。3.3 选课接口怎么写才不超卖选课是教务系统里唯一有并发压力的地方。常见做法是「Redis 预扣 数据库唯一索引兜底」。Transactional public Result? selectCourse(Long studentId, Long courseId) { String key course:stock: courseId; // 1. Redis 预扣库存 Long remain redisTemplate.opsForValue().decrement(key); if (remain null || remain 0) { redisTemplate.opsForValue().increment(key); // 回补 return Result.fail(课程已满); } try { // 2. 写选课记录唯一索引兜底 selectionMapper.insert(new CourseSelection(studentId, courseId)); } catch (DuplicateKeyException e) { redisTemplate.opsForValue().increment(key); // 回补 return Result.fail(已选过该课程); } return Result.ok(); }逻辑说明先扣 Redis 再写库Redis 挡住绝大部分并发数据库唯一索引挡住重复。参数说明decrement返回扣减后的值小于 0 说明超卖必须回补。注意 Redis 库存要在课程创建时初始化别忘。注意Redis 和数据库不是强一致如果写库失败要回补 Redis这段逻辑必须放在 catch 里否则库存会越扣越少。4. PC 管理后台与微信小程序怎么接同一套接口4.1 Vue 管理后台的请求封装与路由PC 端用 Vue3 Element Plus请求统一走 axios 实例拦截器里塞 token。// src/utils/request.js import axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) config.headers.Authorization Bearer ${token} return config }) service.interceptors.response.use( res { if (res.data.code 401) { // token 过期跳登录 window.location.href /login } return res.data }, err Promise.reject(err) ) export default service逻辑说明拦截器统一处理 token 和 401业务代码里不用重复写。参数说明baseURL在开发环境用vite的 proxy 代理到后端生产环境用 Nginx。4.2 微信小程序登录怎么拿手机号小程序登录分两步先wx.login拿 code 换 openid再getPhoneNumber拿手机号。注意getPhoneNumber必须由用户点击按钮触发不能自动调。// 小程序端 wx.login({ success: res { wx.request({ url: https://your-domain/api/login, method: POST, data: { clientType: MINI, code: res.code }, success: r { wx.setStorageSync(token, r.data.data.token) } }) } }) // 获取手机号按钮 bindgetphonenumber 触发 onGetPhone(e) { const { code } e.detail wx.request({ url: https://your-domain/api/bindPhone, method: POST, header: { Authorization: Bearer ${wx.getStorageSync(token)} }, data: { code } }) }逻辑说明code每次都是新的用完即废。参数说明bindPhone接口后端用 code 调微信接口换手机号别在前端解。小程序顶部导航栏高度用wx.getSystemInfoSync().statusBarHeight拿别写死。4.3 两端权限怎么统一管后端用注解 拦截器做权限。PC 管理员角色ADMIN学生STUDENT教师TEACHER。Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String[] value(); } // 拦截器里校验 if (method.isAnnotationPresent(RequireRole.class)) { String[] roles method.getAnnotation(RequireRole.class).value(); if (!Arrays.asList(roles).contains(currentUser.getRole())) { throw new ForbiddenException(); } }逻辑说明注解标在 Controller 方法上拦截器统一校验业务代码干净。参数说明currentUser从 JWT 解析别每次查库。5. 多端教务系统踩过的坑与排查清单5.1 小程序登录态频繁失效现象学生用小程序查成绩几分钟就提示重新登录。原因JWT 过期时间设太短或者小程序端没做 token 刷新。解决JWT 设 7 天配一个 refreshToken 接口token 快过期时静默刷新。别把过期时间设成 30 分钟教务系统不是银行。5.2 选课接口偶发超卖现象课程限 60 人结果选了 62 个。原因只用了数据库唯一索引没做 Redis 预扣或者 Redis 库存没初始化。解决课程创建时set course:stock:{id} max_students选课走 Redis 预扣。如果 Redis 挂了降级到数据库悲观锁SELECT ... FOR UPDATE。5.3 Vue 打包后放进 SpringBoot 静态目录 404现象PC 端npm run build后丢进static刷新页面 404。原因Vue 是单页应用路由用 history 模式刷新时后端找不到对应路径。解决SpringBoot 加一个 fallback 配置把所有非/api请求转发到index.html。Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/**) .addResourceLocations(classpath:/static/) .resourceChain(true) .addResolver(new PathResourceResolver() { Override protected Resource getResource(String path, Resource location) throws IOException { Resource resource location.createRelative(path); if (resource.exists() resource.isReadable()) return resource; return new ClassPathResource(/static/index.html); } }); } }5.4 微信小程序请求域名没配现象小程序真机调试请求失败开发者工具正常。原因小程序后台没配request合法域名。解决登录小程序管理后台在「开发设置」里把后端域名加进request合法域名。注意必须是 HTTPS且不能带端口。5.5 数据库时区导致排课时间错乱现象排课时间存进去差 8 小时。原因MySQL 时区和 JVM 时区不一致。解决JDBC URL 加serverTimezoneAsia/ShanghaiJVM 启动参数加-Duser.timezoneAsia/Shanghai两边对齐。6. 让这套源码真正跑起来的一个关键技巧前面讲的都是「怎么搭」最后说一个让整套系统从「能跑」到「好用」的技巧把接口文档和前端类型定义打通。教务系统接口多PC 和小程序两端都要调手写类型定义必错。我一般用 Knife4j 生成 OpenAPI JSON再用openapi-typescript生成 TS 类型PC 端直接引用小程序端用miniprogram-api-typings配合。# 生成 PC 端 TS 类型 npx openapi-typescript http://localhost:8080/v3/api-docs -o src/types/api.d.ts这样后端改字段前端重新生成一次就能发现所有受影响的地方比人肉对文档靠谱得多。参数说明/v3/api-docs是 SpringDoc 的默认路径如果用 Knife4j 就是/v3/api-docs/default。验证方法改一个接口的返回字段名重新生成类型看 PC 端tsc是否报错。报错说明类型链路通了不报错说明哪里断了。我自己的习惯是每加一个接口先写 OpenAPI 注解再写 Controller最后生成类型给前端。这个顺序反过来后面补注解会补到怀疑人生。这套多端教务系统源码的价值不在代码本身而在「一套后端喂两端」这个结构你把这个结构吃透换成任何业务都能套。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?