Przeglądaj źródła

docs(spec): 小程序新版本强制更新功能设计 T40(uni.getUpdateManager + applyUpdate 强制重启)

Agnes Agent 1 tydzień temu
rodzic
commit
b9f82aad77

+ 122 - 0
docs/superpowers/specs/2026-11-03-miniapp-force-update.md

@@ -0,0 +1,122 @@
+# 小程序新版本强制更新功能设计
+
+- 日期:2026-11-03
+- 状态:设计定稿(待派发)
+- 需求方:廖总
+- 来源:「小程序增加新版本强制更新功能,在有新版本发布时,如果用户本地还是老版本,提醒用户强制升级」
+
+## 0. 现状核查(只读)
+
+- `cfc-frontend` 全仓 `grep getUpdateManager` **零命中** → 当前**无任何版本更新检测逻辑**。
+- `manifest.json` 有 `versionName: 1.0.0` / `versionCode: 100`,但代码未读取利用(前端不依赖此值做判断)。
+- `App.vue` 的 `onLaunch` 已承载:splash 跳转、登录态恢复、分享参数捕获、跨小程序 SSO。更新检测应尽早接入,且 `applyUpdate()` 会重启整个小程序,须独立于路由逻辑。
+- 微信小程序代码包更新**仅微信官方机制**可触发(`wx.getUpdateManager` / `uni.getUpdateManager`),前端无法自行感知"线上已发新版"。
+
+## 1. 方案选型
+
+### 推荐:纯前端 · 官方 UpdateManager(本次实现)
+
+微信官方机制 `uni.getUpdateManager()`:
+
+| API | 触发时机 | 用途 |
+|-----|---------|------|
+| `manager.onCheckForUpdate(cb)` | 启动后微信自动检查 | 回调 `{ hasUpdate }`(仅日志用) |
+| `manager.onUpdateReady(cb)` | **新版本代码包下载完成** | 弹窗引导 → `applyUpdate()` |
+| `manager.onUpdateFailed(cb)` | 新版本下载失败(弱网/被拒) | 提示删除重进 |
+| `manager.applyUpdate()` | 主动调用 | **强制重启小程序并应用新包**(官方"强制更新"手段) |
+
+> 微信在冷启动时自动检查代码包更新并**静默下载**,下载完成后才触发 `onUpdateReady`。`applyUpdate()` 会立即重启整机小程序用上新包——这是微信生态下"强制更新"的标准且唯一手段。
+
+### 不采用:后端版本门禁(列为后续可选增强,本次不做)
+
+"硬阻断老版本"(接口级拒绝旧版)需后端配合:接口返回特定 code(如 4999 版本过旧)→ `api.js` 拦截 → 弹窗引导升级。适用于接口重大不兼容。本次需求用前端标准方案即可覆盖,硬阻断待主理人拍板后单独立项。
+
+## 2. 实现设计
+
+### 2.1 新增 `cfc-frontend/utils/checkAppUpdate.js`
+
+```js
+// 小程序新版本强制更新(微信官方 UpdateManager 机制)
+// 文档:docs/superpowers/specs/2026-11-03-miniapp-force-update.md
+export function checkAppUpdate() {
+  // 能力探测:部分基础库/平台不支持
+  if (typeof uni.getUpdateManager !== 'function') {
+    console.warn('[update] 当前环境不支持 getUpdateManager,跳过更新检测')
+    return
+  }
+
+  const manager = uni.getUpdateManager()
+
+  manager.onCheckForUpdate(function (res) {
+    // res.hasUpdate:仅用于日志,无新版本时不需要任何交互
+    console.log('[update] 检查更新:', res.hasUpdate ? '有新版本' : '已是最新')
+  })
+
+  manager.onUpdateReady(function () {
+    // 新版本下载完毕 → 强制弹窗(无取消按钮)→ 用户点确定即重启应用新包
+    uni.showModal({
+      title: '发现新版本',
+      content: '小程序已发布新版本,点击「立即更新」后即可使用最新功能。',
+      showCancel: false,        // 强制:用户必须更新才能继续
+      confirmText: '立即更新',
+      success: function () {
+        manager.applyUpdate()   // 强制重启小程序并应用新版本代码包
+      }
+    })
+  })
+
+  manager.onUpdateFailed(function () {
+    // 下载失败(弱网/存储不足/被微信拒)
+    uni.showModal({
+      title: '更新失败',
+      content: '新版本下载失败,请检查网络后删除小程序重新进入。',
+      showCancel: false,
+      confirmText: '我知道了'
+    })
+  })
+}
+```
+
+### 2.2 `App.vue` 接入
+
+在 `onLaunch` 开头(任何路由跳转/页面逻辑之前)调用一次:
+
+```js
+import { checkAppUpdate } from '@/utils/checkAppUpdate.js'
+
+export default {
+  onLaunch: function (options) {
+    // 尽早检测新版本:applyUpdate 会重启整机,须在路由逻辑前
+    checkAppUpdate()
+    // ... 既有 syncFontScale / restoreSession / splash 逻辑保持不变
+  }
+}
+```
+
+## 3. 关键约束与边界(开发必读)
+
+1. **环境限制(重要)**:`onUpdateReady` **仅体验版 / 正式版**触发;开发版(`envVersion=develop`)因每次编译即最新,**不会**触发——属微信正常行为,非缺陷。开发自测时控制台会打印 `[update] 已是最新 / 当前环境不支持`,属预期。
+2. **"强制"语义**:通过 `showCancel: false` 实现**软强制**——用户不点「立即更新」就不能继续用当前页面,但已运行的老版本进程在重启前不会被系统杀掉。真正的"硬阻断"(完全禁止老版本)需后端门禁(见 §1),本次不做。
+3. **调用幂等**:`onLaunch` 整个生命周期仅一次,无需防重。
+4. **不依赖 manifest 版本号**:判断由微信后台代码包版本自动完成,`versionName/versionCode` 仅作发布运维规范(建议每次发布递增),前端逻辑不读取。
+5. **失败静默不阻塞**:`getUpdateManager` 不可用 / 下载失败时只提示,不影响主流程。
+6. **不改既有业务逻辑**:仅在 `onLaunch` 增加一行调用 + 新增一个 utils 文件,不动 splash / 登录 / 路由。
+
+## 4. 测试与验证
+
+- **开发版不可测**(微信限制):必须在**体验版或正式版**验证 `onUpdateReady` 弹窗与 `applyUpdate` 重启。
+- 验证路径:提交新版本代码包到微信后台 → 旧版冷启动 → 后台静默下载 → 弹出「发现新版本」→ 点「立即更新」→ 小程序重启并应用新包。
+- 弱网模拟:断网/弱网下 `onUpdateFailed` 应弹出"更新失败"提示。
+- 可在 `checkAppUpdate` 首行加 `console.warn('[update] develop 跳过')` 便于排查(production 下无影响)。
+
+## 5. 任务书
+
+- 编号 **T40**(P1,module=cfc-frontend)
+- spec = 本文件
+- 改动文件:`cfc-frontend/utils/checkAppUpdate.js`(新增)、`cfc-frontend/App.vue`(onLaunch 接入一行)
+- 验收:`npx vue-cli-service lint`(或 uni 编译)通过;体验版部署后旧包冷启动能弹出强制更新弹窗并 `applyUpdate` 生效。
+
+## 6. 待主理人拍板(非阻断)
+
+- [ ] 是否需要**硬阻断**(后端版本门禁,接口级拒绝老版本)?当前仅实现软强制(前端弹窗引导)。
+- [ ] 发布版本号递增规范是否纳入发布 checklist(manifest versionCode 每次 +1)。