温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系名片!
一、项目背景与意义
随着汽车保有量的持续增长,汽车维修、保养、美容、保险等后市场服务需求快速扩张。传统汽车服务门店多依赖纸质工单、Excel 表格和电话沟通来管理客户、车辆、维修项目与配件库存,普遍存在信息分散、工单流转不透明、库存账实不符、客户复购难以追踪等问题。
本系统基于Spring Boot + Vue3前后端分离架构,构建一套覆盖「客户—车辆—服务工单—配件库存—结算统计」全流程的汽车服务管理系统,帮助门店实现业务数字化,提升服务效率与客户体验。
二、系统目标与核心功能
系统面向汽车维修保养门店,主要服务于管理员、服务顾问、维修技师、仓库管理员四类角色,核心目标如下:
- 客户与车辆档案统一管理:一个客户可关联多台车辆,记录车牌、车型、里程、保养记录。
- 服务工单全流程闭环:从预约、接车、派工、维修、质检到结算,状态可追溯。
- 配件库存精细化管理:入库、出库、领料、盘点与库存预警。
- 经营数据可视化:营业额、工单量、热门服务项目、客户复购率等统计报表。
- 权限与安全控制:基于角色的菜单与接口权限控制,操作日志留痕。
三、技术选型
| 层次 | 技术栈 | 说明 |
|---|---|---|
| 后端框架 | Spring Boot 3.x | 快速构建 RESTful 接口,内置 Tomcat |
| 持久层 | MyBatis-Plus | 简化 CRUD,支持分页与条件构造器 |
| 数据库 | MySQL 8.0 | 存储业务数据,支持事务与索引优化 |
| 缓存 | Redis | 缓存登录令牌、字典数据与热点统计 |
| 认证授权 | Spring Security + JWT | 无状态认证,接口级权限校验 |
| 前端框架 | Vue3 + TypeScript | 组合式 API,类型安全 |
| 构建工具 | Vite | 快速冷启动与热更新 |
| UI 组件库 | Element Plus | 后台管理常用组件 |
| 状态与路由 | Pinia + Vue Router | 全局状态管理与动态路由 |
| 图表 | ECharts | 经营数据可视化 |
四、系统架构设计
系统采用前后端分离架构,前端通过 Axios 调用后端 RESTful 接口,后端按 Controller、Service、Mapper 三层组织代码,公共能力(认证、日志、异常处理、文件上传)以切面与拦截器方式统一实现。
flowchart TD A[Vue3 前端 Element Plus] -->|HTTP / JSON| B[Nginx 静态资源与反向代理] B --> C[Spring Boot 应用] C --> D[Controller 层] D --> E[Service 层] E --> F[Mapper 层 MyBatis-Plus] F --> G[(MySQL 8.0)] C --> H[(Redis 缓存)] C --> I[文件存储 本地或 OSS] C --> J[Spring Security + JWT 认证]五、数据库设计
核心表设计围绕客户、车辆、工单、服务项目与配件展开,主要表结构如下:
| 表名 | 说明 | 关键字段 |
|---|---|---|
| sys_user | 系统用户 | id、username、password、role_id、status |
| customer | 客户档案 | id、name、phone、level、create_time |
| vehicle | 车辆档案 | id、customer_id、plate_no、brand、model、mileage |
| service_item | 服务项目 | id、name、price、duration、category |
| work_order | 服务工单 | id、order_no、vehicle_id、status、total_amount、create_time |
| order_item | 工单明细 | id、order_id、item_type、item_id、quantity、price |
| part | 配件库存 | id、name、spec、stock、warn_stock、unit_price |
| stock_record | 出入库记录 | id、part_id、type、quantity、operator、create_time |
工单状态建议使用枚举统一管理:待接车 → 已接车 → 维修中 → 待质检 → 已完成 → 已结算,并通过状态机校验流转合法性,避免越级变更。
六、后端核心实现
6.1 统一响应与全局异常处理
@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.setCode(200); r.setMessage("操作成功"); r.setData(data); return r; } public static <T> Result<T> fail(String message) { Result<T> r = new Result<>(); r.setCode(500); r.setMessage(message); return r; } }6.2 JWT 认证与权限校验
登录成功后签发 JWT,前端将令牌存入 Pinia 与 localStorage,请求时通过拦截器放入Authorization请求头。后端自定义过滤器解析令牌并写入SecurityContext,结合@PreAuthorize实现接口级权限控制。
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Resource private JwtUtil jwtUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String header = request.getHeader("Authorization"); if (header != null && header.startsWith("Bearer ")) { String token = header.substring(7); if (jwtUtil.validate(token)) { String username = jwtUtil.getUsername(token); List<String> roles = jwtUtil.getRoles(token); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, roles.stream().map(SimpleGrantedAuthority::new).toList()); SecurityContextHolder.getContext().setAuthentication(auth); } } chain.doFilter(request, response); } }6.3 工单创建与库存扣减
工单结算时需要同时更新工单状态、写入明细并扣减配件库存,必须保证事务一致性。这里使用@Transactional配合乐观锁字段version防止并发超卖。
@Service public class WorkOrderService { @Resource private WorkOrderMapper workOrderMapper; @Resource private PartMapper partMapper; @Transactional(rollbackFor = Exception.class) public void settle(Long orderId) { WorkOrder order = workOrderMapper.selectById(orderId); if (order == null || !"已完成".equals(order.getStatus())) { throw new BizException("工单状态不允许结算"); } List<OrderItem> items = order.getItems(); for (OrderItem item : items) { if ("PART".equals(item.getItemType())) { int rows = partMapper.deductStock(item.getItemId(), item.getQuantity()); if (rows == 0) { throw new BizException("配件库存不足:" + item.getItemId()); } } } order.setStatus("已结算"); workOrderMapper.updateById(order); } }七、前端核心实现
7.1 请求封装与统一拦截
import axios from 'axios' import { ElMessage } from 'element-plus' import { useUserStore } from '@/stores/user' const request = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) request.interceptors.request.use(config => { const userStore = useUserStore() if (userStore.token) { config.headers.Authorization = `Bearer ${userStore.token}` } return config }) request.interceptors.response.use( res => { const { code, message, data } = res.data if (code === 200) return data ElMessage.error(message || '请求失败') return Promise.reject(new Error(message)) }, err => { if (err.response?.status === 401) { useUserStore().logout() } ElMessage.error(err.message) return Promise.reject(err) } ) export default request7.2 工单列表页
<template> <el-table :data="orderList" v-loading="loading"> <el-table-column prop="orderNo" label="工单号" width="180" /> <el-table-column prop="plateNo" label="车牌号" width="120" /> <el-table-column prop="customerName" label="客户" width="120" /> <el-table-column prop="status" label="状态"> <template #default="{ row }"> <el-tag :type="statusType(row.status)">{{ row.status }}</el-tag> </template> </el-table-column> <el-table-column prop="totalAmount" label="金额" /> <el-table-column label="操作" width="160"> <template #default="{ row }"> <el-button link type="primary" @click="handleDetail(row)">详情</el-button> <el-button link type="success" :disabled="row.status !== '已完成'" @click="handleSettle(row)">结算</el-button> </template> </el-table-column> </el-table> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue' import { getOrderPage, settleOrder } from '@/api/order' const orderList = ref([]) const loading = ref(false) const loadData = async () => { loading.value = true try { orderList.value = await getOrderPage({ page: 1, size: 10 }) } finally { loading.value = false } } const handleSettle = async (row: any) => { await settleOrder(row.id) loadData() } onMounted(loadData) </script>八、系统部署与运行
- 准备环境:JDK 17、Node.js 18、MySQL 8.0、Redis 6。
- 导入数据库脚本,修改后端
application.yml中的数据源与 Redis 配置。 - 后端执行
mvn clean package,运行java -jar car-service.jar。 - 前端执行
npm install与npm run build,将dist目录交由 Nginx 托管。 - Nginx 配置
/api反向代理到后端服务,解决跨域问题。
九、总结与展望
本文围绕 Spring Boot + Vue3 前后端分离架构,完成了汽车服务管理系统从需求分析、技术选型、数据库设计到核心功能实现的整体方案。系统通过工单状态机与事务控制保证了业务一致性,通过 JWT 与角色权限保障了数据安全。
后续可继续扩展的方向包括:接入微信小程序实现客户自助预约、引入消息队列处理库存与通知异步任务、基于历史工单数据做保养提醒与客户流失预警,以及对接第三方支付与电子发票能力。