fastbee/yack-mini/miniprogram/utils/storage.js

388 lines
9.5 KiB
JavaScript
Raw 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.

/**
* 本地存储工具
*/
const { LIMITS, EXPIRE } = require('./constants.js');
const { encryptFields, decryptFields } = require('./crypto.js');
const TOKEN_KEY = 'yack_token';
const USER_INFO_KEY = 'yack_user_info';
const DEVICE_LIST_KEY = 'yack_device_list';
const PENDING_DATA_KEY = 'yack_pending_data';
// 需要加密的字段
const ENCRYPT_FIELDS = ['productSecret'];
/**
* 获取 Token
* @returns {string|null}
*/
function getToken() {
try {
return wx.getStorageSync(TOKEN_KEY) || null;
} catch (e) {
console.error('[Storage] getToken error:', e);
return null;
}
}
/**
* 设置 Token
* @param {string} token
*/
function setToken(token) {
try {
wx.setStorageSync(TOKEN_KEY, token);
} catch (e) {
console.error('[Storage] setToken error:', e);
}
}
/**
* 移除 Token
*/
function removeToken() {
try {
wx.removeStorageSync(TOKEN_KEY);
} catch (e) {
console.error('[Storage] removeToken error:', e);
}
}
/**
* 获取用户信息
* @returns {Object|null}
*/
function getUserInfo() {
try {
return wx.getStorageSync(USER_INFO_KEY) || null;
} catch (e) {
console.error('[Storage] getUserInfo error:', e);
return null;
}
}
/**
* 设置用户信息
* @param {Object} userInfo
*/
function setUserInfo(userInfo) {
try {
wx.setStorageSync(USER_INFO_KEY, userInfo);
} catch (e) {
console.error('[Storage] setUserInfo error:', e);
}
}
/**
* 移除用户信息
*/
function removeUserInfo() {
try {
wx.removeStorageSync(USER_INFO_KEY);
} catch (e) {
console.error('[Storage] removeUserInfo error:', e);
}
}
/**
* 获取设备列表
* @returns {Array}
*/
function getDeviceList() {
try {
const devices = wx.getStorageSync(DEVICE_LIST_KEY) || [];
// 解密敏感字段
return devices.map(device => decryptFields(device, ENCRYPT_FIELDS));
} catch (e) {
console.error('[Storage] getDeviceList error:', e);
return [];
}
}
/**
* 保存设备列表(加密敏感字段)
* @param {Array} devices
*/
function saveDeviceList(devices) {
try {
// 加密敏感字段
const encryptedDevices = devices.map(device => encryptFields(device, ENCRYPT_FIELDS));
wx.setStorageSync(DEVICE_LIST_KEY, encryptedDevices);
} catch (e) {
console.error('[Storage] saveDeviceList error:', e);
}
}
/**
* 添加设备(加密敏感字段)
* @param {Object} device
*/
function addDevice(device) {
const devices = getDeviceList();
// 检查是否已存在
const index = devices.findIndex(d => d.serialNumber === device.serialNumber);
// 加密敏感字段
const encryptedDevice = encryptFields(device, ENCRYPT_FIELDS);
if (index >= 0) {
devices[index] = encryptedDevice;
} else {
// 检查设备数量限制
if (devices.length >= LIMITS.MAX_DEVICE_COUNT) {
console.warn('[Storage] 设备数量已达上限:', LIMITS.MAX_DEVICE_COUNT);
wx.showToast({
title: `最多添加 ${LIMITS.MAX_DEVICE_COUNT} 个设备`,
icon: 'none',
});
return;
}
devices.push(encryptedDevice);
}
saveDeviceList(devices);
}
/**
* 删除设备
* @param {string} serialNumber
*/
function removeDevice(serialNumber) {
const devices = getDeviceList();
const newDevices = devices.filter(d => d.serialNumber !== serialNumber);
saveDeviceList(newDevices);
}
/**
* 更新设备
* @param {Object} device
*/
function updateDevice(device) {
const devices = getDeviceList();
const index = devices.findIndex(d => d.serialNumber === device.serialNumber);
if (index >= 0) {
devices[index] = device;
saveDeviceList(devices);
}
}
/**
* 获取单个设备(解密敏感字段)
* @param {string} serialNumber
* @returns {Object|null}
*/
function getDevice(serialNumber) {
const devices = getDeviceList();
return devices.find(d => d.serialNumber === serialNumber) || null;
}
/**
* 激活设备为唯一启用(其余全部禁用)
* 保证全列表最多一个 enabled=true
* @param {string} serialNumber
*/
function activateDevice(serialNumber) {
// 直接读取加密数据并解密
const devices = getDeviceList();
let found = false;
devices.forEach((d) => {
if (d.serialNumber === serialNumber) {
d.enabled = true;
found = true;
} else if (d.enabled) {
d.enabled = false;
}
});
if (found) {
saveDeviceList(devices);
console.log('[Storage] 激活设备为唯一启用:', serialNumber);
}
}
/**
* 禁用设备
* @param {string} serialNumber
*/
function deactivateDevice(serialNumber) {
const devices = getDeviceList();
const index = devices.findIndex(d => d.serialNumber === serialNumber);
if (index >= 0) {
devices[index].enabled = false;
saveDeviceList(devices);
console.log('[Storage] 禁用设备:', serialNumber);
}
}
/**
* 获取当前启用的设备(最多一个)
* 存量数据若出现多个 enabled,自动归一化:保留列表中第一个,其余置 false
* @returns {Object|null}
*/
function getEnabledDevice() {
const devices = getDeviceList();
const enabledList = devices.filter(d => d.enabled);
if (enabledList.length === 0) return null;
if (enabledList.length > 1) {
console.warn('[Storage] 检测到多个启用设备,自动归一化为第一个:', enabledList.map(d => d.serialNumber));
const keep = enabledList[0].serialNumber;
devices.forEach((d) => {
if (d.serialNumber !== keep) d.enabled = false;
});
saveDeviceList(devices);
return devices.find(d => d.serialNumber === keep) || null;
}
return enabledList[0];
}
/**
* 清理过期的待上报数据
* @param {Array} pendingList - 待上报数据列表
* @returns {Array} 清理后的列表
*/
function _cleanExpiredData(pendingList) {
if (!pendingList || pendingList.length === 0) return [];
const expireTime = EXPIRE.PENDING_DATA * 24 * 60 * 60 * 1000; // 转换为毫秒
const now = Date.now();
return pendingList.filter(item => {
const cacheTime = item.cacheTime || 0;
return (now - cacheTime) < expireTime;
});
}
/**
* 缓存待上报数据(带限制和过期清理)
* @param {Object} data
*/
function cachePendingData(data) {
try {
let pendingList = wx.getStorageSync(PENDING_DATA_KEY) || [];
// 清理过期数据
pendingList = _cleanExpiredData(pendingList);
// 检查数量限制
if (pendingList.length >= LIMITS.MAX_PENDING_DATA) {
console.warn('[Storage] 离线缓存已达上限,移除最旧数据');
pendingList = pendingList.slice(-LIMITS.MAX_PENDING_DATA + 1);
}
// 添加新数据
pendingList.push({
...data,
cacheTime: Date.now(),
});
wx.setStorageSync(PENDING_DATA_KEY, pendingList);
console.log('[Storage] 缓存待上报数据:', data);
} catch (e) {
console.error('[Storage] cachePendingData error:', e);
}
}
/**
* 批量缓存待上报数据(一次 setStorageSync,避免高频逐条写磁盘阻塞 BLE 接收)
* 带限制和过期清理
* @param {Array<Object>} items
*/
function cachePendingDataBatch(items) {
if (!items || items.length === 0) return;
try {
let pendingList = wx.getStorageSync(PENDING_DATA_KEY) || [];
// 清理过期数据
pendingList = _cleanExpiredData(pendingList);
// 添加新数据
items.forEach((data) => {
// 检查数量限制
if (pendingList.length >= LIMITS.MAX_PENDING_DATA) {
console.warn('[Storage] 离线缓存已达上限,移除最旧数据');
pendingList = pendingList.slice(-LIMITS.MAX_PENDING_DATA + 1);
}
pendingList.push({
...data,
cacheTime: Date.now(),
});
});
// 最终限制检查(确保不超过上限)
if (pendingList.length > LIMITS.MAX_PENDING_DATA) {
pendingList = pendingList.slice(-LIMITS.MAX_PENDING_DATA);
}
wx.setStorageSync(PENDING_DATA_KEY, pendingList);
} catch (e) {
console.error('[Storage] cachePendingDataBatch error:', e);
}
}
/**
* 获取待上报数据(自动清理过期数据)
* @returns {Array}
*/
function getPendingData() {
try {
let pendingList = wx.getStorageSync(PENDING_DATA_KEY) || [];
// 清理过期数据
pendingList = _cleanExpiredData(pendingList);
// 如果清理后数据有变化,更新存储
const originalList = wx.getStorageSync(PENDING_DATA_KEY) || [];
if (pendingList.length !== originalList.length) {
wx.setStorageSync(PENDING_DATA_KEY, pendingList);
console.log('[Storage] 已清理过期离线数据');
}
return pendingList;
} catch (e) {
console.error('[Storage] getPendingData error:', e);
return [];
}
}
/**
* 覆写待上报数据(用于补传成功后只移除本设备已上传项)
* @param {Array} list
*/
function setPendingData(list) {
try {
wx.setStorageSync(PENDING_DATA_KEY, list || []);
} catch (e) {
console.error('[Storage] setPendingData error:', e);
}
}
/**
* 清除待上报数据
*/
function clearPendingData() {
try {
wx.removeStorageSync(PENDING_DATA_KEY);
} catch (e) {
console.error('[Storage] clearPendingData error:', e);
}
}
/**
* 清除所有数据
*/
function clearAll() {
try {
wx.clearStorageSync();
console.log('[Storage] 已清除所有数据');
} catch (e) {
console.error('[Storage] clearAll error:', e);
}
}
module.exports = {
getToken,
setToken,
removeToken,
getUserInfo,
setUserInfo,
removeUserInfo,
getDeviceList,
saveDeviceList,
addDevice,
removeDevice,
updateDevice,
getDevice,
activateDevice,
deactivateDevice,
getEnabledDevice,
cachePendingData,
cachePendingDataBatch,
getPendingData,
setPendingData,
clearPendingData,
clearAll,
};