驾校这种业务场景平时看着不起眼真要做一套管理系统的时候才发现它比想象中复杂得多——学员报名、教练排班、车辆调度、约考计时、费用结算每一块都能单独拆成一个模块。最近我把这套 SpringBoot Vue MySQL 的驾校管理系统源码完整跑通了前后端分离架构功能覆盖面很全而且属于配置好环境就能直接启动运行的那种。这篇文章就围绕这套系统的实际落地过程把业务拆解、数据库设计、后端接口、前端对接、环境部署和踩坑记录都梳理一遍适合正在做毕设、课设或者刚接触前后端分离项目的同学参考。1. 驾校管理系统到底在管什么业务模块拆解很多人在拿到这类可直接运行的系统源码之后第一反应是先把项目跑起来跑通之后却不知道从哪里看起。我的建议正好相反先别急着启动先把业务摸清楚。因为你只有知道这套系统是给谁用的、解决什么问题后面看代码、改功能、扩展模块的时候才有方向。1.1 三种核心角色和它们的权限边界驾校管理系统跟普通的进销存、内容管理系统相比最大的特点就是角色边界非常清晰。典型的三类用户分别是管理员负责整体运营。包括教练信息的增删改查、车辆信息的维护、课程与价格的配置、学员报名审核、财务数据的统计查看等。管理员是系统里权限最大的一类角色几乎所有后端接口都需要对它开放。教练核心工作对象是学员和课时。教练登录后可以看到自己的排课计划、名下学员列表、课时完成情况以及学员的约考进度。教练不应该看到财务数据也不应该能修改系统配置。学员面向终端用户。学员通过系统完成报名信息提交、查看自己的学车进度、预约练车时段、查询考试安排。学员端的功能是三者中最窄的只需要保证流畅、直观即可。这套权限模型落到技术上就是后端接口的角色鉴权 前端路由的权限控制。后面讲 SpringBoot 实现的时候会具体说这里先强调一个经验做权限设计之前一定要把每个角色能看到什么、能改什么列成一张表再照着表去写接口注解和前端路由守卫否则后面加角色或调权限的时候会非常痛苦。1.2 从报名到拿证的完整业务链路驾校的业务本质上是一条线性流程学员提交报名信息姓名、身份证号、手机号、报考车型等。管理员审核报名审核通过后学员正式建档。学员进行科目一理论学习与约考通过后进行科目二场地训练。教练根据学员的预约安排练车时间系统记录每次训练的课时。学员达到规定学时后系统允许预约对应科目的考试。考试通过后继续下一科目直到科目四全部完成学员毕业归档。理解这条链路非常重要因为数据库的表结构设计、后端接口的命名、前端页面的跳转逻辑全都是围绕这条流程展开的。比如学时管理的实现绝不是简单在学员表里加一个学时数字段而是要有一张课时记录表每次教练确认练车后插入一条记录再通过聚合计算学员的累计学时。只有把业务链路理解透了你在看代码的时候才能迅速定位这个接口是干嘛的、这张表为什么这么建。1.3 为什么选 SpringBoot Vue MySQL 这套技术栈这套组合在中小型管理系统里几乎是事实标准选它不是因为大家都在用而是因为它在开发效率、运行稳定性和学习成本之间取得了很好的平衡SpringBoot内置 Tomcat、自动配置、约定优于配置一个人从零搭起一个后端服务的成本很低。而且生态成熟做鉴权有 Spring Security JWT做持久层有 MyBatis-Plus几乎不用重复造轮子。Vue组件化开发让页面复用变得很容易比如学员列表和教练列表本质上都是表格 搜索 分页抽成公共组件之后每个页面只需要几十行配置代码。配合 Element UI 这类组件库后台管理界面的颜值和交互都能快速拉起来。MySQL驾校系统的数据量级完全不需要上分布式数据库一台 MySQL 实例承载几千个学员、几万条课时记录毫无压力。事务支持保证了报名、缴费这类关键操作的数据一致性。提示如果你是拿去交课程设计或者毕业设计这套技术栈也是最稳妥的选择。答辩的时候评委老师大概率会问为什么选这个框架上面的理由足够撑住场面。2. 数据库设计把驾校业务拆成表结构看一套管理系统源码我最推荐先打开数据库脚本文件一般是项目里的sql目录下的.sql文件把表结构浏览一遍。数据库设计的水准基本决定了这套系统的上限。2.1 核心表清单与关系梳理这套驾校管理系统的表结构我梳理下来核心的包括以下这些表名用途关键字段sys_user系统用户表三类角色统一存放username、password、role、statusstudent_info学员档案表name、id_card、phone、student_no、subject_statuscoach_info教练信息表name、phone、teach_subject、service_statuscourse_info课程/价格配置表course_name、price、durationenrollment报名记录表student_id、course_id、status、create_timeschedule练车排课表coach_id、student_id、date、time_slot、statustraining_record课时记录表schedule_id、student_id、hours、confirm_statusexam_booking考试预约表student_id、subject、exam_date、status这些表之间的关系并不复杂student_info 和 coach_info 都挂在 sys_user 下面因为登录账号一般就是手机号或者工号enrollment 关联 student 和 courseschedule 关联 coach 和 studenttraining_record 挂在 schedule 下面形成一条完整的多对一链。画一个简单的脑图就能理清用户表是根业务表通过外键关联到它身上。2.2 容易被忽略的字段设计细节看这套系统的建表语句时有几个细节值得单独拿出来讲第一个是状态字段的取值设计。比如报名状态status系统里用的是0-待审核、1-已通过、2-已拒绝、3-已归档这样一组数字枚举而不是直接存汉字。这样做的好处是后端逻辑判断 1就很直白但坏处是可读性差所以系统里一定会在后端定义对应的枚举类或者在 SQL 注释里写清楚。我自己做项目的时候还喜欢在统一返回的字典接口里把枚举值下发到前端前端直接渲染中文标签这样后面前端页面不用写死文案。第二个是软删除 vs 物理删除。比如教练离职了系统不会直接把教练记录删掉而是把他的service_status改成停用。因为历史的排课记录、课时记录里都引用了教练 id物理删除会导致这些历史数据查不到、关联断裂。这是管理系统里一个非常经典的坑很多新手在开发的时候图省事直接 delete上线之后被历史数据问题追着打。第三个是时间字段的统一。排课表和课时记录表里会涉及日期、时段、创建时间等多个时间属性系统统一使用datetime类型由后端LocalDateTime映射。既然选择了datetime数据库的连接参数里就必须带上serverTimezoneAsia/Shanghai否则本机 MySQL 和项目所在服务器时区不一致的时候会出现时间差八小时这种诡异问题。后面踩坑部分我会再细说。3. SpringBoot 后端分层结构与接口实现跑通这套系统的后端之后我觉得它的工程结构属于标准到几乎可以当模板的程度。理解了它的分层方式你后续不管是二次开发还是照着写自己的项目都会顺手很多。3.1 后端工程结构与分层后端项目的包结构大致是这样com.driving.school ├── controller // 控制层接收请求、参数校验、返回结果 ├── service // 业务层核心业务逻辑 ├── mapper // 持久层MyBatis-Plus 的 Mapper 接口 ├── entity // 实体类对应数据库表 ├── dto // 数据传输对象接收前端传入参数 ├── vo // 视图对象返回给前端的数据结构 ├── config // 配置类跨域、拦截器等 ├── utils // 工具类JWT 生成解析、密码加密等 └── common // 公共类统一返回结果、异常处理等这套分层的核心思想是**每一层只做自己该做的事**。Controller 不做业务运算只负责接收参数和返回结果Service 写业务逻辑Mapper 只做数据访问。我见过很多初学者把业务逻辑全堆在 Controller 里一个方法几百行后面调试的时候能崩溃。这套系统的写法是规范的特别适合作为参考模板。3.2 登录鉴权与 JWT 的实现思路系统登录接口走的是JWTJSON Web Token 拦截器的经典方案。整体流程是前端把用户名和密码通过POST /api/auth/login提交给后端。后端校验用户是否存在、密码是否匹配密码在数据库里存的是 BCrypt 加密后的密文不是明文。校验通过后后端根据用户 id、用户名、角色生成一个有效期若干小时的 token。前端把 token 存到本地之后每次请求都在请求头里带上Authorization: Bearer token。后端拦截器在请求进入 Controller 之前解析并校验 token把用户信息放入上下文。这里有一个非常容易踩的坑JWT 的拦截器放行名单一定要配置好。登录接口、静态资源、前端页面这些路径不能拦截否则项目启动之后你会发现前端连登录都提交不上去返回 401。另外这套系统的密码存储用的是 BCrypt这比 MD5、SHA 这类可逆性较强的算法要安全得多。BCrypt 每次加密同一个明文得到的密文都不一样因为里面加入了随机盐数据库被拖库之后也很难通过彩虹表反推出密码。3.3 排课与约考的接口设计要点驾校业务里最有技术含量的接口集中在排课和约考这两块。排课接口POST /api/schedule/create接收教练 id、学员 id、练车日期、时段。后端要做三件事校验教练和学员状态正常校验该时段是否已被占用校验学员是否达到预约条件比如必须通过科目一之后才能约科目二练车。这三件事必须在同一个事务里完成一旦任一步骤失败就整体回滚避免出现课时记上了但排课没占上这种数据不一致。约考接口POST /api/exam/booking。驾校约考的逻辑是学时达标才能约考所以接口内部会先查询training_record表用SUM聚合出该学员在当前科目下的累计学时跟课时阈值比较不满足就直接返回业务提示满足才创建约考记录。这种**先查前置条件再写数据**的接口设计思路是管理系统里所有写接口的通用套路非常值得借鉴。注意源码里所有的业务接口返回的都是统一结果类结构是code message data。前端拿到数据后第一件事就是判断code是否为 200而不是等后端抛异常。这种约定从第一天开始就要遵守前后端协作才不会乱套。4. Vue 前端页面组织与后端无缝对接前端这块系统用的是 Vue 2 Element UI 的组合。虽然 Vue 3 已经出来很久了但 Vue 2 Element UI 在管理系统领域依然有庞大的存量项目学会看这种代码在下班接私活和做课设的时候都非常有用。4.1 前端目录结构与路由设计前端项目目录里最关键的是src/router和src/views两个目录。路由表的设计直接反映了权限控制的前端实现思路src ├── api // 封装所有后端接口请求 ├── router // 路由配置文件 ├── store // 全局状态管理 ├── views // 页面组件 │ ├── login.vue │ ├── admin // 管理员相关页面 │ ├── coach // 教练相关页面 │ └── student // 学员相关页面 └── components // 公共组件路由配置里用到了动态路由 路由守卫。登录成功后前端拿到用户角色根据角色动态注册对应的路由表路由守卫router.beforeEach里做登录态判断——没有 token 一律重定向到登录页。这套双保险的机制保证了一个学员账号就算手动在地址栏输入管理员页面的路径也只会被弹回登录页或者 404 页面因为对应的路由压根没注册进去。4.2 用 Axios 统一处理请求前端请求库用的是 Axios并且在src/api/request.js里做了统一封装。这个封装有几个非常实用的点请求拦截器在每次请求发出前从登录信息里取到 token自动挂到请求头。这样业务代码里不用每个接口都手动写 token。响应拦截器统一处理返回结果。如果后端返回的code不是 200直接弹出错误提示并把用户引导到对应处理逻辑如果 HTTP 状态码是 401说明 token 过期或无效自动清理登录信息并跳转回登录页。baseURL 配置开发环境指向http://localhost:8080/api生产环境指向线上地址。配置抽出来之后换环境只需要改一个变量。这里特别想说一个细节响应拦截器一定要处理token 过期这个场景。很多管理系统只在登录时校验 token忽略 token 过期后的自动续期和重新登录引导结果用户用着用着突然所有接口都报错只能手动刷新页面体验很差。这套系统在 401 场景下的处理是规范和完整的值得照着学。4.3 学员管理页面的实现逻辑以管理员端的学员管理页面为例它的实现思路代表了系统中绝大多数列表页面的通用写法页面加载时调用GET /api/student/list接口传入pageNum、pageSize、keyword等参数后端返回分页数据和总数前端用 Element UI 的el-table渲染数据el-pagination渲染分页组件搜索框绑定keyword并在点击搜索时重新请求数据。除了展示页面上还有新增、编辑、删除三个操作。新增和编辑用弹窗表单实现表单校验用 Element UI 的rules规则——比如手机号必须 11 位且是数字身份证号必须 18 位。校验规则在前端做一层、后端再校验一层这套系统两个方向都做了这也应该成为你写所有项目的默认习惯。一个实战小技巧学员状态在数据库里是数字枚举比如0-在读、1-已结业但表格里不能直接显示数字所以前端会做一个过滤器statusFilter把数字映射为中文标签。我看这套系统的表格列里都有:formatter或过滤器处理这个细节非常体现工程素养。5. 环境准备与一键启动直接运行的全流程标题里写着【可直接运行】这个可运行不是说说而已我得负责任地告诉你在正确的环境下这套系统真的可以做到从零到跑通只需要二十分钟左右。下面把完整流程拆开讲。5.1 本地环境清单与版本注意事项在动手之前先确认本机已经具备以下环境软件建议版本用途JDK1.8 或 11运行 SpringBoot 后端Maven3.6 及以上管理后端依赖MySQL5.7 或 8.0存储业务数据Node.js12 及以上运行 Vue 前端开发工具IDEA 或 VSCode开发和调试版本上有几个细节必须说清楚JDK 版本不要盲目选新。如果你用的是 JDK 17 甚至更高而项目本身是基于 SpringBoot 2.x 构建的有可能因为 javax 到 jakarta 的迁移问题导致启动失败。优先按照项目pom.xml里声明的 Java 版本来配。MySQL 建议 8.0。如果是 5.7连接驱动版本要跟数据库版本匹配否则会报Public Key Retrieval is not allowed这个错我后面踩坑部分细讲。Node 版本与依赖锁文件。前端package.json里如果指定了依赖版本npm install一般没问题。如果你用的是 Node 18 及以上的高版本个别旧依赖可能报警告但不影响运行。5.2 初始化数据库与启动后端数据库的初始化是整个流程里最不能跳步的一环。正确操作顺序是打开 MySQL 客户端Navicat 或命令行执行CREATE DATABASE driving_school DEFAULT CHARACTER SET utf8mb4;创建数据库。在项目的sql目录下找到初始化脚本通常是driving_school.sql用source命令或者直接导入该脚本把表结构和初始数据一次性建好。修改后端配置文件application.yml里的数据库连接参数包括 URL、用户名、密码。URL 里一定要带上characterEncodingutf8和serverTimezoneAsia/Shanghai这两项缺了极大概率出乱码或者时间偏差。在 IDEA 里导入 Maven 项目等待依赖下载完成第一次可能比较久因为要拉全所有包然后运行启动类DrivingSchoolApplication。启动成功后控制台会打印 SpringBoot 的启动日志和 Tomcat 端口默认 8080。看到Started DrivingSchoolApplication in xx seconds就说明后端已经起来了这个时候先用浏览器访问一下http://localhost:8080/api/...之类的健康检查接口确认不报错再进下一步。5.3 启动前端与联调验证后端起不来前端再漂亮也是白搭。这一节的操作要点如下打开前端项目目录先执行npm install安装 node_modules 依赖。执行npm run serve启动开发服务器默认监听 8080 端口。注意如果后端占了 8080前端开发服务器会自动换到 8081页面访问地址要以控制台输出的为准。打开浏览器访问前端地址正常情况下会跳转到登录页。用系统自带的初始账号登录一般在初始化 SQL 里比如admin/123456登录成功后进入首页点开几个核心菜单验证接口连通性。最常见的联调问题是跨域。因为前端跑在http://localhost:8080后端在http://localhost:8081端口号不一样浏览器会拦截跨域请求。这套系统在后端WebMvcConfigurer里已经写好了跨域配置允许了前端地址的CORS请求所以正常情况下不会遇到这个错。如果你是自己写前后端分离项目记得主动配好跨域这是绕不开的一步。6. 联调与部署中踩过的坑这套系统我实际跑下来整体是很顺的但中间也遇到了几个典型的坑。每一个我都把排查链路写出来你遇到同样问题的时候可以直接照着查。6.1 MySQL 连接报错的排查链路第一个坑出现在数据库连接阶段报错信息是java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed这个错的原因一句话就能说清我用的是 MySQL 8.0默认的认证插件是caching_sha2_password而连接驱动版本和配置不允许在第一次连接时从服务器获取公钥。解决办法有两个在 JDBC URL 后面加上allowPublicKeyRetrievaltrueuseSSLfalse或者把 MySQL 用户的认证插件改回mysql_native_password。我自己的做法是改 URL因为不用动数据库用户对现有系统影响最小。这个坑在 MySQL 8.0 时代几乎必踩建议你把这两个参数直接写进你的项目模板里。第二个相关的问题是时区偏差。我最初连接 URL 里没写serverTimezone结果系统里所有时间字段都比北京时间差了 8 个小时。排查的时候用 SQL 直接查表发现数据是对的但通过后端接口返回的时间就是错的最后定位到是连接参数缺了时区配置。加上serverTimezoneAsia/Shanghai后一切正常。这个坑的特点是查库正常、接口异常非常迷惑建议第一时间检查连接参数。6.2 跨域问题前后端分离的第一道坎跨域报错是前后端分离开发最经典的问题报错长这样Access to XMLHttpRequest at http://localhost:8081/api/... from origin http://localhost:8080 has been blocked by CORS policy这套系统在后端已经用CrossOrigin注解或全局配置做了跨域处理所以直接运行不会遇到。但如果你改动过程中不小心把跨域配置删了或者自己新写了一个 Controller 而忽略了跨域就会立刻踩到。排查跨域问题的标准步骤是先确认后端有没有配置CorsFilter或者WebMvcConfigurer.addCorsMappings再确认配置的allowedOrigins里有没有包含前端实际地址最后打开浏览器开发者工具看Network - Response Headers里有没有Access-Control-Allow-Origin字段。如果带了但值不对也是跨域失败。另外提醒一句allowedOrigins配置不要写成通配符*再配合allowCredentials(true)这种组合会被浏览器直接拒绝。6.3 Vue 打包产物如何放进 SpringBoot最后讲一个部署层面的坑。开发模式下前端用npm run serve跑得很欢但要正式部署的时候更常见的做法是把前端打包成静态文件然后放进 SpringBoot 的static目录做到一个 SpringBoot 进程全搞定。具体做法是先在 Vue 项目根目录执行npm run build生成dist目录然后把dist里的文件复制到 SpringBoot 项目的src/main/resources/static下重新打包后端即可。但有三个细节容易踩Vue 的路由模式。如果你的前端用了history模式路由刷新页面时后端会返回 404因为 SpringBoot 默认找不到对应的路径。解法有三个改用hash模式路由或者配置后端把未知路径转发到index.html或者搞一个错误页跳转。这套系统我建议直接用 hash 模式配置简单部署省心。前端接口地址必须改成相对路径或者和部署域名一致。如果request.js里写死了http://localhost:8081/api部署到服务器上就失效了必须在构建前改成正式环境地址。端口冲突。SpringBoot 默认 8080如果服务器上 8080 被占用了就用java -jar xxx.jar --server.port9090指定新端口前端部署在 Nginx 里通过代理转发到后端端口这是生产环境更推荐的做法。我在实际部署这套系统的时候最开始直接用了前端打进 static 目录的方案图省事结果被 history 路由的刷新 404 问题折腾了一阵。后来切换成 hash 模式问题直接消失。这里分享给大家能少走弯路就少走。这套驾校管理系统说到底是一个标准的业务管理系统样板清晰的业务链路、规范的前后端分层、合理的数据库设计再加上能直接运行的完整代码无论你是用它来交作业、做二次开发还是把它当作学习前后端分离项目的范本都是很合适的切入点。我个人在跑通整个流程之后的感受是看这套源码最大的收获不是学会了某个功能怎么写而是理解了一个完整管理系统从数据库设计到前端展示的整套闭环。建议你也按我的顺序来——先看业务再看表先跑后端再接前端最后带着踩坑记录去做改造。等你把整个链路都摸透了后面写任何管理类系统都会有一种不过如此的底气。
阅读完成 · 觉得有帮助?