文档目录
离线数据处理

简介

本技术文档面向 DouPHP 小程序的“离线数据处理”能力,围绕以下目标展开:

  • 离线数据检测机制:网络状态监听、连接质量评估、断网提示。
  • 数据冲突解决方案:版本号管理、时间戳比较、用户选择策略。
  • 增量同步机制:变更追踪、差异计算、批量提交。
  • 断网重试策略:指数退避、失败队列、手动重试。
  • 本地存储方案:数据结构设计、存储空间管理、数据清理。
  • 离线模式 UI 交互与体验优化。

说明:当前仓库中已实现统一的 HTTP 层(信封解析、缓存/去重、错误拦截)、Store 持久化封装(schema_version 失效)以及环境探测工具;订单页存在“离线支付”流程。基于这些基础能力,可构建完整的离线处理体系。

项目结构

小程序端关键目录与职责:

  • services/http.ts:统一 HTTP 请求封装,负责信封解析、缓存/去重、错误拦截、调试增强。
  • stores/persist.ts:基于 localStorage 的 Store 持久化封装,带 schema_version 版本控制。
  • utils/env.ts:运行环境与调试开关探测。
  • pages/order/offlinepay.ts:订单“离线支付”页面,演示上传凭证等异步操作。
graph TB
subgraph "小程序前端"
A["HTTP 层<br/>services/http.ts"]
B["持久化封装<br/>stores/persist.ts"]
C["环境探测<br/>utils/env.ts"]
D["订单离线支付页<br/>pages/order/offlinepay.ts"]
end
A --> |"发起请求/解析信封"| E["后端 API"]
B --> |"读写本地存储"| F["wx.storage"]
C --> |"调试开关/环境判断"| A
D --> |"调用 HTTP/上传文件"| A

核心组件

  • 统一 HTTP 层
    • 信封解析:code === 'OK' 为成功,否则构造 ApiError。
    • 缓存与去重:GET 支持内存缓存与飞行中复用,避免重复请求。
    • 错误拦截:统一捕获网络异常与业务码异常,支持调试增强。
    • 调试钩子:request_id 注入与展示,便于定位问题。
  • 持久化封装
    • 写入结构包含 __v(schema_version),读取时不匹配则丢弃旧缓存。
    • 提供 load/save/clear 与 autorun 自动写回能力。
  • 环境探测
    • isDebugEnv() 区分开发/试用/正式版,控制调试 UI 是否显示。
    • isServerDebug() 镜像服务端 debug_enable。
  • 订单离线支付
    • 通过 http.get 获取订单信息,使用 wx.uploadFile 上传支付凭证。

架构总览

下图展示了小程序在在线/离线两种路径下的数据流与控制点:

sequenceDiagram
participant U as "用户界面"
participant H as "HTTP 层<br/>services/http.ts"
participant S as "后端 API"
participant P as "持久化封装<br/>stores/persist.ts"
U->>H : 发起请求(含 cache/revalidate/dedupe)
alt 在线且命中缓存
H-->>U : 返回内存缓存数据
else 在线未命中缓存
H->>S : 发送请求
S-->>H : 返回信封(code/message/data/errors/request_id)
H-->>U : 成功返回 data<T> / 失败抛出 ApiError
else 离线
H-->>U : 抛出 NETWORK_ERROR
U->>P : 读取本地缓存/待提交队列
U-->>U : 降级展示本地数据并提示离线
end

详细组件分析

离线数据检测机制

  • 网络状态监听
    • 可在应用启动时订阅网络变化事件,结合 http.onError 中的 NETWORK_ERROR 判定,维护全局“是否在线”状态。
  • 连接质量评估
    • 基于多次请求耗时与成功率估算网络质量,用于决定是否启用强缓存或限制刷新频率。
  • 断网提示
    • 当检测到 NETWORK_ERROR 时,通过 UI 提示用户当前处于离线状态,并提供“稍后重试”入口。

实践建议

  • 将“是否在线”状态放入共享 store,配合 UI 组件进行条件渲染。
  • 对关键读接口开启 cache=true,提升离线可用性。
  • 对写操作进入失败队列,等待网络恢复后重试。

数据冲突解决方案

  • 版本号管理
    • 使用 persist.ts 的 SCHEMA_VERSION 作为数据模型版本,升级时旧缓存自动失效,避免脏数据。
  • 时间戳比较
    • 服务端响应携带更新时间戳,客户端合并时以较新者为准;对于并发修改,采用“最后写入胜出”或“字段级合并”。
  • 用户选择
    • 当冲突不可自动解决时,弹出对比视图,由用户决定保留本地还是远端数据。

实施要点

  • 每次写回持久化前,校验 __v 与当前 SCHEMA_VERSION。
  • 对关键字段(如金额、库存)引入乐观锁字段(version/timestamp)。
  • 冲突弹窗文案清晰,提供“撤销/确认”操作。

增量同步机制

  • 变更追踪
    • 对本地可写实体维护 last_sync_time 与 version,仅拉取大于该时间的变更。
  • 差异计算
    • 服务端返回 diff 列表(新增/更新/删除),客户端按 id 合并到本地。
  • 批量提交
    • 将离线期间的写操作聚合为批次,在网络可用时一次性提交,减少往返次数。

流程图(差异合并)

flowchart TD
Start(["开始"]) --> LoadLocal["加载本地数据"]
LoadLocal --> FetchDiff["拉取增量差异"]
FetchDiff --> Merge{"合并策略"}
Merge --> |时间戳优先| TS["按时间戳比较"]
Merge --> |版本号优先| Ver["按版本号比较"]
TS --> Apply["应用到本地"]
Ver --> Apply
Apply --> Batch["加入批量提交队列"]
Batch --> End(["结束"])

