spring
23 小时以前 6afb492942b17ebdb80f8e57af1b0df7ba5ee821
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
import config from "@/config";
import { getAllVersion } from "@/api/viewIndex";
import bus from "@/plugins/bus";
 
function compareVersion(v1, v2) {
  const s1 = String(v1 || "").replace(/[^\d.]/g, "").split(".").map((n) => Number(n) || 0);
  const s2 = String(v2 || "").replace(/[^\d.]/g, "").split(".").map((n) => Number(n) || 0);
  const len = Math.max(s1.length, s2.length);
  for (let i = 0; i < len; i += 1) {
    const n1 = s1[i] || 0;
    const n2 = s2[i] || 0;
    if (n1 > n2) return 1;
    if (n1 < n2) return -1;
  }
  return 0;
}
 
function formatFileSize(fileSize) {
  const size = Number(fileSize);
  if (!Number.isFinite(size) || size <= 0) return "";
  if (size >= 1024 * 1024 * 1024) return `${(size / (1024 * 1024 * 1024)).toFixed(2)}GB`;
  if (size >= 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(2)}MB`;
  if (size >= 1024) return `${(size / 1024).toFixed(2)}KB`;
  return `${size}B`;
}
 
async function getCurrentVersion(logPrefix) {
  let currentVersion = config?.appInfo?.version || "1.0.0";
  console.log(`${logPrefix} 开始获取当前版本,默认值:`, currentVersion);
  // #ifdef MP-WEIXIN
  try {
    const accountInfo = uni.getAccountInfoSync();
    if (accountInfo?.miniProgram?.version) {
      currentVersion = accountInfo.miniProgram.version;
      console.log(`${logPrefix} 当前环境=MP-WEIXIN,版本=`, currentVersion);
      return currentVersion;
    }
  } catch (e) {
    console.log(`${logPrefix} MP-WEIXIN 获取版本失败:`, e);
  }
  // #endif
  // #ifdef APP-PLUS
  try {
    // APP-PLUS 下,plus.runtime.version 不是业务版本号(经常是运行时/SDK版本),
    // 这里改用 getProperty 取系统层面的 app version。
    // @ts-ignore
    if (plus?.runtime?.getProperty) {
      // @ts-ignore
      const appid = plus.runtime.appid;
      const appInfo = await new Promise((resolve) => {
        // @ts-ignore
        plus.runtime.getProperty(appid, (info) => resolve(info || {}));
      });
      const v = appInfo?.version || appInfo?.versionName || appInfo?.appVersion || "";
      if (v) {
        currentVersion = String(v);
        console.log(`${logPrefix} 当前环境=APP-PLUS,版本=`, currentVersion);
        return currentVersion;
      }
      console.log(`${logPrefix} APP-PLUS 获取到的版本字段为空,将使用配置版本:`, currentVersion);
    }
  } catch (e) {
    console.log(`${logPrefix} APP-PLUS 获取版本失败:`, e);
  }
  // #endif
  console.log(`${logPrefix} 未读取到运行时版本,使用配置版本:`, currentVersion);
  return currentVersion;
}
 
function installPackage(tempFilePath, logPrefix) {
  console.log(`${logPrefix} 开始安装更新包,临时路径:`, tempFilePath);
  // #ifdef APP-PLUS
  // @ts-ignore
  plus.runtime.install(
    tempFilePath,
    {},
    () => {
      console.log(`${logPrefix} 安装成功,等待用户确认是否重启`);
      uni.showModal({
        title: "更新完成",
        content: "安装包下载成功,是否立即重启应用生效?",
        success: (res) => {
          if (res.confirm) {
            // @ts-ignore
            plus.runtime.restart();
          }
        },
      });
    },
    (err) => {
      console.log(`${logPrefix} 安装失败:`, err);
      uni.showToast({ title: err?.message || "安装更新包失败", icon: "none" });
    }
  );
  // #endif
}
 
function buildFullDownloadUrl(rawUrl) {
  const u = String(rawUrl || "").trim();
  if (!u) return "";
  // 已经是绝对地址,直接返回
  if (/^https?:\/\//i.test(u)) return u;
  const base = String(config?.fileUrl || config?.baseUrl || "").replace(/\/+$/, "");
  const path = u.startsWith("/") ? u : `/${u}`;
  return `${base}${path}`;
}
 
function emitDownloadProgress(payload, logPrefix) {
  try {
    bus.$emit("versionUpgrade:downloadProgress", payload);
  } catch (e) {
    console.log(`${logPrefix} downloadProgress 事件派发失败:`, e);
  }
}
 
function downloadVersionPackage(url, logPrefix, fileSize) {
  const fullUrl = buildFullDownloadUrl(url);
  console.log(`${logPrefix} 开始下载更新包:`, { rawUrl: url, fullUrl });
  if (!fullUrl) {
    uni.showToast({ title: "更新附件地址无效", icon: "none" });
    return;
  }
  const totalSize = Number(fileSize);
  const hasTotalSize = Number.isFinite(totalSize) && totalSize > 0;
  console.log(`${logPrefix} 下载大小信息:`, { fileSize, totalSize, hasTotalSize });
  emitDownloadProgress({ show: true, progress: 0, title: "更新包下载中..." }, logPrefix);
  const task = uni.downloadFile({
    url: fullUrl,
    success: (res) => {
      console.log(`${logPrefix} 下载结果:`, res);
      emitDownloadProgress({ show: false }, logPrefix);
      if (res.statusCode !== 200 || !res.tempFilePath) {
        console.log(`${logPrefix} 下载失败,状态码或临时路径异常`);
        uni.showToast({ title: "下载失败,请稍后重试", icon: "none" });
        return;
      }
      const lowerUrl = String(fullUrl || "").toLowerCase();
      const isInstallPkg = lowerUrl.endsWith(".wgt") || lowerUrl.endsWith(".wgtu") || lowerUrl.endsWith(".apk");
      console.log(`${logPrefix} 文件类型判断:`, { lowerUrl, isInstallPkg });
      if (isInstallPkg) {
        installPackage(res.tempFilePath, logPrefix);
        return;
      }
      uni.openDocument({
        filePath: res.tempFilePath,
        showMenu: true,
        fail: () => {
          console.log(`${logPrefix} 非安装包,openDocument 失败,提示用户手动查看`);
          uni.showToast({ title: "下载成功,请在文件管理中查看", icon: "none" });
        },
      });
    },
    fail: (err) => {
      console.log(`${logPrefix} 下载请求失败:`, err);
      emitDownloadProgress({ show: false }, logPrefix);
      uni.showToast({ title: "下载失败,请检查网络", icon: "none" });
    },
  });
 
  // 下载进度:部分端(APP/小程序)支持 onProgressUpdate
  try {
    if (task && typeof task.onProgressUpdate === "function") {
      task.onProgressUpdate((p) => {
        const downloadedSize = Number(p?.totalBytesWritten ?? p?.writtenBytes ?? p?.downloadedSize);
        const expectedSize = Number(p?.totalBytesExpectedToWrite ?? p?.totalBytesExpected ?? p?.totalSize);
        const denom = hasTotalSize ? totalSize : (Number.isFinite(expectedSize) && expectedSize > 0 ? expectedSize : 0);
 
        let progress = Number(p?.progress);
        if (!(Number.isFinite(progress) && progress >= 0 && progress <= 100) && denom > 0 && Number.isFinite(downloadedSize) && downloadedSize >= 0) {
          progress = (downloadedSize / denom) * 100;
        }
        if (!Number.isFinite(progress)) return;
 
        emitDownloadProgress({ show: true, progress, title: "更新包下载中..." }, logPrefix);
      });
    }
  } catch (e) {
    console.log(`${logPrefix} onProgressUpdate 绑定失败:`, e);
  }
}
 
async function checkAppVersionUpgrade(logPrefix, currentVersion) {
  try {
    console.log(`${logPrefix} 开始检查版本升级`);
    const params = {
      records: "",
      total: 1,
      size: 1,
      current: 1,
      orders: "",
      optimizeCountSql: true,
      searchCount: true,
      optimizeJoinOfCountSql: true,
      maxLimit: 1,
      countId: "",
      id: 1,
      name: "",
      version: "",
      createTime: "2026-04-09 10:10:33",
      updateTime: "2026-04-09 10:10:33",
      createUser: 1,
      updateUser: 1,
      tenantId: 1,
    };
    console.log(`${logPrefix} 查询参数:`, params);
    const res = await getAllVersion(params);
    console.log(`${logPrefix} 接口返回:`, res);
    const first = Array.isArray(res?.rows)
      ? res.rows[0]
      : Array.isArray(res?.data?.records)
        ? res.data.records[0]
        : Array.isArray(res?.data)
          ? res.data[0]
          : res?.data;
    if (!first) {
      console.log(`${logPrefix} 未获取到第一条版本数据,结束检查`);
      return;
    }
    console.log(`${logPrefix} 第一条版本数据:`, first);
    const latestVersion = String(first?.version || "");
    if (!latestVersion) {
      console.log(`${logPrefix} 第一条数据无版本号,结束检查`);
      return;
    }
    const compareResult = compareVersion(latestVersion, currentVersion);
    console.log(`${logPrefix} 版本比较:`, { currentVersion, latestVersion, compareResult });
    if (compareResult <= 0) {
      console.log(`${logPrefix} 当前已是最新版本,无需更新`);
      return;
    }
    const firstFile = Array.isArray(first?.commonFileList) ? first.commonFileList[0] : null;
    const downloadUrl = firstFile?.url || firstFile?.downloadUrl || "";
    const fileSizeText = formatFileSize(firstFile?.fileSize);
    console.log(`${logPrefix} 更新附件信息:`, {
      downloadUrl,
      fileSize: firstFile?.fileSize,
      fileSizeText,
      firstFile,
    });
    const desc = fileSizeText ? `,更新包大小约 ${fileSizeText}` : "";
    uni.showModal({
      title: "发现新版本",
      content: `当前版本 ${currentVersion},最新版本 ${latestVersion}${desc},是否立即下载?`,
      confirmText: "立即下载",
      success: (modalRes) => {
        console.log(`${logPrefix} 更新弹窗操作:`, modalRes);
        if (!modalRes.confirm) return;
        if (!downloadUrl) {
          console.log(`${logPrefix} 用户确认更新,但附件地址为空`);
          uni.showToast({ title: "未找到更新附件地址", icon: "none" });
          return;
        }
        downloadVersionPackage(downloadUrl, logPrefix, firstFile?.fileSize);
      },
    });
  } catch (e) {
    console.log(`${logPrefix} 版本检查失败:`, e);
  }
}
 
export function createVersionUpgradeChecker(options = {}) {
  const throttleMs = Number(options.throttleMs) > 0 ? Number(options.throttleMs) : 3000;
  const logPrefix = options.logPrefix || "[version]";
  let lastVersionCheckAt = 0;
 
  const triggerVersionCheck = async (from = "unknown") => {
    const now = Date.now();
    if (now - lastVersionCheckAt < throttleMs) {
      console.log(`${logPrefix} 跳过重复检查,来源=${from}`);
      return;
    }
    lastVersionCheckAt = now;
    console.log(`${logPrefix} 触发版本检查,来源=${from}`);
    const currentVersion = await getCurrentVersion(logPrefix);
    await checkAppVersionUpgrade(logPrefix, currentVersion);
  };
 
  return { triggerVersionCheck };
}