mm_os
Version:
MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。
1,756 lines (1,543 loc) • 47.4 kB
JavaScript
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;