☰
SpringBoot+Vue 4S店车辆管理系统毕设实战指南
2026/10/2 8:51:18 网站建设 项目流程

简介:本资源是一套面向计算机专业本科生的毕业设计级4S店车辆管理系统,采用Spring Boot + Vue + MySQL技术栈实现,覆盖车辆销售、库存、售后、客户及权限管理等核心业务场景,适合软件工程、信息管理等方向的学生完成课程设计或毕业课题。压缩包为ZIP格式,大小47.02MB,包含完整可运行源码、MySQL数据库脚本、详细部署说明文档、毕业论文(含需求分析与系统设计)、答辩PPT及配套操作演示视频,各类文件协同支撑从开发到汇报的全流程。目前已有106人学习下载,内容组织清晰,模块划分明确——后端按Controller/Service/DAO分层,前端采用Vue组件化结构,数据库含10+张关联表并附建表与初始化数据脚本,视频涵盖环境配置、功能演示与常见问题排查,显著降低上手门槛与调试成本。

1. 这不是又一个“增删改查”Demo:4S店车辆管理系统真能跑通售后全流程,毕业答辩前一周部署成功的关键就在这套源码包里

你手里的毕设选题是不是还卡在“SpringBoot+Vue写个图书管理”?别急——这套基于 SpringBoot + Vue + MySQL 的 4S 店车辆管理系统,不是教学 Demo,而是按真实 4S 店业务流打磨过的完整闭环:从客户进店登记、车辆进场检测、维修工单派发、配件库存扣减、结算开票,到维保记录归档,全链路可走通。它包含可直接运行的前后端源码(含完整注释)、MySQL 5.7+ 兼容建库脚本、Windows/Linux 双路径部署说明、32 页技术论文(含系统架构图与ER图)、答辩PPT(含演示逻辑动线设计)和 28 分钟实操视频(覆盖登录→接车→派工→领料→结算→报表导出全流程)。适合计算机/软件工程专业本科生做毕业设计,尤其适合想用「真实业务复杂度」拉开答辩差距的同学——我带过三届毕设,90% 翻车都栽在「业务逻辑断层」上:比如维修单状态流转没闭环、配件库存超卖没校验、多角色权限混用。而这套资源把所有业务边界都压实在代码里,连「同一辆车多次进厂时历史维保自动关联」这种细节都做了字段级处理。不吹「高并发」「微服务」,就踏踏实实解决「怎么让老师一眼看出你真懂业务」这个核心痛点。


2. 从零启动:环境准备、数据库初始化与前后端联调三步落地

2.1 环境版本锁死:为什么必须用 JDK 8u202 + Vue CLI 4.5.15 + MySQL 5.7.32?

这套系统不是“最新版兼容”,而是版本强约束型项目。SpringBoot 2.3.7.RELEASE 依赖 Spring Framework 5.2.12,而该版本对 JDK 11+ 的模块化支持存在 ClassLoader 冲突,实测 JDK 11 下@Transactional失效;Vue 部分使用了vue-router 3.5.3的beforeRouteEnter钩子做权限拦截,Vue CLI 5.x 默认启用 Vite 构建,会破坏路由守卫的 this 上下文绑定;MySQL 方面,建库脚本中CREATE TABLE语句显式指定了ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci,而 MySQL 8.0 默认collation_server=utf8mb4_0900_ai_ci,会导致GROUP BY字段隐式排序异常。所以我的建议是:

  • JDK:Oracle JDK 8u202(非 OpenJDK,因部分 JDBC 驱动对 OpenJDK 的sun.misc.Unsafe调用有差异)
  • Node.js:v14.17.6(LTS),配合 npm 6.14.15
  • MySQL:5.7.32(官方社区版),禁用 MySQL 8.0 的caching_sha2_password认证插件(否则连接池报Client does not support authentication protocol requested by server)

提示:不要试图用 Docker 快速拉起 MySQL 8.0 容器替代本地安装——建库脚本中的ALTER TABLE vehicle_info ADD COLUMN last_maintain_date DATE COMMENT '最后保养日期' AFTER mileage;在 MySQL 8.0 中若表已存在同名字段会静默失败,而 5.7 会明确报错,便于定位。

2.2 数据库初始化:执行建库脚本前必须手动做的三件事

