← 返回文档导航
一句话: 印尼当地网络不稳定时,BeeX 不能把“打开 App”建立在实时网络成功上。
App 先加载本地 H5 离线包,H5 先展示可信缓存,核心接口在后台刷新;但涉及钱、库存、购买入口的动作必须回到云端做实时校验。
这份文档专门讲清楚每一层缓存的职责、过期策略、失效方式和测试方法。
当前状态
🔵 专项建设中
离线包核心已通;首页商品列表有 72 小时缓存;热门品牌不过期缓存;商品详情和库存校验还需要进一步收口。
核心原则
缓存只解决“快”和“弱网可用”;不能用缓存决定“能不能付款、能不能返现、有没有库存”。这些必须实时验。
本页给谁看
App、H5、服务端、测试和运营。排查“首页慢、白屏、旧商品又出现、无库存商品展示”都从这里开始。
一、为什么要做弱网专项
BeeX 的主要用户在印尼和马来,实际网络环境会出现高延迟、DNS 慢、切后台重连、运营商不稳定、WhatsApp 跳转回来后网络瞬断等情况。如果每次打开首页都等远端 H5 和所有接口返回,用户会看到长时间白屏或空列表。
所以我们采用“本地先可用,云端再校准”的策略:
- App 启动优先加载本地 H5 离线包,不等网络。
- 首页先读 H5 本地接口缓存,页面先有内容。
- 后台刷新接口,刷新成功后覆盖缓存并更新界面。
- 商品详情、转链、提现、支付、登录等强一致动作必须实时请求云端。
- 只要平台字段明确商品不可买,后端和前端都要过滤;平台字段缺失时,进入详情或购买前再做二次校验。
铁律: 缓存可以让页面先显示,但不能让用户基于旧数据完成资金动作。返现金额、商品价格、优惠券核销、提现金额、支付密码、最终购买链接都必须以云端实时返回为准。
二、弱网下 App 启动主流程
flowchart TD
A["用户打开 BeeX App"]:::app
B["Native 查本地 active H5 包"]:::app
C{"active 包存在且可读?"}:::decision
D["加载内置 H5 包"]:::app
E["加载 active/index.html"]:::app
F["H5 首屏渲染"]:::h5
G["H5 读接口缓存
首页商品/品牌/活动"]:::cache
H["Native 延迟检查 H5 包更新"]:::native
I["H5 后台刷新首页接口"]:::h5
J{"刷新成功?"}:::decision
K["覆盖缓存并刷新 UI"]:::ok
L["保留缓存
显示弱网提示"]:::bad
M{"发现新 H5 包?"}:::decision
N["下载 ready 包
校验 sha256"]:::native
O["提示用户刷新或下次启动激活"]:::native
A-->B-->C
C--"否"-->D-->F
C--"是"-->E-->F
F-->G
F-->H
G-->I-->J
J--"是"-->K
J--"否"-->L
H-->M
M--"否"-->F
M--"是"-->N-->O
classDef app fill:#f0fdf4,stroke:#15803d,color:#166534;
classDef native fill:#eff6ff,stroke:#1d4ed8,color:#1e40af;
classDef h5 fill:#f5f3ff,stroke:#8b5cf6,color:#5b21b6;
classDef cache fill:#fff7ed,stroke:#f97316,color:#9a3412;
classDef decision fill:#fef3c7,stroke:#e07c00,color:#7a4f24,stroke-width:2px;
classDef ok fill:#f0fdf4,stroke:#15803d,color:#15803d,stroke-width:2px;
classDef bad fill:#fff5f5,stroke:#dc2626,color:#991b1b,stroke-width:2px;
三、缓存分层总表
| 层级 | 缓存什么 | 保存位置 | 过期/刷新 | 能否直接决定业务动作 | 代码位置 |
| App H5 离线包 | 完整 H5 静态资源: HTML/CSS/JS/图片 | App 沙盒目录 + 安装包内置资源 | 启动后后台查新;下载完成后用户刷新或下次启动激活 | 不能。只决定页面代码版本。 | beex-app/lib/src/services/h5_package_manager.dart |
| 首页商品列表 | ALL / 高返佣 / TikTok / Shopee 商品摘要 | H5 localStorage | 72 小时内先命中缓存;进入首页后台刷新;缓存 schema 变更会整体失效 | 不能。点击详情/购买仍需实时校验。 | beex-app-h5/app/composables/useHighCommissionProducts.ts |
| 热门品牌 | 品牌图标、关键词、品牌名、返现文案,以及品牌配置更新时间 | H5 localStorage | 品牌入口不过期;后台刷新成功后覆盖。固定商品发生增删改时服务端同步更新品牌时间戳 | 品牌入口不能直接决定购买。进入品牌页后优先读取运营配置的固定商品快照,点击详情/购买仍需实时校验;未配置固定商品才回退关键词或店铺搜索。 | beex-app-h5/app/utils/popularBrandsCache.ts beex-app-h5/app/pages/brands/result.vue |
| 商品详情摘要 | 列表点击时带过去的商品卡片数据 | sessionStorage / 路由参数 | 只用于秒开;详情页随后调 /products/resolve 刷新 | 不能。只做首屏占位。 | beex-app-h5/app/pages/product-detail.vue |
| 推荐商品 | 店铺推荐、品类推荐 | H5 localStorage | 短期缓存 + 后台刷新;空结果要有 fallback | 不能。推荐列表只负责导流。 | beex-app-h5/app/pages/product-detail.vue |
| 用户资料/收益 | 我的页、收益页部分数据 | H5 状态缓存 | 进入页面后台刷新;失败时保留旧数据并提示 | 不能。提现前必须实时查钱包。 | beex-app-h5/app/composables/useCachedApiState.ts |
| 平台高佣商品池 | TikTok Partner Campaign 商品 | BeeX 数据库 | 服务端定时同步 TikTok;商品状态和库存仍可能变化 | 不能。购买前仍需平台生成链接成功。 | TiktokPartnerCampaignProductSyncService.java |
四、首页商品缓存逻辑
首页商品缓存的目标是:弱网下打开首页不要空白,用户切换 ALL / TikTok / Shopee 时不要反复等待同一批数据。
4.1 当前策略
| 项目 | 当前值 | 说明 |
| 缓存时间 | 72 小时 | 72 小时内先显示缓存;后台刷新成功后更新缓存。 |
| 缓存 key | high-commission-products:{schema}:{country}:{keyword}:{platform}:{source}:{pageSize} | schema 用来一次性废掉旧缓存。当前已加入 v2-stock-filter,避免旧的无库存脏数据继续展示。 |
| 切 Tab | 先读对应 Tab 缓存 | 切换 ALL / TikTok / Shopee 不重复拉取同一份 72 小时内的新缓存。 |
| 下拉刷新 | 强制当前 Tab 请求云端 | 当前 Tab 刷新;其他 Tab 可后台预取,但不会阻塞当前页面。 |
| 加载更多 | 带 cursor 请求云端 | 追加到当前缓存快照,并做去重。 |
| 前端兜底过滤 | 隐藏无价格/无返现/无购买链接商品 | 不能替代后端过滤,只防止明显脏缓存展示。 |
flowchart TD
A["进入首页或切换商品 Tab"]:::h5
B["计算缓存 key
国家+关键词+平台+来源+页大小+schema"]:::cache
C{"本地缓存存在?"}:::decision
D["先展示缓存"]:::cache
E["显示骨架屏"]:::h5
F{"缓存 fresh 且非强刷?"}:::decision
G["结束:不请求云端"]:::ok
H["请求 /api/v1/products/high-commission"]:::be
I{"请求成功?"}:::decision
J["过滤明显异常商品
写入新缓存+刷新 UI"]:::ok
K["保留旧缓存
展示弱网/失败提示"]:::bad
A-->B-->C
C--"是"-->D-->F
C--"否"-->E-->H
F--"是"-->G
F--"否/强刷"-->H
H-->I
I--"是"-->J
I--"否"-->K
classDef h5 fill:#f5f3ff,stroke:#8b5cf6,color:#5b21b6;
classDef cache fill:#fff7ed,stroke:#f97316,color:#9a3412;
classDef be fill:#ecfeff,stroke:#0891b2,color:#155e75;
classDef decision fill:#fef3c7,stroke:#e07c00,color:#7a4f24,stroke-width:2px;
classDef ok fill:#f0fdf4,stroke:#15803d,color:#15803d,stroke-width:2px;
classDef bad fill:#fff5f5,stroke:#dc2626,color:#991b1b,stroke-width:2px;
4.2 为什么过滤了无库存商品还会回来
| 原因 | 表现 | 处理方式 |
| 旧 H5 本地缓存 | 服务端已过滤,但用户设备上仍展示旧商品 | 缓存 key 增加 schema 版本;当前为 v2-stock-filter。 |
| 平台字段缺失 | TikTok Partner Campaign 缓存商品没有 stock/has_inventory/soldCount | 列表只能过滤“已知不可买”;进入详情/生成链接前必须实时校验。 |
| 平台实时变更 | 同步时有库存,用户点击时已经售罄 | 购买前以平台生成链接和详情解析结果为准,失败时提示商品不可购买。 |
| 缓存时间过长 | 72 小时内商品状态变化但用户仍看到旧摘要 | 关键字段变化时升 schema;运营可临时下发禁用/黑名单。 |
库存规则: 只要平台字段明确表示 stock=0、has_inventory=false、soldOut=true、outOfStock=true、canBuy=false,列表必须过滤。字段不存在时不能假装“有库存”,只能标记为库存未知,购买前实时校验。
五、哪些数据可以缓存,哪些不能缓存
| 数据/动作 | 是否可缓存展示 | 是否可用缓存提交 | 规则 |
| 首页 Banner / 品牌入口 | 可以 | 不涉及提交 | 允许长期缓存,后台刷新覆盖。 |
| 商品列表摘要 | 可以 | 不可以 | 列表可先展示缓存,详情/转链前必须刷新。 |
| 商品详情 | 可以先展示摘要 | 不可以 | 进入详情后调用 /api/v1/products/resolve 更新价格、返现、来源、库存线索。 |
| 最终购买链接 | 不建议缓存 | 不可以 | 每次点击购买都调用 /api/v1/affiliate-links/generate,拿最新 tracking 和平台链接。 |
| 商品价格 | 可以缓存展示 | 不可以 | 提交购买时云端重新计算,邀请码当场校验。 |
| 优惠券可用状态 | 可以缓存展示 | 不可以 | 使用券时云端按订单/活动/有效期实时核销。 |
| 钱包余额/可提现金额 | 可以缓存展示 | 不可以 | 提现提交前实时查余额、KYC、支付密码和风控。 |
| 登录态 | Native 安全存储 | 可以用于请求鉴权 | Token 过期时刷新;刷新失败引导重新登录。 |
六、弱网体验规则
| 场景 | 用户看到什么 | 系统动作 | 不能做什么 |
| 首次安装,无网络 | 加载安装包内置 H5,首页可打开;接口区显示弱网/暂无数据 | 等待网络恢复后后台刷新 | 不能白屏;不能卡死在启动页。 |
| 已有缓存,网络慢 | 先看到上次商品列表和品牌入口 | 接口在后台刷新,成功后替换 | 不能每次切 Tab 都重新等待。 |
| 缓存商品已售罄 | 详情页应提示商品不可购买或隐藏购买按钮 | 详情/转链前实时校验平台状态 | 不能让用户继续承诺返现。 |
| H5 有新包 | 页面先正常用;下载完成后提示刷新 | ready 包校验通过后等待激活 | 不能启动时先等更新包下载。 |
| H5 新包白屏 | 用户自动回到上一版 | Native 激活失败回滚 previous 并上报 | 不能把用户永久锁在坏包。 |
| WhatsApp 回跳后网络断 | 保留当前页,显示登录/换链处理中 | 继续轮询或给重试按钮 | 不能丢失用户刚刚复制/回跳的上下文。 |
七、监控与日志
| 监控点 | 需要记录 | 用途 |
| 启动耗时 | Native 开始启动、本地 H5 加载完成、首个 H5 API 请求时间 | 判断白屏是离线包、WebView 还是接口慢。 |
| H5 包状态 | currentH5Version、active/ready/previous、下载结果、sha256 校验、回滚原因 | 排查“为什么还是旧页面/为什么白屏”。 |
| 接口缓存命中 | cacheKey、updatedAt、trigger、platform、cursor、是否 force | 解释“为什么没有重新请求/为什么旧商品出现”。 |
| 商品过滤 | platform、productId、source、过滤原因: NO_PRICE/NO_CASHBACK/OUT_OF_STOCK/UNAVAILABLE | 排查无库存商品和 Rp0 商品。 |
| 用户反馈日志包 | 设备、App 版本、H5 版本、接口失败、截图前后的本地日志 | 弱网问题必须能由非开发人员提交证据。 |
八、测试用例清单
| 编号 | 用例 | 验收标准 | 状态 |
| NW-01 | 断网启动 App | 不能白屏;能加载内置 H5;接口区有弱网提示。 | 🟡 待补自动化 |
| NW-02 | 弱网 3G 启动 | 本地 H5 先打开;更新检查不阻塞首屏。 | 🔵 手工验证中 |
| NW-03 | 首页有缓存后切换 Tab | 72 小时内命中缓存;不重复拉同一 Tab;后台刷新成功后 UI 替换。 | ✅ 已接入 |
| NW-04 | 服务端过滤规则调整后 | 升缓存 schema 后旧商品不再展示。 | ✅ 已接入 v2-stock-filter |
| NW-05 | 商品无价格/无返现/无购买链接 | 前端兜底不展示。 | ✅ 已接入 |
| NW-06 | Partner Campaign 库存未知商品 | 列表可展示但详情/购买前实时校验;失败时提示不可买。 | 🟡 待完善 |
| NW-07 | 下载到坏 H5 包 | sha256 不一致直接丢弃;激活失败自动回滚。 | 🔵 需回归 |
| NW-08 | 用户提交日志反馈 | zip 上传私有 OSS,AI Worker 完成脱敏分析、飞书通知并把最终状态写回数据库。 | ✅ 测试/生产已验收 |
九、后续专项任务
| 优先级 | 任务 | 说明 | 负责人 |
| P0 | 商品详情/购买前实时校验 | Partner Campaign 商品进入详情后,用平台最新接口补价格、返现、库存线索;生成购买链接失败时给用户明确提示。 | 服务端 + H5 |
| P0 | 库存未知字段透出 | 后端返回 availabilityStatus: AVAILABLE / UNAVAILABLE / UNKNOWN,前端展示“预计”并隐藏不可靠购买入口。 | 服务端 + H5 |
| P1 | 商品/店铺黑名单 | 运营或用户反馈不可购买商品后,按平台商品 ID/店铺 ID 临时禁用。 | 后台 + 服务端 |
| P1 | 缓存可观测面板 | 调试浮窗里展示 H5 版本、缓存 key、缓存时间、最近接口结果,支持一键清首页缓存。 | App + H5 |
| P1 | 弱网自动化用例 | 用 Playwright/移动端脚本模拟离线、慢网、接口 500、缓存命中。 | QA + 前端 |
| P2 | 缓存策略后台化 | 把首页商品 TTL、品牌 TTL、详情推荐 TTL 放到 runtime-config,用于线上动态调参。 | 服务端 + H5 |
十、本专项可配参数
| 参数 | 当前值 | 配置位置 | 说明 | 状态 |
| 首页商品缓存 TTL | 72 小时 | useHighCommissionProducts.ts | 后续建议进入 runtime-config。 | 🔵 代码固定 |
| 首页商品缓存 schema | v2-stock-filter | useHighCommissionProducts.ts | 用于一次性废掉旧缓存。 | ✅ 已接入 |
| 热门品牌缓存 TTL | 不过期 | popularBrandsCache.ts | 接口刷新成功后覆盖。 | ✅ 已接入 |
| H5 包更新检查延迟 | 页面加载后延迟检查 | h5_package_manager.dart | 不能阻塞首屏。 | ✅ 已接入 |
| H5 包灰度比例 | 由版本记录决定 | h5_package_versions.rollout_percent | 控制新包覆盖范围。 | ✅ 已接入 |
| H5 包强更 | 由版本记录决定 | h5_package_versions.force_update | 强更时提示用户刷新。 | ✅ 已接入 |
| 商品黑名单 | 〔TODO〕 | 待建后台配置 | 用于处理平台不返回库存字段但实际不可买的商品。 | 🟡 待做 |