Files
ks-app-employment-service/common/UniStorageHelper.js
2025-05-15 14:17:51 +08:00

256 lines
7.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 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;