尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
汽车保养管理系统实战:从保养周期算法到工单流转的完整实现
汽车保养管理系统实战从保养周期算法到工单流转的完整实现汽车保养类系统的技术难点并不在于页面有多少而在于两件事一是把「什么时候该保养」这件事算准二是把「保养过程」这件事管住。前者是一套基于里程与时间的双维度定时算法后者是一套工单状态机。本文以 Spring Boot MyBatis Plus MySQL 作为后端技术栈以 UniAppVue 语法作为用户端、Vue Element UI 作为管理后台拆解汽车保养系统从数据建模到接口落地的完整实现路径。一、业务建模把汽车保养拆成可计算的三张核心表汽车保养业务看似是「车主约、门店做」的流程落到代码层面其实只有三层数据车辆档案、保养项目字典、保养工单。车辆档案记录车牌、车型、当前里程、上次保养里程与时间。它是所有提醒计算的输入。保养项目字典机油、机滤、空滤、刹车片等每个项目维护两个周期字段——里程周期与时间周期。保养工单把「预约—接单—服务—完工」的全过程串起来并在完工时回写车辆的里程与时间基线。这样设计的价值在于提醒逻辑不依赖任何硬编码规则全部由字典表驱动。新增一个保养项目时只需要插一行数据不需要改动任何一行业务代码。CREATETABLEvehicle(idBIGINTNOTNULLAUTO_INCREMENT,user_idBIGINTNOTNULLCOMMENT车主用户ID,plate_noVARCHAR(16)NOTNULLCOMMENT车牌号,modelVARCHAR(64)DEFAULTNULLCOMMENT车型,current_mileageINTNOTNULLDEFAULT0COMMENT当前里程(km),last_service_mileageINTDEFAULTNULLCOMMENT上次保养里程(km),last_service_timeDATETIMEDEFAULTNULLCOMMENT上次保养时间,PRIMARYKEY(id),KEYidx_user(user_id))ENGINEInnoDBDEFAULTCHARSETutf8mb4COMMENT车辆档案;CREATETABLEmaintain_item(idBIGINTNOTNULLAUTO_INCREMENT,item_codeVARCHAR(32)NOTNULLCOMMENT项目编码,item_nameVARCHAR(64)NOTNULLCOMMENT项目名称,cycle_mileageINTDEFAULTNULLCOMMENT里程周期(km)为空表示不按里程判定,cycle_monthINTDEFAULTNULLCOMMENT时间周期(月)为空表示不按时间判定,sortINTNOTNULLDEFAULT0COMMENT排序,PRIMARYKEY(id),UNIQUEKEYuk_code(item_code))ENGINEInnoDBDEFAULTCHARSETutf8mb4COMMENT汽车保养项目字典;CREATETABLEmaintain_order(idBIGINTNOTNULLAUTO_INCREMENT,order_noVARCHAR(32)NOTNULLCOMMENT工单号,vehicle_idBIGINTNOTNULLCOMMENT车辆ID,statusTINYINTNOTNULLDEFAULT0COMMENT0待接单 1服务中 2待确认 3已完成 4已取消,appoint_timeDATETIMEDEFAULTNULLCOMMENT预约时间,finish_mileageINTDEFAULTNULLCOMMENT完工里程,create_timeDATETIMENOTNULLDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(id),UNIQUEKEYuk_order_no(order_no),KEYidx_vehicle(vehicle_id))ENGINEInnoDBDEFAULTCHARSETutf8mb4COMMENT汽车保养工单;需要特别说明的是周期字段允许为NULL。这个细节很重要——有些项目只按里程判定有些只按时间判定。如果用0表示「不判定」会和「周期为 0」的语义混淆后续排查问题时会非常痛苦。二、汽车保养提醒算法里程与时间的「先到为准」判定提醒的核心逻辑是对每个保养项目分别计算里程是否到期、时间是否到期任意一个满足即判定为到期并把触发原因回传给前端展示。这里有几个容易被忽略的边界情况当前里程小于上次保养里程车主换过仪表、录入错误或调表都会造成这种情况此时差值应为负数必须直接判定为「数据异常」而不是「到期」。上次保养记录为空新车或首次录入应按「不判定」处理否则会给全量用户推提醒。刚好等于阈值应采用而不是否则到期当天不会触发。publicclassMaintainReminder{publicstaticDueResultjudge(MaintainItemitem,Vehiclevehicle,LocalDatetoday){IntegerremainKmnull;IntegerremainDaysnull;booleanmileageDuefalse;booleantimeDuefalse;// 里程维度if(item.getCycleMileage()!nullvehicle.getLastServiceMileage()!nullvehicle.getCurrentMileage()!null){intdeltavehicle.getCurrentMileage()-vehicle.getLastServiceMileage();if(delta0){returnDueResult.error(item,里程数据异常请先校准);}remainKmitem.getCycleMileage()-delta;mileageDueremainKm0;}// 时间维度if(item.getCycleMonth()!nullvehicle.getLastServiceTime()!null){LocalDatenextvehicle.getLastServiceTime().toLocalDate().plusMonths(item.getCycleMonth());remainDays(int)ChronoUnit.DAYS.between(today,next);timeDueremainDays0;}booleanduemileageDue||timeDue;StringreasonbuildReason(item,due,mileageDue,timeDue,remainKm,remainDays);returnnewDueResult(item.getItemCode(),item.getItemName(),due,reason,remainKm,remainDays);}}buildReason负责把结构化的判定结果翻译成人话例如「距下次更换机油还有 800 公里」「空滤已超期 23 天」。前端拿到的就是可以直接渲染的文案不需要再写一套判断逻辑避免了两端规则不一致的问题。三、接口层基于 Spring Boot MyBatis Plus 的提醒查询汽车保养提醒接口应当保持「无状态、可缓存、易分页」的特点。由于大多数项目的判定结果只依赖于车辆档案和字典接口可以直接在内存中完成映射与过滤避免写复杂的 SQL 关联。RestControllerRequestMapping(/api/maintain)publicclassMaintainController{ResourceprivateVehicleMappervehicleMapper;ResourceprivateMaintainItemMapperitemMapper;GetMapping(/remind/{vehicleId})publicRListDueResultremind(PathVariableLongvehicleId){VehiclevehiclevehicleMapper.selectById(vehicleId);if(vehiclenull){returnR.fail(车辆档案不存在);}LocalDatetodayLocalDate.now();ListDueResultlistitemMapper.selectList(Wrappers.MaintainItemlambdaQuery().orderByAsc(MaintainItem::getSort)).stream().map(item-MaintainReminder.judge(item,vehicle,today)).filter(DueResult::isDue).collect(Collectors.toList());returnR.ok(list);}}字典表的数量级通常在几十条以内全量取出后内存计算完全没有性能压力。真正需要分页的是工单列表接口直接使用 MyBatis Plus 的Page对象即可GetMapping(/order/page)publicRPageMaintainOrderVOpageOrder(OrderQueryquery){LambdaQueryWrapperMaintainOrderwrapperWrappers.MaintainOrderlambdaQuery().eq(query.getStatus()!null,MaintainOrder::getStatus,query.getStatus()).eq(query.getVehicleId()!null,MaintainOrder::getVehicleId,query.getVehicleId()).orderByDesc(MaintainOrder::getCreateTime);returnR.ok(orderMapper.selectPage(newPage(query.getPageNo(),query.getPageSize()),wrapper));}四、工单状态机用受限流转替代自由改状态汽车保养工单怕的就是状态被随意修改。管理后台如果直接给一个下拉框让人把「待接单」改成「已完成」数据很快就会失去可信度。建议把状态流转收窄为固定路径0 待接单 → 1 服务中 → 2 待确认 → 3 已完成 0 待接单 / 1 服务中 → 4 已取消代码层面不要写 if-else而是维护一张状态转移表MapInteger,SetIntegerTRANSITIONMap.of(0,Set.of(1,4),// 待接单 → 服务中 / 已取消1,Set.of(2,4),// 服务中 → 待确认 / 已取消2,Set.of(3)// 待确认 → 已完成);每次更新状态前先查表校验是否合法非法直接拒绝。状态变更时同时记录变更时间与操作人形成审计日志。五、完工回写与提醒重置工单进入「已完成」时需回写车辆的 last_service_mileage 和 last_service_time作为下一轮提醒计算的基线。同时将该工单关联的保养项目从提醒列表中移除直到下一次周期触发。常见问题解答FAQQ1提醒算法为什么不直接写 SQLA里程和时间是两个维度SQL 里用 OR 关联会导致索引失效且边界逻辑如里程倒挂、首次录入在 SQL 中难以优雅处理。全量字典在内存中计算几十条数据毫秒级完成可读性和可维护性远高于复杂 SQL。Q2如何防止技师跳过「待确认」直接完工A状态机表里不配置 1 → 3 的转移路径后端校验层统一拦截。前端按钮根据当前状态动态渲染从 UI 和 API 两层堵住越权操作。Q3多辆车、多项目怎么批量查提醒A先按 user_id 查出用户名下所有车辆再对每辆车遍历保养项目字典做判定。数据量不大时串行即可若车辆数较多可用 CompletableFuture 并行计算最后汇总返回。Q4保养周期支持自定义吗A支持。maintain_item 表的 cycle_mileage 和 cycle_month 均可单独配置任一字段为 NULL 即表示该维度不参与判定。门店可在后台自行维护项目字典无需改代码。
RELATED

相关推荐

Atlas 300V Pro部署YOLO全流程:从环境配置到模型调优

Atlas 300V Pro部署YOLO全流程:从环境配置到模型调优

在聊怎么把 YOLO 跑在 Atlas 300V Pro 上之前,我想先回答那个被反复问起的问题:atlas 300v 24g 是运算加速卡吗?是,但它不是你想的那种运算加速卡。很多以前玩 GPU 的兄弟看到“24GB”第一反应就是“这不就是一张显卡吗”&#xf…

📅 2026/9/25 12:31:30
CiLocks权限重置指南:adb pm reset-permissions命令完全解析

CiLocks权限重置指南:adb pm reset-permissions命令完全解析

CiLocks权限重置指南:adb pm reset-permissions命令完全解析 【免费下载链接】CiLocks Crack Interface lockscreen, Metasploit and More Android/IOS Hacking 项目地址: https://gitcode.com/GitHub_Trending/ci/CiLocks CiLocks 是一款开源的 Android/iOS…

📅 2026/9/25 12:26:30
收藏!小白程序员也能学会的大模型入门指南:用 TaoToken 统一 Key 打通 AI 工具链

收藏!小白程序员也能学会的大模型入门指南:用 TaoToken 统一 Key 打通 AI 工具链

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/25 12:26:30
MORE NEWS

更多资讯

📰

GHelper:华硕笔记本的奥创平替,3 步装好不折腾

GHelper:华硕笔记本的奥创平替,3 步装好不折腾 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook…

📰

免费CRM与私人网站的区别:永久在线客户管理系统如何驱动销售闭环

1. 谈选型前,先把“免费CRM”和“私人网站”这两个概念掰开做销售管理这行超过十年,我见过太多团队在CRM选型上栽跟头。尤其是这两年,市面上冒出大量打着“永久在线”“免费”旗号的CRM网站,从蝉鸣、飞鱼到各种不知名的小平台&…

📰

Semi Design Dropdown 下拉菜单组件实战指南:用法、API 与无障碍设计全解析

前端UI组件设计系统 【免费下载链接】semi-design 🚀A modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.🎨Provide 3000 Design Tokens, easy to build your design system. Make Semi Design to Any Design…

📰

从桌面沟通场景切入的CRM设计与落地实践——以DeskcommCRM为例

做CRM项目这么多年,我见过太多团队一上来就怼着一套高大上的系统使劲折腾,最后发现销售根本不买账。原因很简单,客户管理系统如果脱离了业务一线人员的使用习惯,再强大的功能也只是一堆按钮。DeskcommCRM这个项目让我比较想聊的原…

📰

Windows-universal-samples 中的 BasicFaceDetection 示例:使用 FaceDetector 在 UWP 应用中实现静态人脸检测

示例工程 【免费下载链接】Windows-universal-samples API samples for the Universal Windows Platform. 项目地址: https://gitcode.com/gh_mirrors/wi/Windows-universal-samples 点击查看 免费下载 本指南以 Windows-universal-samples 仓库中的 BasicFaceDete…

📰

企业级 Agent 平台落地实践:Agent、CodeBuddy 与 SkillHub 如何协同

1. 从单兵作战到团队协同:企业级 Agent 平台要解决的真问题过去一年,我接触过不少团队在推进 AI 辅助研发这件事。一个很普遍的现象是:个人开发者用 AI 编码工具用得风生水起,效率提升肉眼可见,但一旦把视角拉到几十人…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