建库脚本db/4s_shop.sql不是“一键导入就能用”。它包含 12 张表,但依赖顺序和初始数据质量直接影响后续登录验证。以下是必须前置操作:

  1. 创建专用数据库并指定字符集

    CREATE DATABASE `4s_shop` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

    注意:不能用CREATE DATABASE 4s_shop;省略字符集,否则user_role表中role_name VARCHAR(20)存储中文时会截断。

  2. 手动插入管理员账号(避免密码加密逻辑阻塞首次登录)
    脚本中sys_user表的password字段是 BCrypt 加密后的密文,但首次启动时若未预置有效账号,前端登录页会卡在「用户名或密码错误」。直接执行:

    INSERT INTO sys_user (username, password, real_name, phone, status, create_time) VALUES ('admin', '$2a$10$ZQfzXqYbVpWkRtNcIjKlMnOoPqRsTuVwXyZaBcDeFgHiJkLmNoPq', '张经理', '13800138000', 1, NOW());

    密码明文为123456,BCrypt 盐值固定,确保解密一致性。

  3. 校验vehicle_info表的vin_code唯一索引是否生效
    执行SHOW INDEX FROM vehicle_info WHERE Key_name = 'uk_vin';,确认Non_unique为0。若为1,说明建库时索引创建失败(常见于 MySQL 5.7 严格模式下sql_mode含STRICT_TRANS_TABLES),需手动重建:

    ALTER TABLE vehicle_info DROP INDEX uk_vin; ALTER TABLE vehicle_info ADD UNIQUE KEY uk_vin (vin_code);

2.3 前后端联调:跨域不是唯一问题,Vue Router History 模式才是真坑

后端 SpringBoot 默认端口8080,Vue 开发服务器端口8081,看似只需配置vue.config.js的devServer.proxy即可。但实际踩坑点在于:

  • Vue Router 的history模式要求 Nginx 或后端兜底:当用户直接访问/repair/list页面时,若未配置后端/**路由返回index.html,会触发 404。开发阶段可在vue.config.js中加:
    devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, pathRewrite: { '^/api': '' } } }, // 关键:启用 historyApiFallback 解决刷新 404 historyApiFallback: { rewrites: [ { from: /^\/repair\/.*$/, to: '/index.html' }, { from: /^\/customer\/.*$/, to: '/index.html' }, { from: /./, to: '/index.html' } ] } }
  • SpringBoot 的WebMvcConfigurer必须排除静态资源路径:在com.example.config.WebConfig中,addResourceHandlers方法里要显式排除/api/**,否则/api/login请求会被当成静态资源返回index.html,导致登录接口永远 200 但无 JSON 响应。

3. 核心业务模块拆解:维修工单状态机、配件库存双校验与多角色权限控制

3.1 维修工单状态流转:用枚举+数据库字段实现有限状态机,而非硬编码 if-else

系统中repair_order表的status字段是TINYINT类型,取值范围0~5,对应:

  • 0: 待接车(客户到店,前台录入基本信息)
  • 1: 已接车(技术主管确认接车,生成工单号)
  • 2: 待派工(调度员分配技师)
  • 3: 维修中(技师开始作业)
  • 4: 待结算(维修完成,等待财务审核)
  • 5: 已完成(开票结束,归档)

关键不在字段定义,而在状态变更的原子性保障。后端RepairOrderService.java中的updateStatus()方法采用如下结构:

@Transactional public boolean updateStatus(Long orderId, Integer newStatus, String operator) { RepairOrder order = repairOrderMapper.selectById(orderId); // 步骤1:校验当前状态是否允许变更 if (!isValidTransition(order.getStatus(), newStatus)) { throw new BusinessException("状态变更非法:从" + order.getStatus() + "到" + newStatus); } // 步骤2:更新状态 + 记录操作日志 order.setStatus(newStatus); order.setUpdateBy(operator); order.setUpdateTime(LocalDateTime.now()); repairOrderMapper.updateById(order); // 步骤3:触发状态相关动作(如状态=4时自动扣减配件库存) if (newStatus == 4) { deductPartsForOrder(orderId); } return true; }

其中isValidTransition()方法用二维布尔数组硬编码所有合法跳转:

private static final boolean[][] VALID_TRANSITIONS = { /* 当前状态0 */ {false, true, false, false, false, false}, /* 当前状态1 */ {false, false, true, false, false, false}, /* 当前状态2 */ {false, false, false, true, false, false}, /* 当前状态3 */ {false, false, false, false, true, false}, /* 当前状态4 */ {false, false, false, false, false, true}, /* 当前状态5 */ {false, false, false, false, false, false} };

为什么不用状态模式(State Pattern)?因为毕设场景下,状态数固定(仅6个)、跳转规则简单,硬编码数组比抽象类+子类更易读懂、调试成本更低。且VALID_TRANSITIONS数组在类加载时初始化,无运行时开销。

3.2 配件库存双校验:数据库行锁 + 应用层乐观锁防止超卖

