ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

微信小程序设备报修系统实战:工单设计、状态流转与上线避坑

微信小程序设备报修系统实战:工单设计、状态流转与上线避坑 我们单位一年前也是典型的状态报修基本靠喊维修靠等设备有没有人管全凭师傅的心情。行政群里每天“打印机又卡了”“会议室投屏没信号”刷屏报修信息淹没在斗图里师傅挨个打电话确认位置白跑一趟是常态。后来我把这套基于微信小程序的设备报修系统落地整个流程才算理顺。这篇文章不聊虚的就把我从设计到上线踩过的坑、定下来的方案、写出来的关键代码一次性说清楚。这套系统能做什么师生或员工在微信里拍照、选地点、填描述就能提交报修单维修师傅接到工单后上门处理、回传进度管理员在后台看到实时数据按部门、按设备类型统计维修率月底对着报表做资产盘点。它解决的核心问题是“报修信息从口头转达变成结构化数据流转”状态可查、责任到人、过程留痕。适合正在做毕业设计、企业内训项目或者单纯想给单位解决实际问题的开发者参考前端以微信小程序为主后端我用的是Node.js MySQL的经典组合全文包含完整字段设计、状态机流转逻辑、登录鉴权和图片压缩上传的实测代码。1. 项目定位与核心设计思路1.1 三个角色的痛点决定了功能边界先梳理清楚谁在用这套系统功能才不会做泛。我把使用方拆成三类人每类人只给他最必要的操作入口。报修人普通员工/学生/住户要的是“零门槛”打开小程序就能报不用下载App不用记设备编号拍照传图、选一下所在位置、填一句“什么坏了”就完事。他最关心的是“我报完以后有没有人管”所以要给他一个单号能随时看进度状态。维修师傅要的是“少跑冤枉路”接单前能看到清晰的位置信息、故障描述、现场照片避免到现场才发现缺工具、缺配件。他还要能更新状态比如“已接单”“处理中”“待验收”让其他人知道活干到哪一步了。管理员要的是“心里有数”哪些设备故障频发哪栋楼报修最多师傅平均响应时间是多长这些数据如果靠人工翻聊天记录永远统计不出来。所以后台必须要有按时间、按区域、按类型的多维筛选报表。三类角色对应到系统里就是三种身份、三种页面、三套权限。登录以后后端返回角色字段小程序首页按角色渲染成不同Tab这样既保证操作效率也避免把界面搞复杂。1.2 为什么选微信小程序而不是App或网页后台技术选型这事我对比过三条路最终选了小程序理由很直接安装成本为零。单位里的设备报修人群覆盖老中青让所有人去应用商店搜App并下载注册门槛太高一定会有人嫌麻烦就绕开系统走老路子。小程序扫二维码或从微信里直接进入用完即走没有心理负担。身份体系蹭微信。微信提供了完整的登录和手机号快速验证能力省掉了自建账号体系、短信验证码这些重复工作。用户不用记密码打开就是“微信一键授权”对非技术人群极度友好。消息触达闭环。报修工单状态变更时我可以用微信订阅消息通知报修人和服务人员形式上就是微信聊天列表里的服务通知。相比短信通知的成本这笔钱完全省了。当然小程序也有它的局限最明显的是包体积限制——主包加分包不能超过2MB现在可以到20MB但需要分包处理富文本编辑器、复杂图表库不能随便往主包里塞。我的对策是图表类一律走后端生成图片返回尽量用原生组件把插件依赖控制在最小范围。1.3 整体架构与数据流设计整个系统的数据流其实是一条线性链路报修人提交工单 → 写入报修主表 → 管理员或系统自动派单 → 师傅接单 → 处理中 → 完工 → 报修人验收 → 归档入历史台账前端小程序负责采集和展示后端提供RESTful API数据库落盘所有状态。为了简化部署和运维我没有引入Redis做缓存报表数据直接查MySQL量级在几千条工单的应用场景下完全够用。文件存储用云存储OSS拍照的图片走前端压缩后直接上传数据库里只存URL避免把大字段塞进表里拖慢查询。数据库表我建了五张核心表users用户/角色、repair_orders工单主表、order_status_log状态变更日志、devices设备台账、feedback验收评价。其中工单表是整个系统的核心下面细说字段设计。2. 核心功能模块拆解与关键字段设计2.1 报修单的字段设计决定数据质量填报表单是用户第一道门槛字段多一个用户就多一分流失。我最终定了以下提交字段每个字段都有存在的理由字段控件类型是否必填设计说明设备/设施名称输入框必填自然语言描述不搞严格台账绑定降低用户操作成本故障位置位置选择必填调用微信chooseLocation选点经纬度自动写入附带地址文字故障描述textarea必填限制200字内预置“无法开机、异响、漏水、接触不良”等常见标签可点选现场图片图片上传选填最多传3张前端压缩到约200KB再传云存储联系方式手机号必填默认从微信号授权获取用户可修改这里值得多说一句为什么设备名称不用半结构化的资产编号。我在一期做过严格绑定每台设备打二维码标签绑定固定资产编号。结果发现两个实际问题——第一公共区域的设备比如走廊灯、公共打印机没有专人维护标签二维码经常破损或被覆盖第二用户不知道编号在哪反而产生逆反情绪。二期的方案妥协成“文字描述 位置选点”系统后台再做一次人工或关键词匹配去关联台账。数据不一定完美但胜在真实、可持续。从信息完整度来看一张带经纬度的照片 一段描述已经足够维修人员事先判断大致问题了。工单主表的动手字段我列一下CREATE TABLE repair_orders ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 工单号如BX20250101001, reporter_id bigint(20) NOT NULL COMMENT 报修人ID关联users表, reporter_name varchar(50) DEFAULT NULL, reporter_phone varchar(20) DEFAULT NULL, device_name varchar(100) DEFAULT NULL, location_desc varchar(255) DEFAULT NULL, latitude decimal(10,7) DEFAULT NULL, longitude decimal(10,7) DEFAULT NULL, fault_desc varchar(500) DEFAULT NULL, priority tinyint(1) DEFAULT 2 COMMENT 1紧急 2普通 3低, status tinyint(1) DEFAULT 0 COMMENT 0待分配 1已接单 2处理中 3待验收 4已完工 5已关闭, assignee_id bigint(20) DEFAULT NULL COMMENT 维修师傅ID, assign_time datetime DEFAULT NULL, finish_time datetime DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_status (status), KEY idx_create_time (create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;工单号我采用BX 年月日 三位流水号的格式例如BX20250115001。这里不要用自增ID直接展示给用户因为ID会暴露当天总单量不适合对外展示。生成方式后端取当天已有单数加一利用数据库唯一索引防止并发重复。2.2 工单状态机与派单策略整个系统的灵魂是工单状态机。我前期吃过亏一开始只设“待处理 / 已处理”两个状态结果师傅到了现场发现缺配件单子一挂好几天报修人疯狂催单。后来我重新梳理把状态调成六个并且记录了状态变更日志0待分配 → 1已接单 → 2处理中 → 3待验收 → 4已完工 → 5已关闭为什么需要“待验收”这个状态在设备维修场景里“修完了”应该由使用者说了算。我可以选“修好了挂单完结”但可能出现师傅标完工、实际没解决用户还得重新提单的尴尬。增加一个待验收状态报修人确认没问题后再点“确认完工”如果没修好可以选择“重新打开”工单直接回到“处理中”保证流程闭环。派单策略我做了两种管理员手动派单和师傅抢单。小场景里抢单效率更高——师傅打开小程序看到待分配列表根据位置和空闲状态自主认领。大场景比如几百人报修、几十个师傅时抢单容易变成“选择性接单”——简单的单子秒抢难搞的漏水、电路问题没人碰。所以我做的是手动派单为主、抢单为辅管理员可以把单压给指定区域负责人负责人再派给具体师傅。系统后台有一张区域-师傅映射表如果某个区域只有一个对口的师傅系统自动派单否则进入手动池。2.3 管理后台的统计维度后台统计不是锦上添花它是系统上线后获取话语权最关键的模块。我得拿着数据去向行政领导证明系统有用比如平均响应时间从一个工作日缩短到2小时月度故障TOP5设备名单各楼栋的报修趋势。有了这些后面申请预算、升级功能才有依据。我做的是三个维度按时间维度今日报修数、本周处理完成率、月度环比报表曲线按天聚合。按区域维度每栋楼/每个楼层的报修单量和完工率及时发现设施老化密集区。按设备类型故障描述里的关键词分组投影、空调、门锁、网络统计重复故障率。报表实现上后端SQL做group by返回前端拿wx-charts或纯canvas绘制。这里需要注意小程序里直接引图表库包体积大而且canvas绘图在部分安卓机型上渲染不稳定。我给小程序端只展示数字卡片和简单柱状条view宽度比例模拟详细的图表走后台网页端。想在小程序里看详细趋势就直接让后端生成一张PNG图片返回效果稳定又省开发量。3. 实操过程与关键代码实现3.1 微信登录与手机号授权小程序登录是典型的wx.login 后端换 session流程我的登录函数写成了这样// pages/login/login.js async function wxLogin() { const { code } await wx.login(); if (!code) { wx.showToast({ title: 获取登录凭证失败, icon: none }); return; } const res await request({ url: /api/user/login, method: POST, data: { code } }); // res.data 里返回 { token, role, nickName, avatarUrl, phone } wx.setStorageSync(token, res.data.token); wx.setStorageSync(userInfo, res.data); // 根据 role 跳转不同首页 wx.switchTab({ url: res.data.role worker ? /pages/worker/index : /pages/user/index }); }后端拿到code后调微信的jscode2session接口换openid。这个openid就是用户的唯一身份标识不要拿来当登录token用应该你自己生成一个sessionToken我是用uuid存在后端返回给前端。因为openid泄露会有一定风险客户端拿到它没用而且它也不该被前端感知。关于手机号授权需要注意微信在2023年之后收紧了规则getPhoneNumber得到的code必须配合access_token调用phonenumber.getPhoneNumber接口才能换取明文手机号而且个人主体小程序没有这个权限需要企业/组织主体。我在开发调试阶段被这个卡了很久模拟器里能弹窗真机上后端报61001错误码。如果你的主体没权限一个妥协方案是让用户手动输入手机号做一次短信验证码校验需要买短信服务一条几分钱不验证的话数据质量会明显下降乱填手机号的用户很多。3.2 图片压缩与上传照片是报修单里最有价值的信息。原始照片动辄3-5MB直接上传既慢又费用户流量。我在前端做了一层压缩实际体验从“上传一张等3秒”降到“基本是秒传”显著提高报修完成率。// utils/upload.js function chooseAndCompressImage(count 3) { return new Promise((resolve, reject) { wx.chooseMedia({ count, mediaType: [image], sizeType: [compressed], // 先拿微信压缩后的 sourceType: [album, camera], success: async (res) { const compressed []; for (const file of res.tempFiles) { const result await new Promise((r) { wx.compressImage({ src: file.tempFilePath, quality: 60, success: (s) r(s.tempFilePath), fail: () r(file.tempFilePath) // 压缩失败就退回原路径 }); }); compressed.push(result); } resolve(compressed); }, fail: reject }); }); }wx.compressImage的quality参数取值0-100我实测60是最合适的平衡点——画质肉眼无感损耗体积基本能缩到1/5以下。要注意的是小程序真机摄像头输出的原图非常大光一次chooseMedia的sizeType: [compressed]不够那个压缩是微信提供的快速压缩对部分Android机型效果较差。二次wx.compressImage能兜底。上传我用的是wx.uploadFile需要注意这个API和普通的wx.request不是一回事header里的content-type是multipart/form-data且无法在wx.uploadFile里配置自定义请求头的时候再关联wx.request的拦截器里的token逻辑。很多人在这里踩坑说“为什么我登录了uploadFile还是401”。我的做法是手动给wx.uploadFile加formData里的token后端从formData取值验权不走header。3.3 地理位置定位与顶部导航栏适配报修地点能不能选直接决定师傅找不找得到地方。我用的是wx.chooseLocation需要先wx.getLocation授权用户在地图上拖拽选点然后前端把经纬度和地址文字一起提交。这里有一个权限细节从2022年之后微信规定wx.getLocation必须在答题申请后方可使用个人主体不能调wx.chooseLocation。个人开发者小程序如果没有权限两个解决路径用wx.choosePoi替代需要类目资质保存“省市区 详细门牌号”文本经纬度为0后台人工匹配。顶部导航栏高度适配是整个小程序里最不显眼但最容易踩的坑。设备的“胶囊”右上角那个圆形按钮位置不是固定的刘海屏和普通屏不一样要算menuButton的边界。我的方案是写一个通用工具函数// utils/nav.js function getNavBarInfo() { const { statusBarHeight } wx.getWindowInfo(); const menu wx.getMenuButtonBoundingClientRect(); const navBarHeight (menu.top - statusBarHeight) * 2 menu.height; return { statusBarHeight, navBarHeight, menu }; }然后在自定义导航栏的页面动态设置占位 view 的高度避免页面内容被刘海和胶囊盖住。这里有个细节先用wx.getWindowInfo()不要用已被废弃的wx.getSystemInfoSync()后者在新版本基础库已经下线。真机调试时务必用iPhone X系列和带挖孔的安卓机型各测一遍宽度和高度公式在不同机型上表现差异极大。3.4 订阅消息通知的实现与限制微信订阅消息是工单流转通知的免费方案但有一个非常恶心的限制一次授权只能发给用户一条通知。用户点击“允许订阅”时相当于授权你“发送一条一次性消息”。要让用户持续收到通知必须每次都调wx.requestSubscribeMessage请求授权。我的策略是“在关键节点请求”用户提交报修单成功后立刻弹授权框请求“工单状态更新提醒”的一次性订阅。这个时间点的用户正处于“期待后续”的状态授权通过率最高。后台状态流转的关键动作接单 / 完工触发时调用订阅消息接口推送。// 提交报修成功后选择订阅 async function subscribeAfterSubmit() { try { const res await wx.requestSubscribeMessage({ tmplIds: [YOUR_TEMPLATE_ID_REPAIR_STATUS] }); // res[YOUR_TEMPLATE_ID_REPAIR_STATUS] accept 表示用户同意 } catch (e) { // 用户拒绝或基础库版本过低静默处理即可 } }推送端后端用cloudbase或原生Node发送订阅消息时需要传用户的openid和提交时带的page点击通知后跳转的小程序页面路径必须是线上存在的页面且需带参数比如pages/detail/index?id123。多次踩坑后的结论后端发送订阅消息尽量用微信云开发的云函数来发API调用频次限制是每分钟10次足以支撑小范围的报修场景而且原生cloudbase封装好了openapi调用省得自己维护access_token的刷新和存储。如果是自建Node服务需要自己维护access_token的定时刷新access_token有效期只有2小时在过期前用定时任务续上。4. 常见问题与排查技巧实录4.1 图片上传失败率高的根因治理上线第一周报修单图片成功率只有82%用户传图经常失败。排查下来原因非常清晰第一个坑是上传时没有超时设置。微信公众号后台默认上传超时是10秒但校园弱网环境下3-5MB的图经常超过。压缩后降到200-500KB10秒内基本没问题。如果你的图片在弱网下还是失败一种办法是改为“先提交文字图片后上传”的异步策略工单状态不阻塞。第二个坑是iOS和Android的临时文件路径不一致。wx.compressImage返回的路径在某些Android机型上带wxfile://前缀有的安卓文件管理器能读有的不能。wx.uploadFile的filePath参数接收的是本地临时路径如果你拿到tempFilePath后直接丢给wx.uploadFile在部分安卓上会报file not found。稳妥做法写完tempFilePath后先在wx.getFileSystemManager().access()里测一下文件是否存在如果不存在要从tempFiles[0].thumbTempFilePathchooseMedia 自带的缩略图路径兜底。4.2 手机号快速验证组件与隐私协议配置登录时用button open-typegetPhoneNumber获取手机号在2023年9月前后微信对隐私协议进行了强校验。如果你没在「小程序后台-设置-服务内容声明-用户隐私保护指引」中声明收集手机号前端弹授权时微信会直接拦截getPhoneNumber并报“请先声明隐私协议”。处理办法是在「小程序管理后台 - 设置 - 基本设置 - 服务内容声明」中勾选并填写收集“手机号”的用途。本地开发时你在开发者工具的“详情-本地设置”勾选“不校验合法域名、web-view域名、TLS版本以及HTTPS证书”并没有用隐私协议是硬校验必须在后台配好。另外调wx.getPhoneNumber所得的code有效期只有5分钟拿到的手机号解密后不要明文存数据库建议MD5脱敏或只存后四位防止用户隐私数据外泄引发合规问题。4.3 工单“丢单”问题的排查“用户明明报修了师傅说没看到”是运营中最大的信任危机。我排查后发现原因很简单状态流转时前端拉列表的接口没有做分页渲染的容错。小程序端滚动到底部用onReachBottom加载更多我在第一版没有处理“重复点击导致的重复请求”。用户在列表页快速下拉同一接口并发请求了两次后端幂等没做好导致同样一条单子出现在列表里两次但status还是0待分配看起来就像新报的单没更新。解决方案前端加载更多时加一个loading锁同一时间只允许一个请求发出后端在做状态流转时增加乐观锁校验UPDATE repair_orders SET status #{newStatus} WHERE id #{orderId} AND status #{currentStatus}如果影响行数为0说明状态已被别人改过抛出“工单状态已变动”提示。这个乐观锁机制救了我的列表页至少三次。4.4 包体积超限与分包策略小程序主包大小限制是2MB这是硬上限。我在加完图表库、区域划分配置、地图sdk之后主包一度逼近1.9MB每次发布心惊胆战。后来做了分包处理主包只放登录页、首页框架、公共组件、工具函数。分包A报修流程报修表单、图片上传、提交成功页。分包B管理后台工单列表、详情、统计面板。分包C个人中心我的报修、消息通知、帮助反馈。分包的好处除了压体积还有加载速度提升——首屏只需要加载主包用户点进分包的瞬间再加载对应页面。注意tabBar页面不能放在分包里所以报修工作台我放在了主包但操作页全部走分包实际体验很流畅。关于source size 2612kb exceed max limit 2mb这类报错直接在app.json里配置{ subpackages: [ { root: pages/repair, pages: [form/index, success/index] }, { root: pages/admin, pages: [orders/list, orders/detail, stats/index] } ], preloadRule: { pages/index/index: { network: all, packages: [pages/repair] } } }preloadRule是指进入主包某个页面后预先加载对应的分包这样用户从首页进报修表单时几乎感觉不到加载过程。4.5 无法“正式发给别人试用”与真机测试标题里提到的“微信开发者工具里的小程序怎么发给其他人试用收集反馈”这是我开发早期最困惑的一件事。工具里你生成的预览二维码有效期只有2分钟而且只能自己扫码看。正确流程是在微信公众平台开启“开发-开发管理-开发设置-体验版二维码”把工具上传的版本设置为体验版。把体验版二维码发给测试成员需要先在后台“成员管理”添加他们的微信号为体验成员限15人。体验版可以与正式版并存体验数据用的是线上正式环境如果后端API已经部署在公网。注意体验版和开发版调用的API域名校验不一致开发版在工具里可以不校验域名但体验版和正式版都会校验request合法域名。在微信公众平台「开发管理-服务器域名」里配置request合法域名、uploadFile合法域名、downloadFile合法域名必须用HTTPS且证书有效。我本地调试时经常跳过这个等部署到体验版才报url not in domain list这个配置别等到最后一刻再处理。4.6 抓包调试的正确姿势排查接口问题时手机真机上的请求没法直接看console。我之前用Charles抓包微信小程序但小程序的HTTPS证书校验很严格新版微信基本都把SSL Pinning做了Charles默认是抓不到小程序的包的还要花时间配置CA证书到系统信任区并且Android 7.0以上用户级别的CA证书默认不被信任操作极其繁琐。实测下来更省事的方案是用微信开发者工具的“真机调试”模式直接在手机上看console和Network面板后端加全局请求日志中间件把每个请求的method url body response 耗时打印到服务端日志文件排查问题直接登录服务器看日志需要抓HTTP层看请求详情时在开发者工具里用自带的Network工具最干净。我一开始走了弯路在抓包上耗了一整天后来干脆在后端写了一个debug.log每进来一个请求把入参出参都记录下来。生产环境里排查问题效率远高于抓包。结尾说点实际的回头看整个系统从立项到跑通内部试用大约用了一个月。核心功能只占20%的代码量剩下80%的时间全花在适配、鉴权、域名配置、真机兼容这些问题上。如果你是从零开始做建议先在网上找一个包含用户登录、表格提交、上传图片这些基础功能的小程序模板在此基础上改业务逻辑比从空项目手写每个基础组件要快得多。还有两个我实操后最想提醒的点第一所有状态流转务必加乐观锁这能救你命第二表单只要超过两个字段尽早做自动暂存功能小程序页面切后台太久会被回收用户填了一半的表单说没就没流失率极高。这套系统后续还可以往“租借预约”“会议室预订”“耗材申领”的方向扩展底层的角色权限和工单流程都是通用的换一批字段就能变成另一套管理系统。
返回列表