const mysql = require('mysql'); const sys = require('../config/sys_test'); const pool = mysql.createPool({ connectionLimit: 1, connectTimeout: 60 * 60 * 1000, host: sys.mysql.host, user: sys.mysql.user, password: sys.mysql.pwd, database: sys.mysql.db }); class DBModule { constructor(args = {}) { Object.keys(args).forEach(key => { this[key] = args[key]; }) } /** * 使用连接池中已有的连接执行sql脚本 * @param {string} sql sql脚本 * @param {array} args sql中的参数 * @return {promise} */ _query(sql, args = []) { return new Promise((resolve, reject) => { pool.getConnection((err, connection) => { if (err) return reject(err); if (sys.debug) { console.log(`run ${sql} \narray ${args.map(item=> `${item} type: ${typeof item}\n`)}`) } connection.query(sql, args, (err, result) => { if (err) { reject(err); } else { resolve(result); } connection.release(); }); }); }); } /** * 执行对象插入数据库操作 * @return {promise} */ insert() { let sql, {attrs, vals, err} = DBModule.getDBModuleArgs(this); if (err) { throw `object ${this.constructor.toString()} lack necessary field (table, fields)`; } sql = `insert into \`${this.table}\`(${attrs.join(',')}) values(${vals.map(()=>'?')})`; return this._query(sql, vals); } /** * 执行对象更新映射到数据库操作,对象必须存在有效的主键 * @return {promise} */ update() { let sql, setFields, attrs, vals, err; if (this[this.primary] === undefined || this[this.primary] === null) { throw `object ${this.constructor.toString()} ${this.primary} is ${this[this.primary]}`; } ({attrs, vals, err} = DBModule.getDBModuleArgs(this)); if (err) { throw `object ${this.constructor.toString()} lack necessary field (table, fields)`; } vals.push(this[this.primary]); setFields = attrs.map(attr=>`${attr}=?`); sql = `update \`${this.table}\` set ${setFields.join(',')} where ${this.primary}=?`; return this._query(sql, vals); } /** * 查看某条件的数据是否存在 * @return {promise} */ exists() { let fields, {attrs, vals, err} = DBModule.getDBModuleArgs(this); if (err) { throw `object ${this.constructor.toString()} lack necessary field (table, fields)`; } fields = attrs.map(attr=>` ${attr}=? `); fields.push(' 1=1 '); return this._query(`select count(*) as count from \`${this.table}\` where ${fields.join(' and ')}`, vals) .then(result => { return result[0].count !== 0 }); } /** * 删除数据库中单行操作,对象必须存在有效的主键 * @return {promise} */ delete() { let sql, vals; if (this[this.primary] === undefined || this[this.primary] === null) { throw `object ${this.constructor.toString()} ${this.primary} is ${this[this.primary]}`; } else if (DBModule.getDBModuleArgs(this).err) { throw `object ${this.constructor.toString()} lack necessary field (table, fields)`; } vals = [this[this.primary]] sql = `delete from \`${this.table}\` where ${this.primary}=?`; return this._query(sql, vals); } /** * 查询该对象表中的数据 * @param connect 多字段要连接字符 如 and or * @param start 分页时开始位置 * @param limit 分页时要获取的条数 * @param afterAgs 要附加的参数 * @return {promise} */ query({connect = DBModule.AND, start = 0, limit, sql} = {}, beforeAgs) { let conneSql = '', fields, {attrs, vals, err} = DBModule.getDBModuleArgs(this); if (err) { throw `object ${this.constructor.toString()} lack necessary field (table, fields)`; } else if (connect !== DBModule.AND && connect !== DBModule.OR) { throw `query functiton connect args must be DBModule.AND or DBModule.OR`; } if (Number.isFinite(Number(this[this.primary]))) { attrs.push(this.primary); vals.push(this[this.primary]); } if (limit > 0) { conneSql = ` LIMIT ?,?`; vals.push(start); vals.push(limit); } fields = attrs.map(attr => ` \`${this.table}\`.${attr}=? `); fields.push(' 1=1 '); if (sql) { sql = `select * from (${sql}) as \`${this.table}\` `; } else { sql = `select * from \`${this.table}\` `; } sql = `${sql} where ${fields.join(connect)} ${conneSql}` if (beforeAgs && beforeAgs.length > 0) { vals = beforeAgs.concat(vals); } return this._query(sql, vals); } /** * 分页模式数据统一处理 * @param {*} paging number 是否要开启分页模式 * limit number 每页的条数 * start number 从哪条开始读取 * header string 获取表的表头 * vals array sql的参数 * sql string sql语句表头用{{header}}代替 */ paging({paging = 0, limit = 15, start = 0, sql, headers = 'header', vals = []} = {}) { let pData, pCount, conneSql = ''; if (!sql) return Promise.resolve(1); if (limit > 0) { conneSql = ` LIMIT ?,?`; vals.push(start); vals.push(limit); } pData = this._query(sql.replace('{{header}}', headers) + conneSql, vals); if (paging) { pCount = this._query(sql.replace('{{header}}', 'count(*) as len'), vals); return Promise.all([pData, pCount]) .then((arrs) => { return { total: arrs[1][0].len, data: arrs[0], average: limit } }); } else { return pData; } } /** * 检测对象是否符合DBModule要求并获取对象能执行_query的参数 * @param {object} dbModule要检测的对象 * @return {object} fileds: err attrs vals * {boolean} err 是否符合DBModule的要求 * {array} attrs 要进行数据库操作的字段 * {array} vals 要进行数据库操作的字段对应的值 */ static getDBModuleArgs(dbModule) { if(!((dbModule.table && dbModule.table.length > 0) && (dbModule.fields && dbModule.fields instanceof Array && dbModule.fields.length > 0) )) { return {err: true}; } let attrs = [], vals = []; for (let field of dbModule.fields) { if (dbModule[field] !== undefined) { attrs.push('`'+field+'`'); vals.push(dbModule[field]); } } return {attrs, vals, err: false}; } static get AND() { return 'and'; } static get OR() { return 'or'; } static get HOT() { return 'hot'; } static get SOAR() { return 'soar'; } } module.exports = exports = DBModule;