UNPKG

mm_os

Version:

MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。

1,756 lines (1,543 loc) 47.4 kB
const { Drive } = require('mm_machine'); const { idGen } = require('../../ulits/id_gen.js'); const Sql = require('./sql.js'); /** * 数据存储基类 * 提供数据访问层的基础功能和CRUD操作接口,调用缓存和持久存储 */ class Store extends Drive { /** * 验证配置参数 * @param {object} config 配置参数 * @returns {boolean} 配置是否有效 */ static validateConfig(config) { if (!config || typeof config !== 'object') { throw new TypeError('配置参数必须是对象'); } Store._validateReqFields(config); Store._validateCacheType(config); Store._validateSaveType(config); Store._validateWay(config); Store._validateNumFields(config); return true; } /** * 验证必需字段 * @param {object} config 配置参数 */ static _validateReqFields(config) { var required_fields = ['name', 'cache_type', 'save_type', 'way', 'table']; for (var field of required_fields) { if (!config[field]) { throw new Error(`配置参数缺少必要字段: ${field}`); } } } /** * 验证缓存类型 * @param {object} config 配置参数 */ static _validateCacheType(config) { var valid_cache_types = ['none', 'memory', 'redis', 'mongodb']; if (!valid_cache_types.includes(config.cache_type)) { throw new Error(`无效的缓存类型: ${config.cache_type}`); } } /** * 验证保存类型 * @param {object} config 配置参数 */ static _validateSaveType(config) { var valid_save_types = ['none', 'sqlite', 'mysql', 'json']; if (!valid_save_types.includes(config.save_type)) { throw new Error(`无效的保存类型: ${config.save_type}`); } } /** * 验证读写方式 * @param {object} config 配置参数 */ static _validateWay(config) { if (config.way < 1 || config.way > 4) { throw new Error(`无效的读写方式: ${config.way}`); } } /** * 验证数值类型参数 * @param {object} config 配置参数 */ static _validateNumFields(config) { var numeric_fields = ['cache_interval', 'save_interval', 'page_size']; for (var num_field of numeric_fields) { if (config[num_field] && (typeof config[num_field] !== 'number' || config[num_field] < 0)) { throw new Error(`${num_field} 必须是正数`); } } } static config = { // 名称 'name': 'default', // 标题 'title': '示例标题', // 描述 'description': '示例描述', // 入口文件 'main': '', // 作用域,决定加载到什么区域 'scope': 'server', // 状态 0:禁用 1:启用 'state': 1, // 排序,越小越靠前 'sort': 100, // 缓存方式 none、memory、redis、mongodb 'cache_type': 'none', // 保存方式 none、sqlite、mysql、json 'save_type': 'none', // 缓存更新间隔(秒) 'cache_interval': 600, // 默认10分钟 // 定时持久化间隔(秒) 'save_interval': 300, // 默认5分钟 // 读写方式 1.直接读写数据库;2.读缓存,写持久存,写后更新缓存;3.读缓存,写缓存,定时持久存;4.读缓存,写持久存,定时更新缓存 'way': 1, // 表名 'table': 'store', // 主键 'key': 'id', // ID 'id': 'id', // 默认分页大小 'page_size': 30, // 排序方式,{0}表示支持前端传值来排序,传值方式例如:orderby=user_id 'orderby': '{0}', // 默认排序方式,当前端没有传值的情况下,采用默认传值方式 'orderby_default': '`user_id` desc', // 查询列表时返回的字段,{0}表示支持前端传值 'field': '{0}', // 查询对象时返回的字段 'field_obj': '{0}', // 默认返回字段 'field_default': '`username`, `phone`', // 需要过滤的字段,*代表泛匹配 前面*表示以xxx结尾,后面*表示以xxx开头,前后*表示包含xxx的过滤 'field_hide': ['*password*', '*token*'], // 传值分隔符,如果允许多个值,可以通过这个来分割,如果不允许传多个值则设置为空 'separator': '|', // 过滤参数 'filter': { /** * 表名 */ 'table': 'table', /** * 查询的页码 */ 'page': 'page', /** * 查询每页条数 */ 'size': 'size', /** * 操作方式: 传入参数method=add, 支持参数 add增、del删、set改、get查,为空则为get */ 'method': 'method', /** * 排序 */ 'orderby': 'orderby', /** * 查询显示的字段 */ 'field': 'field', /** * 统计结果: 统计符合条件的结果数,只有当page等于1或0时才会统计 */ 'count_ret': 'count_ret' }, // 允许调用的方法 add增 del删 set改 get查 get_obj查一条 import导入 export导出, 支持什么方式就传什么值,支持多个用空格分隔 'method': 'add del set get get_obj import export del_repeat avg sum count update', // sql查询语句 'query': { // 当username=xx时,转为`username` like '%xxx%'的SQL语法 // 'username': '`username` like \'%{0}\'' }, // 默认查询, 当查询条件中不包含该项时,默认添加该项 'query_default': {}, // sql更改语句 'update': { // 当修改的正文中出现username是转换为指定格式 // 'username': '`username` = \'{0}\'' }, // 默认添加条件,当不包含该项时,默认添加该项 'body_default': {}, // 逻辑符 'logic': {}, // 输出sql语句 'log': false, // 文件路径, 当调用函数不存在时,会先从文件中加载 'main': '', // 回调函数名 用于决定调用脚本的哪个函数 'func_name': '', // 参数 [] 'params': null, // 导入导出格式,转换导入、导出Eexcel时字段名和字段值 'format': [], // 去重条件,需要查重、并去重的字段在这里写 'del_repeat': { // 判断重复的字段 'groupBy': '', // 排序方式 'orderBy': '' } }; /** * 构造函数 * @param {object} config 配置参数 * @param {object} parent 父对象 */ constructor(config, parent) { // 验证配置参数 Store.validateConfig({ ...Store.config, ...config }); super({ type: 'store', ...Store.config, ...config }, parent); } } /** * 预设 */ Store.prototype._preset = function () { // 数据库管理器 this._sql = null; // 缓存管理器 this._cache = null; // 持久存定时器 this._save_timer = null; // 缓存定时器 this._cache_timer = null; this._cache_interval = this.config.cache_interval || 600; // 默认10分钟 this._save_interval = this.config.save_interval || 300; // 默认5分钟 }; /** * 获取模板目录 * @returns {string} 模板目录 */ Store.prototype.getTplDir = function () { return __dirname; }; /** * 初始化核心 * @param {object} db 数据库管理器 * @param {object} cache 缓存管理器 * @param {object} logger 日志管理器 */ Store.prototype._initCore = async function (db, cache, logger) { // 初始化依赖项 if (logger) { this.setLogger(logger); } if (db) { this._db = db; } if (cache) { this._cache = cache; } // 驱动实例 this._drive = new Sql(this.config); // 初始化驱动 this._drive.sql = this._sql; this._drive.cache = cache; // 根据读写方式启动定时任务 this._startTimers(); }; /** * 添加数据到数据库 * @param {object} body - 要添加的数据对象 * @returns {Promise<object>} - 添加结果 */ Store.prototype._addToDb = async function (body) { let db = this._sql.db(); return await this._drive.add(db, body); }; /** * 添加数据到缓存 * @param {object} body - 要添加的数据对象 * @returns {Promise<object>} - 添加结果 */ Store.prototype._addToCache = async function (body) { // 生成临时ID或使用现有ID let temp = body[this.config.id] || `temp_${Date.now()}`; let cached = { ...body, [this.config.id]: temp }; // 保存到缓存 let cache_key = this._genCacheKey('get', {}); let cache_obj_key = this._genCacheKey('getObj', { [this.config.id]: temp }); // 获取当前列表缓存 let current = await this.getCache(cache_key) || []; current.push(cached); // 更新缓存 await this.setCache(cache_key, current); await this.setCache(cache_obj_key, cached); return cached; }; /** * 添加单条数据 * @param {object} body - 要添加的数据对象 * @returns {Promise<object>} - 添加成功后的数据对象 */ Store.prototype.add = async function (body) { // 入参校验 if (typeof body !== 'object' || body === null) { throw new TypeError('添加的数据必须是对象'); } if (Object.keys(body).length === 0) { throw new TypeError('添加的数据不能为空'); } try { let way = this.config.way; return await this._addByWay(body, way); } catch (error) { this.log('error', '创建数据失败:', error); return $.ret.error(error.code || 500, '创建数据失败'); } }; /** * 根据方式添加数据 * @param {object} body - 要添加的数据对象 * @param {number} way - 存储方式 * @returns {Promise<object>} - 添加结果 * @private */ Store.prototype._addByWay = async function (body, way) { let result; // 方式1、2、4、2、4:写数据库 if ([1, 2, 4].includes(way)) { result = await this._addToDb(body); } // 方式3:写缓存 if (way === 3 && this._cache) { result = await this._addToCache(body); } // 方式2:写后更新缓存 if (way === 2 && this._cache) { await this._updateCacheForWay2(result); } return result; }; /** * 批量添加数据到数据库 * @param {Array} list - 要添加的数据对象数组 * @returns {Promise<object>} - 添加结果 */ Store.prototype._addListToDb = async function (list) { let db = this._sql.db(); return await this._drive.addList(db, list); }; /** * 批量添加数据到缓存 * @param {Array} list - 要添加的数据对象数组 * @returns {Promise<object>} - 添加结果 */ Store.prototype._addListToCache = async function (list) { let cache_key = this._genCacheKey('get', {}); let current = await this.getCache(cache_key) || []; // 为每条数据生成临时ID let cached = list.map(item => { let temp = item[this.config.id] || idGen.genId('temp'); let data = { ...item, [this.config.id]: temp }; // 设置单条数据缓存 let cache_obj_key = this._genCacheKey('getObj', { [this.config.id]: temp }); this.setCache(cache_obj_key, data); return data; }); // 更新列表缓存 current.push(...cached); await this.setCache(cache_key, current); return { result: { success: list.length, error: 0, total: list.length, errors: [] } }; }; /** * 批量添加数据 * @param {Array} list - 要添加的数据对象数组 * @returns {Promise<object>} - 添加结果 */ Store.prototype.addList = async function (list) { // 入参校验 if (!Array.isArray(list)) { throw new TypeError('批量添加的数据必须是数组'); } if (list.length === 0) { throw new TypeError('批量添加的数据不能为空'); } try { let way = this.config.way; let result; // 方式1、2、4、2、4:写数据库 if ([1, 2, 4].includes(way)) { result = await this._addListToDb(list); } // 方式3:写缓存 if (way === 3 && this._cache) { result = await this._addListToCache(list); } // 方式2:写后更新缓存 if (way === 2 && this._cache && result.result) { // 清除列表缓存,下次读取时会自动更新 let cache_key = this._genCacheKey('get', {}); await this.delCache(cache_key); } return this._formatAddListRet(result, list.length); } catch (error) { this.log('error', '批量创建数据失败:', error); return { error: true, message: '批量创建数据失败' }; } }; /** * 格式化批量添加结果 * @param {object} result - 原始结果 * @param {number} list_length - 列表长度 * @returns {object} - 格式化后的结果 */ Store.prototype._formatAddListRet = function (result, list_length) { if (result.error) { return { success: 0, error: list_length, total: list_length, errors: [result.message] }; } else { return { success: result.result.success, error: result.result.error, total: result.result.total, errors: result.result.errors }; } }; /** * 尝试从缓存读取单条数据 * @param {string} cache_key - 缓存键 * @param {number} way - 读写方式 * @returns {Promise<object|null>} - 缓存数据或null */ Store.prototype._tryGetFromCache = async function (cache_key, way) { if ([2, 3, 4].includes(way) && this._cache) { let cached = await this.getCache(cache_key); if (cached) { return cached; } } return null; }; /** * 从数据库读取单条数据 * @param {object} query - 查询条件 * @returns {Promise<object|null>} - 查询结果 */ Store.prototype._getFromDb = async function (query) { let db = this._sql.db(); db.table = this.config.table; let result = await this._drive.getObj(db, query); return result.result ? result.result : null; }; /** * 查询单条数据 * @param {object} query - 查询条件 * @returns {Promise<object|null>} - 查询到的数据对象或null */ Store.prototype.getObj = async function (query = {}) { // 入参校验 if (typeof query !== 'object' || query === null) { throw new TypeError('查询条件必须是对象'); } let way = this.config.way; let cache_key = this._genCacheKey('getObj', query); // 尝试从缓存读取 let cached = await this._tryGetFromCache(cache_key, way); if (cached) { return cached; } // 从数据库读取 let data = await this._getFromDb(query); // 更新缓存(方式2) await this._updateCache2Get(cache_key, way, data); return data; }; /** * 查询多条数据 * @param {object} query - 查询条件 * @returns {Promise<Array>} - 查询结果数组 */ Store.prototype.get = async function (query = {}) { // 入参校验 if (typeof query !== 'object' || query === null) { throw new TypeError('查询条件必须是对象'); } return await this._getCache(query); }; /** * 带缓存的查询实现 * @private * @param {object} query - 查询条件 * @returns {Promise<Array>} - 查询结果数组 */ Store.prototype._getCache = async function (query) { let way = this.config.way; let cache_key = this._genCacheKey('get', query); // 尝试从缓存读取 let cached = await this._tryGetListFromCache(cache_key, way); if (cached) { return cached; } // 从数据库读取 let data = await this._getListFromDb(query); // 更新缓存(方式2) await this._updateCache2List(cache_key, way, data); return data; }; /** * 根据ID查询数据 * @param {string|number} id - 数据ID * @param {string} orderby - 排序字段 * @param {string} fields - 查询字段 * @returns {Promise<object|null>} - 查询到的数据对象或null */ Store.prototype.getById = async function (id, orderby = '', fields = '') { // 入参校验 if (!id && id !== 0) { throw new TypeError('ID不能为空'); } return await this._getByIdCache(id, orderby, fields); }; /** * 带缓存的根据ID查询实现 * @private * @param {string|number} id - 数据ID * @param {string} orderby - 排序字段 * @param {string} fields - 查询字段 * @returns {Promise<object|null>} - 查询到的数据对象或null */ Store.prototype._getByIdCache = async function (id, orderby, fields) { let way = this.config.way; let query = this._buildQueryById(id); let cache_key = this._genCacheKey('getObj', query); // 尝试从缓存读取 let cached = await this._tryGetFromCache(cache_key, way); if (cached) { return cached; } // 从数据库读取 let db = this._sql.db(); db.table = this.config.table; // 设置查询字段/排序 this._configDbQuery(db, fields, orderby); let data = await this._getFromDb(query); // 更新缓存(方式2) await this._updateCache2Get(cache_key, way, data); return data; }; /** * 更新数据 * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @returns {Promise<number>} - 更新成功的记录数量 */ Store.prototype.set = async function (query, body) { // 入参校验 this._validateSet(query, body); let way = this.config.way; let cache_key = this._genCacheKey('get', query); let cache_obj_key = this._genCacheKey('getObj', query); let result = await this._runSetByWay(way, query, body, cache_key, cache_obj_key); return result.success ? 1 : 0; }; /** * 验证set方法参数 * @private * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @throws {TypeError} 参数验证失败时抛出 */ Store.prototype._checkSet = function (query, body) { if (typeof query !== 'object' || query === null) { throw new TypeError('查询条件必须是对象'); } if (typeof body !== 'object' || body === null) { throw new TypeError('更新内容必须是对象'); } if (Object.keys(body).length === 0) { throw new TypeError('更新内容不能为空'); } }; /** * 根据方式处理set操作 * @private * @param {number} way - 配置方式 * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @param {string} cache_key - 缓存键 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<object>} - 更新结果 */ Store.prototype._runSetByWay = async function (way, query, body, cache_key, cache_obj_key) { let result = { success: false }; // 方式1、2、4、2、4:写数据库 if ([1, 2, 4].includes(way)) { result = await this._setDb(query, body); } // 方式3:写缓存 if (way === 3 && this._cache) { result = await this._setCache(query, body, cache_key, cache_obj_key); } // 方式2:写后更新缓存 if (way === 2 && this._cache && result.success) { await this._clearCacheAfterSet(cache_key, cache_obj_key); } return result; }; /** * 更新数据库中的数据 * @private * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @returns {Promise<object>} - 更新结果 */ Store.prototype._setDb = async function (query, body) { let db = this._sql.db(); db.table = this.config.table; return await this._drive.set(db, query, body); }; /** * 更新缓存中的数据 * @private * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @param {string} cache_key - 缓存键 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<object>} - 更新结果 */ Store.prototype._setCache = async function (query, body, cache_key, cache_obj_key) { // 更新列表缓存 let cached = await this.getCache(cache_key); if (cached && Array.isArray(cached)) { let updated = cached.map(item => { let match = Object.keys(query).every(key => { return item[key] === query[key]; }); if (match) { return { ...item, ...body }; } return item; }); await this.setCache(cache_key, updated); } // 更新单条数据缓存 let item = await this.getCache(cache_obj_key); if (item) { await this.setCache(cache_obj_key, { ...item, ...body }); } return { success: 1 }; }; /** * 设置后清除缓存 * @private * @param {string} cache_key - 缓存键 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<void>} - 无返回值 */ Store.prototype._clearCacheAfterSet = async function (cache_key, cache_obj_key) { await this.delCache(cache_key); await this.delCache(cache_obj_key); }; /** * 设置缓存失效时间 * @param {string} key - 缓存键 * @param {number} expire_time - 失效时间(秒) * @returns {Promise<boolean>} - 设置是否成功 */ Store.prototype.setCacheExpire = async function (key, expire_time) { if (!this._cache) { return false; } try { await this._cache.expire(key, expire_time); return true; } catch (error) { this.log('error', '设置缓存失效时间失败:', error); return false; } }; /** * 更新缓存中的数据 * @param {string} action - 操作类型 * @param {object} params - 参数 * @param {any} data - 数据 * @returns {Promise<boolean>} - 更新是否成功 */ Store.prototype.setCache = async function (action, params, data) { if (!this._cache) { return false; } try { return await this._updateCache(action, params, data); } catch (error) { this.log('error', '更新缓存失败:', error); return false; } }; /** * 实际更新缓存数据 * @private * @param {string} action - 操作类型 * @param {object} params - 参数 * @param {any} data - 数据 * @returns {Promise<boolean>} - 更新是否成功 */ Store.prototype._setCache = async function (action, params, data) { let cache_key = this._genCacheKey(action, params); await this.setCache(cache_key, data); return true; }; /** * 根据ID更新数据 * @param {string|number} id - 数据ID * @param {object} body - 更新内容 * @returns {Promise<number>} - 更新成功的记录数量 */ Store.prototype.setById = async function (id, body) { // 入参校验 if (!id && id !== 0) { throw new TypeError('ID不能为空'); } if (typeof body !== 'object' || body === null) { throw new TypeError('更新内容必须是对象'); } if (Object.keys(body).length === 0) { throw new TypeError('更新内容不能为空'); } let way = this.config.way; let query = this._buildQueryById(id); let cache_keys = this._genCacheKeysForId(query); let result = await this._handleSetById(way, query, body, cache_keys); await this._handleCacheAfterSet(way, cache_keys); return result; }; /** * 构建ID查询条件 * @private * @param {string|number} id - 数据ID * @returns {object} - 查询条件 */ Store.prototype._buildGetById = function (id) { return { [this.config.id]: id }; }; /** * 为ID生成缓存键 * @private * @param {object} query - 查询条件 * @returns {object} - 缓存键对象 */ Store.prototype._genCacheKeysForId = function (query) { return { list_key: this._genCacheKey('get', query), obj_key: this._genCacheKey('getObj', query) }; }; /** * 处理根据ID更新数据 * @private * @param {number} way - 缓存方式 * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @param {object} cache_keys - 缓存键 * @returns {Promise<number>} - 更新结果 */ Store.prototype._runSetById = async function (way, query, body, cache_keys) { // 方式1、2、4、2、4:写数据库 if ([1, 2, 4].includes(way)) { return await this._setByIdDb(query, body); } // 方式3:写缓存 if (way === 3 && this._cache) { return await this._setByIdCache(query, body, cache_keys); } return 0; }; /** * 处理更新后的缓存操作 * @private * @param {number} way - 缓存方式 * @param {object} cache_keys - 缓存键 * @returns {Promise<void>} */ Store.prototype._runCacheAfterSet = async function (way, cache_keys) { // 方式2:写后更新缓存 if (way === 2 && this._cache) { await this._clearCacheSetById(cache_keys.list_key, cache_keys.obj_key); } }; /** * 根据ID更新数据库中的数据 * @private * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @returns {Promise<number>} - 更新结果 */ Store.prototype._setByIdDb = async function (query, body) { let db = this._sql.db(); db.table = this.config.table; let result = await this._drive.set(db, query, body); return result.success ? 1 : 0; }; /** * 根据ID更新缓存中的数据 * @private * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @param {object} cache_keys - 缓存键对象 * @returns {Promise<number>} - 更新结果 */ Store.prototype._setByIdCache = async function (query, body, cache_keys) { let id = query[this.config.id]; // 更新单条数据缓存 let cached = await this.getCache(cache_keys.obj_key); if (cached) { await this.setCache(cache_keys.obj_key, { ...cached, ...body }); } // 更新列表缓存 let lt = await this.getCache(cache_keys.list_key); if (lt && Array.isArray(lt)) { let updated = lt.map(item => { if (item[this.config.id] === id) { return { ...item, ...body }; } return item; }); await this.setCache(cache_keys.list_key, updated); } return 1; }; /** * 根据ID设置后清除缓存 * @private * @param {string} cache_key - 缓存键 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<void>} - 无返回值 */ Store.prototype._clearCacheSetById = async function (cache_key, cache_obj_key) { await this.delCache(cache_key); await this.delCache(cache_obj_key); }; /** * 删除数据 * @param {object} query - 查询条件 * @returns {Promise<number>} - 删除成功的记录数量 */ Store.prototype.del = async function (query) { // 入参校验 if (typeof query !== 'object' || query === null) { throw new TypeError('查询条件必须是对象'); } if (Object.keys(query).length === 0) { throw new TypeError('查询条件不能为空'); } let way = this.config.way; let cache_key = this._genCacheKey('get', query); let cache_obj_key = this._genCacheKey('getObj', query); let affected_rows = 0; // 方式1、2、4、2、4:删除数据库中的数据 if ([1, 2, 4].includes(way)) { affected_rows = await this._delDb(query); } // 方式3:删除缓存中的数据据 if (way === 3 && this._cache) { affected_rows = await this._delCache(query, cache_key, cache_obj_key); } // 方式2:删除数据后清除相关缓存 if (way === 2 && this._cache) { await this._clearCacheAfterDel(cache_key, cache_obj_key); } return affected_rows; }; /** * 删除数据库中的数据 * @private * @param {object} query - 查询条件 * @returns {Promise<number>} - 影响行数 */ Store.prototype._delDb = async function (query) { let db = this._sql.db(); db.table = this.config.table; let result = await this._drive.del(db, query); return result.success ? 1 : 0; }; /** * 删除缓存中的数据据 * @private * @param {object} query - 查询条件 * @param {string} cache_key - 缓存键 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<number>} - 影响行数 */ Store.prototype._delCache = async function (query, cache_key, cache_obj_key) { // 从缓存获取当前数据 let cached = await this.getCache(cache_key); let affected_rows = 0; if (cached && Array.isArray(cached)) { // 过滤掉需要删除的数据 let filtered = cached.filter(item => { // 检查item是否匹配query条件 return !Object.keys(query).every(key => { return item[key] === query[key]; }); }); await this.setCache(cache_key, filtered); affected_rows = cached.length - filtered.length; } // 同时删除单条数据缓存 await this.delCache(cache_obj_key); return affected_rows; }; /** * 删除数据后清除相关缓存 * @private * @param {string} cache_key - 缓存键 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<void>} - 无返回值 */ Store.prototype._clearCacheAfterDel = async function (cache_key, cache_obj_key) { // 清除相关缓存,下次读取时会自动更新 await this.delCache(cache_key); await this.delCache(cache_obj_key); // 清除空查询的列表缓存 let all_cache_key = this._genCacheKey('get', {}); await this.delCache(all_cache_key); }; /** * 根据ID删除数据 * @param {string|number} id - 数据ID * @returns {Promise<number>} - 删除成功的记录数量 */ Store.prototype.delById = async function (id) { // 入参校验 if (!id && id !== 0) { throw new TypeError('ID不能为空'); } try { let way = this.config.way; // 构建查询条件 const query = { [this.config.id]: id }; let cache_key = this._genCacheKey('get', query); let cache_obj_key = this._genCacheKey('getObj', query); let affected_rows = 0; // 方式1、2、4、2、4:删除数据库中的数据 if ([1, 2, 4].includes(way)) { affected_rows = await this._delByIdDb(query); } // 方式3:删除缓存中的数据据 if (way === 3 && this._cache) { affected_rows = await this._delByIdCache(id, cache_key, cache_obj_key); } // 方式2:删除数据后清除相关缓存 if (way === 2 && this._cache) { await this._clearCacheAfterDel(cache_key, cache_obj_key); } return affected_rows; } catch (error) { this.log('error', '根据ID删除数据失败:', error); return 0; } }; /** * 根据ID删除数据库中的数据 * @private * @param {object} query - 查询条件 * @returns {Promise<number>} - 影响行数 */ Store.prototype._delByIdDb = async function (query) { let db = this._sql.db(); db.table = this.config.table; let result = await this._drive.del(db, query); return result.success ? 1 : 0; }; /** * 根据ID删除缓存中的数据据 * @private * @param {string|number} id - 数据ID * @param {string} cache_key - 缓存键 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<number>} - 影响行数 */ Store.prototype._delByIdCache = async function (id, cache_key, cache_obj_key) { // 从缓存获取当前数据 let all_cache_key = this._genCacheKey('get', {}); let cached = await this.getCache(all_cache_key); let affected_rows = 0; if (cached && Array.isArray(cached)) { // 过滤掉需要删除的数据 let filtered = cached.filter(item => { return item[this.config.id] !== id; }); await this.setCache(all_cache_key, filtered); affected_rows = cached.length - filtered.length; } // 同时删除单条数据缓存 await this.delCache(cache_key); await this.delCache(cache_obj_key); return affected_rows; }; /** * 根据ID删除数据后清除相关缓存 * @private * @param {string} cache_key - 缓存键 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<void>} - 无返回值 */ Store.prototype._clearCacheAfterDel = async function (cache_key, cache_obj_key) { // 清除相关缓存,下次读取时会自动更新 await this.delCache(cache_key); await this.delCache(cache_obj_key); // 清除空查询的列表缓存 let all_cache_key = this._genCacheKey('get', {}); await this.delCache(all_cache_key); }; /** * 统计记录数量 * @param {object} query - 查询条件 * @returns {Promise<number>} - 记录数量 */ Store.prototype.count = async function (query = {}) { // 入参校验 if (typeof query !== 'object' || query === null) { throw new TypeError('查询条件必须是对象'); } try { let db = this._sql.db(); db.table = this.config.table; db.count_ret = 'true'; let result = await this._drive.getMain(db, query, 'count'); return result.result ? result.result.count : 0; } catch (error) { this.log('error', '统计数据失败:', error); return 0; } }; /** * 统计字段总和 * @param {object} query - 查询条件 * @param {string} field - 统计字段 * @returns {Promise<number>} - 总和 */ Store.prototype.sum = async function (query, field) { // 入参校验 if (!field || typeof field !== 'string') { throw new TypeError('字段名必须是字符串'); } try { let db = this._sql.db(); db.table = this.config.table; return await db.sum(query, field); } catch (error) { this.log('error', '统计字段总和失败:', error); return 0; } }; /** * 统计字段平均值 * @param {object} query - 查询条件 * @param {string} field - 统计字段 * @returns {Promise<number>} - 平均值 */ Store.prototype.avg = async function (query, field) { // 入参校验 if (!field || typeof field !== 'string') { throw new TypeError('字段名必须是字符串'); } try { let db = this._sql.db(); db.table = this.config.table; return await db.avg(query, field); } catch (error) { this.log('error', '统计字段平均值失败:', error); return 0; } }; /** * 统计字段最大值 * @param {object} query - 查询条件 * @param {string} field - 统计字段 * @returns {Promise<number>} - 最大值 */ Store.prototype.max = async function (query, field) { // 入参校验 if (!field || typeof field !== 'string') { throw new TypeError('字段名必须是字符串'); } try { let db = this._sql.db(); db.table = this.config.table; return await db.max(query, field); } catch (error) { this.log('error', '统计字段最大值失败:', error); return 0; } }; /** * 统计字段最小值 * @param {object} query - 查询条件 * @param {string} field - 统计字段 * @returns {Promise<number>} - 最小值 */ Store.prototype.min = async function (query, field) { // 入参校验 if (!field || typeof field !== 'string') { throw new TypeError('字段名必须是字符串'); } try { let db = this._sql.db(); db.table = this.config.table; return await db.min(query, field); } catch (error) { this.log('error', '统计字段最小值失败:', error); return 0; } }; /** * 去重查询 * @param {object} query - 查询条件 * @param {string} field - 去重字段 * @param {string} orderby - 排序字段 * @returns {Promise<Array>} - 查询结果数组 */ Store.prototype.distinct = async function (query = {}, field, orderby = '') { // 入参校验 if (!field || typeof field !== 'string') { throw new TypeError('去重字段必须是字符串'); } try { let db = this._sql.db(); db.table = this.config.table; return await db.distinct(query, field, orderby); } catch (error) { this.log('error', '去重查询数据失败:', error); return []; } }; /** * 分组查询 * @param {object} query - 查询条件 * @param {string} groupby - 分组字段 * @param {string} fields - 查询字段 * @param {string} orderby - 排序字段 * @returns {Promise<Array>} - 查询结果数组 */ Store.prototype.group = async function (query, groupby, fields = '*', orderby = '') { // 入参校验 if (!groupby || typeof groupby !== 'string') { throw new TypeError('分组字段必须是字符串'); } try { let db = this._sql.db(); db.table = this.config.table; return await db.group(query, groupby, fields, orderby); } catch (error) { this.log('error', '分组查询数据失败:', error); return []; } }; /** * 开始事务 * @returns {Promise<object>} - 事务对象 */ Store.prototype.begin = async function () { try { let db = this._sql.db(); return await db.begin(); } catch (error) { this.log('error', '开始事务失败:', error); return null; } }; /** * 提交事务 * @param {object} txn - 事务对象 * @returns {Promise<boolean>} - 提交结果 */ Store.prototype.commit = async function (txn) { // 入参校验 if (!txn) { throw new TypeError('事务对象不能为空'); } try { let db = this._sql.db(); return await db.commit(txn); } catch (error) { this.log('error', '提交事务失败:', error); return false; } }; /** * 回滚事务 * @param {object} txn - 事务对象 * @returns {Promise<boolean>} - 回滚结果 */ Store.prototype.rollback = async function (txn) { // 入参校验 if (!txn) { throw new TypeError('事务对象不能为空'); } try { let db = this._sql.db(); return await db.rollback(txn); } catch (error) { this.log('error', '回滚事务失败:', error); return false; } }; /** * 获取缓存 * @param {string} key - 缓存键名 * @returns {Promise<*>|null} - 缓存值或null */ Store.prototype.getCache = async function (key) { // 入参校验 if (!key) { throw new TypeError('缓存键名不能为空'); } try { if (this._cache) { return await this._cache.get(key); } return null; } catch (error) { this.log('error', '获取缓存失败:', error); return null; } }; /** * 设置缓存 * @param {string} key - 缓存键名 * @param {*} value - 缓存值 * @param {number} expire - 过期时间(秒) * @returns {Promise<boolean>} - 设置结果 */ Store.prototype.setCache = async function (key, value, expire = 3600) { // 入参校验 if (!key) { throw new TypeError('缓存键名不能为空'); } try { if (this._cache) { return await this._cache.set(key, value, expire); } return false; } catch (error) { this.log('error', '设置缓存失败:', error); return false; } }; /** * 删除缓存 * @param {string} key - 缓存键名 * @returns {Promise<boolean>} - 删除结果 */ Store.prototype.delCache = async function (key) { // 入参校验 if (!key) { throw new TypeError('缓存键名不能为空'); } try { if (this._cache) { return await this._cache.del(key); } return false; } catch (error) { this.log('error', '删除缓存失败:', error); return false; } }; /** * 生成缓存键 * @param {string} action - 操作类型(get/set/add/del) * @param {object} params - 参数对象 * @returns {string} - 生成的缓存键 */ Store.prototype._genCacheKey = function (action, params = {}) { let base = `${this.config.table}:${action}`; if (!params || typeof params !== 'object') { return base; } // 对参数进行排序,确保相同参数生成相同的键 let sorted = Object.keys(params) .sort() .map(key => `${key}:${JSON.stringify(params[key])}`) .join('_'); return `${base}:${sorted}`; }; /** * 启动定时任务 */ Store.prototype._startTimers = function () { // 清除已有定时器 this._clearTimers(); let way = this.config.way; // 方式3:读缓存,写缓存,定时持久存 if (way === 3 && this._cache) { this._startSaveTimer(); } // 方式4:读缓存,写持久存,定时更新缓存 if (way === 4 && this._cache) { this._startCacheTimer(); } }; /** * 清除定时任务 */ Store.prototype._clearTimers = function () { if (this._save_timer) { clearInterval(this._save_timer); this._save_timer = null; } if (this._cache_timer) { clearInterval(this._cache_timer); this._cache_timer = null; } }; /** * 启动定时持久化定时器 */ Store.prototype._startSaveTimer = function () { // 定时将缓存数据持久化到数据库 this._save_timer = setInterval(() => { this._saveCacheToDb().catch(error => { this.log('error', '定时持久化数据失败:', error); }); }, this._save_interval * 1000); this.log('info', `已启动定时持久化定时器,间隔${this._save_interval}秒`); }; /** * 启动定时更新缓存定时器 */ Store.prototype._startCacheTimer = function () { // 定时从数据库更新缓存 this._cache_timer = setInterval(() => { this._updateCacheFromDb().catch(error => { this.log('error', '定时更新缓存失败:', error); }); }, this._cache_interval * 1000); this.log('info', `已启动定时更新缓存定时器,间隔${this._cache_interval}秒`); }; /** * 将缓存数据持久化到数据库 * @returns {Promise<void>} */ Store.prototype._saveCacheToDb = async function () { try { // 获取缓存中的所有数据 let cache_key = this._genCacheKey('get', {}); let cached = await this.getCache(cache_key); if (!cached || !Array.isArray(cached)) { return; } let db = this._sql.db(); db.table = this.config.table; // 批量保存数据到数据库 await this._drive.addList(db, cached); this.log('info', `成功持久化${cached.length}条数据到数据库`); } catch (error) { this.log('error', '持久化缓存到数据库失败:', error); throw error; } }; /** * 从数据库更新缓存 * @returns {Promise<void>} */ Store.prototype._setCacheFromDb = async function () { try { // 从数据库获取最新数据 let db = this._sql.db(); db.table = this.config.table; let result = await this._drive.get(db, {}); let data = result.result ? result.result.list : []; // 更新缓存 let cache_key = this._genCacheKey('get', {}); await this.setCache(cache_key, data); // 更新单条数据缓存 for (let item of data) { let cache_obj_key = this._genCacheKey('getObj', { [this.config.id]: item[this.config.id] }); await this.setCache(cache_obj_key, item); } this.log('info', `成功从数据库更新${data.length}条数据到缓存`); } catch (error) { this.log('error', '从数据库更新缓存失败:', error); throw error; } }; /** * 关闭定时器和资源 */ Store.prototype.close = function () { this._clearTimers(); }; /** * 构建ID查询条件 * @private * @param {string|number} id - 数据ID * @returns {object} - 查询条件 */ Store.prototype._buildQueryById = function (id) { return { [this.config.id]: id }; }; /** * 处理根据ID更新数据 * @private * @param {number} way - 缓存方式 * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @param {object} cache_keys - 缓存键 * @returns {Promise<number>} - 更新结果 */ Store.prototype._handleSetById = async function (way, query, body, cache_keys) { return await this._runSetById(way, query, body, cache_keys); }; /** * 处理更新后的缓存操作 * @private * @param {number} way - 缓存方式 * @param {object} cache_keys - 缓存键 * @returns {Promise<void>} */ Store.prototype._handleCacheAfterSet = async function (way, cache_keys) { return await this._runCacheAfterSet(way, cache_keys); }; /** * 根据ID从数据库获取数据 * @private * @param {string|number} id - 数据ID * @param {string} orderby - 排序字段 * @param {string} fields - 查询字段 * @returns {Promise<object|null>} - 查询到的数据对象或null */ Store.prototype._getByIdDb = async function (id, orderby, fields) { let query = this._buildQueryById(id); let db = this._sql.db(); db.table = this.config.table; // 设置查询字段/排序 this._configDbQuery(db, fields, orderby); return await this._getFromDb(query); }; /** * 从数据库获取列表数据 * @private * @param {object} query - 查询条件 * @returns {Promise<Array>} - 数据列表 */ Store.prototype._getListFromDb = async function (query) { let db = this._sql.db(); db.table = this.config.table; let result = await this._drive.get(db, query); return result.result ? result.result.list : []; }; /** * 更新方式2的缓存 * @private * @param {object} data - 要缓存的数据 * @returns {Promise<void>} */ Store.prototype._updateCacheForWay2 = async function (data) { if (!this._cache) { return; } let cache_key = this._genCacheKey('get', {}); let cached = await this.getCache(cache_key) || []; // 查找是否已存在 let idx = cached.findIndex(item => item[this.config.id] === data[this.config.id]); if (idx >= 0) { cached[idx] = data; } else { cached.push(data); } await this.setCache(cache_key, cached); }; /** * 更新方式2的列表缓存 * @private * @param {Array} list - 数据列表 * @returns {Promise<void>} */ Store.prototype._updateCacheForWay2List = async function (list) { if (!this._cache) { return; } let cache_key = this._genCacheKey('get', {}); await this.setCache(cache_key, list); }; /** * 更新缓存(用于set操作后) * @private * @param {object} query - 查询条件 * @param {object} body - 更新内容 * @param {string} cache_obj_key - 对象缓存键 * @returns {Promise<void>} */ Store.prototype._updateCache2Set = async function (query, body, cache_obj_key) { if (!this._cache) { return; } // 更新单条数据缓存 let item = await this.getCache(cache_obj_key); if (item) { await this.setCache(cache_obj_key, { ...item, ...body }); } }; /** * 批量添加数据处理(按way分发) * @private * @param {Array} list - 要添加的数据列表 * @param {number} way - 读写方式 * @returns {Promise<object>} - 添加结果 */ Store.prototype._addListByWay = async function (list, way) { let result; // 方式1、2、4:写数据库 if ([1, 2, 4].includes(way)) { result = await this._addListToDb(list); } // 方式3:写缓存 if (way === 3 && this._cache) { result = await this._addListToCache(list); } // 方式2:写后更新缓存 if (way === 2 && this._cache && result && result.result) { // 清除列表缓存,下次读取时会自动更新 let cache_key = this._genCacheKey('get', {}); await this.delCache(cache_key); } return result; }; /** * 初始化驱动 * @private * @returns {Promise<void>} */ Store.prototype._initDrive = async function () { if (this._drive) { return; } this._drive = new Drive(this.config); this._drive.sql = this._sql; this._drive.cache = this._cache; }; exports.Store = Store;