文档目录
数据同步机制

简介

本技术文档围绕 DouPHP 小程序与主站的数据同步机制,系统阐述以下方面:

  • API 接口设计与信封格式、请求/响应处理流程
  • 本地缓存策略(内存缓存、持久化、过期与失效)
  • 状态管理模式(全局状态、局部状态同步、状态持久化)
  • 离线数据处理方案(冲突解决、增量同步、断网重试)
  • 性能优化技巧(批量操作、懒加载、预加载)
  • 错误处理与日志记录方案

该机制以“统一 HTTP 层 + Store 状态管理 + 持久化”为核心,结合 SWR(Stale-While-Revalidate)刷新策略,确保首屏秒开与数据一致性。

项目结构

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

  • services/http.ts:统一 HTTP 封装,负责信封解析、拦截器、去重、内存缓存、调试增强
  • services/bootstrap.ts:应用引导数据拉取(site/param/features/nav_list/lang_v/version)
  • stores/*:状态管理(commonStore、authStore、cartStore),提供 hydrate/refresh/restore 等能力
  • stores/persist.ts:基于 storage 的持久化封装(带 schema_version 版本控制)
  • app.ts:应用启动入口,注册全局错误处理、自动更新、推广参数解析、store 引导
  • api/controller/BaseController.php:API 控制器基类,承载 API 端通用逻辑
graph TB
subgraph "小程序"
A["app.ts<br/>应用启动"] --> B["stores/index.ts<br/>Store 聚合与引导"]
B --> C["stores/common.ts<br/>公共数据 Store"]
B --> D["stores/auth.ts<br/>登录态 Store"]
C --> E["services/bootstrap.ts<br/>引导数据服务"]
E --> F["services/http.ts<br/>统一 HTTP 层"]
D --> F
end
subgraph "后端"
G["api/controller/BaseController.php<br/>API 控制器基类"]
end
F --> G

核心组件

  • 统一 HTTP 层(services/http.ts)
    • 标准信封解析:code/message/data/errors/request_id
    • 默认头注入:Content-Type、Authorization: Bearer &lt;api_token>
    • 拦截器:onRequest/success/error 钩子
    • 去重:相同 GET 在飞行中复用 Promise
    • 内存缓存:cache/ttl/revalidate 支持
    • 调试:request_id 追踪、异常弹窗(仅调试环境)
  • 应用引导服务(services/bootstrap.ts)
    • 拉取 bootstrap/index 数据,驱动 commonStore.applyData
  • Store 体系
    • commonStore:站点信息、语言包、功能开关 features、导航等;SWR 刷新并持久化
    • authStore:登录态、鉴权标志、模块降级(features.user=false 时短路)
    • cartStore:购物车角标绑定(由 index.ts 驱动)
  • 持久化(stores/persist.ts)
    • 读写 storage,带 schema_version 版本控制,变更即失效旧缓存
    • autorun 订阅 selector,变化写回 storage

架构总览

小程序通过统一 HTTP 层访问后端 API,Store 作为单一真相源,配合持久化实现“种子数据 → 本地缓存 → 网络刷新”的 SWR 模式。应用启动时先 hydrate 再 refresh,保证首屏秒开与数据最新性。

sequenceDiagram
participant App as "app.ts"
participant Stores as "stores/index.ts"
participant Common as "stores/common.ts"
participant Auth as "stores/auth.ts"
participant Boot as "services/bootstrap.ts"
participant Http as "services/http.ts"
participant API as "后端 API"
App->>Stores : 调用 bootstrapStores()
Stores->>Common : hydrate()
Stores->>Auth : hydrate()
Stores->>Common : refresh()
Common->>Boot : fetchBootstrap()
Boot->>Http : http.get(route('bootstrap'))
Http->>API : 发起请求
API-->>Http : 返回信封 {code,message,data,...}
Http-->>Boot : 解析后返回 data
Boot-->>Common : BootstrapData
Common->>Common : applyData(合并 lang/site/features...)
Common->>Common : savePersisted(PERSIST_KEY, merged)
Stores->>Auth : restore()
Auth->>Http : http.get(user/index)
Http->>API : 发起请求
API-->>Http : 返回认证信息
Http-->>Auth : 解析并设置 is_login 等标志

详细组件分析

统一 HTTP 层(services/http.ts)

  • 信封解析:严格读取 code/message/data/errors/request_id,非 OK 则抛出 ApiError
  • 默认头:自动注入 Authorization: Bearer &lt;api_token>
  • 拦截器:onRequest/success/error 扩展点,便于全局处理(如 UNAUTHORIZED 登出)
  • 去重:GET 请求在飞行中复用同一 Promise,避免重复网络开销
  • 内存缓存:支持 cache/ttl/revalidate,命中且未过期直接返回
  • 调试:request_id 追踪,调试环境可弹出服务端异常堆栈
flowchart TD
Start(["进入 request(config, opts)"]) --> BuildCfg["构建配置<br/>注入默认头"]
BuildCfg --> Interceptors["执行请求拦截器"]
Interceptors --> CacheCheck{"启用缓存且未强制刷新?"}
CacheCheck --> |是| HitCache{"命中内存缓存且未过期?"}
HitCache --> |是| ReturnCache["直接返回缓存数据"]
HitCache --> |否| DedupeCheck{"是否允许去重?"}
CacheCheck --> |否| DedupeCheck
DedupeCheck --> |是且有进行中| Reuse["复用进行中 Promise"]
DedupeCheck --> |否| DoRequest["发起 wx.request"]
Reuse --> End(["返回 Promise"])
DoRequest --> OnSuccess{"HTTP 状态码 2xx?"}
OnSuccess --> |否| ParseErr["解析信封并构造 ApiError"]
ParseErr --> RunErrInterceptors["运行错误拦截器"]
RunErrInterceptors --> Reject["reject(ApiError)"]
OnSuccess --> |是| ParseOk["解析信封"]
ParseOk --> Ok{"code === 'OK'?"}
Ok --> |否| BizErr["构造业务 ApiError"]
BizErr --> RunErrInterceptors
Ok --> |是| AttachMeta["附加 __message/__request_id"]
AttachMeta --> SaveCache{"是否启用缓存?"}
SaveCache --> |是| PutCache["写入内存缓存"]
SaveCache --> |否| SuccessInterceptors["运行成功拦截器"]
PutCache --> SuccessInterceptors
SuccessInterceptors --> Resolve["resolve(data)"]
Reject --> End
Resolve --> End

应用引导与 Store 聚合(stores/index.ts)

  • 启动顺序:hydrate → refresh → restore
  • 购物车角标:autorun 监听 cartStore.badge,统一设置 tabBar 角标
  • 模块降级:根据 commonStore.features 决定是否继续 auth/cart 刷新

公共数据 Store(stores/common.ts)

  • 数据来源:bootstrap/index + lang/fetchLang
  • 数据合并:优先使用网络返回的语言包,否则回退到已有或种子语言包
  • 持久化:refresh 成功后将合并后的数据持久化,schema_version 控制失效
  • 功能开关:features 用于模块级降级(如 user/product/article)

登录态 Store(stores/auth.ts)

  • 状态字段:api_token/user_id/loginEd/is_login/is_vip/is_work/is_distribution
  • 恢复流程:restore 调用 user/index 获取认证标志,UNAUTHORIZED 时清空登录态
  • 模块降级:当 features.user=false 时,is_login 恒 false,不发起任何请求
  • 登录/登出:login 写入 storage 并更新状态;logout 清理 storage 与状态

持久化(stores/persist.ts)

  • 读写 storage:loadPersisted/savePersisted/clearPersisted
  • 版本控制:SCHEMA_VERSION 变更导致旧缓存自动失效
  • 自动写回:persistAutorun 用 autorun 订阅 selector,变化即写回 storage

应用入口(app.ts)

  • 全局错误处理:onError/onUnhandledRejection/onPageNotFound
  • 推广参数解析:从 launch options 解析 user_sn 并落本地缓存
  • 自动更新:检查新版本并提示用户重启
  • Store 引导:bootstrapStores 触发 hydrate/refresh/restore

后端 API 控制器基类(api/controller/BaseController.php)

  • API 控制器继承基础类,复用前台会员中心导航构建器
  • 在 API 端无模板相关字段,聚焦数据输出与业务逻辑

依赖关系分析

  • 小程序侧依赖链:
    • app.ts → stores/index.ts → stores/common.ts → services/bootstrap.ts → services/http.ts
    • stores/index.ts → stores/auth.ts → services/http.ts
    • stores/common.ts → stores/persist.ts
  • 后端侧:
    • BaseController.php 为 API 控制器基类,承载通用逻辑
graph LR
App["app.ts"] --> Index["stores/index.ts"]
Index --> Common["stores/common.ts"]
Index --> Auth["stores/auth.ts"]
Common --> Persist["stores/persist.ts"]
Common --> Boot["services/bootstrap.ts"]
Boot --> Http["services/http.ts"]
Auth --> Http
Http --> API["api/controller/BaseController.php"]

性能考虑

  • 首屏秒开:
    • 种子数据 + hydrate:commonStore/hydrate 从 storage 立即渲染
    • SWR 刷新:refresh 后台异步拉取最新数据并写回
  • 网络优化:
    • 去重:相同 GET 请求复用 Promise,减少并发压力
    • 内存缓存:cache/ttl/revalidate 控制缓存命中与刷新策略
  • 懒加载与预加载:
    • 按需刷新:authStore.restore 仅在必要时拉取认证信息
    • 模块降级:features.user=false 时跳过不必要请求
  • 建议:
    • 对列表页采用分页与懒加载
    • 对热点数据使用较长 ttl 与 revalidate 组合
    • 对写操作进行批量化与幂等设计

故障排查指南

  • 统一错误类型:ApiError 包含 code/statusCode/errors/request_id,便于定位
  • 调试增强:
    • 调试环境打印请求信息与错误详情
    • 服务端异常堆栈弹窗(errors.exception/file/line/trace)
    • 最近一次 request_id 可通过 getLastRequestId 获取
  • 常见错误分类:
    • NETWORK_ERROR:网络异常
    • HTTP_XXX:HTTP 状态码异常
    • UNAUTHORIZED:会话过期,触发登出
    • 业务错误:code !== 'OK',携带 errors 字段
  • 排查步骤:
    • 查看控制台日志与 vConsole(调试环境开启)
    • 通过 request_id 在后端日志中定位请求链路
    • 检查 store 状态与缓存是否一致(storage 中的 store:* 键)

结论

DouPHP 小程序数据同步机制以“统一 HTTP 层 + Store 状态管理 + 持久化”为核心,结合 SWR 刷新策略,实现了首屏秒开、数据一致性与良好的用户体验。通过拦截器、去重、内存缓存与版本化持久化,系统在弱网与离线场景下具备较强鲁棒性。建议在业务层进一步引入批量化与幂等设计,以提升整体性能与可靠性。

附录

  • API 信封规范(小程序侧解析):
    • code:字符串,'OK' 表示成功
    • message:字符串,提示信息
    • data:对象,业务数据
    • errors:对象,字段级错误
    • request_id:字符串,请求追踪号
  • 请求头:
    • Content-Type: application/x-www-form-urlencoded
    • Authorization: Bearer &lt;api_token>
  • Store 持久化键:
    • store:&lt;key>,包含 __v 与 data
  • 功能开关:
    • features.*:用于模块级降级(如 user/product/article)
添加日期:2026-10-05