250 lines
6.4 KiB
JavaScript
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;
|
|
}
|