
简介这是一套面向计算机专业本科生毕业设计的全栈汽车销售系统实战源码基于SpringBoot后端与Vue前端技术栈构建覆盖用户管理、车辆展示、订单处理、库存查询等核心业务模块适合Java Web开发初学者巩固前后端分离开发流程与企业级项目结构认知。资源包共622个文件包含60个Java后端业务类、55个Vue组件、115个JS交互脚本、98个XML配置及SQL映射文件、49个CSS与SCSS样式文件以及SVG图标、GIF动效等前端资源整体压缩后仅4.53MB轻量易部署。已有215人学习下载代码结构清晰含OrderServiceImpl、CustomerServiceImpl、UserController等典型服务层与控制层实现辅以DetailsList、OrderList等业务组件便于理解分层架构与API对接逻辑可直接用于课程设计、毕设原型开发或SpringBootVue技术栈入门实践。1. 这不是又一个“Hello World”后台一套能真跑起来、能改价格、能查库存、能导出Excel的汽车销售系统源码你手头正赶着毕业设计导师说“别抄网上烂大街的图书管理系统”同学发来个“基于SpringBootVue的汽车销售系统.zip”点开一看——pom.xml里Spring Boot版本是2.7.18package.json里Vue是2.6.14后端有CarController.java、OrderService.java前端有car-list.vue、order-create.vue连application-prod.yml和Nginx部署示例都塞进docs/deploy/里了。这不是PPT演示项目这是能连真实MySQL、能走完整下单流程、能导出带车架号和销售员签名的PDF合同的可交付工程。它不炫技没上Redis集群、没接RocketMQ但每层都踩在Java Web开发的“稳态区”MyBatis-Plus做CRUD、Shiro管权限、ECharts画销量趋势、EasyExcel导出报表。适合本科毕设答辩、中小车行内部试用、或作为SpringBootVue全栈学习的“最小可行锚点”——你改3个字段、换2张表、调通1个接口就能看到真实数据在前后端之间流动。别被“源码”俩字吓住它比你想象中更“接地气”也比你下载过的90%所谓“完整项目”更经得起mvn clean package和npm run build的双重暴击。2. 从解压到登录5步跑通本地开发环境含Spring Boot与Vue双端启动细节2.1 解压即得完整工程结构看清src/main与src/views的对应关系拿到基于springBootVue的汽车销售系统源码.zip后先别急着IDEA打开。用命令行解压并观察目录层级unzip 基于springBootVue的汽车销售系统源码.zip -d car-sales-system cd car-sales-system ls -l你会看到两个核心目录backend/Spring Boot后端和frontend/Vue前端外加docs/部署文档、sql/建库脚本。重点看这两个路径backend/src/main/java/com/example/carsales/主包名下有controller/CarController,OrderController、service/CarService,OrderService、mapper/CarMapper,OrderMapper所有业务逻辑都在这里frontend/src/views/CarList.vue对应车辆列表页OrderCreate.vue对应下单页Login.vue是登录入口路由定义在router/index.js中。提示不要把frontend/直接拖进IDEA当Maven项目——它是个独立Vue CLI工程需用Node.js环境运行同理backend/才是Maven项目需用JDK 8编译。2.2 后端启动配好MySQL、改对端口、绕过Shiro拦截才能进登录页系统默认使用MySQL 5.7先执行建库脚本-- 在MySQL中执行路径car-sales-system/sql/car_sales_db.sql CREATE DATABASE IF NOT EXISTS car_sales_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE car_sales_db; -- 脚本内含t_car, t_order, t_user等8张表及初始数据含3台测试车辆、2个销售员账号然后配置后端连接参数。打开backend/src/main/resources/application-dev.ymlspring: datasource: url: jdbc:mysql://localhost:3306/car_sales_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_mysql_password # ← 必须改成你本地MySQL密码 redis: host: localhost port: 6379 database: 0 server: port: 8081 # ← 建议改为8081避免与常见服务冲突 # Shiro权限控制开关开发时建议关闭否则登录后跳转401 shiro: enable: false # ← 关键设为false才能绕过登录拦截直通首页保存后在backend/目录下执行# 确保已安装Maven 3.6 mvn clean compile mvn spring-boot:run若看到Started CarSalesApplication in X.XXX seconds且无Connection refused报错说明后端已就绪。此时访问http://localhost:8081/api/car/list应返回JSON格式车辆列表含id、brand、model、price等字段。2.3 前端启动Vue 2.6需匹配Node 14.xproxy跨域配置不能漏该系统基于Vue CLI 3.12非Vite对Node版本敏感。必须使用Node.js 14.x如14.21.3Node 16会因vue-template-compiler版本冲突报错# 检查Node版本 node -v # 应输出 v14.x.x npm -v # 应输出 6.x.x若为8用nvm切换 # 进入前端目录 cd frontend # 安装依赖注意不要用cnpm某些包镜像不同步导致build失败 npm install # 启动开发服务器自动代理/api请求到localhost:8081 npm run serve关键看frontend/vue.config.js中的代理配置module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, // ← 必须指向你后端启动的端口 changeOrigin: true, pathRewrite: { ^/api: // 把/api/car/list重写为/car/list发给后端 } } } } }启动成功后浏览器打开http://localhost:8080应看到登录页。输入默认账号admin/123456或sales/123456即可进入系统主页。2.4 首次登录后的关键验证检查车辆列表、订单创建、Excel导出三连通登录后依次点击左侧菜单车辆管理 → 车辆列表页面应加载出宝马X3、奥迪A4L等测试数据表格右上角有“新增”按钮订单管理 → 创建订单选择一辆车、填写客户姓名、电话点击“提交”应弹出“创建成功”提示且订单列表中出现新记录报表统计 → 销量导出点击“导出Excel”浏览器应下载car_sales_report_20240515.xlsx用Excel打开可见“品牌”、“销量”、“销售额”三列数据。这三步通了证明MyBatis-Plus能正确映射Car实体与t_car表OrderController的PostMapping(/order)能接收JSON并调用OrderService落库EasyExcel.write()能生成带样式的真实Excel文件非空文件。注意若导出文件打不开大概率是frontend/src/utils/export.js中filename参数含中文未编码需改为encodeURIComponent(filename)——这是Vue 2.6 EasyExcel 3.x的经典兼容坑。3. 毕设答辩高频问题预演数据库设计、权限模型、前后端交互链路拆解3.1 数据库ER图核心三张表为什么t_car不直接存库存而要t_stock系统采用分离式库存设计而非在t_car表加stock_count字段。查看sql/car_sales_db.sqlCREATE TABLE t_car ( id BIGINT PRIMARY KEY AUTO_INCREMENT, brand VARCHAR(50) NOT NULL COMMENT 品牌, model VARCHAR(100) NOT NULL COMMENT 型号, price DECIMAL(10,2) NOT NULL COMMENT 售价, status TINYINT DEFAULT 1 COMMENT 状态1-在售0-停售 ); CREATE TABLE t_stock ( id BIGINT PRIMARY KEY AUTO_INCREMENT, car_id BIGINT NOT NULL COMMENT 关联t_car.id, quantity INT NOT NULL DEFAULT 0 COMMENT 库存数量, last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (car_id) REFERENCES t_car(id) ); CREATE TABLE t_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, car_id BIGINT NOT NULL, customer_name VARCHAR(100) NOT NULL, phone VARCHAR(20), order_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 1 COMMENT 1-待发货2-已发货3-已完成 );选型理由t_car专注描述车辆静态属性品牌、型号、定价符合“单一职责”t_stock独立记录库存支持多仓库扩展未来加warehouse_id字段即可下单时OrderService执行“扣减库存”逻辑先SELECT FOR UPDATE查t_stock再UPDATE t_stock SET quantity quantity - 1 WHERE car_id ? AND quantity 1失败则抛异常回滚事务——这比在t_car里UPDATE ... SET stock_count stock_count - 1更易保证并发安全。3.2 Shiro权限控制落地如何用RequiresPermissions注解实现“销售员只能看自己订单”系统权限粒度控制到菜单级和按钮级。查看backend/src/main/java/com/example/carsales/controller/OrderController.javaRestController RequestMapping(/order) public class OrderController { GetMapping(/list) RequiresPermissions(order:list) // ← 菜单级权限 public Result list(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer limit) { // 返回所有订单管理员可见全部 return Result.success(orderService.page(new Page(page, limit))); } GetMapping(/mylist) RequiresPermissions(order:mylist) // ← 按钮级权限销售员专属 public Result myOrders(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer limit, AuthenticationToken token) { // 从Shiro Subject中获取当前用户ID只查该用户创建的订单 Long userId (Long) SecurityUtils.getSubject().getPrincipal(); return Result.success(orderService.myPage(userId, new Page(page, limit))); } }对应ShiroConfig.java中权限配置Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean bean new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager); MapString, String filterChainDefinitionMap new LinkedHashMap(); filterChainDefinitionMap.put(/api/order/list, authc, perms[order:list]); filterChainDefinitionMap.put(/api/order/mylist, authc, perms[order:mylist]); // 其他URL... bean.setFilterChainDefinitionMap(filterChainDefinitionMap); return bean; }前端OrderList.vue中根据权限动态渲染按钮template div el-button v-if$hasPermission(order:mylist) clickloadMyOrders我的订单/el-button el-button v-if$hasPermission(order:list) clickloadAllOrders全部订单/el-button /div /template血泪经验答辩时老师常问“如果销售员手动改URL访问/api/order/list怎么办”——答案是Shiro的RequiresPermissions注解在Controller层强制校验即使URL被篡改后端也会返回403 Forbidden前端JS无法绕过。3.3 前后端交互全链路从Vue点击“下单”到MySQL写入t_order的7个关键节点以OrderCreate.vue中submitOrder()方法为例追踪一次完整下单前端触发this.$http.post(/api/order, this.formData)→ 发送JSON到/api/orderVue代理转发vue.config.js将/api/order代理至http://localhost:8081/orderSpring MVC路由OrderController.create(RequestBody Order order)接收参数参数校验Valid注解触发Order实体上的NotNull、Min(1)等校验失败返回400业务逻辑orderService.create(order)中查询t_stock确认库存充足SELECT quantity FROM t_stock WHERE car_id ?扣减库存UPDATE t_stock SET quantity quantity - 1 WHERE car_id ?插入订单INSERT INTO t_order (...) VALUES (...)事务控制整个create()方法被Transactional包裹库存扣减与订单插入要么全成功要么全回滚响应返回Result.success(下单成功)封装成JSON前端then()中跳转订单列表页。这个链路覆盖了Vue Axios、Webpack DevServer代理、Spring MVC、MyBatis-Plus、数据库事务五大技术点是毕设答辩最硬核的“可讲清楚”环节。4. 避坑指南那些让毕设答辩前夜崩溃的5个真实翻车现场4.1 现象前端登录后空白页控制台报Cannot find module vue原因frontend/node_modules/下vue包损坏或package.json中vue: ^2.6.14被误删。Vue 2.6要求vue-template-compiler版本严格匹配若npm install中途断网会导致node_modules/vue与node_modules/vue-template-compiler版本不一致。解决删除frontend/node_modules/和frontend/package-lock.json重新执行npm install。切勿用npm update vue这会升级到2.7与vue-template-compiler2.6.x不兼容。4.2 现象后端启动报错java.lang.NoClassDefFoundError: org/springframework/boot/context/properties/ConfigurationPropertiesBean原因pom.xml中Spring Boot父POM版本与实际依赖冲突。该源码基于Spring Boot 2.7.x若你本地Maven仓库存在旧版spring-boot-starter-parent如2.3.xMaven可能错误继承。解决强制指定父POM版本。打开backend/pom.xml确认parent节点为parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version !-- 必须是2.7.18不能是2.7.0或2.7 -- relativePath/ /parent然后执行mvn clean dependency:purge-local-repository清理本地缓存。4.3 现象车辆列表显示“暂无数据”但http://localhost:8081/api/car/list返回正常JSON原因前端CarList.vue中axios.get(/api/car/list)发起请求但vue.config.js代理配置未生效请求实际发向http://localhost:8080/api/car/list404。常见于vue.config.js文件名拼错如vue.config.js.多了一个点、或未重启npm run serve。解决在浏览器开发者工具Network标签页筛选XHR请求看/api/car/list的Status是否为200。若为404检查vue.config.js路径是否在frontend/根目录且文件名无空格或隐藏字符修改后必须重启npm run serve。4.4 现象导出Excel文件只有表头无数据行原因backend/src/main/java/com/example/carsales/service/impl/ReportServiceImpl.java中EasyExcel.write()调用时sheet()方法未传入Sheet对象导致默认Sheet为空。源码中此处有笔误// ❌ 错误写法源码原始bug EasyExcel.write(response.getOutputStream()).sheet().doWrite(dataList); // ✅ 正确写法需手动指定sheet名和类 EasyExcel.write(response.getOutputStream(), CarSaleReport.class) .sheet(销量报表) .doWrite(dataList);解决找到ReportServiceImpl.exportSalesReport()方法将sheet()替换为sheet(销量报表, CarSaleReport.class)并确保CarSaleReport.java实体类有ExcelProperty(品牌)等注解。4.5 现象修改车辆价格后前端列表未更新F5刷新才显示新值原因Vue组件CarList.vue中data()返回的carList数组未响应式更新。源码中this.carList response.data赋值后未触发视图更新因response.data是新数组引用但Vue 2.x对数组索引赋值如this.carList[0].price 250000才触发更新。解决在CarList.vue的updateCar()方法成功回调中使用Vue.set或this.$set// 替换原代码this.carList res.data; this.$set(this, carList, res.data); // 强制响应式更新 // 或更稳妥this.carList.splice(0, this.carList.length, ...res.data);5. 毕设加分项实战3个可快速落地的定制化改造含代码与SQL5.1 改造一为车辆列表增加“库存预警”红标前端后端联动需求当t_stock.quantity 5时车辆列表中该行背景变浅红色并显示“库存紧张”标签。后端改造CarController.javaGetMapping(/list) public Result list() { ListCar cars carService.list(); // 关联查询库存注入预警状态 cars.forEach(car - { Stock stock stockService.getByCarId(car.getId()); car.setStockQuantity(stock ! null ? stock.getQuantity() : 0); car.setLowStockWarning(stock ! null stock.getQuantity() 5); }); return Result.success(cars); }前端改造CarList.vuetemplate el-table :datacarList el-table-column propbrand label品牌 / el-table-column propmodel label型号 / el-table-column propprice label售价 / el-table-column label库存 width120 template slot-scope{row} span :class{text-red: row.lowStockWarning} {{ row.stockQuantity }} /span span v-ifrow.lowStockWarning classtag tag-danger库存紧张/span /template /el-table-column /el-table /template style scoped .text-red { color: #f56c6c; } .tag-danger { background: #f56c6c; color: white; padding: 2px 6px; border-radius: 3px; font-size: 12px; margin-left: 4px; } /style提示Car.java实体需新增private Integer stockQuantity; private Boolean lowStockWarning;及getter/setter。5.2 改造二订单导出PDF合同集成iText75分钟搞定系统已有Excel导出但毕设常要求“生成PDF合同”。用iText7轻量集成后端添加依赖backend/pom.xmldependency groupIdcom.itextpdf/groupId artifactIditext7-core/artifactId version7.2.5/version typepom/type /dependency dependency groupIdcom.itextpdf/groupId artifactIdkernel/artifactId version7.2.5/version /dependency dependency groupIdcom.itextpdf/groupId artifactIdlayout/artifactId version7.2.5/version /dependency新增PDF导出接口OrderController.javaGetMapping(/pdf/{id}) public void exportPdf(PathVariable Long id, HttpServletResponse response) throws IOException { Order order orderService.getById(id); Car car carService.getById(order.getCarId()); response.setContentType(application/pdf); String filename URLEncoder.encode(合同_ order.getId() .pdf, UTF-8); response.setHeader(Content-Disposition, attachment; filename filename); PdfWriter writer new PdfWriter(response.getOutputStream()); PdfDocument pdf new PdfDocument(writer); Document document new Document(pdf); document.add(new Paragraph(汽车销售合同).setBold().setFontSize(16)); document.add(new Paragraph(甲方购车人 order.getCustomerName())); document.add(new Paragraph(乙方销售方XX汽车销售有限公司)); document.add(new Paragraph(车辆信息 car.getBrand() car.getModel())); document.add(new Paragraph(合同金额 ¥ order.getAmount())); document.add(new Paragraph(签订日期 new Date())); document.close(); }前端调用OrderList.vue中加按钮el-button sizemini clickdownloadPdf(scope.row.id)PDF合同/el-buttonmethods: { downloadPdf(id) { window.open(/api/order/pdf/${id}, _blank); // 直接跳转触发下载 } }5.3 改造三Vue路由动态加载菜单从后端读取权限菜单告别hardcode当前菜单写死在frontend/src/router/index.js不符合企业级权限管理。改为从后端API动态获取后端新增接口MenuController.javaRestController RequestMapping(/menu) public class MenuController { GetMapping(/list) public Result list() { // 模拟从数据库查菜单实际应关联用户角色 ListMenu menus Arrays.asList( new Menu(1L, 车辆管理, /car, el-icon-truck), new Menu(2L, 订单管理, /order, el-icon-document), new Menu(3L, 报表统计, /report, el-icon-s-data) ); return Result.success(menus); } }前端改造路由frontend/src/router/index.js// 删除原有routes常量改为异步加载 const router new Router({ mode: history, base: process.env.BASE_URL, routes: [ { path: /login, component: () import(/views/Login.vue) }, { path: *, redirect: /login } ] }); // 登录后动态添加路由 export function addDynamicRoutes(menus) { menus.forEach(menu { router.addRoute({ path: menu.path, name: menu.name, component: () import(/views/${menu.component}.vue), meta: { title: menu.title, icon: menu.icon } }); }); } export default router;登录成功后调用Login.vuethis.$http.get(/api/menu/list).then(res { addDynamicRoutes(res.data); this.$router.push(/car); // 跳转首个菜单 });从那以后我每次做毕设只要看到src/views/和src/main/java/目录齐整、pom.xml与package.json版本明确、sql/里有建库脚本就敢拍胸脯说“这项目能跑通”。因为真正的工程能力不在炫技的架构图而在你能把mvn clean package的每一行日志看懂能在console.log里揪出undefined的源头能在application.yml里改对一个冒号的位置。这份汽车销售系统源码它不承诺教你成为架构师但它保证让你亲手把一个“能卖车”的系统从zip包里解压出来跑在自己电脑上改出属于你的第一行业务代码——这比任何PPT里的“高可用”“微服务”都实在。希望帮到你。本文还有配套的精品资源点击获取