断网重试策略

  • 指数退避
    • 首次失败立即重试,随后按 1s、2s、4s、8s… 递增间隔重试,设置最大重试次数与上限间隔。
  • 失败队列
    • 将失败的写操作入队,网络恢复后按序重试;支持优先级与幂等键。
  • 手动重试
    • 提供“重试全部/单个”入口,允许用户主动触发。

序列图(指数退避重试)

sequenceDiagram
participant U as "用户界面"
participant H as "HTTP 层"
participant Q as "失败队列"
participant S as "后端 API"
U->>H : 发起写请求
H->>S : 发送请求
alt 失败
H-->>Q : 入队(含幂等键/优先级)
U-->>U : 提示“已加入重试队列”
loop 指数退避
Q->>S : 延迟后重试
alt 成功
S-->>Q : 成功
Q-->>U : 通知完成
else 仍失败
S-->>Q : 失败
end
end
else 成功
S-->>H : 成功
H-->>U : 返回结果
end

本地存储方案

  • 数据结构设计
    • 每个 key 对应一个 Envelope{v, data},v 用于 schema 版本控制。
    • 针对业务实体建立独立 key,便于局部清理与迁移。
  • 存储空间管理
    • 定期统计各 key 大小,超过阈值时清理过期或低频数据。
    • 对大对象(图片/富文本)采用压缩或分片存储。
  • 数据清理
    • 提供 clearPersisted 接口,支持按 key 或全量清理。
    • 结合用户行为(如退出登录)清理敏感数据。

离线模式的 UI 交互设计与用户体验优化

  • 状态可见性
    • 顶部常驻条显示“离线/弱网”,点击可查看最近一次请求的 request_id。
  • 降级展示
    • 读接口命中缓存时,标注“缓存数据,可能不是最新”。
  • 操作反馈
    • 写操作失败时,明确告知“已加入重试队列”,并提供“立即重试”按钮。
  • 资源占用
    • 弱网下禁用大图/视频预加载,降低带宽消耗。
  • 调试友好
    • 非正式版环境下,错误消息附加 request_id 片段,便于定位。

订单“离线支付”流程

  • 流程说明
    • 页面加载后先鉴权,再拉取订单详情;用户选择图片后上传支付凭证,成功后跳转至订单列表。
  • 与离线能力的结合
    • 若网络不可用,应提示“当前离线,无法上传凭证”,并将“上传凭证”动作加入失败队列,待网络恢复后重试。

序列图(订单离线支付)

sequenceDiagram
participant P as "订单页<br/>offlinepay.ts"
participant H as "HTTP 层"
participant S as "后端 API"
participant W as "微信API"
P->>H : GET 订单详情
H->>S : 请求
S-->>H : 返回订单数据
H-->>P : 渲染订单信息
P->>W : chooseImage 选择凭证
W-->>P : 返回临时路径
P->>W : uploadFile 上传凭证
W-->>P : 返回上传结果
P->>H : 可选:提交支付凭证状态
H->>S : 请求
S-->>H : 返回结果
H-->>P : 成功则跳转

依赖关系分析

  • HTTP 层依赖
    • 微信原生 wx.request/wx.showModal/wx.setClipboardData。
    • 环境探测 utils/env.ts 控制调试行为。
  • 持久化封装依赖
    • 微信 storage API 与 MobX 的 autorun/toJS。
  • 页面依赖
    • 订单页依赖 HTTP 层与微信文件上传能力。
graph LR
Env["utils/env.ts"] --> Http["services/http.ts"]
Http --> Storage["stores/persist.ts"]
Page["pages/order/offlinepay.ts"] --> Http
Page --> Storage

性能考量

  • 请求去重与缓存
    • GET 请求默认去重,避免抖动;合理设置 ttl,平衡新鲜度与性能。
  • 批量提交
    • 将多次写操作合并为一次提交,降低网络开销。
  • 弱网适配
    • 弱网下关闭非必要资源加载,降低首屏时间与带宽占用。
  • 存储体积
    • 定期清理过期数据,避免 storage 膨胀影响性能。

故障排查指南

  • 快速定位
    • 查看最近一次请求的 request_id,与服务端日志对照。
    • 在非正式版环境,错误消息会附带 request_id 片段。
  • 常见问题
    • 网络异常:检查设备网络与域名配置;必要时提示用户切换网络。
    • 业务码非 OK:根据 errors 字段定位具体原因。
    • 缓存不一致:调整 ttl 或强制 revalidate。
  • 调试技巧
    • 利用 onError 拦截器记录上下文(URL、参数、时间戳)。
    • 使用 clearCache 清空内存缓存后复现问题。

结论

本项目已具备构建完整离线数据处理体系的基础:

  • HTTP 层提供稳定的请求封装与错误处理。
  • 持久化封装提供版本化的本地存储能力。
  • 环境探测确保调试体验可控。 在此基础上,可通过网络监听、失败队列、增量同步与冲突解决策略,实现高可用的离线模式,并在 UI 层面提供清晰的降级与反馈。

附录

  • 术语
    • 信封:标准响应体结构 code/message/data/errors/request_id。
    • 去重:相同 GET 请求在飞行中复用同一 Promise。
    • 幂等:重复提交不会产生副作用。
  • 最佳实践
    • 所有写操作均标记幂等键,便于重试安全。
    • 对关键数据采用“本地优先 + 后台同步”的策略。
    • 定期审计本地存储大小与命中率,持续优化。
添加日期:2026-10-05