仓库管理系统(WMS)这种活儿,放在普通手机上一个表格页面就能应付,但真正走进库房你会发现,现场用的是防摔PDA、仓库主管盯的是平板、办公室墙上还挂着一块数据看板。以前的做法是同类系统按设备形态各做一版,维护成本直接把小团队压垮。后来我改用了 Flutter for HarmonyOS 这一套来做统一底座,在一套 Dart 代码里把手机、PDA、平板和大屏的仓库管理系统全部覆盖掉,这里面的导航组织和业务落地方式,值得单独拿出来聊聊。
这篇文章不讲那种只跑通一个 Demo 的入门内容,而是切实际开发里躲不开的几个核心问题:为什么选型 Flutter 而不是纯原生开发,多设备下导航架构到底怎么组织,入库出库盘点这些核心业务如何数字化,以及仓库现场断网弱网时怎么保证数据不乱。如果你正准备在鸿蒙设备上启动一个企业级应用,或者已经在做仓库系统但被多设备适配折磨得头疼,这篇内容对你应该会有直接帮助。
1. 选型复盘:为什么是 Flutter 而不是原生鸿蒙应用
1.1 仓库管理系统的设备现状,比想象中复杂
我在做项目前期调研时,整理了园区仓库里实际在用的设备类型,发现一个规律:越是数字化程度高的仓库,设备形态越杂。
一线操作工手里拿的一般是工业 PDA,屏幕小、按键大、有实体扫码键,很多还支持条形码和 RFID 读取,这类设备对续航和抗摔要求极高。仓库主管和复核员通常用平板,屏幕尺寸在 10 寸左右,需要同时看任务列表和操作详情,经常双手握持。办公室或者库区入口会放一块大屏显示器,用于展示库存总量、当天出入库进度、异常任务提醒,这块屏大多是无人值守状态,信息密度要和电视墙看齐。再加上管理人员偶尔用普通手机远程审批单据,同一个系统至少要覆盖四种截然不同的交互场景。
如果用 ArkTS 原生开发,手机、平板、PDA、大屏各写一套,且不说 UI 适配每个工程都要做一遍,光是 PDA 上那些扫码硬件调用、蓝牙打印机对接,就要分别封装一次。当时我们团队只有三四个客户端开发,按这个进度排期,光是客户端就有做不完的活儿。更麻烦的是,不同设备上的业务逻辑完全相同,重复实现意味着同一套库存扣减规则要在四个工程里各维护一遍,一旦业务规则调整就是灾难。
1.2 Flutter 在鸿蒙上的落地方式
Flutter 能在鸿蒙生态里跑起来,靠的是社区维护的 OpenHarmony 适配分支,它把 Flutter 引擎的渲染层、平台通道层对接到了鸿蒙的设备能力上。对我们业务开发来说,最大的变化是:Dart 层的业务代码、Widget 树、状态管理逻辑,可以原封不动地在鸿蒙设备上编译运行,需要用到底层系统能力的地方,通过平台通道(类似 MethodChannel)调鸿蒙原生实现。
这套方式的优势我用一句话概括:业务逻辑只写一次,UI 适配按设备形态做分支,底层能力按平台注入实现。比如扫码能力,PDA 上的实体扫码键和手机上的摄像头扫码走的完全是两条链路,但在 Flutter 层我只定义一个“扫码器接口”,手机端注入相机扫码实现,PDA 端注入硬件扫码监听实现,页面代码不需要知道背后是相机还是激光头。
对比一下其他跨端方案:基于 WebView 的混合方案在普通手机上问题不大,但在工业 PDA 上渲染性能和输入响应明显吃亏,而且库房里很多设备系统版本较旧、WebView 内核能力参差不齐。Flutter 自绘引擎的优势是每一次点击、滚动、键盘输入都由自己的渲染管线处理,不依赖系统 WebView,在低配置设备上表现稳定得多。
1.3 选型前的三个技术验证
选型不是拍脑袋,我先做了三个方向的小型验证。
第一个是渲染与交互验证。在鸿蒙平板和 PDA 模拟器上跑了一个 200 行列表 + 复杂表单页面的 Demo,重点测快速滚动、软键盘弹出、图片加载耗时。Flutter 的 Skia 渲染在鸿蒙上的表现接近原生帧率,滑动刷新率能稳定在 60 帧上下,对这个量级的企业应用足够。
第二个是第三方库兼容性验证。扫地式检查了一轮项目里必须用的库:网络库 dio、本地存储 sqflite、状态管理 provider、二维码扫描 mobile_scanner。结论是纯 Dart 实现的库基本无感,而依赖 Android 原生实现的插件需要逐个替换为鸿蒙适配版,或者自己写平台通道封装。
第三个是打包与分发链路的验证。鸿蒙应用包有单独的签名、打包格式和上架流程,需要在项目早期就把工程配置文件、签名脚本这块跑通,避免开发到一半才发现构建流程走不通。这个验证花的时间比预想中多,但比起上线前再补救,成本已经低很多了。
提示:如果你们的系统还需要对接扫描头、蓝牙打印机、RFID 读卡器这类硬件,一定在选型阶段就把设备厂商的 SDK 清单拉出来,确认它们是否有鸿蒙原生版本。没有的话,就要评估自封装协议的成本,这块才是 Flutter 跨端真正容易翻车的地方。
2. 多设备导航架构:一套代码怎么覆盖 PDA 和平板
2.1 设备形态不同,导航方式必须跟着变
导航架构是"一套代码适配多设备"里最容易想当然的部分。我见过不少项目,把手机上的底部 Tab 栏硬生生搬到平板上,结果用户在 10 寸屏幕上戳那个窄窄的底部栏,误触率极高。反过来,把大屏看板的侧边导航搬到手机 PDA 上,又把宝贵的操作区域全部占满了。
仓库系统的导航需求其实很清楚:操作类设备(PDA、手机)讲究单手可达,用底部导航最顺手;管理类设备(平板)屏幕宽,左右分栏效率最高,左侧菜单 + 右侧内容区是标准形态;展示类设备(大屏)根本不适合放常规导航,它要的是信息雷达图式的首页,所有功能入口直接铺在可视区域里,靠按钮直达。
所以导航方案不能是"一套 UI 组件自适应缩放",而应该是"一套路由模型 + 按设备形态切换导航容器"。
2.2 断点设计:用宽度感知决定导航容器
我采用的方案是基于可用宽度的断点切换。核心思路是用 LayoutBuilder 包裹根组件,通过约束宽度判断设备形态,然后返回不同的导航容器:
- 紧凑模式(宽度小于 600dp):对应手机和窄屏 PDA,使用底部导航栏 + 页面堆栈,页面跳转保留返回逻辑。
- 中等模式(600dp 到 1024dp):对应平板,使用左侧窄菜单(图标 + 文字精简版),内容区域居中,支持左右分栏并行展示列表和详情。
- 宽松模式(大于 1024dp):对应大屏看板和桌面窗口,使用左侧完整菜单,内容区同时承载多个任务卡片和看板组件,顶部放关键指标数据。
写出来大概是这样的结构:
class AppNavigator extends StatelessWidget { const AppNavigator({super.key}); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final width = constraints.maxWidth; if (width < 600) { return const BottomNavigationLayout(); } else if (width < 1024) { return const SideNavigationLayout(compact: true); } return const SideNavigationLayout(compact: false); }, ); } }这个断点值不是拍脑袋定的。600dp 大致对应平板横竖屏切换的分界,1024dp 则是常见侧边栏折叠的阈值。实际测试中,把断点放在 600 和 1000 之间浮动几像素,对用户感知几乎没有影响,因为设备物理宽度是离散的,不存在中间态。
2.3 导航容器与页面路由的职责划分
导航容器只负责"把当前路由对应的页面框架呈现出来",具体页面内容由路由表统一管理。我保留了单页面应用的路由映射思路,但做了两层拆分:
第一层是路由注册表。所有业务页面在统一的映射表中注册,页面不感知自己在什么设备上显示,只需要暴露页面 ID、标题和参数类型。
final routes = { '/dashboard': (args) => DashboardPage(args), '/inbound/detail': (args) => InboundDetailPage(args), '/outbound/picking': (args) => PickingTaskPage(args), '/inventory/list': (args) => InventoryListPage(args), };第二层是导航容器。不同形态的容器从同一张路由表取页面构建函数,只是入口摆放位置不同。页面内的跳转统一通过一个go(routePath, params)方法完成,由导航容器决定是压入页面栈还是替换工作区内容。
这样做的直接好处是:如果一个功能只有 PDA 上用,只需要在 PDA 的底部导航里不显示入口,但路由依旧可用;如果平板和大屏的首页布局完全不同,只需要各写一个首页壳子,内部业务组件完全复用。
2.4 导航状态在鸿蒙多任务环境下的特殊处理
鸿蒙系统对应用前后台切换的管理方式和其他平台不太一样,尤其强调任务卡片和窗口恢复。如果导航层没有处理好状态恢复,用户从桌面切回应用时会看到一片空白。
我的做法是在导航容器层接管组件的状态保存,把所有当前路由、页面栈、选中菜单项统一存到一个可序列化的导航状态对象里。切到后台时序列化保存,恢复时从对象重建页面栈。这样即便系统把应用进程回收了,重启后也能直接恢复到用户上次操作的页面,而不是回到首页。
另外,仓库里很多 PDA 是专用设备,用户不需要像手机上那样频繁切换任务,及时退出当前任务比退出登录更常见,我干脆在导航层加了一个"返回工作台"手势,从任意任务页一步回到任务池,现场体验好了很多。
3. 核心业务模块落地:入库、出库、盘点与移库的数字化拆解
3.1 先搭主数据与权限模型,再谈功能
仓库系统如果只做单据和流程,后面一定得返工。我接手这个项目时,第一件事是拉着业务方把基础数据和权限模型定了。
主数据涵盖四个维度:仓库与库位(仓库、库区、巷道、货架层格)、货品资料(SKU、规格、条码、批次属性、效期要求)、往来单位(供应商、客户、承运商)、以及人员账号与角色。其中库位是仓库管理的灵魂,没有库位概念就没有库内作业的优化空间。我们采用"仓库-库区-巷道-货位"四级编码体系,例如A-03-02-01表示 A 库区第三条巷道第二层第一个货位,这个编码在手持终端上要能直接扫码录入。
权限模型用的是基于角色的访问控制(RBAC),角色分成收货员、拣货员、盘点员、复核员、仓管主管五类。每个角色绑定的是"可见菜单 + 可执行动作",而动作又和数据状态绑定。比如拣货员只能看到已分配给自己、状态为待拣货的任务,一旦扫描完成第一件货,任务立刻进入"拣货中"状态,其他人不能再操作。
3.2 入库流程:收货、质检、上架
入库环节是数据准确性的源头,这里的核心矛盾是"单据上的数"和"实物数"经常不一致。流程拆成三步走:
第一步收货。上游 ERP 推送采购单/到货单到系统,仓管员在 PDA 上打开收货任务,逐箱扫码登记数量。这里的关键点在于扫码不是单纯录入条码,而是要把条码对应的货品、批次、效期自动带出来。遇到实物条码扫不出来的情况,PDA 要支持手工输入条码并标记异常。
第二步质检。抽检比例按货品品类设置,高风险品类默认全检。质检结果分为合格、不合格、待定三种,不合格品直接进入退货区,不再参与上架。这个环节在现场很容易被忽略,但少了它,后续库存质量会把整个系统拖垮。
第三步上架。上架不能随意找空位,而是要走推荐逻辑。我用一个简单但有效的策略:同一 SKU 优先放到已有该 SKU 的库位,保证同类货品聚拢;没有已有库位时,按库区剩余容量和货位承重推荐空闲货位。PDA 端每次只展示一个推荐库位,操作员扫一下库位码确认,完成了再推下一个,避免连续闪烁干扰。
入库单据的状态我用一张表维护:
| 状态 | 含义 | 下一步动作 |
|---|---|---|
| 待收货 | 单据已同步,未开始作业 | 操作员领取任务 |
| 收货中 | 已扫入部分数量 | 继续扫描或提交差异 |
| 差异确认 | 实物与单据数量不一致 | 主管确认差异 |
| 待上架 | 收货完成,等待分配库位 | 系统推荐库位 |
| 已上架 | 货物落入库位 | 库存实时更新 |
3.3 出库流程:订单、波次、拣货、复核
出库是仓库系统里交互最复杂、也是最影响仓库效率的环节。订单从上游系统进来后,不是一个一个去拣,而是按波次批量处理。波次合并的原则一般有两种:按订单类型合并(整箱出和拆零出分开),按承运商/线路合并(同一个物流车次的订单一起拣)。波次一旦产生,系统自动为它生成拣货任务。
拣货环节是手持设备的主场。系统会按订单明细生成一条拣货路线,顺序上采用"蛇形路径"策略:从波次涉及的第一条巷道入口开始,按库位编码顺序依次经过每个涉及货位,避免操作员在通道里来回折返。PDA 界面显示当前货位、SKU、应拣数量,操作员扫货品条码 + 扫数量确认,一件一确认。如果出现实际库位无货或数量不足,操作员可以直接上报缺货,系统自动把缺货信息回传订单池,并在波次中标记异常。
拣货完成进入复核台。复核员扫总单条码,系统逐件播报应出数量,复核员核对实物,全部通过后打包称重。这一步最容易产生交错货、漏拣,我的建议是复核界面不要做批量操作,必须逐件确认,宁可多花几秒也不能让错误出仓。
3.4 盘点与移库:手持设备上最容易被忽视的交互细节
盘点模块逻辑不复杂,麻烦在于操作体验。传统盘点拿着纸质单子对数量,效率低且极易出错。系统里我设计了两种盘点模式:全面盘点和循环盘点。全面盘点一般集中在月末或年末,需要库位冻结,所有库位逐一清点。循环盘点按 ABC 分类法分配频率,A 类高流转货品每月盘一次,B 类每季度,C 类半年一次。
循环盘点的关键在于盘点任务的随机性。如果每次盘点都按固定的库位顺序,操作员会产生路径依赖,失去了抽盘的意义。系统会随机抽取 N 个库位生成盘点任务,同时对每个库位只显示"应存数量",不直接显示实际系统库存,要求操作员先录入实盘数,再与系统数对账,避免先入为主的引导。
移库在手持设备上则要尽量简化。仓库里常见的移库动作是补货和整理,比如从整托区挪到拣货区。PDA 上的交互是:扫货品条码 → 系统显示当前库位 → 扫目标库位 → 确认移库数量。这个流程基本可以单手完成,另一只手拿货。如果一次要移多个数量,数量输入框尽量做成大号数字键盘,减少误触。
3.5 单据状态机与异常闭环
业务模块开发完,我最深的体会是:没有状态机的流程就是在裸奔。入库单、出库单、盘点单、移库单,每一类单据都要有明确的流转规则,以及被允许的跳转路径。
比如入库单不允许从"收货中"直接跳"已上架",必须经过"待上架";盘点单在"差异确认"状态下,必须由主管填写差异原因后才能关闭。这些规则我会统一放到状态机配置里,而不是散落在各个页面的判断逻辑中。页面只负责触发动作,状态是否允许由底层引擎裁决。
异常闭环同样重要。收货差异单、缺货记录、盘点差异、破损登记,不能停留在一条日志里,而是要生成待处理任务,推送到主管的工作台。主管处理完,任务状态关闭,相关联的单据才能继续流转。跑通这个闭环之后,仓库的账实相符率才会有实际保障。
4. 断网弱网现场:仓库作业稳定性的关键设计
4.1 仓库现场的网络状况比想象中差
办公楼里的网络环境干干净净,但库房是个完全不同的世界。钢架结构对无线信号的屏蔽效应明显,巷道深处信号极差,加上叉车、电机、金属货架带来的干扰,手持 PDA 经常出现"刚才还显示的按钮,下一分钟就转圈"的情况。
如果系统设计成"每一步操作都必须在线才能完成",仓库现场会变成一场灾难:操作员每扫一件货都要等网络回包,断网时只能放下设备干等,一天的作业量直接砍半,货收不进来、发不出去。
4.2 离线优先:操作先落本地,再异步同步
我采用的方案是"离线优先"架构。核心原则是:所有关键操作先在本地设备上完成,写入本地数据库,然后异步同步到服务端。这样即使完全断网,操作员依然可以正常扫码、录数、执行任务,等信号恢复后,任务队列自动把数据推上去。
本地存储用的是 SQLite,配合轻量级状态管理来维护任务队列。每个操作生成一条唯一的事件 ID,包含操作类型、单据 ID、货品、数量、时间戳、操作员,以及当前同步状态。同步状态分为待上传、上传失败、已确认、冲突待处理四种。断网状态时,所有新操作直接置为"待上传";网络恢复时,同步引擎按时间顺序逐条推送。
这个方案的关键问题是幂等。仓库的同一个补货动作可能在弱网环境下被人工重试多次,服务端必须能识别重复事件。我用的办法是服务端对事件 ID 做唯一索引,收到重复事件直接返回"已确认",不做二次扣减。
4.3 数据冲突怎么处理
离线操作必然带来冲突:两个人同时在两个设备上改同一个库位的库存,或者主管在平板上变更了单据状态,而操作员 PDA 上的旧数据还在继续操作。
冲突处理采用"先到先得 + 人工裁决"两层机制。第一层,服务端对每一条库存流水做行级锁,同一库位的并发写入只允许一个成功,另一个返回冲突标记;第二层,客户端收到冲突标记后,提示操作员重新拉取最新数据,如果涉及数量差异,由主管在 Web 后台或平板上做裁决。
这个机制还需要配合合理的同步策略才能好用。主数据采用版本号机制,货品、库位、单据每次变更都递增版本号,客户端启动时先拉版本号,发现落后才全量拉取,否则只拉增量。操作数据则采用断点续传,单条事件按 ID 分批上传,失败就停留在原批次,不阻塞后面的数据。
4.4 弱网环境下 UI 反馈的节奏
网络不稳定时,UI 反馈不能假装一切正常。我在 PDA 端做了一套统一的网络状态提示组件,头部有一条细条横幅,断网时显示灰色,同步中显示蓝色进度条,冲突待处理时显示黄条。每个操作提交后,页面角标会有一个小圆点表示"已记录,等待同步",操作员不需要理解底层的队列机制,看一眼颜色就知道现在的数据状态。
这一点非常重要:仓库操作员不关心技术实现,他们只想知道"我刚才扫的那件货,到底记上了没有"。给一个明确的视觉反馈,比任何技术方案都更能建立信任感。
5. 从开发到交付:绕不开的那些坑
5.1 生态库适配不足,抽象层是唯一的解药
跨端开发最头痛的就是生态库适配。很多 Dart 插件底层依赖 Android 的原生能力,到了鸿蒙上运行时会直接抛异常。我踩得最深的是扫码插件和蓝牙打印机。
扫码这件事,手机端和 PDA 端实现机制完全不同。手机端摄像头扫码是标准能力,PDA 则是激光扫描头直接输出键盘事件。我最后没有依赖任何现成插件,而是自己抽象了一个Scanner接口:
- 手机端:使用相机帧分析,配合条码识别算法解析。
- PDA 端:监听系统键盘事件,扫描头扫到的内容以一个特殊前缀结束符传入,直接截取。
写起来是这样的结构:
abstract class Scanner { Future<void> start(ScanResultCallback callback); Future<void> stop(); } class CameraScanner implements Scanner { ... } class HardwareScanner implements Scanner { ... }页面只管调用Scanner接口,具体实现由设备形态初始化时注入。这个抽象层的成本是两三天,但它让整个系统的扫码逻辑没有绑定任何单一设备厂商。
蓝牙打印机的坑则在于各家协议不统一。小票打印机、标签打印机、热敏打印纸规格不同,命令集不同。我把打印需求抽象成了一个统一的Printer接口,封装出"打印入库标签""打印拣货小票""打印盘点单"三个方法,具体品牌的差异全部在接口实现类里消化,业务侧完全无感。
5.2 扫描枪与软键盘打架
PDA 外接的蓝牙扫描枪本质上是个键盘输入设备。它扫到条码后,会以按键序列的方式输入到当前聚焦的文本框里。问题在于:如果文本框是普通输入框,操作员扫完条码,软键盘也弹出来了,半屏 UI 被键盘遮挡,操作员还得手动收起。
这个问题的解法我试过几种。一种是把扫描输入的文本框设置为不可聚焦,改成自绘组件接收全局键盘事件,这样可以完全绕过软键盘,但代价是输入框的编辑体验丢了。最终方案是采用一个折中策略:扫码输入框默认可聚焦,但监听焦点事件,一旦检测到扫码头输入结束(连续快速输入 + 结束符),立即收起软键盘,把焦点移交到下一个操作按钮。
这个策略在真机测试时发现一个细节:不同品牌扫描枪的结束符不一致,有的发回车,有的发 Tab,有的发自定义后缀。我在抽象层里做了配置化,按设备厂商设置结束符,上线后在系统配置页留了一个后台开关,方便现场调整。
5.3 页面状态恢复与任务队列入坑
断网离线时,最尴尬的事情是用户操作到一半切到后台,回来发现页面被系统回收了,本地未同步的数据也丢了。我第一版没处理这个问题,结果在一次仓库实测中被吐槽"扫了三十件货,切出去接个电话,回来全没了"。
问题根源在于 Flutter 页面默认状态没有和本地数据库做绑定,任务队列又只存在内存里。修复思路有两个方向:一是页面状态尽量从本地数据库读取,不依赖内存;二是任务队列每次写入都落 SQLite,而不是内存 List。
现在的做法是:PDA 端所有任务列表的页面数据来源优先查本地表,轮询等待同步引擎更新;任务队列的每一步状态变更同步写本地日志表,服务端确认后再标记清理。这样即使进程被回收,重启后任务列表和未推送事件都能恢复,操作员最多重新打开一次页面,不需要重新扫描。
5.4 真机测试不能只看模拟器
鸿蒙生态的设备形态差异比普通 Android 生态还要大,模拟器上顺滑的表现到了真机上可能完全不同。特别是 PDA 这种特殊设备,屏幕分辨率、触控灵敏度、扫描头集成方式、系统对键盘事件的处理策略都不一样。
我后来立了条规矩:每到一轮功能开发完成,必须至少在手机、平板、PDA 三种真机上跑一遍核心流程,尤其是扫码、离线同步、前后台切换这几个场景。模拟器只用来验证布局和样式,真机才是验证逻辑的地方。
6. 这套方案的可复制性:给打算入坑的团队的几点经验
6.1 先做主数据和任务模型,再碰 UI
我第一次做 WMS 的时候犯过错误,先把页面画得漂漂亮亮,结果一对接业务逻辑,发现很多页面得重做。后来我学乖了,任何功能模块先定义清楚:主数据实体有哪些字段、单据状态怎么流转、任务如何分配到人、库存流水如何记录。这些底子不打好,页面画得再好看都是空中楼阁。
对仓库系统来说,核心数据模型就几个:货品、库位、库存、单据(入库/出库/盘点/移库)、任务、流水。把这些模型的字段关系、状态机、索引设计想清楚,后面的开发速度可以快一倍。
6.2 组件库按设备形态沉淀,别按页面沉淀
开发过程中踩过不少重复建设的坑。同一个"扫码数量输入组件"在入库页写一遍、出库页又写一遍,等盘点模块又得再写一遍。这种问题用传统思路很容易走偏:按页面拆组件,结果不同页面里的相似控件没法复用。
我的做法是按设备形态拆共享组件库。基础组件(按钮、输入框、弹窗、状态标签)一套,扫码录入组件分手机版和 PDA 版两套实现,数量编辑组件、任务列表组件、异常上报组件各自封装成独立模块。业务页面更像一个组装车间,把组件按业务规则组合起来,新增模块时越来越轻松。
6.3 工程化从第一天抓起
这个项目的工程化经历告诉我,跨端项目如果不从第一天做 CI 和自动化,后面会非常痛苦。我搭了一个持续的构建验证流程,每次提交代码自动触发多端构建,至少确保代码能在手机、平板、PDA 三个目标上编译通过。测试阶段引入了自动化日志收集,真机运行的报错和堆栈能自动回传到管理后台,定位问题不用再一台台设备去翻日志。
分阶段灰度也是必须的。企业级应用最怕全量更新出问题,我在发布流程上预留了按设备型号、账号列表两个维度的灰度开关,新版本先在办公室平板上小范围验证,稳定后再推 PDA 端,避免现场陷入完全不可用的状态。
6.4 日志和埋点是在帮自己省时间
仓库系统的错误大多是稀疏出现的,网络抖动、操作员误操作、设备老化的偶发问题,如果没有日志,排查就是大海捞针。我在关键节点埋了一整套事件日志:扫码事件、同步事件、状态跳转、异常上报,每条日志带上设备型号、内存状态、信号强度、当前版本号。后期维护时,远程连一台 PDA 现场复现,基本能通过日志把问题点定位到具体函数,效率提升明显。
另外,日志别一股脑全部收集,否则大海捞针的问题会换成"日志量太大,不知道看哪条"。我按事件重要级分了三档:错误级(必须收集)、警告级(抽样收集)、信息级(本地保留三天)。远程拉取只拿错误和警告级别的数据,信息级日志通过手动触发才上传。
最后再分享一个小技巧,这个项目让我养成了一个习惯:每开发完一个模块,都要回到仓库现场待一个下午,找个话多的操作员聊聊。系统好不好用,看操作员的微表情就知道。那些让你改需求的操作员不是麻烦,是整个项目最珍贵的免费测试员。多设备适配、离线容错这些技术方案,最终都是为了让他们在大热天少跑几步路、少点几下屏幕而已。