/** * 地址数据加载器 - 支持缓存和版本控制 * 优化90M+地址JSON文件的加载性能 */ import IndexedDBHelper from '@/common/IndexedDBHelper.js'; class AddressDataLoader { constructor() { this.dbHelper = null; this.dbName = 'AddressDataDB'; this.storeName = 'addressData'; this.cacheKey = 'address_data_cache'; this.versionKey = 'address_data_version'; this.cacheExpireDays = 7; // 缓存有效期7天 this.isInitialized = false; } /** * 初始化数据库 */ async init() { if (this.isInitialized && this.dbHelper) { return; } try { this.dbHelper = new IndexedDBHelper(this.dbName, 1); await this.dbHelper.openDB([{ name: this.storeName, keyPath: 'key', indexes: [ { name: 'version', key: 'version', unique: false }, { name: 'updateTime', key: 'updateTime', unique: false } ] }]); this.isInitialized = true; console.log('✅ 地址数据加载器初始化成功'); } catch (error) { console.error('❌ 地址数据加载器初始化失败:', error); // 降级到 uni.storage this.isInitialized = true; } } /** * 获取缓存版本号 */ async getCacheVersion() { try { const cached = await this.dbHelper?.get(this.storeName, this.versionKey); return cached?.version || null; } catch (e) { // 降级方案:从 uni.storage 读取 return uni.getStorageSync(this.versionKey) || null; } } /** * 保存版本号 */ async saveVersion(version) { try { if (this.dbHelper) { const versionData = { key: this.versionKey, version: version, updateTime: Date.now() }; // 先尝试获取,如果不存在则添加,存在则更新 try { const existing = await this.dbHelper.get(this.storeName, this.versionKey); if (existing) { await this.dbHelper.update(this.storeName, versionData); } else { await this.dbHelper.add(this.storeName, versionData); } } catch (e) { // 如果获取失败,尝试直接添加 await this.dbHelper.add(this.storeName, versionData); } } } catch (e) { // 降级方案:保存到 uni.storage uni.setStorageSync(this.versionKey, version); } } /** * 从缓存获取数据 */ async getCachedData() { try { if (this.dbHelper) { const cached = await this.dbHelper.get(this.storeName, this.cacheKey); if (cached && cached.data) { // 检查缓存是否过期 const updateTime = cached.updateTime || 0; const expireTime = this.cacheExpireDays * 24 * 60 * 60 * 1000; if (Date.now() - updateTime < expireTime) { console.log('✅ 从 IndexedDB 缓存加载地址数据'); return cached.data; } else { console.log('⚠️ 缓存已过期,需要重新加载'); } } } } catch (e) { console.warn('从 IndexedDB 读取缓存失败,尝试 uni.storage:', e); } // 降级方案:从 uni.storage 读取 try { const cached = uni.getStorageSync(this.cacheKey); if (cached && cached.data) { const updateTime = cached.updateTime || 0; const expireTime = this.cacheExpireDays * 24 * 60 * 60 * 1000; if (Date.now() - updateTime < expireTime) { console.log('✅ 从 uni.storage 缓存加载地址数据'); return cached.data; } } } catch (e) { console.warn('从 uni.storage 读取缓存失败:', e); } return null; } /** * 保存数据到缓存 */ async saveToCache(data) { const cacheData = { key: this.cacheKey, data: data, updateTime: Date.now() }; try { if (this.dbHelper) { // 先尝试获取,如果不存在则添加,存在则更新 try { const existing = await this.dbHelper.get(this.storeName, this.cacheKey); if (existing) { await this.dbHelper.update(this.storeName, cacheData); } else { await this.dbHelper.add(this.storeName, cacheData); } console.log('✅ 地址数据已保存到 IndexedDB'); } catch (e) { // 如果获取失败,尝试直接添加 await this.dbHelper.add(this.storeName, cacheData); console.log('✅ 地址数据已保存到 IndexedDB'); } return; } } catch (e) { console.warn('保存到 IndexedDB 失败,降级到 uni.storage:', e); } // 降级方案:保存到 uni.storage try { uni.setStorageSync(this.cacheKey, cacheData); console.log('✅ 地址数据已保存到 uni.storage'); } catch (e) { console.error('❌ 保存缓存失败,可能超出存储限制:', e); // 如果存储空间不足,尝试清理旧数据 if (e.errMsg?.includes('exceed')) { await this.clearOldCache(); // 重试保存 try { uni.setStorageSync(this.cacheKey, cacheData); } catch (e2) { console.error('❌ 重试保存仍然失败:', e2); } } } } /** * 清理过期缓存 */ async clearOldCache() { try { if (this.dbHelper) { await this.dbHelper.delete(this.storeName, this.cacheKey); } uni.removeStorageSync(this.cacheKey); console.log('✅ 已清理旧缓存'); } catch (e) { console.warn('清理缓存失败:', e); } } /** * 从服务器获取数据版本号(如果服务器支持) */ async fetchRemoteVersion() { try { // 如果服务器提供版本接口,可以在这里实现 // const res = await uni.request({ // url: 'http://124.243.245.42/ks_cms/address_version.json', // method: 'GET' // }); // return res.data?.version || null; return null; } catch (e) { return null; } } /** * 从服务器加载地址数据 */ async fetchRemoteData(url, onProgress) { return new Promise((resolve, reject) => { console.log('📥 开始从服务器加载地址数据...'); const startTime = Date.now(); uni.request({ url: url, method: 'GET', timeout: 300000, // 5分钟超时 success: (res) => { const endTime = Date.now(); const duration = ((endTime - startTime) / 1000).toFixed(2); console.log(`✅ 地址数据加载完成,耗时 ${duration} 秒`); if (res.statusCode === 200) { // 如果返回的是字符串,尝试解析JSON let data = res.data; if (typeof data === 'string') { try { data = JSON.parse(data); } catch (e) { reject(new Error('JSON解析失败: ' + e.message)); return; } } resolve(data); } else { reject(new Error(`请求失败,状态码: ${res.statusCode}`)); } }, fail: (err) => { console.error('❌ 地址数据加载失败:', err); reject(err); } }); // 注意:uni.request 不支持进度回调,如果需要进度提示,可以考虑: // 1. 使用流式请求(如果服务器支持) // 2. 显示固定加载提示 if (onProgress) { onProgress({ loaded: 0, total: 0, percent: 0 }); } }); } /** * 加载地址数据(带缓存) * @param {string} url - 地址数据URL * @param {boolean} forceRefresh - 是否强制刷新 * @param {Function} onProgress - 进度回调 */ async loadAddressData(url = 'http://124.243.245.42/ks_cms/address.json', forceRefresh = false, onProgress = null) { // 初始化数据库 await this.init(); // 如果不是强制刷新,先尝试从缓存加载 if (!forceRefresh) { const cachedData = await this.getCachedData(); if (cachedData) { return cachedData; } } // 检查版本(如果服务器支持版本控制) const remoteVersion = await this.fetchRemoteVersion(); if (remoteVersion) { const cachedVersion = await this.getCacheVersion(); if (cachedVersion === remoteVersion && !forceRefresh) { const cachedData = await this.getCachedData(); if (cachedData) { console.log('✅ 使用缓存数据(版本匹配)'); return cachedData; } } } // 显示加载提示 uni.showLoading({ title: '正在加载地址数据...', mask: true }); try { // 从服务器加载数据 const data = await this.fetchRemoteData(url, onProgress); // 保存到缓存 await this.saveToCache(data); // 保存版本号 if (remoteVersion) { await this.saveVersion(remoteVersion); } return data; } catch (error) { console.error('❌ 加载地址数据失败:', error); // 如果加载失败,尝试使用缓存数据(即使已过期) if (!forceRefresh) { try { const cachedData = await this.getCachedData(); if (cachedData) { console.log('⚠️ 使用过期缓存数据'); uni.showToast({ title: '使用缓存数据', icon: 'none', duration: 2000 }); return cachedData; } } catch (e) { console.warn('获取缓存数据失败:', e); } } throw error; } finally { uni.hideLoading(); } } /** * 清除所有缓存 */ async clearCache() { await this.init(); await this.clearOldCache(); await this.saveVersion(null); console.log('✅ 已清除所有地址数据缓存'); } } // 单例模式 const addressDataLoader = new AddressDataLoader(); export default addressDataLoader;