Files
2026-10-19 17:49:19 +08:00

226 lines
7.7 KiB
TypeScript

import { fetchPageConfig } from './services/site-api';
import { normalizePreviewImages } from './config/assets';
import { defaultPrintConfig } from './config/config';
import { getUser } from './utils/auth';
import { apiContextKey, getApiConfig, isCurrentApiConfig } from './config/api';
import { generateUUID, getAppUUID, setAppUUID } from './utils/uuid';
let _refreshGeneration = 0;
let _loadGeneration = 0;
const pageConfigStorageKey = () => `doodle_page_config_v2:${apiContextKey()}`;
// Pending resolvers for getPageConfig() callers waiting on config
let _configResolvers: Array<(config: PageConfigData | null) => void> = [];
// app.ts
App<IAppOption>({
globalData: {
env: 'release',
uuid: '',
user: null,
printConfig: defaultPrintConfig,
pageConfig: undefined,
pageConfigReady: false,
},
onLaunch() {
try {
wx.removeStorageSync('user_info');
wx.removeStorageSync('user_info_expire');
} catch {
/* 旧云登录缓存清理失败不影响新凭证命名空间。 */
}
const accountInfo = wx.getAccountInfoSync();
const env = accountInfo.miniProgram.envVersion || 'release';
this.globalData.env = env;
let uuid = getAppUUID();
if (!uuid) {
uuid = generateUUID();
setAppUUID(uuid);
console.log('生成并存储 UUID', uuid);
}
this.globalData.uuid = uuid;
if (env !== 'release') {
const printConfig =
wx.getStorageSync('printConfig') || defaultPrintConfig;
this.globalData.printConfig = printConfig;
}
// 启动与回到前台共用登录 Promise,不阻塞公开页面渲染。
this.warmAuthentication();
void this.loadPageConfig();
},
onShow() {
this.warmAuthentication();
if (this.globalData.pageConfigReady) void this.refreshPageConfig();
},
warmAuthentication() {
void getUser().catch(() => {
console.warn('静默登录暂未完成,公开浏览仍可使用');
});
},
onHide() {},
/** 切环境不重启 App:作废旧加载、释放等待者并重置共享业务状态。 */
resetApiState() {
_refreshGeneration++;
_loadGeneration++;
this.globalData.user = null;
this.globalData.pageConfig = undefined;
this.globalData.pageConfigReady = false;
this.globalData.pendingCategoryTabId = undefined;
for (const resolve of _configResolvers) resolve(null);
_configResolvers = [];
try {
wx.removeStorageSync('favorites_active_tab');
} catch {
/* 临时页签偏好清理失败不影响环境与用户隔离。 */
}
},
getPrintConfig(): PrintConfig {
return this.globalData.printConfig || defaultPrintConfig;
},
setPrintConfig(config: PrintConfig) {
this.globalData.printConfig = config;
wx.setStorageSync('printConfig', config);
},
async loadPageConfig(forceRefresh = false) {
let context: ReturnType<typeof getApiConfig>;
try {
context = getApiConfig();
} catch {
// 地址被移除时结束首页等待,开发者仍可进入 Debug 改选可用环境。
this.globalData.pageConfig = undefined;
this.globalData.pageConfigReady = true;
for (const resolve of _configResolvers) resolve(null);
_configResolvers = [];
return false;
}
const generation = ++_loadGeneration;
const current = () =>
generation === _loadGeneration && isCurrentApiConfig(context);
const storageKey = pageConfigStorageKey();
let config: PageConfigData | null = null;
// 仅开发版跳过旧线上预拉取,保证环境切换后的内容来自目标服务。
// 体验/正式版继续沿用原预拉取模式、地址与旧云端任务。
if (context.version !== 'develop')
try {
const res = await new Promise<{ fetchedData: string }>(
(resolve, reject) => {
wx.getBackgroundFetchData({
fetchType: 'pre',
success: resolve as any,
fail: reject,
});
},
);
if (res.fetchedData) {
const parsed = JSON.parse(res.fetchedData);
config = parsed?.data || parsed;
}
} catch {
// 预拉取失败,继续降级
}
if (!current()) return false;
// L2: storage 缓存
if (!config && !forceRefresh) {
try {
const cached =
wx.getStorageSync(storageKey) ||
(context.version !== 'develop'
? wx.getStorageSync('pageConfig')
: null);
if (cached && typeof cached === 'object') {
config = cached;
}
} catch {
// storage 读取失败,继续降级
}
}
// L3: 新后端动态兜底;微信预拉取机制和地址保持不变。
const hadCachedConfig = !!config;
if (!config) {
try {
config = await fetchPageConfig();
} catch {
/* 新后端不可用时继续展示空状态,本地生成仍可使用。 */
}
}
if (!current()) return false;
// 三种数据入口都先转换预览图,旧缓存会随成功加载写回新地址。
config = normalizePreviewImages(config);
// 写入 globalData
if (config) {
this.globalData.pageConfig = config;
// 成功获取后写入 storage 缓存
try {
wx.setStorageSync(storageKey, config);
} catch {
// storage 写入失败不影响主流程
}
}
// 标记就绪,通知等待中的页面
this.globalData.pageConfigReady = true;
for (const resolve of _configResolvers) {
resolve(config);
}
_configResolvers = [];
// 预拉取/缓存先渲染,再用新后端刷新,避免旧静态配置长期覆盖管理修改。
if (hadCachedConfig) void this.refreshPageConfig();
return !!config;
},
async refreshPageConfig() {
const context = getApiConfig();
const storageKey = pageConfigStorageKey();
const generation = ++_refreshGeneration;
try {
const config = normalizePreviewImages(await fetchPageConfig());
if (
generation !== _refreshGeneration ||
!isCurrentApiConfig(context)
)
return;
this.globalData.pageConfig = config;
try {
wx.setStorageSync(storageKey, config);
} catch {
/* 缓存失败不影响当前数据。 */
}
for (const page of getCurrentPages()) {
if (
page.route === 'pages/home/home' &&
typeof page.initData === 'function'
)
void page.initData(config);
}
} catch {
/* 保留已显示的数据,网络错误不清空预拉取首屏。 */
}
},
getPageConfig(): Promise<PageConfigData | null> {
if (this.globalData.pageConfigReady) {
return Promise.resolve(this.globalData.pageConfig || null);
}
return new Promise((resolve) => {
_configResolvers.push(resolve);
});
},
});