353 lines
12 KiB
JavaScript
353 lines
12 KiB
JavaScript
|
|
/**
|
|||
|
|
* 地址数据加载器 - 支持缓存和版本控制
|
|||
|
|
* 优化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;
|
|||
|
|
|