基于Spring Boot+Vue的家电维修系统:全流程设计与实现

发布时间:2026/11/5 7:29:03
基于Spring Boot+Vue的家电维修系统:全流程设计与实现 简介这是一套面向Java全栈学习者与中小型维修服务企业开发者的家电家具维修系统完整项目源码基于SpringBoot、Mybatis-Plus与Vue.js技术栈构建覆盖用户管理、订单提交、维修工单调度、在线支付、评价反馈与统计报表等核心业务模块可用于课程设计、毕业设计参考或二次开发实践。资源包共2009个文件以1234个md文档、710个js脚本、47个json配置为主另含sql建表脚本、html页面与项目介绍docx压缩包约60.36MB目录结构清晰便于按前后端模块检索阅读。目前已有137人学习下载。项目将后端请求处理、数据持久化与前端组件化界面串联成完整数字化维修流程读者可从中获取分层架构设计思路、接口与数据库组织方式以及业务模块落地细节适合具备一定Java与前端基础、希望积累完整项目经验的学习者参考。1. 家电维修系统为什么值得做成一套完整方案家电家具维修这件事线下门店最头疼的不是修不好而是报修、派单、配件、结算全靠微信群和纸质单子师傅跑一趟才发现配件不对客户催三次也没人知道单子卡在谁手里。把这套流程搬进系统本质是把「谁报修、谁接单、谁上门、换了什么件、收多少钱」五个问题用一条数据链串起来。基于 Spring Boot MyBatis-Plus Vue 的家电家具维修系统正好是课程设计和毕设里既能讲清业务闭环、又能体现前后端分离工程能力的题目。它适合两类人一类是要交完整设计文档和可运行代码的学生另一类是想给小型维修门店搭一套能直接改的业务底座的开发者。下面按需求、架构、模块、避坑、验收的顺序把这份方案拆到能照着做的程度。2. 需求与角色建模先把五类用户和三条主流程定死2.1 五类角色各自要看到什么维修系统最容易翻车的地方是需求阶段只画了「用户下单、师傅接单」两条线做到一半发现还有客服、仓库、财务三类人没有入口。我一般会把角色先定成五类每类只给最小必要权限角色核心诉求首屏看到的内容普通用户报修、查进度、评价我的报修单列表与状态维修师傅接单、填维修记录、报配件待接单池与我的进行中工单客服/派单员审核报修、指派师傅待派单列表与师傅负载仓库管理员配件出入库、低库存预警库存列表与预警系统管理员用户、品类、价格、统计数据看板与基础配置这张表不是摆设它直接决定后面数据库要建几张主表、前端要拆几个路由分组。角色定得越早后面改权限的成本越低。通常的做法是权限用「角色-菜单」两级控制不做细到按钮的权限课程设计层面够用也留了扩展口。2.2 三条主流程串起整个业务闭环角色定完把流程收敛成三条其余都是分支报修流程用户提交报修品类、故障描述、上门地址、期望时间→ 客服审核 → 派单给师傅 → 师傅接单 → 上门 → 填写维修记录故障原因、更换配件、工时→ 用户确认 → 评价。配件流程师傅在维修记录里勾选更换的配件 → 系统扣减库存 → 库存低于阈值触发预警 → 仓库补货入库。结算流程维修记录确认后生成费用明细工时费 配件费→ 用户支付课程设计里可用模拟支付→ 生成结算记录供统计。三条流程共用一张「工单」主表状态机是核心。状态不要随手写字符串我一般定义成枚举待审核、待派单、待接单、维修中、待确认、已完成、已取消。每次状态流转都记一条操作日志出问题时这就是黑匣子。2.3 非功能需求别漏掉学生方案最容易只写功能不写约束。这个场景下至少要写清三条一是工单状态变更必须留痕方便客服追溯二是配件扣减要防并发超卖两个人同时报同一个配件不能扣成负数三是列表查询要分页工单表数据量上来后不分页会直接拖垮首屏。这三条写进需求文档答辩时就是加分项因为它们说明你想过系统跑起来之后的事。3. 总体架构设计前后端分离下的分层与数据模型3.1 三层结构和技术选型理由整体是经典的前后端分离三层结构Vue 前端负责页面与交互Spring Boot 提供 REST 接口MyBatis-Plus 负责数据访问MySQL 存业务数据。选型理由要能说出口Spring Boot 把配置和依赖管理收拢起步快MyBatis-Plus 在 MyBatis 之上补了通用 CRUD 和分页插件工单、配件这类表的增删改查不用手写一堆 XMLVue 的组件化让「工单卡片」「状态标签」这类重复 UI 可以复用。三层职责划分如下表现层Vue 页面 路由 状态管理只做展示和表单校验不写业务规则。接口层Controller 收参数、做权限校验、调 Service返回统一响应体。业务层Service 承载状态流转、库存扣减、费用计算等规则。数据层Mapper 接口 MyBatis-Plus复杂统计查询再写自定义 SQL。统一响应体建议固定成code / message / data三段前端拦截器按 code 统一处理登录失效和错误提示避免每个页面各写一套判断。3.2 核心数据表怎么划表不用多六张主表能撑起整个闭环表名作用关键字段user用户与角色id、username、role、phonerepair_order工单主表id、user_id、technician_id、status、address、create_timerepair_record维修记录id、order_id、fault_reason、work_hours、total_feepart配件表id、name、stock、warn_threshold、pricepart_record配件出入库流水id、part_id、change_num、type、order_idorder_log工单操作日志id、order_id、from_status、to_status、operator、create_time工单主表只存「当前状态」历史状态全部进 order_log这样主表轻、追溯全。配件扣减不直接改 part.stock 了事而是先写 part_record 流水再更新库存账实能对上这也是仓库场景的基本要求。3.3 接口分层与统一约定接口按资源划分前缀/api/order、/api/part、/api/user动词用 HTTP 方法表达。分页参数统一pageNum / pageSize返回体里带total。下面是一个典型的工单分页查询接口骨架MyBatis-Plus 的分页插件在这里省了大量手写代码// OrderController.java RestController RequestMapping(/api/order) public class OrderController { Resource private RepairOrderService orderService; // 按状态和关键字分页查询工单客服和师傅共用 GetMapping(/page) public ResultPageRepairOrder page( RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String status, RequestParam(required false) String keyword) { PageRepairOrder page new Page(pageNum, pageSize); LambdaQueryWrapperRepairOrder wrapper new LambdaQueryWrapper(); // 状态为空表示查全部避免前端传空串导致条件失效 wrapper.eq(StringUtils.hasText(status), RepairOrder::getStatus, status); wrapper.and(StringUtils.hasText(keyword), w - w.like(RepairOrder::getAddress, keyword) .or().like(RepairOrder::getContactName, keyword)); wrapper.orderByDesc(RepairOrder::getCreateTime); return Result.ok(orderService.page(page, wrapper)); } }这段代码有两个设计点值得说一是条件构造用StringUtils.hasText做判空前端不传或传空串都不会拼出错误 SQL二是关键字查询用and(...)包一层避免和状态条件用or拼在一起导致条件串味这是 LambdaQueryWrapper 最常见的踩坑点。分页插件需要在配置类里注册MybatisPlusInterceptor并加PaginationInnerInterceptor否则page方法返回的 total 永远是 0。4. 核心模块实现工单状态机、库存扣减与前端交互4.1 工单状态流转与操作日志状态流转必须收口在 Service 里不能让 Controller 直接改 status 字段。我一般写一个changeStatus方法校验「当前状态能否流转到目标状态」再更新主表并写日志// RepairOrderServiceImpl.java private static final MapString, ListString FLOW Map.of( 待审核, List.of(待派单, 已取消), 待派单, List.of(待接单, 已取消), 待接单, List.of(维修中, 已取消), 维修中, List.of(待确认), 待确认, List.of(已完成) ); Transactional(rollbackFor Exception.class) public void changeStatus(Long orderId, String toStatus, Long operatorId) { RepairOrder order getById(orderId); if (order null) { throw new BizException(工单不存在); } ListString allowed FLOW.getOrDefault(order.getStatus(), List.of()); if (!allowed.contains(toStatus)) { // 非法流转直接拒绝防止前端传任意状态 throw new BizException(当前状态不允许流转到 toStatus); } String fromStatus order.getStatus(); order.setStatus(toStatus); updateById(order); // 每次流转都落日志作为追溯依据 OrderLog log new OrderLog(); log.setOrderId(orderId); log.setFromStatus(fromStatus); log.setToStatus(toStatus); log.setOperatorId(operatorId); orderLogService.save(log); }FLOW这张流转表是整个状态机的规则来源改流程只改这一处不用满项目找 if-else。Transactional保证「改状态 写日志」要么都成功要么都回滚否则会出现状态变了但日志丢了的情况排查时非常难受。参数operatorId从登录态里取不要信前端传的值。4.2 配件库存扣减防超卖库存扣减是这类系统里最容易出问题的地方。正确做法是用带条件的 UPDATE把「库存够不够」的判断交给数据库!-- PartMapper.xml -- update iddeductStock UPDATE part SET stock stock - #{num} WHERE id #{partId} AND stock #{num} /updateService 里判断影响行数等于 0 就说明库存不足抛异常回滚整个维修记录提交Transactional(rollbackFor Exception.class) public void submitRecord(RepairRecord record, ListPartUse parts) { // 先扣库存任何一件不足都整体回滚 for (PartUse use : parts) { int rows partMapper.deductStock(use.getPartId(), use.getNum()); if (rows 0) { throw new BizException(配件库存不足 use.getPartName()); } partRecordService.saveFlow(use, record.getOrderId(), 出库); } repairRecordService.save(record); orderService.changeStatus(record.getOrderId(), 待确认, record.getOperatorId()); }这里的关键是「先扣库存再落记录」且整个方法在一个事务里。如果先查库存再更新两个请求同时查到库存为 1就会都扣成功变成 -1这就是典型的超卖。用stock #{num}作为更新条件数据库行锁会保证只有一个请求扣成功。这个点在答辩时讲出来比讲十个页面都管用。4.3 Vue 前端的工单列表与状态标签前端按角色拆路由工单列表用一张卡片列表渲染状态用统一组件映射颜色避免每个页面各写一套// OrderList.vue 片段 const statusMap { 待审核: { color: #909399, text: 待审核 }, 待派单: { color: #e6a23c, text: 待派单 }, 待接单: { color: #409eff, text: 待接单 }, 维修中: { color: #67c23a, text: 维修中 }, 待确认: { color: #f56c6c, text: 待确认 }, 已完成: { color: #67c23a, text: 已完成 } } // 根据当前登录角色决定显示哪些操作按钮 function actionsOf(order, role) { if (role 客服 order.status 待审核) return [派单, 取消] if (role 师傅 order.status 待接单) return [接单] if (role 师傅 order.status 维修中) return [提交维修记录] if (role 用户 order.status 待确认) return [确认完成] return [] }statusMap把状态和展示解耦后端加状态时前端只改这一处。actionsOf用角色加状态两个维度算按钮比在后端返回一堆布尔字段更直观也避免前端硬编码判断散落各处。列表请求带上pageNum / pageSize / status切换状态标签时只改查询参数重新拉数据不做本地过滤保证分页总数准确。4.4 统计看板与费用计算管理端看板通常要三个数今日新增工单、待派单数量、本月配件消耗金额。前两个用 MyBatis-Plus 的count加条件即可第三个需要联表统计写自定义 SQL 更直接-- 本月配件消耗金额按配件分组 SELECT p.name AS partName, SUM(r.change_num) AS totalNum, SUM(r.change_num * p.price) AS totalAmount FROM part_record r JOIN part p ON p.id r.part_id WHERE r.type 出库 AND r.create_time DATE_FORMAT(CURDATE(), %Y-%m-01) GROUP BY p.id, p.name ORDER BY totalAmount DESC;费用计算放在 Service 里工时费按品类单价乘工时配件费按出库流水汇总两者相加写入维修记录。金额字段统一用BigDecimal不要用 double否则结算时会出现 0.1 0.2 这类精度问题对账时很难解释。5. 避坑与常见问题上线前必须排查的五件事5.1 分页 total 为 0现象列表数据能出来但分页组件显示总数为 0翻页失效。原因MyBatis-Plus 分页插件没注册或者注册了但没加PaginationInnerInterceptor。解决在配置类里显式注册拦截器并确认DbType与数据库一致如果用了多数据源拦截器要挂在正确的 SqlSessionFactory 上。5.2 状态被前端随意篡改现象测试时发现工单能从「待审核」直接跳到「已完成」。原因更新接口直接接收前端传来的整个实体并updateById没有校验流转规则。解决更新状态一律走changeStatus方法实体更新时用LambdaUpdateWrapper只允许改白名单字段status 字段不接受前端直传。5.3 配件库存扣成负数现象并发提交维修记录后库存出现负数。原因先查后改判断和更新不在一条 SQL 里。解决改成UPDATE ... WHERE stock num并判断影响行数配合事务回滚。压测时用两个线程同时提交同一配件验证只有一个成功。5.4 跨域和登录态丢失现象前端本地调试接口全部 403 或 CORS 报错登录后刷新页面又变回未登录。原因后端没配跨域或者 token 只存在内存里没持久化。解决后端统一配 CORS 允许前端域名前端 token 存 localStorage 并在请求拦截器里统一带上后端拦截器放行登录和静态资源路径其余接口校验 token。5.5 时间字段时区错乱现象工单创建时间比实际早或晚 8 小时统计按天分组时数据跑到前一天。原因数据库、JDBC 连接、应用三处时区不一致。解决连接串里显式指定时区实体时间字段统一用LocalDateTime统计 SQL 里的日期函数与数据库时区对齐改完用一条跨零点的测试数据验证。6. 验收与答辩用一条工单跑通全流程验收不要按页面一个个点那样容易漏。我一般准备一条「全链路用例」从用户提交报修开始依次切换五个角色账号把工单从待审核推到已完成中间故意触发一次库存不足观察是否回滚。具体步骤用户提交报修并记下工单号客服登录审核并派单师傅登录接单、提交维修记录并勾选一个库存为 1 的配件仓库登录确认库存扣减和流水用户确认完成并评价管理员看板核对今日工单数和配件消耗金额。每一步都截图存档答辩时按这条线讲比念 PPT 有说服力。验收标准可以列成一张对照表逐项打勾验收项通过标准状态流转非法流转被拒绝并提示原因操作日志每次流转都有 from/to/操作人记录库存扣减并发提交不出现负库存流水与库存一致权限隔离师傅看不到派单按钮用户看不到他人工单分页查询总数正确翻页数据不重复不丢失统计看板今日工单数、配件金额与手工核对一致答辩时最容易被问的三个问题提前准备好答案为什么用 MyBatis-Plus 而不是 JPA通用 CRUD 省事、复杂 SQL 可控库存并发怎么保证条件更新加事务状态机为什么不用工作流引擎业务简单枚举加流转表足够引入引擎反而增加维护成本。回答时落到具体代码位置比空谈架构更可信。血泪经验是我第一次做这类系统时验收只点了页面没跑并发结果演示当天两个账号同时提交库存直接变负当场被问住。从那以后我每次交付前都强制走一遍「全链路用例 一次并发提交」哪怕时间再紧也不省这一步。希望这套拆法能帮到你照着把需求、表结构、状态机和验收用例落下来这份方案就能直接改成你自己的设计文档和可运行代码。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询