ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

车辆维修保养报销系统:微信小程序+Spring Boot全栈开发实践

车辆维修保养报销系统:微信小程序+Spring Boot全栈开发实践 1. 这类系统到底在管什么——先把业务边界画清楚很多做毕业设计或者公司内部工具的朋友一上来就急着建表、写接口结果做到一半发现需求根本说不清。“车辆维修保养报销”这几个字看着简单实际拆开之后里面至少藏着三套完全不同的管理逻辑车辆本身的档案与维修记录、维修费用的预算与报销审批、以及围绕这些数据产生的统计与提醒。我最初接手这类需求的时候甲方给的描述也就是“搞个小程序让员工报修车费用领导能审批”。听着简单可真要落地你首先得回答几个问题谁可以提交维修申请维修厂的信息是员工自己填还是管理员维护报销的审批流是两级还是一级维修记录和报销单是两条独立的业务线还是维修单审核通过之后自动生成报销单如果这些问题在设计阶段不敲死后面写代码就是反复返工。我的建议是第一版不要贪多把系统拆成四个核心模块就够了车辆档案模块、维修保养工单模块、报销审批模块、系统管理模块。车辆档案管“哪些车在用、车况如何”维修工单管“车坏了修了什么、换了什么件、花了多少钱”报销审批管“这笔钱怎么走流程、最后谁拍板”系统管理管“谁是什么角色、能看到什么数据、维修厂有哪些”。这四块业务逻辑捋顺之后你会发现一个很有意思的点维修工单是“事实层”报销单是“资金层”。事实层只负责记录客观信息比如维修项目、更换配件、工时费、维修厂名称资金层负责把符合条件的维修工单转成可报销的财务单据叠加审批状态。很多初学的人会把这两层混在一个表里数据库字段越加越多逻辑越写越乱。我的经验是业务边界一定要在动手写第一行代码前分清它决定了你后面所有接口怎么设计、权限怎么划、页面怎么跳。再说角色。这类系统最少有三类角色普通员工提交维修申请和查看自己的记录、审批人通常是部门主管或行政负责人审核维修必要性和报销真实性、管理员维护车辆档案、维修厂信息、查看所有报表。如果公司有财务角色可以把审批人再细分为“业务审批”和“财务复核”两步。小程序端主要给普通员工和审批人用管理员的大部分操作放在Web管理后台会更舒服——毕竟在手机上维护一堆车辆档案和维修厂名录体验实在不怎么样。2. 技术选型与整体架构微信小程序Java后端的搭配逻辑这类管理系统的技术栈比较固定但还是值得说一下我的选型思路。小程序端毫无疑问用原生微信小程序开发原因有二一是这个项目的核心逻辑在数据流转和审批流程上页面交互并不复杂原生框架足够二是原生小程序不用引入额外的编译链路出问题好排查。如果团队熟悉Vue语法用uniapp也没问题但要注意扫码、相机调用、定位这些原生能力在uniapp里偶尔会有兼容性坑调试成本会高一些。后端我选的是Spring Boot MyBatis Plus数据库用MySQL。这个组合最大的优点是生态成熟、资料多遇到问题搜一圈基本都能解决。如果只是做课程设计可以用更轻的方案比如Node.js的Express或者Python的Flask但如果你考虑到后续要加功能、要写论文中的“系统设计”章节Spring Boot的三层架构Controller-Service-Mapper是最标准的表述方式答辩的时候也更好讲清楚。小程序端通过HTTP接口和Spring Boot通信数据格式统一用JSON。这里特别说一下部署和鉴权。很多人在本地跑通了接口一上真机就傻眼主要原因就是域名和HTTPS的问题。微信小程序正式环境要求所有请求域名必须是HTTPS且在小程序管理后台配置过白名单开发阶段可以在“详情-本地设置”里勾选“不校验合法域名”但这只是权宜之计。真正的项目部署需要一台云服务器或云开发环境把后端打成的jar包丢上去跑再用Nginx做反向代理和SSL证书配置。用户登录这块我推荐直接使用微信的wx.login获取code后端拿code去调微信的jscode2session接口换openid和session_key。这个方案比让用户填用户名密码体验好得多因为员工不需要额外记一套账号密码。首次登录时后端根据openid自动创建用户记录并给一个默认的“普通员工”角色管理员在后台修改角色。后续每次请求小程序端携带后端下发的自定义token我习惯用UUID过期时间存Redis后端用一个拦截器统一校验。这张架构图在我的项目里基本是长这样的小程序端WXML/WXSS/JS负责页面展示和用户交互Spring Boot后端提供RESTful接口处理业务逻辑和数据持久化MySQL存储车辆信息、维修工单、报销单、用户角色等结构化数据Redis缓存登录态和部分热点数据比如维修厂列表对象存储云存储或者服务器本地目录存放维修照片、发票照片对于文件存储不建议直接往数据库里塞图片的Base64字符串。我踩过这个坑数据库会变得特别臃肿查询速度明显下降。正确做法是前端调用wx.uploadFile把图片传到后端后端把文件保存在服务器指定目录或者传到云存储数据库里只存图片的URL路径。如果预算有限没有云存储本地存储也够用只要做好文件命名和时间分目录就行。3. 数据库设计维修工单和报销流程的“地基”数据库设计是这类系统最见功夫的地方。我见过不少项目把所有字段塞进一张大表里看起来省事后期维护简直噩梦。先晒出我对核心表的拆法再逐张解释为什么这样设计。用户表sys_userid、openid、用户姓名、手机号、部门、角色1普通员工/2审批人/3管理员、创建时间。这张表不要放维修相关的字段它只负责“谁在用系统”这件事。车辆档案表car_infoid、车牌号、车辆品牌型号、车辆颜色、购买日期、当前里程数、车辆状态1正常/2维修中/3已报废、归属部门或归属人、备注。如果公司车辆多建议加一个“是否启用”的字段做软删除而不是直接删记录这样历史报表不会断档。维修厂表repair_shopid、维修厂名称、联系人、联系电话、地址、是否合作维修厂、备注。这张表独立出来而不是让用户手填是为了规范数据。员工提交工单时只需从下拉列表里选维修厂避免同一个人名写出“XX修车厂”和“XX汽车维修中心”两种格式统计时不方便。维修工单表repair_orderid、工单编号、车辆id、维修厂id、送修人id即当前员工、维修类别保养/小修/大修/事故维修、送修日期、预计取车日期、故障描述、维修项目详情、更换配件详情、维修总费用、里程数照片、维修工单照片、维修状态1待审核/2审核通过/3已驳回/4已完成、审核意见、创建时间、更新时间。报销单表reimburse_orderid、报销编号、关联维修工单id、报销人id、报销金额、发票照片URL、报销说明、审批状态1待审批/2审批通过/3已驳回/4已打款、当前审批人id、审批时间、审批意见、创建时间。两张核心表的关系是维修工单先产生报销单后产生。工单审核通过后员工可以对已完成的工单发起报销系统自动把工单里的维修费用带过来生成报销单。这样做的好处是报销金额不能手输从源头上避免乱填金额的问题。如果维修工单本身被驳回了那就根本不存在报销的可能。再补充两张关联表维修配件明细表repair_part和审批记录表approve_log。配件明细表挂在维修工单下记录配件名称、配件编号、数量、单价审批记录表挂在报销单下记录每一级审批的审批人、动作、意见、时间。后者特别重要因为“审批痕迹”是报销类系统的刚需财务审计的时候翻历史记录全靠它。建表的时候几个容易忽略的细节所有金额字段用DECIMAL(10,2)千万别用FLOAT不然浮点数精度问题会让你对账对到怀疑人生时间字段统一用DATETIME每个表都要有create_time和update_time关联字段要建索引特别是repair_order表的car_id和reimburse_order表的repair_order_id因为这是最高频的查询路径。4. 小程序端几个绕不开的实现要点小程序端页面通常包括首页工作台、车辆列表、维修工单列表、维修工单详情、新建维修工单、报销列表、新建报销、审批中心、个人中心。页面不多但有几个实现要点容易出问题我逐个说。4.1 用户登录和拦截器小程序端的登录不弹窗启动时静默登录。在app.js的onLaunch里调wx.login拿code发给后端换token存到wx.setStorageSync(token, ...)。后面每个请求都把token放进header里后端用拦截器校验。这里有个小细节token过期之后接口会返回401前端要统一处理这个状态码重新走一遍登录流程而不是让用户重新打开小程序——不然早晚被用户骂。页面级的身份校验也不能省。比如“新建维修工单”的入口普通员工可见审批人进入这个页面应该在onLoad里判断角色并跳转不然只是隐藏入口会被人用路径直接打开。4.2 图片上传的两种形态维修工单和报销单都要传照片。这里有两种上传思路一是用户在表单页面选中图片之后立即上传得到URL随表单一起提交二是表单提交时再把图片一次性上传。我强烈推荐第一种因为图片上传慢且受网络影响大如果用户在填写内容的过程中网络断了图片可能已经成功传上去了但表单没提交重新填写时至少不用再重新选照片。实现上就是wx.chooseMedia拿到临时文件路径后调wx.uploadFile后端返回URL后前端把URL存在表单的数据里。4.3 表单项的动态组合维修工单里的“维修项目”和“更换配件”是动态增减的。前端维护一个数组点击“添加配件”就往数组里push一个新对象每个对象有名称、数量、单价字段点击删除就splice掉。提交时把整个数组转成JSON字符串跟着表单一起提交后端接收后解析再逐条插入维修配件明细表。这个方法对新手最友好不用引额外的库。4.4 审批流的页面表现审批人在“审批中心”里看到的是待办列表点击进入详情看到维修工单里的照片和费用明细然后做“同意/驳回”操作。这里我做过一个优化维修工单和报销单在详情页用同一个组件展示车辆信息、维修项目、费用明细区别只在于底部按钮栏不同。如果不做组件复用两套页面光样式和逻辑就要写两遍。4.5 金额计算在前端还是一致新建维修工单的时候前端会把配件费用和工时费加起来实时显示总金额但这个金额只是给用户看个大概后端接收到数据后一定要重新计算一遍总金额再存库。你永远不能相信前端传过来的金额这是做财务相关系统的基本素养。后端计算完之后的费用写进repair_order之后生成报销单时直接取这个值不做二次校验与修改。5. 前后端交互与状态流转报销审批是怎么跑通的很多人在设计接口时只顾着写CRUD增删改查忘了这类系统的核心价值在于“流程”。我把自己项目中接口设计的经验拆解一下重点讲状态流转。维修工单的状态流转相对简单员工提交工单 - 管理员或审批人审核 - 审核通过后工单变为已完成 - 员工对该工单发起报销。工单的审核本质是“审车辆维修的必要性”所以审核人一般是有车辆管理权限的行政人员。报销单的流程就复杂一点。我给报销单定了四种状态待审批、审批通过、已驳回、已打款。审批通过后还有“打款”动作这个动作可以由财务角色的管理员执行。在小程序端普通员工只能发起报销、查看自己的报销单列表和详情审批人能看到所有流转到自己名下的报销单管理员能看到全部报销单并且执行“已打款”操作。在这个流程中最容易被忽略的是“当前审批人”字段。如果只有一级审批这个字段就是审批人的id如果有两级审批就需要加一个approve_level字段标明当前在第几级。业务不复杂时我建议第一版只做一级审批等流程跑顺了再扩展。做毕业设计或者内部小工具过度设计比功能不足更可怕。接口层面核心接口大概长这样POST /api/repair/order新建维修工单GET /api/repair/order/list维修工单列表分页按状态筛选GET /api/repair/order/detail维修工单详情PUT /api/repair/order/audit工单审核同意/驳回POST /api/reimburse/order新建报销单必须关联工单GET /api/reimburse/order/list报销单列表GET /api/reimburse/order/detail报销单详情PUT /api/reimburse/order/approve报销审批PUT /api/reimburse/order/pay确认打款每个接口的入参校验要认真做。比如新建报销单时后端要校验这个工单确实存在、确实已完成、还没有生成过报销单防止重复报销、当前用户是工单的送修人防止拿别人的维修单报销。这些业务规则写起来不复杂但确实是整个项目的灵魂所在。5.1 审批流程的状态机实现我在后端实现审批状态机时定义了一个工具类专门处理状态迁移的合法性检查。比如“审批通过”只能从“待审批”转过来“已打款”只能从“审批通过”转过来其他任何非法迁移都直接抛异常。这个检查虽然写起来很机械但是能拦截掉不少因为并发操作产生的脏数据。另外审批操作一定要做成事务。先更新报销单状态再插入审批记录任何一个失败都要回滚要不然会出现报销单状态变了但审批日志没记录的情况后面审计对不上账会很痛苦。5.2 消息通知的简单实现微信小程序没有像公众号那样的模板消息推送通道订阅消息的触发条件又比较苛刻所以在第一版里我做的是“站内信”机制。数据库加一张message表id、接收人id、消息类型、消息内容、是否已读、创建时间每次审批操作或工单审核操作产生一条站内信小程序端的消息中心定时拉取未读消息。这个方案简单可靠虽然不像推送那样主动但用户点进小程序就能看到红点提醒需求完全够用。5.3 统计报表后端怎么写管理员端通常需要一个简单的数据看板本月维修总费用、各车辆维修次数排名、各维修厂费用占比。SQL写法不复杂核心就是GROUP BY和SUM聚合。这里有个小经验统计类的接口不要每次都实时去查流水表数据量大之后会很慢。每天凌晨跑一个定时任务把统计结果存到一张汇总表前端直接查汇总表性能完全不一样。如果要写论文这个“应用层缓存”的思路也蛮加分的。6. 真机调试与上线前的坑代码写完之后真机调试才是真正折磨人的开始。我把自己踩过的一些坑做个记录希望你少走弯路。蓝牙与相机权限的问题有些安卓手机在小程序里第一次调用相机权限时会黑屏或者强退原因是用户还没授权就去调了wx.chooseMedia。解决方式是先通过wx.authorize请求权限若用户拒绝则在页面上弹出提示引导去设置页打开权限。这个坑在模拟器里复现不了只有真机上才碰得到。网络请求的域名配置前面提到过开发工具里可以跳过域名校验但真机预览时如果没在微信公众平台配置request合法域名请求会被拦掉。按下手机上的“重新进入小程序”也没用必须去小程序后台的“开发管理-开发设置-服务器域名”里把HTTPS域名加上。这个环节是上线前必须做掉的别拖到测试的时候再弄。安卓和iOS的时间格式差异后端返回的时间字段如果是2024-01-01 12:00:00这种格式安卓上new Date()解析可能不兼容页面显示NaN。建议后端统一返回时间戳毫秒前端做格式化或者返回ISO格式的字符串。这是我见过最多人踩的坑没有之一。图片显示防盗链如果维修照片存在云存储有时候在开发者工具里能显示真机上显示不了。多半是图片链接的域名没配到“downloadFile合法域名”里。注意image组件的图片域名跟wx.downloadFile的域名校验是同一套配置你得一起加上。扫码核销场景如果你做的是一个扫码上传的流程比如扫车上的二维码直接发起报修wx.scanCode在部分手机上有扫码结果返回慢的问题建议扫码成功之后做一个防重复提交的标记比如5秒内禁止再次扫描不然用户以为没扫描成功疯狂连扫会产生一堆重复的维修工单。除了这些技术坑上线前还有两件“非技术”的事也特别重要。一是给普通员工和审批人各准备一份简洁的操作说明这类内部工具最大的问题不是功能少而是用户不看文档乱点二是跟财务对一遍报销字段的命名规范确保报销单编号在财务系统里能对齐不然后续手工对账会疯掉。7. 从一版到二版可能的优化方向如果第一版跑通了我建议的第二个版本再考虑这些优化对接地图API让员工提交维修单时可以直接选维修厂的位置同时管理员在后台能查看车辆是否被开到非合作维修厂增加车辆保养提醒功能按照里程数或时间间隔自动生成保养计划到期推送给车辆责任人对接电子发票识别上传发票照片后自动解析抬头、金额、税号减少手工输入增加企业微信或者邮件通知审批结果直接通知到员工的微信不过这个开发量不小看团队资源我个人最推荐先做保养提醒。因为这个功能不仅实用而且技术实现不复杂车辆档案里加两个字段上次保养里程、保养周期定时任务扫一遍表里程数超标的生成通知记录下来。一个功能就能让整个系统从“报销工具”升级成“车辆管理助手”对内部推广和论文的“创新点”都很有帮助。回过头来总结一下这套设计的核心思路维修工单管事实报销单管资金两个流程串起来形成闭环角色权限从用户表出发贯穿所有接口审批过程务必留痕数据才能经得起核对。按这个框架走下来不管你是做毕业设计还是公司内部的小工具都能少走很多弯路。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进