// uni-storage-helper.js class UniStorageHelper { constructor(dbName, options = {}) { this.dbName = dbName; this.storesMeta = {}; this.options = { maxEntries: 500, // 单个存储空间最大条目数 maxSizeMB: 1, // 单条数据最大限制(微信小程序限制) autoPurge: true, // 是否自动清理旧数据 purgeBatch: 10, // 自动清理批次数量 debug: false, // 调试模式 ...options }; } /*================== 核心方法 ==================*/ /** * 初始化存储空间 * @param {Array} stores - 存储空间配置 */ async openDB(stores = []) { stores.forEach(store => { const storeKey = this._getStoreKey(store.name); if (!this._storageHas(storeKey)) { this._storageSet(storeKey, []); } this.storesMeta[store.name] = { keyPath: store.keyPath, autoIncrement: !!store.autoIncrement, indexes: store.indexes || [] }; if (store.autoIncrement) { const counterKey = this._getCounterKey(store.name); if (!this._storageHas(counterKey)) { this._storageSet(counterKey, 1); } } }); this._log('数据库初始化完成'); return Promise.resolve(); } /** * 添加数据(自动处理容量限制) */ async add(storeName, data) { try { const storeKey = this._getStoreKey(storeName); let storeData = this._storageGet(storeKey) || []; const meta = this.storesMeta[storeName]; const items = Array.isArray(data) ? data : [data]; // 容量预检 await this._checkCapacity(storeName, items); // 处理自增ID if (meta?.autoIncrement) { const counterKey = this._getCounterKey(storeName); let nextId = this._storageGet(counterKey) || 1; items.forEach(item => { item[meta.keyPath] = nextId++; this._createIndexes(meta.indexes, item); }); this._storageSet(counterKey, nextId); } // 保存数据 storeData = [...storeData, ...items]; this._storageSet(storeKey, storeData); this._log(`成功添加${items.length}条数据到${storeName}`); return meta?.autoIncrement ? Array.isArray(data) ? items.map(i => i[meta.keyPath]) : items[0][meta.keyPath] : undefined; } catch (error) { if (error.message.includes('exceed')) { this._log('触发自动清理...'); await this._purgeData(storeName, this.options.purgeBatch); return this.add(storeName, data); } throw error; } } /*================== 查询方法 ==================*/ async get(storeName, key) { const storeData = this._storageGet(this._getStoreKey(storeName)) || []; const keyPath = this.storesMeta[storeName]?.keyPath; return storeData.find(item => item[keyPath] === key); } async getAll(storeName) { return this._storageGet(this._getStoreKey(storeName)) || []; } async queryByField(storeName, fieldName, value) { const storeData = this._storageGet(this._getStoreKey(storeName)) || []; return storeData.filter(item => item[fieldName] === value); } async getRecordCount(storeName) { const storeData = this._storageGet(this._getStoreKey(storeName)) || []; return storeData.length; } async deleteOldestRecord(storeName) { const storeKey = this._getStoreKey(storeName); const storeData = this._storageGet(storeKey) || []; if (storeData.length > 0) { // 删除第一条记录(最早的记录) const newData = storeData.slice(1); this._storageSet(storeKey, newData); this._log(`删除最早的记录,剩余${newData.length}条记录`); } return Promise.resolve(); } /*================== 更新/删除方法 ==================*/ async update(storeName, data, key) { const storeKey = this._getStoreKey(storeName); const storeData = this._storageGet(storeKey) || []; const meta = this.storesMeta[storeName]; const keyPath = meta?.keyPath; const targetKey = key ?? data[keyPath]; const index = storeData.findIndex(item => item[keyPath] === targetKey); if (index === -1) throw new Error('未找到对应记录'); // 合并数据并重建索引 const newItem = { ...storeData[index], ...data }; this._createIndexes(meta.indexes, newItem); storeData[index] = newItem; this._storageSet(storeKey, storeData); return "更新成功"; } async delete(storeName, key) { const storeKey = this._getStoreKey(storeName); const storeData = this._storageGet(storeKey) || []; const keyPath = this.storesMeta[storeName]?.keyPath; const newData = storeData.filter(item => item[keyPath] !== key); this._storageSet(storeKey, newData); return `删除${storeData.length - newData.length}条记录`; } /*================== 存储管理 ==================*/ async clearStore(storeName) { this._storageSet(this._getStoreKey(storeName), []); return "存储空间已清空"; } async deleteDB() { Object.keys(this.storesMeta).forEach(storeName => { uni.removeStorageSync(this._getStoreKey(storeName)); uni.removeStorageSync(this._getCounterKey(storeName)); }); return "数据库已删除"; } /*================== 私有方法 ==================*/ _getStoreKey(storeName) { return `${this.dbName}_${storeName}`; } _getCounterKey(storeName) { return `${this.dbName}_${storeName}_counter`; } _createIndexes(indexes, item) { indexes.forEach(index => { item[index.name] = item[index.key]; }); } async _checkCapacity(storeName, newItems) { const storeKey = this._getStoreKey(storeName); const currentData = this._storageGet(storeKey) || []; // 检查条目数限制 if (currentData.length + newItems.length > this.options.maxEntries) { await this._purgeData(storeName, newItems.length); } // 检查单条数据大小 newItems.forEach(item => { const sizeMB = this._getItemSizeMB(item); if (sizeMB > this.options.maxSizeMB) { throw new Error(`单条数据大小超出${this.options.maxSizeMB}MB限制`); } }); } _getItemSizeMB(item) { try { // 精确计算(支持Blob的环境) return new Blob([JSON.stringify(item)]).size / 1024 / 1024; } catch { // 兼容方案 return encodeURIComponent(JSON.stringify(item)).length * 2 / 1024 / 1024; } } async _purgeData(storeName, count) { const storeKey = this._getStoreKey(storeName); const currentData = this._storageGet(storeKey) || []; const newData = currentData.slice(count); this._storageSet(storeKey, newData); this._log(`自动清理${count}条旧数据`); } /*================== 存储适配器 ==================*/ _storageHas(key) { return !!uni.getStorageSync(key); } _storageGet(key) { try { return uni.getStorageSync(key); } catch (e) { return null; } } _storageSet(key, value) { try { uni.setStorageSync(key, value); return true; } catch (error) { if (error.errMsg?.includes('exceed')) { throw new Error('STORAGE_QUOTA_EXCEEDED'); } throw error; } } _log(...args) { if (this.options.debug) { console.log(`[StorageHelper]`, ...args); } } } export default UniStorageHelper;