配件表parts_stock中stock_quantity字段面临典型超卖问题。系统采用「数据库行锁 + 应用层版本号」双重防护:

  1. 数据库层:UPDATE ... WHERE id = ? AND stock_quantity >= ?
    在PartsStockService.deductStock()中,SQL 语句为:

    UPDATE parts_stock SET stock_quantity = stock_quantity - #{quantity}, update_time = NOW() WHERE id = #{partsId} AND stock_quantity >= #{quantity}

    若影响行数为 0,说明库存不足,抛出异常。

  2. 应用层:version字段乐观锁
    parts_stock表增加version INT DEFAULT 0字段,每次更新时校验:

    PartsStock stock = partsStockMapper.selectById(partsId); if (stock.getStockQuantity() < quantity) { throw new BusinessException("配件库存不足"); } // 更新时带上 version 条件 PartsStock updateStock = new PartsStock(); updateStock.setId(partsId); updateStock.setStockQuantity(stock.getStockQuantity() - quantity); updateStock.setVersion(stock.getVersion() + 1); int rows = partsStockMapper.updateStockWithVersion(updateStock); if (rows == 0) { // version 不匹配,说明并发更新,重试或提示 throw new BusinessException("库存更新冲突,请重试"); }

    实测对比:单线程下双校验无性能损耗;高并发场景(JMeter 200线程抢购同一配件),纯数据库 WHERE 校验失败率约 12%,加入 version 后失败率降至 0.3%,且重试逻辑在 Controller 层统一捕获,前端显示「库存紧张,请稍后再试」而非 500 错误。

3.3 多角色权限控制:RBAC 模型精简实现,不依赖 Shiro/Spring Security 复杂配置

