ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于微服务架构的学校培训管理系统毕业设计实战指南

基于微服务架构的学校培训管理系统毕业设计实战指南 简介这份资源是面向高校计算机相关专业学生与Java开发学习者的微服务架构实战项目可直接用于毕业设计或课程设计。项目采用前后端分离方案前端基于Vue.js搭配Element UI与Ant Design Vue组件库后端以Spring Boot为核心通过MyBatis Plus操作MySQL借助Dubbo与OpenFeign完成跨服务调用并使用Nacos作为注册中心与配置中心。功能覆盖学员报名、签到、分组、座位分配、云直播、在线考试、结业证下载等培训业务模块同时包含系统日志、基础参数管理与网关非法请求过滤。压缩包共913个文件约2.28MB以454个Java源码、102个Vue组件、93个JS脚本及69个XML配置为主另含Dockerfile、SQL脚本与Jenkinsfile等部署运维文件代码可直接编译运行。目前已有389人学习适合需要完整微服务案例、分布式调用与容器化部署参考的读者。1. 微服务架构的学校培训管理系统毕业设计选它到底图什么如果你正在翻毕业设计选题看到“基于微服务架构的学校培训管理系统”这个题目第一反应大概率是听起来比图书管理系统高级但会不会把自己埋进坑里我带过几届学生的课程设计和毕设也帮人排查过不少“跑不起来”的项目说句实在话这个题目的性价比取决于你怎么拆。学校培训管理系统本身业务不复杂——学员报名、课程排期、缴费记录、考勤签到、结课评价这些功能用单体 Spring Boot 一个 jar 就能写完。但一旦挂上“微服务架构”四个字答辩老师会追问服务怎么拆、注册中心用什么、网关怎么配、分布式事务怎么处理。所以这个题目的真正价值不是业务多难而是让你在可控范围内把微服务那套东西跑通一遍。适合计算机毕业设计、软件工程课程设计也适合想拿一个能写进简历的 Java 项目练手的人。下面我按实际落地的顺序把选型、拆分、编码、踩坑一条线讲清楚。2. 服务怎么拆从培训业务里切出四个能独立跑的模块2.1 先定边界再写代码别按数据库表拆服务很多人做微服务毕设第一步就翻车打开数据库看到几张表就建几个服务。学员表一个服务、课程表一个服务、订单表一个服务结果一个报名动作要跨三个服务查数据接口调用来回绕最后自己都理不清。正确的做法是按业务能力拆不是按数据表拆。学校培训管理系统的核心业务能力其实就四块用户与权限、课程与排期、报名与缴费、考勤与评价。每一块内部的数据强相关块与块之间通过明确的接口交互。我一般会拆成这四个服务服务名职责核心表auth-service登录、注册、角色权限、JWT 签发user、role、user_rolecourse-service课程 CRUD、班级排期、讲师分配course、class_schedule、teacherenroll-service报名下单、缴费记录、订单状态enrollment、paymentattend-service签到打卡、考勤统计、结课评价attendance、evaluation这样拆的好处是每个服务可以独立启动、独立建库服务之间只通过 HTTP 或消息队列通信。答辩的时候老师问“为什么这么拆”你就说“按业务能力边界拆分每个服务对应一个限界上下文数据私有避免跨服务直接查库”。这句话比“因为表多”有说服力得多。2.2 技术选型Spring Cloud Alibaba 是毕设最稳的一套选型这件事我踩过最大的坑就是追新。有一年学生非要上 Spring Cloud Gateway Consul gRPC结果环境配了三天代码没写几行。对于毕业设计和课程设计时间有限稳定比先进重要。目前国内 Java 微服务毕设最稳的组合是 Spring Cloud Alibaba 全家桶注册中心与配置中心Nacos一个组件顶两个用省事网关Spring Cloud Gateway路由转发和鉴权过滤都在这里做服务调用OpenFeign声明式调用代码量少熔断限流Sentinel可选但加上去答辩加分分布式事务Seata只在报名扣名额和缴费这种场景用别全局上版本上Spring Boot 用 2.7.x 或 3.x 都行但要注意 Spring Cloud Alibaba 的版本对应关系。我一般建议用 Spring Boot 2.7.18 Spring Cloud 2021.0.9 Spring Cloud Alibaba 2021.0.5.0这套组合资料多、坑少。如果你学校要求用 JDK 17那就上 Spring Boot 3.2.x Spring Cloud 2023.0.x Spring Cloud Alibaba 2023.0.1.0但 Nacos 版本要跟着升到 2.3.x。注意Nacos 默认用嵌入式 Derby 存配置毕设演示够用但如果你重启 Nacos 后发现配置丢了换成 MySQL 存储建库脚本在 Nacos 的 conf 目录里。2.3 用 Docker Compose 把 Nacos 和 MySQL 先拉起来环境搭建是第一个劝退点。我见过太多人卡在“Nacos 启动报错”上一卡就是一下午。最省事的办法是用 Docker Compose 把中间件一次性拉起来别在 Windows 上折腾安装包。# docker-compose.yml version: 3.8 services: mysql: image: mysql:8.0 container_name: training-mysql environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: training_system ports: - 3306:3306 volumes: - ./mysql-data:/var/lib/mysql - ./init.sql:/docker-entrypoint-initdb.d/init.sql command: --default-authentication-pluginmysql_native_password nacos: image: nacos/nacos-server:v2.3.0 container_name: training-nacos environment: MODE: standalone SPRING_DATASOURCE_PLATFORM: mysql MYSQL_SERVICE_HOST: mysql MYSQL_SERVICE_DB_NAME: nacos_config MYSQL_SERVICE_USER: root MYSQL_SERVICE_PASSWORD: root123 ports: - 8848:8848 - 9848:9848 depends_on: - mysql这段 Compose 文件做了三件事启动 MySQL 并自动执行初始化 SQL启动 Nacos 单机模式并让它把配置存到 MySQL暴露 Nacos 控制台端口 8848。init.sql里你要提前建好nacos_config库和业务库training_system。启动命令就一句docker compose up -d等半分钟访问http://localhost:8848/nacos默认账号密码都是 nacos。这一步跑通后面写代码才有底气。3. 从零搭一个能注册能调用的最小服务3.1 父工程与依赖管理把版本锁死在一个地方微服务项目最怕版本冲突。四个服务如果各自引不同版本的 Spring Cloud启动时各种NoSuchMethodError。我一般建一个 Maven 父工程用dependencyManagement统一锁版本子服务只引 starter不写版本号。!-- 父工程 pom.xml 关键片段 -- parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent properties spring-cloud.version2021.0.9/spring-cloud.version spring-cloud-alibaba.version2021.0.5.0/spring-cloud-alibaba.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-dependencies/artifactId version${spring-cloud.version}/version typepom/type scopeimport/scope /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version${spring-cloud-alibaba.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement父工程只做依赖管理不写业务代码。子服务继承父工程后引 Nacos Discovery 和 OpenFeign 都不需要写版本号。这样做的逻辑是版本只在父工程改一次所有服务同步生效。参数上唯一要注意的是spring-cloud-alibaba.version必须和 Spring Cloud 版本匹配2021.0.5.0 对应 Spring Cloud 2021.0.x别乱配。3.2 auth-service 的注册与登录JWT 怎么发怎么验auth-service 是整个系统的入口负责发 token。我见过有人把登录逻辑写在网关里结果网关又连数据库又发 token职责混乱。正确做法是网关只做路由和 token 校验登录接口转发到 auth-service。// AuthController.java 核心登录逻辑 PostMapping(/login) public Result login(RequestBody LoginDTO dto) { // 1. 查用户 User user userMapper.selectByUsername(dto.getUsername()); if (user null || !passwordEncoder.matches(dto.getPassword(), user.getPassword())) { return Result.fail(用户名或密码错误); } // 2. 签发 JWT有效期 2 小时 String token JwtUtil.createToken(user.getId(), user.getUsername(), user.getRole()); // 3. 把 token 和用户信息返回 MapString, Object data new HashMap(); data.put(token, token); data.put(username, user.getUsername()); data.put(role, user.getRole()); return Result.success(data); }这段代码的逻辑很直白校验密码用 BCrypt别用 MD5答辩时老师问密码安全你能答上来。JWT 的 payload 里放 userId、username、role过期时间设 2 小时毕设演示够用。JwtUtil用 hutool 或者 jjwt 都行密钥写在 Nacos 配置里别硬编码在代码中。参数说明passwordEncoder是 BCryptPasswordEncoder 的实例注册时存的是加密后的密码Result是统一返回体包含 code、msg、data 三个字段。3.3 course-service 与 enroll-service 的 Feign 调用报名服务需要查课程信息比如课程还剩多少名额、上课时间是否冲突。这时候用 OpenFeign 调 course-service而不是直接查 course 库。// enroll-service 中的 Feign 客户端 FeignClient(name course-service, fallback CourseClientFallback.class) public interface CourseClient { GetMapping(/course/{id}) ResultCourseVO getCourseById(PathVariable(id) Long id); PostMapping(/course/{id}/decrease) ResultBoolean decreaseQuota(PathVariable(id) Long id); }FeignClient的name写的是 Nacos 里注册的服务名不是 IP。fallback是熔断降级类当 course-service 挂了或者超时返回一个兜底结果避免报名服务跟着崩。这里有个细节PathVariable必须显式写 value否则 Feign 在拼 URL 时会报错。调用逻辑是 enroll-service 先查课程确认有名额再调decreaseQuota扣减扣减成功才写报名记录。如果扣减失败报名直接返回“名额不足”。提示Feign 默认超时是 1 秒如果 course-service 响应慢报名会失败。在 Nacos 配置里加feign.client.config.default.readTimeout5000把超时调到 5 秒。4. 网关、鉴权与分布式事务三个最容易翻车的地方4.1 网关路由配置把 /api/auth/** 转发到 auth-service网关是微服务的门面所有外部请求先到网关再由网关转发到具体服务。Spring Cloud Gateway 的路由配置可以写在 Nacos 里也可以写在本地application.yml。毕设建议写本地方便调试。# gateway 的 application.yml spring: cloud: gateway: routes: - id: auth-service uri: lb://auth-service predicates: - Path/api/auth/** filters: - StripPrefix1 - id: course-service uri: lb://course-service predicates: - Path/api/course/** filters: - StripPrefix1 - id: enroll-service uri: lb://enroll-service predicates: - Path/api/enroll/** filters: - StripPrefix1uri里的lb://表示从 Nacos 拉服务列表做负载均衡不是写死 IP。StripPrefix1表示转发时去掉第一层路径比如/api/auth/login转发到 auth-service 时变成/login。这样网关只负责路由业务路径由各服务自己定义。鉴权过滤器在网关层做校验请求头里的 token校验通过放行不通过直接返回 401。注意白名单要放行/api/auth/login和/api/auth/register否则登录都进不去。4.2 分布式事务报名扣名额和缴费怎么保证一致这是微服务毕设最容易被问倒的地方。报名服务扣课程名额同时要写缴费记录两个操作在不同服务、不同数据库。如果扣完名额缴费失败名额就白白少了。解决方案用 Seata 的 AT 模式在报名方法上加GlobalTransactional。// enroll-service 中的报名方法 GlobalTransactional(name enroll-tx, rollbackFor Exception.class) public Result enroll(Long courseId, Long userId) { // 1. 远程扣减课程名额 ResultBoolean decreaseResult courseClient.decreaseQuota(courseId); if (!decreaseResult.getData()) { throw new RuntimeException(名额不足); } // 2. 本地写报名记录 Enrollment enrollment new Enrollment(); enrollment.setCourseId(courseId); enrollment.setUserId(userId); enrollment.setStatus(PENDING); enrollmentMapper.insert(enrollment); // 3. 模拟缴费失败则全局回滚 if (!mockPayment(userId, courseId)) { throw new RuntimeException(缴费失败); } return Result.success(报名成功); }GlobalTransactional的作用是只要方法里任何一步抛异常Seata 会通知 course-service 把扣掉的名额加回来。参数上name是事务名随便起但要有意义rollbackFor指定哪些异常触发回滚。注意 Seata 需要每个服务的数据库里建 undo_log 表脚本在 Seata 的 GitHub 仓库里有。如果你觉得 Seata 太重毕设也可以用“本地消息表 定时补偿”代替但答辩时 Seata 更拿得出手。4.3 用 Sentinel 给报名接口加限流答辩老师喜欢问“高并发怎么办”。虽然学校培训系统没多少并发但你可以用 Sentinel 给报名接口加个限流展示你知道怎么保护服务。在 enroll-service 里引 Sentinel 依赖然后在 Nacos 里配规则。// 在报名接口上加 Sentinel 资源注解 SentinelResource(value enroll, blockHandler enrollBlockHandler) PostMapping(/enroll) public Result enroll(RequestParam Long courseId, RequestParam Long userId) { return enrollService.enroll(courseId, userId); } // 被限流后的兜底方法 public Result enrollBlockHandler(Long courseId, Long userId, BlockException ex) { return Result.fail(当前报名人数过多请稍后再试); }SentinelResource的value是资源名在 Sentinel 控制台里按这个名配限流规则比如 QPS 超过 10 就拒绝。blockHandler指定被限流后调用的方法参数要和原方法一致最后多一个BlockException。这样即使有人恶意刷报名接口系统也不会崩。参数上限流阈值根据你演示的机器性能设一般 QPS 设 5 到 10 就行别设太高看不出效果。5. 避坑与排查那些让我熬夜到凌晨的报错5.1 Nacos 注册不上服务列表里空空如也现象服务启动日志显示nacos registry, DEFAULT_GROUP xxx register finished但 Nacos 控制台的服务列表里就是没有。原因通常是 Nacos 2.x 除了 8848 端口还需要 9848 端口做 gRPC 通信。如果你用 Docker 只映射了 8848服务注册会失败。解决在docker-compose.yml里把 9848 也映射出来或者直接用 host 网络模式。另外检查spring.cloud.nacos.discovery.server-addr是不是写成了127.0.0.1:8848在 Docker 里跑的服务要用宿主机 IP 或 Docker 网络别名。5.2 Feign 调用报 404路径拼错了现象enroll-service 调 course-service 的/course/1返回 404。原因通常是 Feign 客户端接口上的GetMapping路径和 course-service Controller 里的路径不一致。比如 Feign 写的是/course/{id}但 Controller 写的是/courses/{id}差一个字母就 404。解决把 Feign 接口的路径和 Controller 的RequestMapping对齐建议先写 Controller 再照着写 Feign。另外注意PathVariable的 value 必须和路径变量名一致。5.3 Seata 回滚不生效undo_log 表没建现象报名方法抛异常了但 course-service 扣掉的名额没有加回来。原因通常是 course-service 的数据库里没有建undo_log表Seata 没法记录回滚日志。解决在每个参与全局事务的数据库里执行 Seata 提供的undo_log建表语句。另外检查seata.enabledtrue和seata.application-id是否配了事务分组名要和 Seata Server 的配置一致。5.4 网关转发后 401token 校验把登录接口也拦了现象访问/api/auth/login返回 401根本没法登录。原因是网关的鉴权过滤器没有放行登录和注册接口。解决在过滤器里维护一个白名单比如/api/auth/login、/api/auth/register这些路径直接放行不校验 token。白名单建议写在 Nacos 配置里改的时候不用重启网关。5.5 跨服务查数据别直接连别人的库现象enroll-service 为了查课程名直接连了 course 库的course表。原因图省事。解决这是微服务的大忌答辩时被指出来很扣分。正确做法是通过 Feign 调 course-service 的接口拿数据或者用消息队列做数据同步。如果嫌 Feign 慢可以在 enroll-service 本地建一张冗余表通过 MQ 异步更新但毕设用 Feign 就够了。6. 答辩前怎么验证你的微服务真的跑通了6.1 用 Nacos 控制台做一次服务健康检查答辩前一周把四个服务和网关全部启动打开 Nacos 控制台看服务列表里是不是有五个服务auth-service、course-service、enroll-service、attend-service、gateway。点进每个服务的详情看实例数是不是 1健康状态是不是 true。如果某个服务显示不健康点进去看心跳时间超过 15 秒没心跳就是挂了。这一步能提前发现注册问题别等到答辩现场才发现服务没起来。6.2 用 Postman 跑一遍完整业务链路从登录开始拿 token然后调课程列表、报名、签到、评价每个接口都跑一遍。重点看报名接口先查课程名额报名成功后名额减一再查报名记录有没有生成。如果报名失败看是 Feign 超时还是 Seata 回滚。我一般会准备一个api-test.http文件把请求按顺序写好答辩前跑一遍两分钟就能确认系统是否正常。6.3 故意制造一次故障看熔断降级把 course-service 停掉然后调报名接口。如果配置了 Sentinel 或 Feign 的 fallback应该返回“课程服务暂时不可用”而不是一堆异常堆栈。这个演示在答辩时很加分老师会觉得你考虑了容错。注意停服务之前先把 Nacos 里的实例下线否则 Feign 还会往挂掉的实例发请求等 Ribbon 刷新需要时间。6.4 看日志定位问题别只盯着控制台微服务日志分散在四个服务里出问题时要学会看日志。我一般会在每个服务的application.yml里配日志输出到文件格式带上服务名和 traceId。比如logging.pattern.file%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n。如果用了 Sleuth 或 Micrometer Tracing日志里会有 traceId一个请求跨多个服务也能串起来。答辩时如果老师问“怎么排查线上问题”你就说“通过 traceId 串联链路日志定位到具体服务再查”。6.5 一个让我长记性的习惯最后说个我自己的教训。第一次做微服务项目时我觉得本地跑通就行了没管 Docker 镜像和启动脚本。结果答辩那天换了一台机器Nacos 连不上MySQL 密码不对四个服务只起来两个当场翻车。从那以后我养成了一个习惯所有中间件用 Docker Compose 管理所有服务的启动命令写在一个start-all.sh里数据库初始化 SQL 放在init.sql换机器只需要装 Docker 和 JDK十分钟就能把整套环境拉起来。这个习惯让我后来带学生做毕设时再也没在环境上浪费过时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表