wz-uniapp/common/app-config.js

250 lines
6.4 KiB
JavaScript

import { ENV, STORAGE_KEYS } from './env';
import { version as packageVersion } from '@/package.json';
const CACHE_KEY = STORAGE_KEYS.APP_CONFIG || 'app_config_cache';
const DEFAULT_TTL_SECONDS = 3600;
let inFlight = null;
let lastConfig = null;
function buildUrl(base, path) {
if (!base) return path;
const normalizedBase = String(base).replace(/\/$/, '');
const normalizedPath = String(path || '').replace(/^\//, '');
return `${normalizedBase}/${normalizedPath}`;
}
function parseVersion(value) {
if (!value) return [0];
return String(value)
.replace(/^v/i, '')
.split('.')
.map((item) => {
const num = Number.parseInt(item, 10);
return Number.isNaN(num) ? 0 : num;
});
}
function compareVersion(a, b) {
const left = parseVersion(a);
const right = parseVersion(b);
const maxLen = Math.max(left.length, right.length);
for (let i = 0; i < maxLen; i += 1) {
const l = left[i] || 0;
const r = right[i] || 0;
if (l > r) return 1;
if (l < r) return -1;
}
return 0;
}
function getAppVersion() {
let current = packageVersion || '0.0.0';
// #ifdef MP-WEIXIN
try {
const account = uni.getAccountInfoSync && uni.getAccountInfoSync();
const mpVersion = account && account.miniProgram && account.miniProgram.version;
if (mpVersion) current = mpVersion;
} catch (e) {
// ignore
}
// #endif
return current || '0.0.0';
}
function getCache() {
try {
return uni.getStorageSync(CACHE_KEY);
} catch (e) {
return null;
}
}
function setCache(config) {
const ttlSeconds = Number(config && config.ttlSeconds) || DEFAULT_TTL_SECONDS;
const now = Date.now();
const cache = {
data: config.data,
cachedAt: now,
expireAt: now + ttlSeconds * 1000
};
uni.setStorageSync(CACHE_KEY, cache);
return cache;
}
function isCacheValid(cache) {
if (!cache || !cache.expireAt) return false;
return Date.now() < cache.expireAt;
}
function applyConfig(config) {
if (!config) return;
if (config.api && config.api.baseUrl) {
ENV.API_BASE_URL = config.api.baseUrl;
}
if (config.webview && config.webview.baseUrl) {
ENV.WEBVIEW_BASE_URL = config.webview.baseUrl;
}
lastConfig = config;
}
function buildFailMessage(message) {
return message || '配置获取失败,请退出小程序重新打开。';
}
function showExitModal(message) {
uni.showModal({
title: '提示',
content: message,
showCancel: false,
confirmText: '退出',
success: () => {
// #ifdef MP-WEIXIN
if (uni.exitMiniProgram) {
uni.exitMiniProgram();
}
// #endif
// #ifdef APP-PLUS
if (typeof plus !== 'undefined' && plus.runtime && plus.runtime.quit) {
plus.runtime.quit();
}
// #endif
}
});
}
function validateConfig(config) {
if (!config) {
return { ok: false, message: buildFailMessage('配置无效,请退出小程序重新打开。') };
}
if (config.disabled) {
return {
ok: false,
message: config.disableReason || '服务暂不可用,请退出小程序重新打开。'
};
}
const currentVersion = getAppVersion();
const app = config.app || {};
const api = config.api || {};
const webview = config.webview || {};
if (app.minVersion && compareVersion(currentVersion, app.minVersion) < 0) {
return {
ok: false,
message: '当前版本过低,请退出小程序重新打开。'
};
}
if (api.version && webview.version && api.version !== webview.version) {
return {
ok: false,
message: '当前版本不一致,请退出小程序并重新打开以获取最新配置。'
};
}
if (api.minClientVersion && compareVersion(currentVersion, api.minClientVersion) < 0) {
return {
ok: false,
message: '当前版本过低,请退出小程序重新打开。'
};
}
if (webview.minClientVersion && compareVersion(currentVersion, webview.minClientVersion) < 0) {
return {
ok: false,
message: '当前版本过低,请退出小程序重新打开。'
};
}
return { ok: true, message: '' };
}
function requestConfig() {
const url = buildUrl(ENV.API_BASE_URL, '/api/open/app/config');
return new Promise((resolve, reject) => {
uni.request({
url,
method: 'GET',
timeout: 8000,
success: (res) => {
if (res.statusCode >= 200 && res.statusCode < 300 && res.data) {
resolve(res.data);
return;
}
reject({ message: '配置获取失败', response: res });
},
fail: (err) => reject(err)
});
});
}
export async function initAppConfig(options = {}) {
if (inFlight) return inFlight;
const { force = false } = options;
inFlight = (async () => {
const cache = getCache();
if (!force && isCacheValid(cache)) {
const validation = validateConfig(cache.data);
if (!validation.ok) {
showExitModal(validation.message);
throw new Error(validation.message);
}
applyConfig(cache.data);
return cache.data;
}
try {
const config = await requestConfig();
const validation = validateConfig(config);
if (!validation.ok) {
showExitModal(validation.message);
throw new Error(validation.message);
}
setCache(config);
applyConfig(config);
return config;
} catch (error) {
if (cache && isCacheValid(cache)) {
const validation = validateConfig(cache.data);
if (!validation.ok) {
showExitModal(validation.message);
throw new Error(validation.message);
}
applyConfig(cache.data);
return cache.data;
}
showExitModal(buildFailMessage());
throw error;
}
})();
try {
return await inFlight;
} finally {
inFlight = null;
}
}
export async function ensureAppConfig() {
const cache = getCache();
console.log('【业务代码定位】', cache, new Error().stack);
if (cache && isCacheValid(cache)) {
const validation = validateConfig(cache.data);
if (!validation.ok) {
showExitModal(validation.message);
throw new Error(validation.message);
}
applyConfig(cache.data);
return cache.data;
}
return initAppConfig({ force: true });
}
export function getAppConfig() {
return lastConfig || (getCache() ? getCache().data : null);
}
export function getWebviewBaseUrl() {
const config = getAppConfig();
let webviewBaseUrl = (config && config.webview && config.webview.baseUrl) || ENV.WEBVIEW_BASE_URL || '';
console.log(webviewBaseUrl);
return webviewBaseUrl;
}