UNPKG

mm_os

Version:

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

1,465 lines (1,343 loc) 37.5 kB
const Excel = require('mm_excel'); if (!global.$) { global.$ = {}; } if (!$.dict) { $.dict = {}; } $.dict.user_id = 'user_id'; $.dict.player_id = 'player_id'; $.dict.role_id = 'role_id'; /** * Sql操作驱动类 * @class */ class Drive { static config = { // 表名 {0} 代表可前端传参自定义查询的表 table: '{0}', // ID 用于查询时的主键 例如:id id: 'id', // 主键 用于水平连表查询时 例如:id key: '', // 排序 {0} 代表可前台传参自定义排序规则 格式: `name` asc, `id` desc orderby: '{0}', // 默认排序 `id` desc orderby_default: '', // 显示的字段 {0} 代表可前台传参自定义查询的字段, 例如: `id`,`username`,`name`,`email` field: '{0}', // 默认显示字段 例如: `id`,`username`,`name` field_default: '*', // 隐藏字段 有些字段即使前端请求也不能返回,这是通过隐藏字段将其过滤掉, 例如:password *表示包含匹配 field_hide: ['*password*', '*token*', 'salt'], // 分页大小,默认一页显示条数 page_size: 30, /* 过滤参数 */ 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', }, // 分隔符 用于查询时的多条件处理 sep: '|', // 支持的方法 add增、del删、set改、get查, 只填get表示只支持查询 // import export del_repeat", method: 'add del set get get_obj import export del_repeat avg sum count update', // sql查询语句 query: {}, // 默认查询, 当查询条件中不包含该项时,默认添加该项。 例如: { 'age': '`age` < 20' } , 当查询参含有age,不调用该项,不存在时,sql会增加该项 query_default: {}, // sql更改语句 update: {}, // 默认添加条件,当不包含该项时,默认添加该项。 例如: { 'age': '`age` += 1' } , 当查询参含有age,不调用该项,不存在时,sql会增加该项 body_default: {}, // 文件路径, 当调用函数不存在时,会先从文件中加载 main: '', // 回调函数名 用于决定调用脚本的哪个函数 func_name: '', // 参数 [] params: null, // 格式 format: [ /* { // 表名,当选择转换方式 表转换时需填写 'table': 'mm_web_region', // 查询条件,用于加速转换 'query': { 'group': '市' }, // 表标题名 'title': '所属省份', // 转换ID 'id': 'province_id', // 转换主键 'key': 'province', // 取名 'name': 'name', // 列表 'list': [{ 'province_id': 1, 'name': '广东省' }, { 'province_id': 2, 'name': '广西省' }, { 'province_id': 3, 'name': '湖南省' } ] }, { 'title': '是否可见', 'key': 'show', 'list': ['否', '是'] } */ ], /* 去重 */ del_repeat: { // 判断重复的字段,例如字段名 number group_by: '', // 排序方式 例如: `diJia` ASC order_by: '', }, /* 逻辑符 */ logic: {}, // 输出sql语句 log: false, }; /** * 构造函数 * @param {object} config 配置参数 * @class */ constructor(config) { // 配置参数 this.config = { ...Drive.config }; /* 通用项 */ this.params = null; this.setConfig(config); } } /** * 日志 * @param {string} level 日志级别 * @param {string} msg 日志消息 * @param {...any} args 日志参数 */ Drive.prototype.log = function (level, msg, ...args) { this.getLogger()[level](`[${this.constructor.name}] ${msg}`, ...args); }; /** * 设置配置参数 * @param {object} config 配置参数 */ Drive.prototype.setConfig = function (config) { $.push(this.config, config, true); }; /** * 执行前, 可用于过滤参数 * @param {object} db 数据库管理器 * @param {object} query 查询url参数 * @param {object} body 修改时置入body的参数 * @returns {object} 过滤后的参数 */ Drive.prototype.before = async function (db, query, body) { this.log('info', 'before', query, body, db); return { query, body, }; }; /** * 验证, 用于判断是否执行 * @param {object} db 数据管理器 * @param {object} query 查询url参数 * @param {object} body 修改时置入body的参数 * @returns {boolean} 验证通过返回true, 失败返回false */ Drive.prototype.check = async function (db, query, body) { this.log('info', 'check', query, body, db); return true; }; /** * 执行后操作,可用于附加执行 * @param {object} db 数据管理器 * @returns {object} 最终执行结果 */ Drive.prototype.after = async function (db) { return db.ret; }; /** * 执行修改 * @param {object} db 数据库管理器 * @param {object} query 查询url参数 * @param {object} body 修改时置入body的参数 * @returns {object} 执行结果 */ Drive.prototype.run = async function (db, query, body) { await this.before(db, query, body); if (this.check(db, query, body)) { var ret = await this.main(db, query, body); if (!ret.error) { db.ret = ret; return await this.after(db); } return ret; } return null; }; /** * SQL操作准备 * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @returns {object} 返回准备参数 */ Drive.prototype.ready = function (db, query) { var cg = this.config; var qy = { ...query }; $.push(db.config.filter, cg.filter, true); db.filter(qy); return { cg, qy, }; }; /** * 获取查询字段设置 * @param {object} cg 配置 * @param {string} field 字段值 * @param {string} method 查询方法 * @returns {string} 字段设置 */ Drive.prototype._getFieldSet = function (cg, field, method) { if (cg.field.has('*{0}*')) { if (field) { return cg.field.replace('{0}', field); } else if (method === 'get_obj' && cg.field_obj) { return cg.field_obj + ''; } else if (cg.field_default) { return cg.field_default + ''; } } return cg.field + ''; }; /** * 获取排序方式设置 * @param {object} cg 配置 * @param {string} orderby 排序字段 * @returns {string} 排序设置 */ Drive.prototype._getOrderbySet = function (cg, orderby) { if (cg.orderby.has('*{0}*')) { if (orderby) { return cg.orderby.replace('{0}', orderby); } else if (cg.orderby_default) { return cg.orderby_default + ''; } } return cg.orderby + ''; }; /** * 处理默认查询条件 * @param {string} query_str 查询字符串 * @param {object} query_default 默认查询条件 * @param {object} query 查询参数 * @param {object} db 数据库副本 * @returns {string} 处理后的查询字符串 */ Drive.prototype._runDefaultGet = function (query_str, query_default, query, db) { var ret_str = query_str; if (Object.keys(query_default).length > 0) { var qt_str = db.tplQuery(query, query_default); if (qt_str) { ret_str = query_str + ' AND ' + qt_str; } } return ret_str; }; /** * 生成查询条件 * @param {object} db 数据库管理器 * @param {object} query 查询参数 * @param {string} method 查询方法 * @returns {string} 返回查询条件 */ Drive.prototype.toWhere = function (db, query, method) { var ready = this.ready(db, query, {}); var cg = ready.cg; var qy = $.clone(ready.qy); if (!query.size && cg.page_size) { db.size = cg.page_size + 0; } if (db.size > 0 && db.page === 0) { db.page = 1; } var f = db.config.filter; // 获取查询字段设置 var field = query[f.field]; var fld = this._getFieldSetting(cg, field, method); // 获取排序方式设置 var orderby = query[f.orderby]; var ord = this._getOrderbySetting(cg, orderby); // 处理查询参数 if (!cg.field.has('*{0}*') && field) { qy[f.field] = field; } if (!cg.orderby.has('*{0}*') && orderby) { qy[f.orderby] = orderby; } // 应用字段和排序设置到数据库副本 db.field = fld; db.orderby = ord; var query_str = db.tplQuery(qy, cg.query); var qt = cg.query_default; // 处理默认查询条件 var final_query_str = this._handleDefaultQuery(query_str, qt, qy, db); return final_query_str; }; /** * 查询(主要) * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @param {object} method 方法 * @returns {object} 返回查询结果 */ Drive.prototype.getMain = async function (db, query, method) { var ret; var query_str = this.toWhere(db, query, method); // 查询 try { if (db.count_ret === 'true') { ret = await db.getCountSql(query_str, db.orderby, db.field); } else { ret = await db.getSql(query_str, db.orderby, db.field); } // 对查询结果进行脱敏处理 if ( ret && ret.list && this.config.field_hide && this.config.field_hide.length > 0 ) { ret.list = this._filterFields(ret.list); } return ret; } catch { return $.ret.error(10000, '查询错误'); } }; /** * 查询 * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @returns {object} 返回查询结果 */ Drive.prototype.get = async function (db, query) { var ret = await this.getMain(db, query); if (ret && ret.list) { return $.ret.list(ret.list); } return $.ret.list([]); }; /** * 查询单条数据 * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @returns {object} 返回查询结果 */ Drive.prototype.getObj = async function (db, query) { var qy = $.clone(query); qy.page = 1; qy.size = 1; var ret = await this.getMain(db, qy, 'get_obj'); var result; if (ret && ret.list) { if (ret.list.length > 0) { result = $.ret.obj(ret.list[0]); } else { // 查询不到数据时返回空对象,而不是null result = $.ret.obj({}); } } else { // 查询不到数据时返回空对象,而不是null result = $.ret.obj({}); } return result; }; /** * 修改(主要) * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @param {object} body 修改项 * @returns {object} 返回修改结果 */ Drive.prototype.setMain = async function (db, query, body) { var ret; var ready = this.ready(db, query, body); var cg = ready.cg; var qy = ready.qy; var key = cg.key; if (body[key]) { qy[key] = body[key]; } var query_str = db.tplQuery(qy, cg.query); var qt = cg.query_default; if (Object.keys(qt).length > 0) { var id = $.dict.user_id; var user_id = '0'; var user = db.user; if (user && user[id]) { user_id = user[id]; } for (var k in qt) { if (!qy[k]) { query_str += ' && ' + qt[k].replace('{' + id + '}', user_id); } } if (query_str.startsWith(' && ')) { query_str = query_str.replace(' && ', ''); } } var sql = db.tplBody(body, cg.update); var n = await db.setSql(query_str, sql); if (n < 1) { ret = $.ret.error(500, '修改失败!\n' + db.error.message); $.log.error('修改SQL', db.sql, db.error); } else { ret = $.ret.bl(true, '修改成功!'); } return ret; }; /** * 修改 * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @param {object} body 修改项 * @returns {object} 返回查询结果 */ Drive.prototype.set = async function (db, query, body) { return await this.setMain(db, query, body); }; /** * 添加(主要) * @param {object} db 数据库操作类 * @param {object} body 添加项 * @returns {object} 返回查询结果 */ /** * 处理默认参数值 * @param {object} body 参数对象 * @param {object} bt 默认参数配置 * @param {object} db 数据库操作类 * @returns {object} 处理后的参数对象 */ Drive.prototype._runDefaults = function (body, bt, db) { var id = $.dict.user_id; var user_id = '0'; var user = db.user; var result = $.clone(body); if (user && user[id]) { user_id = user[id]; } for (var k in bt) { if (!result[k]) { result[ bt[k] .replace('{' + id + '}', user_id) .left('=') .trim('`') ] = bt[k] .replace('{' + id + '}', user_id) .right('=') .trim('\''); } } return result; }; /** * 添加(主要) * @param {object} db 数据库操作类 * @param {object} body 添加项 * @returns {object} 返回查询结果 */ Drive.prototype.addMain = async function (db, body) { var ready = this.ready(db, {}); var cg = ready.cg; var result; var bd = $.clone(body); if (Object.keys(bd).length > 0) { var bt = cg.body_default; if (Object.keys(bt).length > 0) { bd = this._processDefaults(bd, bt, db); } var n = await db.add(bd); if (n < 1) { result = $.ret.error(500, '添加失败!\n' + db.error.message); $.log.error('添加SQL', db.sql, db.error); } else { result = $.ret.bl(true, '添加成功!'); } } else { result = $.ret.error(30001, '参数不能为空'); } if (cg.log) { $.log.debug('添加SQL语句', db.sql); } return result; }; /** * 添加 * @param {object} db 数据库操作类 * @param {object} body 修改项 * @returns {object} 返回添加结果 */ Drive.prototype.add = async function (db, body) { return await this.addMain(db, body); }; /** * 删除(主要) * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @returns {object} 返回查询结果 */ Drive.prototype.delMain = async function (db, query) { var ret; var { cg, qy } = this.ready(db, query, {}); var query_str = db.tplQuery(qy, cg.query); var bl = await db.run(query_str); if (bl < 1) { ret = $.ret.error(500, '删除失败!\n' + db.error.message); $.log.error('删除SQL', db.sql, db.error); } else { ret = $.ret.bl(true, '删除成功!'); } if (cg.log) { $.log.debug('删除SQL语句', db.sql); } return ret; }; /** * 删除 * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @returns {object} 返回删除结果 */ Drive.prototype.del = async function (db, query) { return await this.delMain(db, query); }; /** * 批量添加(主要) * @param {object} db 数据库操作类 * @param {Array} list 添加项列表 * @returns {object} 返回批量添加结果 */ Drive.prototype.addListMain = async function (db, list) { if (Array.isArray(list) && list.length > 0) { var success_num = 0, error_num = 0, errors = [], list_error = []; for (var i = 0; i < list.length; i++) { var result = await this.addMain(db, list[i]); if (result.error) { error_num++; errors.push(result.message); list_error.push(list[i]); } else { success_num++; } } var ret = $.ret.bl( success_num > 0, success_num > 0 ? '批量添加成功!' : '批量添加失败!', ); ret.result = { total: list.length, success: success_num, error: error_num, list_error: list_error, errors: errors, }; return ret; } return $.ret.error(30001, '参数不能为空'); }; /** * 批量添加 * @param {object} db 数据库操作类 * @param {Array} list 添加项列表 * @returns {object} 返回批量添加结果 */ Drive.prototype.addList = async function (db, list) { return await this.addListMain(db, list); }; /** * 添加或修改(主要) * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @param {object} body 修改项 * @returns {object} 返回修改结果 */ Drive.prototype.addOrSetMain = async function (db, query, body) { var ret; var { cg, qy } = this.ready(db, query, body); if (Object.keys(body).length > 0 && Object.keys(qy).length > 0) { var n = await db.addOrSet(qy, body); if (n < 1) { ret = $.ret.error(500, '操作失败!\n' + db.error.message); $.log.error('添加或修改SQL', db.sql, db.error); } else { ret = $.ret.bl(true, '操作成功!'); } } else { ret = $.ret.error(30001, '参数不能为空'); } if (cg.log) { $.log.debug('添加或修改SQL语句', db.sql); } return ret; }; /** * 添加或修改 * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @param {object} body 修改项 * @returns {object} 返回查询结果 */ Drive.prototype.addOrSet = async function (db, query, body) { return await this.addOrSetMain(db, query, body); }; /** * 获取数据类型 * @param {string} type 数据库数据类型 * @returns {string} 返回数据类型 */ Drive.prototype.getType = function (type) { if (type.indexOf('int') !== -1) { return 'number'; } else { return 'string'; } }; /** * 从配置获取参数 * @param {object} cg 配置 * @returns {Array|null} 参数列表或null */ Drive.prototype._getArgsFromConfig = function (cg) { if (cg.params) { return cg.params; } if (this.params) { return this.params; } return null; }; /** * 从池获取字段列表 * @param {object} cg 配置 * @returns {Array} 字段列表 */ Drive.prototype._getFieldsFromPool = function (cg) { var lt = []; if ($.pool.sql) { var pool = $.pool.sql['sys']; if (pool) { var dt = pool.get(cg.table); if (dt && dt.config) { var list = dt.config.fields; for (var i = 0; i < list.length; i++) { var name = list[i].replace(/`/g, ''); lt.push({ name, title: name, }); } } } } return lt; }; /** * 从数据库获取字段列表 * @param {object} db 数据库操作类 * @param {object} cg 配置 * @returns {Array} 字段列表 */ Drive.prototype._getFieldsFromDb = async function (db, cg) { var lt = []; db.table = cg.table; var list = await db.fields(); for (var i = 0; i < list.length; i++) { var o = list[i]; var note = o.note ? o.note.replace(':', ':') : o.name; lt.push({ name: o.name, title: note.left(':', true), type: this.getType(o.type), }); } return lt; }; /** * 过滤参数 * @param {Array} lt 字段列表 * @param {string} fields 字段字符串 * @param {object} cg 配置 * @returns {Array} 过滤后的参数列表 */ Drive.prototype._filterArgs = function (lt, fields, cg) { var field = fields; if (!field) { field = cg.field_default; } if (field && field !== '*') { var arr = field.replace(/`/g, '').split(','); var params = []; arr.map((name) => { var obj = lt.getObj({ name, }); if (obj) { params.push(obj); } }); return params; } return lt; }; /** * 获取数据 * @param {object} db 数据库操作类 * @param {Array} fields 要获取的字段数组 * @returns {Array} 返回参数信息列表 */ Drive.prototype.getFields = async function (db, fields) { var cg = this.config; // 从配置获取参数 var params_from_config = this._getFromConfig(cg); if (params_from_config) { return params_from_config; } // 从池获取字段列表 var lt = this._getFieldsFromPool(cg); // 如果池中没有,从数据库获取 if (!lt.length) { lt = await this._getFieldsFromDb(db, cg); } // 根据字段过滤参数 return this._filter(lt, fields, cg); }; /** * 获取导入导出格式 * @param {object} db 数据库操作类 * @returns {Array} 格式列表 */ Drive.prototype.getFormat = async function (db) { var dbs = { ...db }; dbs.size = 0; var fmt = this.config.format; for (var i = 0; i < fmt.length; i++) { var o = fmt[i]; if (o.table) { if (!o.list || o.list.length == 0) { dbs.table = o.table; if (!o.id) { o.id = o.key; } o.list = await dbs.getSql(o.where, null, o.id + ',' + o.name); if (o.id !== o.key) { o.list.map((m) => { var m_clone = $.clone(m); m_clone[o.key] = m_clone[o.name]; return m_clone; }); } } } } return fmt; }; /** * 处理文件路径 * @param {string} file 文件名 * @returns {string} 处理后的文件路径 */ Drive.prototype._runFilePath = function (file) { var file_copy = file; if (file_copy.indexOf($.run_path) !== 0) { file_copy = file_copy.replace(this.url_path, this.save_dir); var path = $.config.get('static_path') || '/static/'; file_copy = file_copy.fullname(path); } return file_copy; }; /** * 加载文件数据 * @param {string} file 文件名 * @param {Array} fields 字段列表 * @param {Array} format 格式列表 * @returns {Array} 数据数组 */ Drive.prototype._loadFileInfo = async function (file, fields, format) { var jarr = []; if (file.endsWith('.json')) { jarr = file.loadJson(); } else if (file.endsWith('.xml')) { jarr = file.loadXml(); } else { var excel = new Excel({ file, fields, format, }); try { jarr = await excel.load(); } catch (e) { $.log.error('导入文件', e); } finally { excel.clear(); excel = null; } } return jarr; }; /** * 导入有主键的数据 * @param {object} db 数据库操作类 * @param {Array} jarr 数据数组 * @param {string} key 主键字段 * @returns {object} 导入结果 */ Drive.prototype._importKey = async function (db, jarr, key) { var list = []; var errors = []; var list_error = []; for (var i = 0; i < jarr.length; i++) { var o = jarr[i]; var qy = {}; qy[key] = o[key]; var n = await db.addOrSet(qy, o); if (n < 1) { errors.push(db.error); list_error.push(o); } else { list.push(o); } } return { list, errors, list_error }; }; /** * 导入无主键的数据 * @param {object} db 数据库操作类 * @param {Array} jarr 数据数组 * @returns {object} 导入结果 */ Drive.prototype._importWithoutKey = async function (db, jarr) { var list = []; var errors = []; var list_error = []; for (var i = 0; i < jarr.length; i++) { var o = jarr[i]; var n = await db.add(o); if (n < 1) { errors.push(db.error); list_error.push(o); } else { list.push(o); } } return { list, errors, list_error }; }; /** * 构建导入结果 * @param {Array} list 成功列表 * @param {Array} jarr 原始数据 * @param {Array} list_error 失败列表 * @param {Array} errors 错误列表 * @returns {object} 结果对象 */ Drive.prototype._buildImportRet = function (list, jarr, list_error, errors) { var bl = list.length === jarr.length; var body = $.ret.bl(bl, bl ? '导入成功!' : '导入失败!'); body.result.list = list; if (errors.length) { body.result.list_error = list_error; body.result.errors = errors; } return body; }; /** * 导入数据(主要) * @param {object} db 数据库操作类 * @param {string} file 文件名 * @returns {object} 返回导入结果 */ Drive.prototype.importMain = async function (db, file) { var fields = await this.getFields(db); var format = await this.getFormat(db); // 处理文件路径 var file_copy = this._processFilePath(file); if (!file_copy.hasFile()) { return $.ret.error(30001, file_copy + '文件不存在!'); } // 加载文件数据 var jarr = await this._loadFile(file, fields, format); if (!jarr.length) { return $.ret.error(10000, '要导入的数据不能为空!'); } db.table = db.table || this.config.table; var key = this.config.key; // 导入数据 var imp; if (jarr[0][key]) { imp = await this._importKey(db, jarr, key); } else { imp = await this._importWithoutKey(db, jarr); } // 构建结果 return this._buildImport( imp.list, jarr, imp.list_error, imp.errors, ); }; /** * 导入数据 * @param {object} db 数据库操作类 * @param {string} file 文件名 * @returns {object} 返回执行结果 */ Drive.prototype.import = async function (db, file) { return await this.importMain(db, file); }; /** * 导出数据(主要) * @param {object} db 数据库操作类 * @param {object} query 查询参数 * @param {object} body 正文参数(导出设置) * @property {string} body.fields 需要导出的字段 例如: `username`,`gm`,`vip` * @property {string} body.file 文件名 例如: 用户名.xlsx 、用户信息.csv 、用户账户.xls * @property {string} body.path 文件路径 例如: /static/download, 可不填写 * @returns {object} 返回执行结果 */ Drive.prototype.exportMain = async function (db, query, body) { var by = await this.getMain(db, query); var message = ''; if (db.error) { message = db.error.message; return $.ret.error(10000, message); } var config = this._getExportConfig(db, query, body); var fields = await this.getFields(db, config.fields); var format = await this.getFormat(db); var file = await this._processExcelExport( config, fields, format, by.result.list, ); return this._buildExport(file, config.name, message); }; /** * 获取导出配置 * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @param {object} body 导出设置 * @returns {object} 导出配置对象 */ Drive.prototype._getExportConfig = function (db, query, body) { var { path, file, fields } = body; var table = db.table || this.config.table; var date = new Date(); var name = table + '_' + date.stamp() + '.xlsx'; path = this._getExportPath(path); file = this._getExportFile(file, name); fields = this._getExportFields(fields, query); return { path, file, fields, name, }; }; /** * 获取导出路径 * @param {string} path 路径参数 * @returns {string} 导出路径 */ Drive.prototype._getExportPath = function (path) { if (!path) { return $.config.get('static_path') || '/static/'; } return path; }; /** * 获取导出文件 * @param {string} file 文件参数 * @param {string} name 文件名 * @returns {string} 导出文件路径 */ Drive.prototype._getExportFile = function (file, name) { if (!file) { return this.save_dir + name; } return file; }; /** * 获取导出字段 * @param {string} fields 字段参数 * @param {object} query 查询条件 * @returns {string} 导出字段 */ Drive.prototype._getExportFields = function (fields, query) { if (!fields) { if (query.field) { return query.field; } else { var f = this.config.field_default; if (f.length !== '*') { return f; } } } return fields; }; /** * 处理Excel导出 * @param {object} config 导出配置 * @param {Array} fields 字段列表 * @param {object} format 格式设置 * @param {Array} list 数据列表 * @returns {string} 导出的文件路径 */ Drive.prototype._runExcelExport = async function ( config, fields, format, list, ) { var file = config.file.fullname(config.path); file.addDir(); var excel = new Excel({ file, params: fields, format, }); try { file = await excel.save(list); } catch (e) { $.log.error('导出保存文件失败!', e); } finally { excel.clear(); excel = null; } return file; }; /** * 构建导出结果 * @param {string} file 文件路径 * @param {string} name 文件名 * @param {string} message 消息 * @returns {object} 导出结果 */ Drive.prototype._buildExportRet = function (file, name, message) { var body = $.ret.bl(!!file, file ? '导出成功!' : '导出失败!'); body.result.file = file; body.result.url = this.url_path + name; if (message) { body.result.message = message; } return body; }; /** * 导出数据 * @param {object} db 数据库操作类 * @param {object} query 查询条件 * @param {object} body 导出设置 * @returns {object} 返回执行结果 */ Drive.prototype.export = async function (db, query, body) { return await this.exportMain(db, query, body); }; /** * 删除重复项(主要) * @param {object} db 数据库管理器 * @param {object} params 查询参数 * @returns {object} 返回执行结果 */ Drive.prototype.delRepeatMain = async function (db, params) { var msg = ''; var cg = this.config.del_repeat; var pm = $.clone(params); var order = pm.orderby || cg.orderBy; delete pm.orderby; var group = pm.groupby || cg.groupBy; delete pm.groupby; var f = db.config.filter; // 设置查询字段 var field = pm[f.field] || group; delete pm[f.field]; db.field = field; var sql = db .toGetSql(pm) .replace(' * ', ' `' + field + '`, count(*) as len '); sql += ` GROUP BY ${group}`; sql = 'SELECT * FROM (' + sql + ') a WHERE len > 1'; var list = await db.run(sql); if (list.length) { db.page = 1; db.size = 1; var key = this.config.key; for (var i = 0; i < list.length; i++) { var o = list[i]; var len = o.len - 1; delete o.len; for (var n = 0; n < len; n++) { var obj = await db.getObj(o, order, key); if (obj) { await db.del(obj); } } } } else { msg = '没有重复项。'; } return $.ret.bl(!msg, msg ? '去重失败!原因:' + msg : '去重成功!'); }; /** * 删除重复项 * @param {object} db 数据库管理器 * @param {object} query 查询条件 * @returns {object} 返回执行结果 */ Drive.prototype.delRepeat = async function (db, query) { return await this.delRepeatMain(db, query); }; /** * 执行模板操作 * @param {object} db 数据管理器 * @param {object} query 查询参数 * @param {object} body 正文参数 * @returns {object} 返回执行结果 */ Drive.prototype.main = async function (db, query, body) { var cg = this.config; var method = query.method; if (!method) { method = 'get'; } if (!cg.method.has('*' + method + '*')) { return $.ret.error(50001, '不支持的操作方式'); } var qy = { ...query }; delete qy.method; if (this[method]) { db.method = method; // 过滤查询参数 var f = cg.filter; var table = query[f.table]; // 设置操作的数据表 if (cg.table.has('*{0}*')) { if (table) { db.table = cg.table.replace('{0}', table); } else { return $.ret.error(30001, '表名不能为空'); } } else { db.table = cg.table + ''; } // 根据方法类型传递不同参数 if (method === 'add' || method === 'addList') { return await this[method](db, { ...body }); } else { return await this[method](db, qy, { ...body }); } } else { return $.ret.error(50001, '不支持的操作方式'); } }; /** * 总计 * @param {object} db 数据库管理器 * @param {object} pm 查询条件 * @returns {object} 返回执行结果 */ Drive.prototype.sumMain = async function (db, pm) { var ret; var pm_clone = $.clone(pm); var orderby = pm_clone.orderby || ''; delete pm_clone.orderby; var groupby = pm_clone.groupby; delete pm_clone.groupby; var f = db.config.filter; var field = pm_clone[f.field]; delete pm_clone[f.field]; var query_str = this.toWhere(db, pm_clone, 'get_list'); if (!groupby || !field) { ret = $.ret.error(30000, '参数groupby、field是必须的,且值不能为空!'); } else { var list = await db.groupSumSql(query_str, groupby, field, orderby); if (!list.length && db.error) { // $.log.error('SUM查询SQL', db.sql, db.error); ret = $.ret.body(db.error); } else { ret = $.ret.list(list); } } return ret; }; /** * 总计 * @param {object} db 数据库管理器 * @param {object} query 查询条件 * @returns {object} 返回执行结果 */ Drive.prototype.sum = async function (db, query) { return await this.sumMain(db, query); }; /** * 平均值 * @param {object} db 数据库管理器 * @param {object} pm 查询条件 * @returns {object} 返回执行结果 */ Drive.prototype.avgMain = async function (db, pm) { var ret; var pm_clone = $.clone(pm); var orderby = pm_clone.orderby || ''; delete pm_clone.orderby; var groupby = pm_clone.groupby; delete pm_clone.groupby; var f = db.config.filter; var field = pm_clone[f.field]; delete pm_clone[f.field]; var query_str = this.toWhere(db, pm_clone, 'get_list'); if (!groupby || !field) { ret = $.ret.error(30000, '参数groupby、field是必须的,且值不能为空!'); } else { var list = await db.groupAvgSql(query_str, groupby, field, orderby); if (!list.length && db.error) { // $.log.error('AVG查询SQL', db.sql, db.error); ret = $.ret.body(db.error); } else { ret = $.ret.list(list); } } return ret; }; /** * 平均值 * @param {object} db 数据库管理器 * @param {object} query 查询条件 * @returns {object} 返回执行结果 */ Drive.prototype.avg = async function (db, query) { return await this.avgMain(db, query); }; /** * 总计 * @param {object} db 数据库管理器 * @param {object} params 查询条件 * @returns {object} 返回执行结果 */ Drive.prototype.countMain = async function (db, params) { var ret; var pm_clone = $.clone(params); var orderby = pm_clone.orderby || ''; delete pm_clone.orderby; var groupby = pm_clone.groupby; delete pm_clone.groupby; var f = db.config.filter; var field = pm_clone[f.field]; delete pm_clone[f.field]; var query_str = this.toWhere(db, pm_clone, 'get_list'); if (!groupby || !field) { ret = $.ret.error(30000, '参数groupby、field是必须的,且值不能为空!'); } else { var list = await db.groupCountSql(query_str, groupby, field, orderby); if (!list.length && db.error) { // $.log.error('COUNT查询SQL', db.sql, db.error); ret = $.ret.body(db.error); } else { ret = $.ret.list(list); } } return ret; }; /** * 总计 * @param {object} db 数据库管理器 * @param {object} query 查询条件 * @returns {object} 返回执行结果 */ Drive.prototype.count = async function (db, query) { return await this.countMain(db, query); }; /** * 过滤敏感字段 * @param {Array} list 数据列表 * @returns {Array} 过滤后的数据列表 */ Drive.prototype._filterSens = function (list) { if (!list || !Array.isArray(list) || list.length === 0) { return list; } var lt = []; for (var i = 0; i < list.length; i++) { var item = this._filter(list[i]); lt.push(item); } return lt; }; /** * 过滤单个对象的敏感字段 * @param {object} item 数据对象 * @returns {object} 过滤后的数据对象 */ Drive.prototype._filterObj = function (item) { if (!item || typeof item !== 'object') { return item; } var obj = { ...item }; // 敏感字段集合 var arr = this.config.field_hide; for (var i = 0; i < arr.length; i++) { var word = arr[i]; for (var key in item) { if (key.has(word)) { delete obj[key]; } } } return obj; }; module.exports = { Drive };