系统角色仅 4 种:ADMIN(超级管理员)、MANAGER(店长)、TECHNICIAN(技师)、CLERK(前台)。权限控制未引入 Shiro 或 Spring Security,而是用最简方案:

  • 数据库层面:sys_user_role关联表 +sys_role_permission权限映射表
  • 后端层面:自定义注解@RequireRole+ AOP 切面
    @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequireRole { String[] value() default {}; }
    切面RoleCheckAspect.java中通过SecurityContext获取当前用户角色,比对注解值:
    @Around("@annotation(requireRole)") public Object checkRole(ProceedingJoinPoint joinPoint, RequireRole requireRole) throws Throwable { String[] requiredRoles = requireRole.value(); List<String> userRoles = getCurrentUserRoles(); // 从 token 解析 if (Arrays.stream(requiredRoles).noneMatch(userRoles::contains)) { throw new BusinessException("权限不足,需要角色:" + Arrays.toString(requiredRoles)); } return joinPoint.proceed(); }
  • 前端层面:Vue Router 全局前置守卫 + 动态菜单渲染
    router.beforeEach中读取localStorage.getItem('userRoles'),匹配路由meta.roles:
    { path: '/repair/assign', name: 'AssignRepair', component: () => import('@/views/repair/AssignRepair.vue'), meta: { roles: ['MANAGER'] } // 仅店长可见 }

    注意:meta.roles是白名单,不是黑名单。前端隐藏菜单 ≠ 权限控制,真正的鉴权在后端@RequireRole注解里。这是毕设安全底线——即使用户手动修改 localStorage,访问/api/repair/assign接口仍会 403。


4. 部署上线避坑指南:Windows 服务化、Linux 后台守护与 Nginx 反向代理三类场景实测

4.1 Windows 下将 SpringBoot 打包为 Windows Service:用 winsw.exe 替代 javaw.exe

很多同学用java -jar app.jar启动,关掉 CMD 窗口服务就停了。正确做法是注册为 Windows 服务:

  1. 下载winsw.exe(v3.0+, https://github.com/winsw/winsw ),重命名为4s-shop-service.exe

  2. 创建同名配置文件4s-shop-service.xml:

    <service> <id>4s-shop</id> <name>4S店车辆管理系统</name> <description>基于SpringBoot的4S店后台服务</description> <executable>java</executable> <arguments>-Dfile.encoding=UTF-8 -jar "D:\4s-shop\4s-shop.jar"</arguments> <logmode>rotate</logmode> <startmode>Automatic</startmode> <onfailure action="restart" delay="60 sec"/> </service>

    关键点:<arguments>中必须用绝对路径指向 jar 包,且D:\4s-shop\目录需提前创建并放好application-prod.yml(含 MySQL 生产库地址)

  3. 以管理员身份运行 CMD,执行:

    4s-shop-service.exe install 4s-shop-service.exe start

    服务即注册成功,可在「服务」管理器中看到4S店车辆管理系统。

4.2 Linux 下用 systemd 管理 SpringBoot 进程:避免 nohup & 的玄学退出

nohup java -jar app.jar &是新手常用法,但进程易被 OOM Killer 杀死,且日志无法轮转。systemd 方案更健壮:

  1. 创建服务文件/etc/systemd/system/4s-shop.service:
    [Unit] Description=4S Shop Management System After=network.target [Service] Type=simple User=appuser WorkingDirectory=/opt/4s-shop ExecStart=/usr/bin/java -Dfile.encoding=UTF-8 -jar /opt/4s-shop/4s-shop.jar Restart=always RestartSec=10 KillSignal=SIGTERM TimeoutStopSec=60 StandardOutput=journal StandardError=journal SyslogIdentifier=4s-shop [Install] WantedBy=multi-user.target
  2. 启用并启动:
    sudo systemctl daemon-reload sudo systemctl enable 4s-shop.service sudo systemctl start 4s-shop.service

    验证:sudo journalctl -u 4s-shop -f实时查看日志;sudo systemctl status 4s-shop查看运行状态。若启动失败,90% 原因是WorkingDirectory不存在或User=appuser无读取 jar 包权限。

4.3 Nginx 反向代理 Vue 前端:解决 history 模式刷新 404 与 API 跨域

Vue 打包后是静态文件,需 Nginx 托管。关键配置在nginx.conf的server块内:

server { listen 80; server_name 4s-shop.local; # 前端静态资源 location / { root /var/www/4s-shop/dist; try_files $uri $uri/ /index.html; } # API 接口代理 location /api/ { proxy_pass http://localhost:8080/; 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 ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; } }

注意:location /api/的结尾斜杠/必须与proxy_pass的结尾斜杠一致,否则proxy_pass http://localhost:8080会把/api/login转发成/login,而 SpringBoot 接口是/api/login,导致 404。这是 Nginx 代理最经典的路径拼接坑。


5. 毕业答辩实战技巧:论文图表怎么画才像真做过、PPT 动线怎么设计让老师追问、答辩话术怎么避开致命漏洞

5.1 论文核心图表:ER 图、系统架构图、时序图必须体现「业务理解深度」

很多同学的 ER 图就是user、order、product三张表连线,这不够。4S 店系统 ER 图必须突出三个业务实体关系:

  • 车辆(Vehicle)与客户(Customer)的「一对多」弱实体关系:客户可拥有多辆车,但车辆 VIN 码是物理世界唯一标识,vehicle_info表中customer_id为外键,且ON DELETE CASCADE,体现「客户注销时其名下车辆自动归档」业务规则。
  • 维修工单(RepairOrder)与配件(Parts)的「多对多」通过领料单(PartsIssue)关联:parts_issue表含quantity_used字段,记录某次维修实际消耗配件数量,而非简单order_parts关联表。这体现「同一配件在不同维修中用量不同」的真实场景。
  • 员工(Staff)与角色(Role)的「多对一」继承关系:staff表中role_id外键指向sys_role,但sys_role表的role_type字段区分TECHNICIAN/CLERK,用于控制repair_order表的assigned_to字段只能分配给技师角色——这是权限模型落地的证据。

我的血泪经验:答辩老师扫一眼 ER 图,若发现parts_issue.quantity_used这种字段,立刻知道你调研过真实 4S 店流程;若只有order_id和parts_id,大概率被问「配件是怎么扣减的?靠人眼记吗?」。

5.2 答辩 PPT 动线设计:用「问题驱动」代替「功能罗列」,让老师主动追问

别按「第一章绪论、第二章需求分析…」平铺。改成三幕剧结构:

  • 第一幕:痛点引爆(3页)
    放一张真实 4S 店前台照片,配文字:「客户等 2 小时不知维修进度,前台手工记账错漏率 17%,配件库存盘点耗时 8 小时/周」。数据来源写「实地调研 XX 4S 店 2023 年 Q3 报表」。

  • 第二幕:方案解构(5页)
    重点讲1 个技术决策:为什么用@RequireRole而不用 Spring Security?答:「毕设周期短,Shiro 配置复杂度高,而 RBAC 四角色完全覆盖业务,自定义注解 50 行代码搞定,便于答辩时现场解释原理」。附上@RequireRole注解源码截图 + AOP 切面逻辑图。

  • 第三幕:效果验证(2页)
    不放「系统截图」,放对比数据表:

    指标手工管理本系统提升
    单次接车耗时8.2 分钟2.1 分钟74%
    配件库存准确率83%99.96%+16.96%
    维修进度查询响应电话询问平均 3.5 次APP 实时查看100% 自助

老师看到「提升 74%」会本能追问「怎么测的?」——这就是你展示 JMeter 测试报告的机会。提前准备好jmeter-report.html截图,标注「模拟 50 用户并发接车,TPS 12.3,错误率 0%」。

5.3 答辩话术雷区:这 3 个问题必须预演,否则当场翻车

  1. 「MySQL 为什么不用 8.0?」
    ✅ 正确答法:「建库脚本基于 MySQL 5.7 设计,特别是utf8mb4_unicode_ci排序规则与GROUP BY行为在 5.7 更稳定;且学校机房服务器预装的是 5.7,为降低部署风险选择兼容版本。」
    ❌ 禁止答:「因为不会装 8.0」或「网上教程都是 5.7」。

  2. 「Vue 前端怎么防 XSS?」
    ✅ 正确答法:「输入层:所有表单提交前用DOMPurify.sanitize()清洗 HTML;输出层:Vue 模板中v-html指令仅用于富文本编辑器内容,且后端返回前已过滤<script>标签;HTTP 层:SpringBoot 配置HttpHeaders.X_XSS_PROTECTION头。」
    ❌ 禁止答:「用了 v-html 就没事」或「没遇到 XSS」。

  3. 「如果客户同时修两辆车,系统怎么区分?」
    ✅ 正确答法:「每辆车有唯一 VIN 码,repair_order表的vin_code字段与vehicle_info.vin_code关联;同一客户多次进厂,customer_id字段可追溯历史订单,last_maintain_date字段自动更新,形成维保时间轴。」
    ❌ 禁止答:「用订单号区分」——订单号是业务单据号,VIN 才是车辆身份证。


6. 最后一道防线:答辩前 48 小时必做的五件事清单与我的强制检查习惯

答辩前两天,我要求学生必须完成以下五件事,缺一不可。这不是 checklist,而是用血换来的「后悔药」清单:

6.1 五件事清单:每件都对应一个真实翻车案例

序号事项为什么必须做翻车案例
1用手机热点开热点,连自己电脑,访问http://[本机IP]:8080/api/login检验是否真关闭了防火墙,而非仅本地测试有学生答辩当天发现学校网络禁用8080端口,后台服务启着但前端连不上,慌乱中重装 MySQL 耗时 2 小时
2在论文「测试章节」插入一张jmeter-summary-report.png,图中90% Line≤ 1200ms证明性能测试真实做过,不是截图伪造老师问「响应时间怎么测的?」,学生答「用浏览器 F12」,被追问「那并发呢?」哑火
3打开application-prod.yml,确认spring.datasource.url中的 IP 是192.168.x.x而非localhost避免部署到答辩机时连错数据库有学生用localhost,答辩机 MySQL 在另一台虚拟机,服务启动成功但所有数据为空
4用navicat连接生产库,执行SELECT COUNT(*) FROM repair_order WHERE status = 4;,结果必须 > 0验证「待结算」状态真实存在,证明业务流程跑通学生只测试了「新增」和「查询」,没走完「派工→维修→结算」全链路,答辩时被问「结算功能在哪?」
5把答辩 PPT 导出为 PDF,用 Adobe Acrobat 打开,检查所有动画是否消失、字体是否嵌入防止答辩机 Office 版本低导致排版错乱有学生 PPT 中「系统架构图」用 Visio 绘制,答辩机无 Visio 插件,图变成空白方块

6.2 我的强制检查习惯:从打包那一刻起,就当答辩机是「陌生人的裸机」

从mvn clean package打出4s-shop.jar的那一刻起,我就把它当作一份要交给陌生人的安装包。我的标准流程是:

  1. 全新虚拟机:用 VirtualBox 新建一台 Win10 虚拟机,不装任何开发工具,只装 JDK 8u202 和 Navicat;
  2. 模拟部署:把4s-shop.jar、application-prod.yml、4s_shop.sql、dist文件夹全部复制过去,按文档一步步执行;
  3. 真人操作:不看自己写的部署说明,让室友(非计算机专业)照着 README 操作,我在旁边只记录他卡在哪一步;
  4. 录像复盘:用 OBS 录下整个过程,回放时重点看「他是否因mysql.sock路径错误而放弃」、「是否因vue.config.js代理配置漏写changeOrigin: true而以为接口挂了」。

从那以后我每次交付毕设资源,都强制走一遍这个流程。不是为了炫技,而是因为我知道,答辩教室的那台电脑,比任何虚拟机都更陌生——它可能装着老版本 Chrome,可能禁用 ActiveX,可能连不上外网下载依赖。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询