简介:这是一套面向汽车修理厂日常运营场景的汽修管理系统源码,适合计算机专业学生做课程设计、毕业设计参考,也适合中小型修理厂二次开发或学习业务管理逻辑。系统围绕客户档案、预约登记、维修订单、配件库存、财务收支、员工任务与统计报表等模块展开,覆盖从接车到结算的完整流程,并配有数据库脚本与权限控制思路。压缩包共53个文件、约2.66MB,以C++源码(.h与.cpp)为主体,辅以MFC资源文件、工程配置(.dsp/.dsw)、Access数据库(.mdb)及可执行程序,结构完整、便于编译调试。目前已有678人学习下载,读者可从中获得一套可直接运行的桌面端管理系统骨架,理解界面与数据库交互方式,并借鉴其模块划分与业务建模思路,用于改造或扩展自己的项目。
1. 汽修管理系统:从接车到结算,一套跑在修理厂里的业务闭环
上个月去一个开修理厂的朋友那儿串门,正赶上他手忙脚乱:一辆车刚做完保养,前台在纸质单上找工时费,库房那边说机油只剩半桶,财务又在翻上个月的结算记录对账。三张纸、两个本子、一个微信群,就是他们全部的“管理系统”。这种场景在中小修理厂太常见了,接车靠喊、配件靠记、结算靠算,车一多就乱套。这套汽修管理系统要解决的正是这个痛点——把接车登记、维修派工、配件领用、工时结算串成一条线,让前台、车间、库房看到的是同一份数据。它适合谁?适合想给自己厂子搭一套轻量业务系统的开发者,也适合拿它当管理系统类项目练手、学完整业务闭环怎么落地的人。下面我按“能干什么、怎么跑、怎么改”的顺序,把这份源码拆开讲。
2. 技术栈与目录结构:一套典型的前后端分离管理系统骨架
拿到源码先别急着跑,先看它用什么搭的、文件怎么摆。这套系统是标准的前后端分离结构,后端管业务和数据,前端管界面和交互,中间靠 HTTP 接口通信。这种结构在管理系统类项目里几乎是默认选择,好处是前后端可以分开开发、分开部署,改界面不用动后端逻辑。
2.1 后端技术选型与分层
后端我一般会按“控制层—服务层—数据访问层”三层来组织,这套项目也是这个路子。控制层负责接收请求、校验参数、返回结果;服务层放业务逻辑,比如“接车时自动生成工单号”“结算时汇总工时和配件金额”;数据访问层只管和数据库打交道。分层的好处是业务改动集中在服务层,不会牵一发动全身。
常见做法是用 Spring Boot 做后端框架,搭配 MyBatis 或 MyBatis-Plus 做数据访问,MySQL 存业务数据。依赖管理用 Maven,启动类一个main方法就能把整个服务拉起来。数据库连接、端口这些配置放在application.yml里,改环境不用动代码。
# application.yml 关键配置示例 server: port: 8080 # 后端服务端口,前端代理要指向这里 spring: datasource: url: jdbc:mysql://localhost:3306/repair_db?useUnicode=true&characterEncoding=utf8 username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true # 数据库下划线字段自动映射成驼峰属性这段配置里最容易被忽略的是map-underscore-to-camel-case,数据库里叫customer_name,实体类里叫customerName,开了这个映射就不用每个字段写@TableField。端口8080要和前端代理配置对上,不然前端请求会 404。
2.2 前端结构与页面组织
前端一般是 Vue 加 Element UI 这类组件库,页面按业务模块分目录:接车登记一个页面、维修派工一个页面、配件管理一个页面、结算一个页面。路由配置里每个模块对应一个路径,菜单栏根据路由生成。请求统一走一个封装好的request.js,里面配了后端地址和拦截器,登录态、错误提示都在拦截器里处理。
// request.js 请求封装核心逻辑 import axios from 'axios' const service = axios.create({ baseURL: '/api', // 开发环境由 devServer 代理到后端 8080 timeout: 10000 }) service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) config.headers['Authorization'] = token // 登录后每个请求带 token return config }) export default servicebaseURL写成/api是为了开发时走代理、上线时走 Nginx 转发,前端代码不用改地址。拦截器里统一加 token,避免每个接口手动传。这套封装是管理系统前端的标配,看懂这一个文件,其他页面的请求逻辑就都通了。
2.3 目录结构速览
把源码解压后,大致是这么摆的:
repair-system/ ├── backend/ # 后端工程 │ ├── src/main/java/com/repair/ │ │ ├── controller/ # 接口层:接车、派工、配件、结算 │ │ ├── service/ # 业务逻辑层 │ │ ├── mapper/ # 数据访问层 │ │ ├── entity/ # 实体类,对应数据库表 │ │ └── config/ # 跨域、拦截器等配置 │ └── src/main/resources/ │ ├── application.yml # 配置文件 │ └── mapper/ # MyBatis XML(如果用了) ├── frontend/ # 前端工程 │ ├── src/ │ │ ├── views/ # 各业务页面 │ │ ├── api/ # 接口定义 │ │ ├── router/ # 路由 │ │ └── utils/request.js # 请求封装 │ └── package.json └── sql/ # 建库建表脚本看到sql/目录就说明作者把建表脚本一起给了,这是跑起来的关键——先建库导表,再启动后端,顺序反了会报“表不存在”。
3. 跑起来:从建库到登录的完整启动链路
这一章是给想直接跑通的人看的。我按实际操作的顺序写:先准备环境,再建库导数据,然后起后端、起前端,最后登录验证。每一步都写清楚失败时看哪里,照着走基本不会卡住。
3.1 环境准备与版本对齐
先确认本机装了什么。后端要 JDK 8 或以上、Maven;前端要 Node.js 和 npm;数据库要 MySQL 5.7 或 8.0。版本这块有个血泪经验:MySQL 8 的驱动类名是com.mysql.cj.jdbc.Driver,MySQL 5 是com.mysql.jdbc.Driver,配置里写错启动就报驱动加载失败。Node 版本也别太新,老项目用太新的 Node 跑npm install容易卡在依赖编译上,我一般用 Node 16 左右。
# 检查环境版本 java -version # 期望 1.8 及以上 mvn -v # 期望 3.6 及以上 node -v # 建议 16.x mysql --version # 5.7 或 8.0版本对不上不用硬扛,装个对应版本或者用版本管理工具切一下,比后面排查依赖冲突省事得多。
3.2 建库导表与配置修改
打开sql/目录下的脚本,里面一般是建库语句加建表语句,可能还带几条初始数据(比如默认管理员账号)。用命令行或者数据库客户端执行都行。
# 命令行导入示例 mysql -u root -p -e "CREATE DATABASE repair_db DEFAULT CHARACTER SET utf8mb4;" mysql -u root -p repair_db < sql/repair_db.sql导完表去改application.yml里的数据库地址、用户名、密码,改成你本机的。字符集用utf8mb4,不然客户姓名里带生僻字或者特殊符号会存成乱码。这一步翻车的典型现象是:后端启动没报错,但一查列表就是空,或者插入报字段超长——多半是字符集或字段长度的问题。
3.3 启动后端与前端
后端在backend/目录下,用 Maven 起:
cd backend mvn spring-boot:run # 或者先打包再运行 mvn clean package -DskipTests java -jar target/repair-system.jar看到控制台打出Started ... Application就说明起来了,默认监听 8080。如果报端口占用,改application.yml里的server.port,或者把占用 8080 的进程关掉。
前端在frontend/目录下:
cd frontend npm install # 第一次跑必须装依赖,网络慢可以换镜像源 npm run dev # 开发模式启动,一般监听 8081 或 3000npm install卡住是常见问题,多半是网络原因,换个国内镜像源再试。启动后控制台会打印本地访问地址,浏览器打开就能看到登录页。
3.4 登录验证与接口连通性检查
用初始账号登录(账号密码一般在 SQL 脚本的初始数据里,或者项目说明里)。登录成功后重点看两件事:一是菜单能不能正常加载,二是列表页有没有数据。如果登录报“用户名或密码错误”,先去数据库user表里确认账号存在、密码字段是不是加密过的——有些项目密码存的是明文,有些是 MD5,对不上就登不进去。
如果页面能打开但接口全报 404 或跨域错误,检查前端request.js的baseURL和后端端口是否一致,以及后端有没有配跨域。开发阶段前端一般配了代理,代理目标要指向后端实际端口。
提示:第一次跑通后,先把数据库备份一份。后面改代码、改表结构,有备份就有后悔药,改坏了直接还原。
4. 核心模块拆解:接车、派工、配件、结算怎么串起来
跑通之后就该看代码了。这套系统的价值不在界面多漂亮,而在业务链路是否闭环。我挑四个核心模块讲:接车登记是入口,派工是流转,配件领用是成本,结算是出口。四个模块的数据是串起来的,看懂它们之间的关联,二次开发就有底了。
4.1 接车登记:工单的起点
接车登记做的事是把客户、车辆、故障描述录进系统,生成一张工单。工单号一般按日期加流水生成,保证唯一。核心逻辑在服务层,控制层只做参数接收。
// RepairOrderService.java 接车登记核心逻辑 public String createOrder(RepairOrderDTO dto) { // 1. 校验车辆是否已存在,不存在则先建档 Vehicle vehicle = vehicleMapper.selectByPlate(dto.getPlateNo()); if (vehicle == null) { vehicle = new Vehicle(); vehicle.setPlateNo(dto.getPlateNo()); vehicle.setOwnerName(dto.getOwnerName()); vehicleMapper.insert(vehicle); } // 2. 生成工单号:日期 + 4 位流水 String orderNo = "RO" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE) + String.format("%04d", orderMapper.countToday() + 1); // 3. 落工单,状态置为“待派工” RepairOrder order = new RepairOrder(); order.setOrderNo(orderNo); order.setVehicleId(vehicle.getId()); order.setFaultDesc(dto.getFaultDesc()); order.setStatus("PENDING"); // 状态机起点 orderMapper.insert(order); return orderNo; }这段代码有三个关键点。第一,车辆按车牌查重,避免同一辆车重复建档,这是数据干净的前提。第二,工单号用“日期+流水”,流水按当天已有单量加一,简单够用;并发高的话要加锁或改用序列,小修理厂一天几十单,这样写没问题。第三,status字段是整个流程的状态机,从PENDING(待派工)往后流转,后面派工、完工、结算都靠改这个字段推进。
4.2 维修派工:状态流转与人员分配
派工是把工单分配给具体的维修师傅,同时记录预计工时。这一步的核心是状态校验——只有“待派工”的单才能派,已经派过的不能再派,否则会出现两个师傅干同一辆车。
// DispatchService.java 派工逻辑 public void dispatch(Long orderId, Long technicianId, BigDecimal estimatedHours) { RepairOrder order = orderMapper.selectById(orderId); // 状态校验:只有待派工状态可以派工 if (!"PENDING".equals(order.getStatus())) { throw new BizException("当前工单状态不允许派工"); } DispatchRecord record = new DispatchRecord(); record.setOrderId(orderId); record.setTechnicianId(technicianId); record.setEstimatedHours(estimatedHours); dispatchMapper.insert(record); // 更新工单状态为“维修中” order.setStatus("REPAIRING"); orderMapper.updateById(order); }状态校验这段是血泪经验换来的。早期没加校验,前台手快点两次派工按钮,同一张单生成两条派工记录,结算时工时翻倍,客户当场就急了。加了这个判断之后,重复提交直接被拦。estimatedHours是预计工时,实际工时在完工时录入,两者对比能看出师傅的估时准不准,这也是后面结算的依据之一。
4.3 配件领用:库存扣减与成本归集
配件领用是修理厂成本的大头,也是最容易出猫腻的地方。领用时要扣库存,同时把配件成本挂到工单上,结算时一起算。库存扣减必须防超卖,两个人同时领最后一个配件,不能都成功。
// PartUsageService.java 配件领用 @Transactional public void usePart(Long orderId, Long partId, Integer quantity) { // 1. 带条件扣库存,库存不足时影响行数为 0 int affected = partMapper.deductStock(partId, quantity); if (affected == 0) { throw new BizException("配件库存不足"); } // 2. 记录领用明细,挂到工单 PartUsage usage = new PartUsage(); usage.setOrderId(orderId); usage.setPartId(partId); usage.setQuantity(quantity); usage.setUnitPrice(partMapper.selectById(partId).getSalePrice()); partUsageMapper.insert(usage); }关键是deductStock这条 SQL,用UPDATE part SET stock = stock - #{qty} WHERE id = #{id} AND stock >= #{qty},把库存判断放进 SQL 的WHERE里,靠数据库行锁保证并发安全,比先查再改靠谱得多。方法上加@Transactional,扣库存和记明细要么都成功要么都回滚,不会出现扣了库存没记账的情况。
4.4 结算:工时费与配件费的汇总
结算是把工单上的工时和配件汇总成一张账单。工时费按实际工时乘工时单价,配件费按领用明细汇总,可能还有折扣。结算完成后工单状态置为“已结算”,这张单就闭环了。
// SettlementService.java 结算汇总 public SettlementVO settle(Long orderId) { RepairOrder order = orderMapper.selectById(orderId); if (!"FINISHED".equals(order.getStatus())) { throw new BizException("工单未完工,不能结算"); } // 工时费:实际工时 × 工时单价 BigDecimal laborFee = order.getActualHours() .multiply(laborPriceConfig.getHourlyRate()); // 配件费:领用明细汇总 BigDecimal partFee = partUsageMapper.sumAmountByOrder(orderId); BigDecimal total = laborFee.add(partFee); // 落结算单,更新工单状态 Settlement s = new Settlement(); s.setOrderId(orderId); s.setLaborFee(laborFee); s.setPartFee(partFee); s.setTotalAmount(total); settlementMapper.insert(s); order.setStatus("SETTLED"); orderMapper.updateById(order); return buildVO(s); }结算前必须校验工单已完工,不然会出现没修完就收钱的荒唐账。工时单价从配置里读,不写死在代码里,调价时改配置就行。配件费从领用明细汇总,保证结算金额和实际用料一致,这也是前面领用要记明细的原因——账能对上,客户才信服。
5. 避坑与排查:跑这套系统最容易翻车的五个地方
这套系统结构不复杂,但真跑起来,坑都藏在细节里。下面五条是我实际踩过或者见别人踩过的,每条按“现象—原因—解决”写,遇到问题对着查。
5.1 启动报“表不存在”或“Unknown database”
现象:后端启动直接抛异常,提示某张表不存在,或者数据库连不上。原因:SQL 脚本没导,或者导到了别的库,或者application.yml里的库名和脚本建的不一致。解决:先确认库建了、表导了,用SHOW TABLES;看一眼;再核对配置里的库名拼写。顺序永远是先建库导表,再启动后端。
5.2 前端请求全部 404 或跨域被拦
现象:页面能打开,但登录、列表接口全红,控制台报 404 或 CORS 错误。原因:前端代理没配或配错端口,或者后端没开跨域。解决:检查前端vue.config.js(或vite.config.js)里代理的target是否指向后端实际端口;后端加跨域配置,开发阶段允许本地来源。两边端口对不上是最常见的。
5.3 中文乱码
现象:客户姓名、故障描述存进去变成问号或乱码。原因:数据库或表字符集不是utf8mb4,或者连接串没指定编码。解决:建库时指定DEFAULT CHARACTER SET utf8mb4,连接串加characterEncoding=utf8,已有表用ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4转一下。
5.4 配件库存扣成负数
现象:库存明明只有 1 个,两个人同时领,都成功了,库存变成 -1。原因:先查库存再更新,两步之间没有锁,并发下都查到有库存。解决:把库存判断写进UPDATE的WHERE条件里,靠数据库行锁保证原子性,影响行数为 0 就抛库存不足。这是并发场景的必修课。
5.5 结算金额和预期对不上
现象:结算出来的总价比手工算的少或多。原因:工时单价读的配置不对,或者配件领用明细里有单价为空的记录,或者实际工时没录。解决:先查结算单的工时费、配件费两个分项,定位是哪一项偏了;再查领用明细的unit_price是否有空值,工时字段是否为空。分项拆开看,问题一目了然。
注意:改表结构前先备份,尤其是给已有数据的表加字段、改类型,改错了数据恢复很麻烦。
6. 二次开发:从单店到多店,改造思路与验证方法
这套系统默认是单店模型,数据表里没有门店维度。如果要做成多店版本,改造的核心是给业务表加门店标识,并在查询层统一过滤。我一般按这个顺序改:先加字段,再改查询,最后改权限。
第一步,给工单、配件、结算这些业务表加store_id字段,默认值设成 1,保证老数据不丢。第二步,在服务层的查询方法里统一带上store_id条件,这一步最容易漏,漏一个查询就会串店。第三步,登录时把用户所属门店写进 token,后端从 token 里取门店过滤,前端不用传。
-- 给工单表加门店维度 ALTER TABLE repair_order ADD COLUMN store_id BIGINT NOT NULL DEFAULT 1; CREATE INDEX idx_store_status ON repair_order(store_id, status);加索引是因为多店之后按门店加状态查列表是高频操作,不建索引数据一多就慢。改完怎么验证?我的办法是造两家店的数据,用不同门店的账号登录,互相看不到对方的单,再交叉查一次配件库存,确认隔离干净。这个验证方法比看代码可靠,数据不会骗人。
还有一个改造点是工单状态机。现在状态是硬编码的字符串,要加“挂起”“退单”这类状态,建议抽成枚举或者状态配置表,流转规则集中管理,别散落在各个if里。我吃过这个亏:状态判断散在五六个方法里,加一个新状态改了四处漏了一处,线上出了张状态错乱的单,查了半天。从那以后我每次动状态机,都先把所有流转点列出来,改完用一条完整流程走一遍——接车、派工、领料、完工、结算,每个状态都过一遍,确认没有断点。希望这套拆解能帮到你,少走我走过的弯路。
本文还有配套的精品资源,点击获取