ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue+MySQL的4S店车辆管理系统:从架构到部署全解析

SpringBoot+Vue+MySQL的4S店车辆管理系统:从架构到部署全解析 简介面向毕业设计及软件工程实训这套基于SpringBootVueMySQL的4S店车辆管理系统是一份完整可用的实践资源。压缩包大小约47.02MB内含源码工程、数据库脚本、部署说明、毕业论文、答辩PPT与操作演示视频既能帮助快速启动项目也方便对照论文梳理系统设计思路。目前已有106人学习/下载适合正在做车辆管理类课题或希望掌握前后端分离开发流程的学习者参考。系统覆盖车辆信息管理、销售管理、库存管理、售后服务、客户管理、报表统计与权限管理等核心模块从车辆入库、销售出库到售后跟踪形成完整业务闭环技术栈上采用Spring Boot构建稳定后端服务Vue处理前端交互MySQL负责数据持久化整体结构清晰、可扩展性强权限管理还能通过多级角色控制保障数据安全。源码与部署文档配合使用可降低环境配置门槛论文和答辩素材则备齐了项目展示与汇报所需。1. 4S店车辆管理系统毕业设计里最稳的选题之一很多做毕业设计的同学一拿到SpringBootVueMySQL的题目第一反应是慌前后端分离的项目怕代码看不懂怕数据库设计不合理怕部署阶段卡壳。这套4S店车辆管理系统就是冲着这个痛点来的——它把车辆管理、客户管理、销售管理、维修工单这几条核心业务线串成一体界面和接口都齐全拿到手里直接改一改就能跑。选4S店场景其实很有讲究业务不算太复杂但正好覆盖了增删改查、权限区分、订单状态流转这些经典考点。把这份源码吃透答辩时不管是问你表关系、接口设计还是权限控制你都能答得上来。适合两类人一是拿它当毕业设计底稿的在校生二是想完整走一遍SpringBootVue前后端分离流程的初学者。接下来我会按架构、数据库、后端、前端、部署这条线拆开讲重点说哪些参数别乱改、哪些坑我踩过。2. 技术架构与数据模型SpringBootVueMySQL如何分工2.1 技术栈选型与项目模块划分这套系统的技术组合是当前Java后端最主流的配置SpringBoot 2.x提供RESTful接口Vue 2.x负责页面渲染MySQL负责持久化。为什么是这三个而不是别的核心原因是它们各有所长且学术界和就业市场上都是主流出了问题随便一搜就有现成答案。比如你要是用JSP写这个系统现在答辩老师基本不看反过来换成Spring Cloud全家桶对一个小型管理系统而言又是杀鸡用牛刀。SpringBoot的好处是内嵌了Tomcat打出来的jar包丢到服务器上java -jar就能跑不用维护单独的Tomcat实例对做毕业设计的人来说少掉了至少三个容易出错的环节。Vue选2.x而不是3.x一来是ElementUI与它的搭配最成熟二来网上现成的代码、视频教程基本都基于这套组合。数据库选MySQL 5.7或8.0都能跑初始化SQL用的是utf8mb4字符集中文字段不会出现乱码。功能模块分四块正好覆盖4S店日常业务闭环模块核心功能数据主表车辆管理车辆入库、上架、在售、已售状态流转vehicle_info客户管理客户档案、接待记录、等级分类customer_info销售管理订单创建、合同生成、销售额统计sale_order维修管理维修工单录入、配件更换记录maintenance_order车辆管理管的是车源客户管理管的是购车人销售管理把车源和客户绑在一起形成订单维修管理处理售后。四块正好构成一个从进车到卖车到服务客户的完整循环。看代码时按这个思路去拆就不会陷在几百个文件里出不来。2.2 数据库设计核心表结构与字段说明数据库是整个系统最值钱的部分。接口和页面都可以改但表结构一旦定了后面的代码全得围着它转。建议花半小时把这十来张表的关系摸清楚再动手改业务。核心表清单与关键字段如下表名中文名关键字段sys_user系统用户user_id自增, username, password, rolevehicle_info车辆信息vehicle_id, vin, brand, model, color, mileage, price, statuscustomer_info客户信息customer_id, name, phone, id_card, address, levelsale_order销售订单order_id, order_no, vehicle_id, customer_id, sale_price, sale_datemaintenance_order维修工单order_id, vehicle_id, customer_id, fault_desc, labor_fee, parts_costvehicle_info里的status字段我建议用0/1/2/3分别表示“在库/在售/已售/维修中”不要直接存中文。原因有两个前端下拉框的值和后端枚举都好处理数据库排序、分组统计也更精准。页面里看到的“在售”字样只是前端做了一次映射展示底层存的还是数字。sale_order通过vehicle_id关联vehicle_info通过customer_id关联customer_infomaintenance_order只关联vehicle_info。销售单和维修单独立走流程互不干扰。外键部分建议不要建物理外键约束而是在代码里靠事务保证一致性。我个人的习惯是逻辑外键比物理外键更好维护数据导入导出时不会被约束顺序卡住。初始化SQL里自带两类数据账号数据默认管理员admin/123456、普通员工user/123456密码是BCrypt加密后的密文业务演示数据大概二十多台车、十几个客户、七八条订单。这些演示数据让页面一打开就有内容可看答辩时不尴尬。有一个细节值得单独考究MySQL的varchar字段长度建议保持在255以内。密码字段60就够了地址255封顶别动不动就text。255以内的varchar字段可以直接建普通索引一旦升成text就只能建前缀索引性能打折查询还麻烦。3. 后端SpringBoot实现从依赖配置到核心接口3.1 项目初始化与依赖配置后端包名一般是com.example.carsystem这类结构核心分controller、service、mapper、entity四层。controller负责接参数、调service、返回结果service处理业务逻辑mapper对接数据库entity映射表结构。写代码时记住一个原则controller里不出现SQLmapper接口里不写业务判断。层间各管各的后续改需求时才清爽。打开pom.xml核心依赖就这么几件事dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.4.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependencyspring-boot-starter-web负责把Web服务撑起来mybatis-plus-boot-starter是数据访问层的核心简化CRUDmysql-connector-java连接数据库lombok省去getter/setter的样板代码。MyBatis-Plus用3.4.3这个版本与SpringBoot 2.x配合最稳不建议升到3.5以上因为分页插件配置方式变了照旧操作容易翻车。配完依赖看application.yml数据源配置是启动成败的关键server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/car_4s?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: configuration: map-underscore-to-camel-case: trueurl里的serverTimezone必须写Asia/Shanghai。MySQL 8.0默认时区是UTC不指定会直接报时间和服务器相差八小时的错误。map-underscore-to-camel-case打开后数据库里的create_time自动映射成entity里的createTime不用每个字段都加TableField注解。密码按你自己数据库实际值修改不必与脚本保持一致。3.2 登录鉴权与核心业务接口登录模块用的是JWT方案不走传统Session。用户输入账号密码后后端校验BCrypt密文通过则生成一个带用户ID和角色的token返回前端。此后前端每次请求都在Header里带Authorization后端用拦截器统一校验token没带或过期的请求直接返回401。登录接口是一个典型的三步流程PostMapping(/login) public Result login(RequestBody LoginRequest req) { SysUser user userMapper.selectOne( new LambdaQueryWrapperSysUser() .eq(SysUser::getUsername, req.getUsername())); if (user null) { return Result.error(用户不存在); } if (!BCrypt.checkpw(req.getPassword(), user.getPassword())) { return Result.error(密码错误); } String token JwtUtil.generateToken(user.getUserId(), user.getRole()); return Result.success(token); }第一步查用户第二步校验密码第三步生成token。注意密码比对用的是BCrypt.checkpw不是user.getPassword().equals(req.getPassword())。因为数据库存的是密文明文直接比永远不相等。答题时老师问安全设计你可以说token里不含敏感信息只有userId和role拦截器解析token后到库里确认身份这个回答比单纯贴代码有说服力得多。业务逻辑集中在service层。以车辆状态流转为例这是整车业务线的骨头public boolean updateVehicleStatus(Integer vehicleId, Integer targetStatus) { VehicleInfo vehicle vehicleMapper.selectById(vehicleId); if (vehicle null) { throw new BusinessException(车辆不存在); } // 已售车辆不允许再流转到在售状态 if (vehicle.getStatus() 3 targetStatus 1) { throw new BusinessException(已售车辆不能重新上架); } VehicleInfo update new VehicleInfo(); update.setVehicleId(vehicleId); update.setStatus(targetStatus); return vehicleMapper.updateById(update) 0; }这段代码的关键不在updateById而在前面的状态校验。车辆状态流转是有业务规则的不是前端传什么就更新什么。这里我加了已售车辆不能重新上架的判断你可以照这个思路给维修模块补充校验规则比如维修中的车辆不允许创建销售订单。BusinessException是自定义异常统一由全局异常处理器捕获返回{code: 500, message: 车辆不存在}格式的JSON。3.3 销售订单事务与MyBatis-Plus分页sale_order的创建涉及两张表先插入订单再把车辆状态改为已售。这里必须加Transactional否则订单插入成功但车辆状态更新失败时数据就对不上。Transactional(rollbackFor Exception.class) public SaleOrder createSaleOrder(SaleOrderDto dto) { SaleOrder order new SaleOrder(); order.setOrderNo(SO System.currentTimeMillis()); order.setVehicleId(dto.getVehicleId()); order.setCustomerId(dto.getCustomerId()); order.setSalePrice(dto.getSalePrice()); order.setSaleDate(new Date()); saleOrderMapper.insert(order); VehicleInfo update new VehicleInfo(); update.setVehicleId(dto.getVehicleId()); update.setStatus(3); // 已售 vehicleMapper.updateById(update); return order; }订单号用当前时间毫秒串同一天不会重复。rollbackFor Exception.class的含义是任何异常都让事务回滚包括自定义业务异常和空指针这个参数别漏。列表分页我自己习惯直接接收pageNum和pageSizeGetMapping(/list) public Result getVehicleList(RequestParam(defaultValue 1) int pageNum, RequestParam(defaultValue 10) int pageSize) { PageVehicleInfo page new Page(pageNum, pageSize); LambdaQueryWrapperVehicleInfo wrapper new LambdaQueryWrapper(); wrapper.orderByDesc(VehicleInfo::getCreateTime); vehicleMapper.selectPage(page, wrapper); return Result.success(page); }Page是MyBatis-Plus的分页对象selectPage执行后总条数挂在page.getTotal()上records是要渲染的列表数据。前端拿到后total用来渲染分页组件。没配分页拦截器时selectPage不会报错但会查全表然后内存分页数据量一大页面就卡这是最常见的隐形翻车。4. 前端Vue实现从路由配置到API对接4.1 Vue项目结构与路由权限前端用的是Vue 2.x加Vue Router 3.x没有上Vuex。原因很简单项目数据流不复杂组件之间共享的状态只有用户信息和几个下拉选项localStorage够用上Vuex反而增加学习成本。src目录结构如下views/ 放页面组件登录页、车辆管理、客户管理、销售管理、维修管理各一个文件夹router/index.js 配置路由和权限控制api/ 封装axios请求utils/request.js 拦截器路由权限通过全局前置守卫实现router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path /login) { next() } else if (!token) { next(/login) } else { next() } })逻辑是访问登录页直接放行其他页面没有token就踢回登录页。注意这里是按“有没有token”判断不是按“角色有没有权限”因为前端没做按钮级细粒度权限控制。想加分的话可以按role字段判断管理员和普通员工能看到哪些菜单代码量不大但系统完整度明显提升。路由模式用了hash。这样部署最简单不需要Nginx配history回退。用history模式刷新页面会404新手在部署阶段容易栽在这里。4.2 Axios封装与业务页面组件axios请求统一封装在utils/request.js核心两件事请求拦截器塞token响应拦截器统一处理结果const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { Vue.prototype.$message.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { Vue.prototype.$message.error(请求超时或服务器异常) return Promise.reject(error) } )baseURL用的是/api。开发环境在vue.config.js里配devServer.proxy把/api转发到http://localhost:8080这样就不会跨域。生产环境打包后前端是静态文件后端是jar服务两者在不同端口需要在Nginx里做反向代理。业务页面以车辆管理列表为例加载数据的方法比较典型created() { this.loadVehicleList() }, methods: { async loadVehicleList() { const params { ...this.queryForm, pageNum: this.pageNum, pageSize: this.pageSize } const res await getVehicleList(params) this.vehicleList res.data.records this.total res.data.total }, handleSearch() { this.pageNum 1 this.loadVehicleList() }, handlePageChange(page) { this.pageNum page this.loadVehicleList() } }这里两个细节值得注意。第一搜索时handleSearch必须重置pageNum为1否则你在第5页点搜索接口带着pageNum5去查返回可能只有一条数据感官上像搜索失效。第二分页组件的current-page要用v-model否则页码和实际数据错位。模板里状态列用el-tag展示结合枚举映射el-table-column propstatus label状态 width90 template slot-scope{ row } el-tag :typestatusTag[row.status]{{ statusText[row.status] }}/el-tag /template /el-table-columnstatusText和statusTag这类枚举映射建议放在常量文件统一维护。0对应在库、1在售、2维修、3已售后端改状态定义时只动一个文件。4.3 开发环境联调与跨域处理开发阶段前端跑在8080端口后端也跑在8080二者必然冲突。解决方式是把前端放到另外一个端口比如默认的8081或5173然后在vue.config.js里配proxymodule.exports { devServer: { port: 8090, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }改完配置必须重启npm run serve才生效。这个代理只对devServer有效生产环境打包后不会自动携带。上线时要么把前端dist放进SpringBoot的static目录要么用Nginx统一代理二选一别临到部署再想方案。5. 部署避坑环境准备与常见问题排查5.1 部署按步骤来从装环境到跑通前后端代码层面摸熟之后最让人头疼的就是部署环节。这部分内容基于我自己部署这套系统时踩过的坑按顺序理给你。第一步装基础环境。JDK版本用1.8Maven 3.6以上Node.js 14或16MySQL 5.7或8.0。JDK从Oracle或Adoptium官网下载安装包不要用IDE自动下载的版本那些经常是OpenJ9与Tomcat兼容性偶尔出问题。Node.js装LTS版本即可npm会随Node一起装好。第二步导入数据库。用Navicat或命令行执行初始化SQL脚本mysql -u root -p car_4s.sqlWindows下命令行导入容易因为编码问题报错建议直接用Navicat运行SQL文件。导入完成后检查sys_user、vehicle_info这两张核心表是否有数据。空表不影响启动但会导致前端登录后列表空白体验很差。第三步启动后端。在项目根目录执行mvn spring-boot:run或先打包再运行mvn clean package -DskipTests java -jar target/car_4s-0.0.1-SNAPSHOT.jar-DskipTests跳过测试是因为测试里如果还有H2数据库写的单元测试打包时会临时切换数据源越跑越乱。启动成功会看到“Started Application”字样。8080端口被占用时可以改application.yml的server.port也可以直接杀进程。第四步启动前端。在vue目录下安装依赖并启动开发服务器npm install npm run servenpm install慢是常规问题建议把镜像源切到国内源再装npm config set registry https://registry.npmmirror.com换完源依赖几分钟能装完。启动后访问http://localhost:8090如果看到登录页说明环境已经通了一半。5.2 高频翻车现场与应对策略现象前端请求全部失效浏览器控制台显示500原因数据表名或列名与实体类字段对不上。MyBatis-Plus默认把驼峰转成下划线vehicleInfo会变成vehicle_info如果数据库表名是vehicles就会报找不到表。 解决用Navicat查看实际表名对照entity类的TableName和TableField注解逐一修正。现象前端登录提示请求失败F12看到404原因后端Controller的RequestMapping路径与api目录下的请求路径不一致。 解决把前端api/xxx.js和后端Controller路径一条条对照注意前缀别重复比如前端请求/api/user/login后端类上别再写/api/user/login。改完路径重启后端。现象页面出来了列表却为空原因数据库没有初始化数据或初始化SQL在导入过程中因为外键约束中途中断。 解决重新导入前先执行SET FOREIGN_KEY_CHECKS0关闭外键检查导入完成后恢复开启。再确认每张表都有INSERT语句执行。现象打包后访问页面样式和图片全部丢失原因vue.config.js的publicPath配置错误。 解决把publicPath设为./这样静态资源按相对路径加载无论部署在根目录还是子目录都能正常显示。现象MySQL 8.0连接报认证错误或报驱动类不存在原因mysql-connector-java版本停留在5.x与MySQL 8.0的caching_sha2_password认证协议不兼容。 解决把驱动版本升到8.0.x或者不加版本号让SpringBoot自动管理。注意8.0驱动默认时区是UTCapplication.yml里的serverTimezoneAsia/Shanghai必须保留。6. 进阶手段验证流程与答辩加分项最后这几个技巧都能直接落地帮你把系统从“能跑”提升到“能讲清楚”。第一验证系统是否跑通的标准流程。不需要每个接口都逐条点但以下核心链路必须完整走一遍管理员登录 → 新增一辆车 → 把车辆状态改为在售 → 新增一个客户 → 为这台车创建销售订单 → 回到车辆列表确认状态变为已售。这五步走完说明车辆、客户、销售三个核心模块联动正常。中间任何一步断掉先确认是接口报错还是数据库没更新再往下一层排查。第二答辩材料的呈现顺序。论文里画一张E-R图把五张核心表的关系画清楚答辩PPT里放一张车辆状态流转图把从在库到在售、已售、维修的路径标出来。老师一眼就能看出你对业务理解到位。讲代码时按后端启动类 → Service核心方法 → Controller → 前端页面的顺序讲不要一上来就翻pom.xml或配置文件。第三功能扩展方向。这套代码结构清晰很适合横向扩展。想加分就加一个通知模块客户下单后向管理员推送待处理提醒前端轮询接口就能实现代码量不超过100行。或者给维修模块加配件库存表维修单保存时自动扣减配件库存这个逻辑涉及事务和并发能体现你对数据一致性的理解比多写一个CRUD页面含金量高得多。第四一个我踩过坑换来的习惯改动任何数据前先复制一份初始化SQL做备份。有次我把vehicle_info整张表清空了前端列表全空重新造数据花了半小时。从那以后我每次动数据库前都强制走一遍全库备份改坏了直接导回来等于给自己买好后悔药。希望这些过程能帮到你——照着这套系统把SpringBootVueMySQL的前后端分离流程完整走一遍比看十篇框架配置文章都管用。本文还有配套的精品资源点击获取
返回列表