1064 lines
37 KiB
JavaScript
1064 lines
37 KiB
JavaScript
'use strict'
|
||
//bamboo入口
|
||
import Koa from 'koa';
|
||
//https://x-extends.gitee.io/xe-utils/#/
|
||
import xe from 'xe-utils'
|
||
|
||
const fs = require('fs'); // 文件模块
|
||
const log4js = require("log4js");
|
||
const path = require('path')
|
||
const requireDirectory = require("require-directory");
|
||
const Router = require("koa-router");
|
||
const EventEmitter = require('events');
|
||
const schedule = require("node-schedule");
|
||
const Sequelize = require("sequelize");
|
||
const Parameter = require('parameter');
|
||
const objectPath = require("object-path");
|
||
module.exports = class Bamboo extends Koa {
|
||
constructor(agrs, options) {
|
||
super(options)
|
||
this.asyncStatus = {} //异步启动状态
|
||
this.fulfill = false //启动完成
|
||
this.modelAmend = false //模型文件是否有改动
|
||
this.config = {}
|
||
this.utils = {}
|
||
this.registeredContextUtils({"xe": xe})
|
||
this.registeredParameter()
|
||
// const ajv = new Ajv({})
|
||
// this.registeredContextUtils({"ajv": ajv})
|
||
this.logger = null
|
||
this.Sequelize = null
|
||
this.mysql = null
|
||
this.sqlite = null
|
||
this.ajv = null
|
||
this.event = new EventEmitter()
|
||
this.registeredConfig()
|
||
this.setLogger()
|
||
this.registeredError()
|
||
this.initDB()
|
||
this.registeredMiddleware()
|
||
this.registeredRouter()
|
||
this.listen(8884)
|
||
this.init()
|
||
|
||
}
|
||
|
||
init() {
|
||
const initInterval = setInterval(() => {
|
||
console.log('启动中...');
|
||
const asyncStatusList = xe.toArray(this.asyncStatus)
|
||
console.log(this.asyncStatus);
|
||
console.log(asyncStatusList);
|
||
const asyncStatus = asyncStatusList.filter(item => item === 0)
|
||
if (asyncStatus.length === 0) {
|
||
this.fulfill = true
|
||
const init = require(this.path('app/init.js'));
|
||
init(this.application)
|
||
this.onFulfill()
|
||
clearInterval(initInterval)
|
||
}
|
||
}, 1000)
|
||
}
|
||
|
||
async initDB() {
|
||
await this.registeredSqlite()
|
||
await this.registeredDB()
|
||
}
|
||
|
||
//启动完成事件
|
||
onFulfill() {
|
||
this.registeredEvent()
|
||
this.registeredSchedule()
|
||
}
|
||
|
||
listen(args) {
|
||
const isNumber = this.isNumber(args) || !args
|
||
const host = isNumber ? "127.0.0.1" : args.host
|
||
const path = isNumber ? "/" : args.host
|
||
const port = isNumber ? args : args.host
|
||
console.log(`bamboo:`)
|
||
console.log(`http://${host}:${port}${path}`)
|
||
return super.listen(args)
|
||
}
|
||
|
||
//注册config
|
||
registeredConfig() {
|
||
console.log('注册 config');
|
||
const hash = requireDirectory(module, this.path('config'));
|
||
this.config = hash
|
||
}
|
||
|
||
//注册 错误
|
||
registeredError() {
|
||
console.log('注册 错误');
|
||
this.errorException = {}
|
||
const hash = requireDirectory(module, this.path('lib/bamboo/err'), {
|
||
visit: (obj) => {
|
||
for (let key of Object.keys(obj)) {
|
||
this.errorException[key] = (message, code) => {
|
||
throw new obj[key](message, code)
|
||
}
|
||
}
|
||
return obj
|
||
}
|
||
});
|
||
|
||
}
|
||
|
||
registeredParameter() {
|
||
this.parameter = new Parameter();
|
||
}
|
||
|
||
|
||
//注册 mysql数据库
|
||
async registeredDB() {
|
||
console.log('注册 数据库');
|
||
this.asyncStatus['registeredDB'] = 0
|
||
const {
|
||
database,
|
||
username,
|
||
password,
|
||
options,
|
||
} = this.config.database.mysql
|
||
this.Sequelize = Sequelize
|
||
|
||
const sequelize = new Sequelize(database, username, password, {
|
||
...options,
|
||
operatorsAliases: this.operatorsAliases
|
||
});
|
||
try {
|
||
await sequelize.authenticate();
|
||
console.log('数据库连接成功');
|
||
} catch (error) {
|
||
console.error('数据库连接失败', error);
|
||
}
|
||
|
||
|
||
requireDirectory(module, this.path('app/model'), {
|
||
visit: (obj, joined, filename) => {
|
||
const parse = path.parse(filename);
|
||
const model = {}
|
||
for (let key of Object.keys(obj.model)) {
|
||
obj.model[key].type = this.Sequelize[obj.model[key].type]
|
||
model[key] = obj.model[key]
|
||
}
|
||
model['id'] = {
|
||
type : this.Sequelize.INTEGER,
|
||
comment : '表自增id',
|
||
allowNull : false,
|
||
unique : 'id',
|
||
primaryKey : true,
|
||
autoIncrement: true,
|
||
}
|
||
|
||
sequelize.define(parse.name, model)
|
||
return obj
|
||
}
|
||
});
|
||
this.mysql = sequelize
|
||
console.log('this.modelAmend', this.modelAmend);
|
||
if (this.modelAmend) {
|
||
console.log('生成模型结构到数据库');
|
||
await sequelize.sync({alter: true});
|
||
}
|
||
this.asyncStatus['registeredDB'] = 1
|
||
}
|
||
|
||
//注册 sqlite 数据库(记录model文件是否有改动,如果有就同步模型到mysql数据库)
|
||
async registeredSqlite() {
|
||
console.log('注册 sqlite 数据库');
|
||
this.asyncStatus['registeredSqlite'] = 0
|
||
const sqlite = new Sequelize(this.config.database.sqlite);
|
||
try {
|
||
await sqlite.authenticate();
|
||
console.log('sqlite数据库连接成功');
|
||
} catch (error) {
|
||
console.error('sqlite数据库连接失败', error);
|
||
}
|
||
|
||
// const MysqlMD5 = sqlite.define('MysqlMD5', {
|
||
// fileName: Sequelize.STRING,
|
||
// md5 : Sequelize.TEXT
|
||
// });
|
||
requireDirectory(module, this.path('app/sqlite/model'), {
|
||
visit: (obj, joined, filename) => {
|
||
const parse = path.parse(filename);
|
||
const model = {}
|
||
for (let key of Object.keys(obj.model)) {
|
||
obj.model[key].type = Sequelize[obj.model[key].type]
|
||
model[key] = obj.model[key]
|
||
}
|
||
sqlite.define(parse.name, model)
|
||
return obj
|
||
}
|
||
});
|
||
await sqlite.sync({alter: true});
|
||
const {MysqlMD5} = sqlite.models
|
||
this.modelAmend = true
|
||
requireDirectory(module, this.path('app/model'), {
|
||
visit: async (obj, joined, filename) => {
|
||
const parse = path.parse(filename);
|
||
const md5 = this.getFileMd5(this.path('app/model/') + filename)
|
||
const MysqlMD5Data = await MysqlMD5.findOne({where: {fileName: 'User'}})
|
||
if (!MysqlMD5Data) {
|
||
this.modelAmend = true
|
||
await MysqlMD5.create({fileName: parse.name, md5: md5})
|
||
}
|
||
else {
|
||
if (MysqlMD5Data.md5 !== md5) {
|
||
console.log('有改动的模型', MysqlMD5Data.fileName);
|
||
this.modelAmend = true
|
||
MysqlMD5.update({md5}, {where: {fileName: 'User'}})
|
||
|
||
}
|
||
}
|
||
return obj
|
||
}
|
||
});
|
||
|
||
this.sqlite = sqlite
|
||
this.asyncStatus['registeredSqlite'] = 1
|
||
}
|
||
|
||
get operatorsAliases() {
|
||
const Op = Sequelize.Op;
|
||
//操作符别名
|
||
const operatorsAliases = {
|
||
$eq : Op.eq,
|
||
$ne : Op.ne,
|
||
$gte : Op.gte,
|
||
$gt : Op.gt,
|
||
$lte : Op.lte,
|
||
$lt : Op.lt,
|
||
$not : Op.not,
|
||
$in : Op.in,
|
||
$notIn : Op.notIn,
|
||
$is : Op.is,
|
||
$like : Op.like,
|
||
$notLike : Op.notLike,
|
||
$iLike : Op.iLike,
|
||
$notILike : Op.notILike,
|
||
$regexp : Op.regexp,
|
||
$notRegexp : Op.notRegexp,
|
||
$iRegexp : Op.iRegexp,
|
||
$notIRegexp : Op.notIRegexp,
|
||
$between : Op.between,
|
||
$notBetween : Op.notBetween,
|
||
$overlap : Op.overlap,
|
||
$contains : Op.contains,
|
||
$contained : Op.contained,
|
||
$adjacent : Op.adjacent,
|
||
$strictLeft : Op.strictLeft,
|
||
$strictRight : Op.strictRight,
|
||
$noExtendRight: Op.noExtendRight,
|
||
$noExtendLeft : Op.noExtendLeft,
|
||
$substring : Op.substring,
|
||
$startsWith : Op.startsWith,
|
||
$endsWith : Op.endsWith,
|
||
$and : Op.and,
|
||
$or : Op.or,
|
||
$any : Op.any,
|
||
$all : Op.all,
|
||
$values : Op.values,
|
||
$col : Op.col
|
||
};
|
||
return operatorsAliases
|
||
}
|
||
|
||
//文件md5值
|
||
getFileMd5(url) {
|
||
const buffer = fs.readFileSync(url);
|
||
const hash = require('crypto').createHash('md5');
|
||
hash.update(buffer, 'utf8');
|
||
const md5 = hash.digest('hex');
|
||
return md5
|
||
}
|
||
|
||
//注册 router
|
||
registeredRouter() {
|
||
console.log('注册 router');
|
||
const router = new Router();
|
||
const hash = requireDirectory(module, this.path('app/controller'), {
|
||
visit: (obj) => {
|
||
for (let methodElement of obj.method) {
|
||
router[methodElement](obj.path, async (ctx, next) => {
|
||
const validate = this.parameter.validate(obj.params, ctx.request.body);
|
||
if (validate) {
|
||
console.error(validate);
|
||
this.errorException.ParameterException(validate);
|
||
}
|
||
else {
|
||
await obj.controller(ctx, this.application)
|
||
next();
|
||
}
|
||
});
|
||
}
|
||
return obj
|
||
}
|
||
});
|
||
super.use(router.routes())
|
||
super.use(router.allowedMethods())
|
||
}
|
||
|
||
//注册 事件
|
||
registeredEvent() {
|
||
console.log('注册 事件');
|
||
const hash = requireDirectory(module, this.path('app/event'), {
|
||
visit: (obj, joined, filename) => {
|
||
const parse = path.parse(filename);
|
||
// super.on(parse.name, obj);
|
||
for (let key of Object.keys(obj)) {
|
||
this.event.on(`${parse.name}.${key}`, e[key]);
|
||
}
|
||
return obj
|
||
}
|
||
});
|
||
console.log(this.event.listeners);
|
||
}
|
||
|
||
//注册 定时任务
|
||
registeredSchedule() {
|
||
console.log('注册 定时任务');
|
||
const hash = requireDirectory(module, this.path('app/schedule'), {
|
||
visit: (obj, joined, filename) => {
|
||
//https://www.cnblogs.com/yalong/p/15601391.html
|
||
if (!process.env.NODE_APP_INSTANCE || process.env.NODE_APP_INSTANCE === '0') { //防止pm2多个线程重复执行
|
||
const parse = path.parse(filename);
|
||
if (obj.run) { obj.schedule(this.application) }
|
||
schedule.scheduleJob(parse.name, obj.time, () => obj.schedule(this.application))
|
||
}
|
||
return obj
|
||
}
|
||
});
|
||
}
|
||
|
||
//注册 middleware
|
||
registeredMiddleware() {
|
||
console.log('注册 middleware');
|
||
const hash = requireDirectory(module, this.path('app/middleware'), {
|
||
visit: (obj, joined, filename) => {
|
||
// console.log(obj, joined, filename);
|
||
// const parse = path.parse(filename);
|
||
// const config = this.config.middleware[parse.name]
|
||
// // super.use(obj(config || {}))
|
||
// super.use(async (ctx, next) => {
|
||
// ctx['logger'] = this.logger
|
||
// return await obj.fun(ctx, next, this.application)
|
||
// })
|
||
return obj
|
||
}
|
||
});
|
||
let hashList = xe.toArray(hash)
|
||
hashList = xe.orderBy(hashList,"sort")
|
||
hashList = hashList.filter(item=>item.use)
|
||
hashList.forEach(item=>{
|
||
super.use(async (ctx, next) => {
|
||
ctx['logger'] = this.logger
|
||
return await item.fun(ctx, next, this.application)
|
||
})
|
||
})
|
||
console.log(hashList);
|
||
}
|
||
|
||
//注册 utils
|
||
registeredContextUtils(args) {
|
||
this.utils = {
|
||
...this.utils,
|
||
...args
|
||
}
|
||
}
|
||
|
||
//logger
|
||
setLogger(args) {
|
||
log4js.configure(this.config['log']);
|
||
this.logger = log4js.getLogger();
|
||
}
|
||
|
||
path(args) {
|
||
return this.isPath + '/' + args
|
||
}
|
||
|
||
isNumber(args) {
|
||
return typeof args === 'number'
|
||
}
|
||
|
||
isString(args) {
|
||
return typeof args === 'string'
|
||
}
|
||
|
||
isObject(args) {
|
||
return typeof args === 'object'
|
||
}
|
||
|
||
|
||
get isPath() {
|
||
return path.resolve('./')
|
||
}
|
||
|
||
get xe() {
|
||
return xe
|
||
}
|
||
|
||
resetDbData() {
|
||
this.tableData = null
|
||
this.tableWhere = null
|
||
this.tablePage = null
|
||
this.tableLimit = null
|
||
this.tableOrder = null
|
||
this.tableGroup = null
|
||
this.tableAttributes = null
|
||
}
|
||
|
||
/**
|
||
* 指定表名
|
||
* @param {object} data 表名.
|
||
*/
|
||
table(data) {
|
||
this.tableName = data
|
||
return this
|
||
}
|
||
|
||
/**
|
||
* 设置事务
|
||
*/
|
||
async setTransaction() {
|
||
console.log("设置事务");
|
||
this.t = await this.mysql.transaction()
|
||
return this.t
|
||
}
|
||
|
||
/**
|
||
* 提交事务,如果设置了事务不提交,任务不会执行
|
||
*/
|
||
async commitTransaction() {
|
||
try {
|
||
await this.t.commit()
|
||
} catch (error) {
|
||
await this.t.rollback()
|
||
}
|
||
this.t = null
|
||
}
|
||
|
||
|
||
/**
|
||
* 筛选条件
|
||
* @param {object} data 筛选条件对象.
|
||
*/
|
||
where(data) {
|
||
this.tableWhere = data
|
||
return this
|
||
}
|
||
|
||
|
||
/**
|
||
* 模糊查询
|
||
* @param {string} value 模糊查询内容.
|
||
* @param {array} searchData 模糊查询搜索的字段(默认表的全部字段).
|
||
*/
|
||
search(value, searchData) {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
if (!this.tableWhere) { this.tableWhere = {} }
|
||
if (!this.tableWhere['$or']) {
|
||
this.tableWhere['$or'] = []
|
||
}
|
||
if (!searchData) {
|
||
searchData = Object.keys(this.mysql.models[this.tableName].rawAttributes)
|
||
}
|
||
for (let key of searchData) {
|
||
const search = {}
|
||
search[key] = {"$substring": value || ''}
|
||
this.tableWhere['$or'].push(search)
|
||
}
|
||
return this
|
||
}
|
||
|
||
|
||
/**
|
||
* 数据分组
|
||
* @param {string|array} value 传需要分组的字段['createdAt'].
|
||
*/
|
||
group(value) {
|
||
this.tableGroup = value
|
||
return this
|
||
}
|
||
|
||
/**
|
||
* 数据分组
|
||
* @param {string|array} value 传需要分组的字段['createdAt'].
|
||
*/
|
||
attributes(value) {
|
||
this.tableAttributes = value
|
||
return this
|
||
}
|
||
|
||
/**
|
||
* 页数
|
||
* @param {int} value 页数从0开始.
|
||
*/
|
||
|
||
page(value) {
|
||
this.tablePage = value
|
||
return this
|
||
}
|
||
|
||
|
||
/**
|
||
* 条数
|
||
* @param {int} value 条数.
|
||
*/
|
||
limit(value) {
|
||
this.tableLimit = value
|
||
return this
|
||
}
|
||
|
||
get getLimit() {
|
||
return this.tableLimit || null
|
||
}
|
||
|
||
/**
|
||
* 数据
|
||
* @param {any} value 数据.
|
||
*/
|
||
data(value) {
|
||
this.tableData = value
|
||
return this
|
||
}
|
||
|
||
/**
|
||
* 时间排序
|
||
* @param {array} value 时间排序(默认按更新时间排序).
|
||
*/
|
||
order(value) {
|
||
this.tableOrder = value
|
||
return this
|
||
}
|
||
|
||
|
||
/**
|
||
* 字段值增加
|
||
* @param {string} data 要增加的字段和值{xxx:1,xxxx:2}.
|
||
* @param {number} max 字段增加后的值不能大于最大值
|
||
* @param {number} setValue 如果增加后的字段大于max,设置字段值为n
|
||
*/
|
||
async setInc(max, setValue) {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
if (!this.tableData) { this.errorException.HttpException("请数据") }
|
||
if (!this.tableWhere) { this.errorException.HttpException("请筛选值") }
|
||
|
||
let res = await this.mysql.models[this.tableName].findOne(
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
|
||
for (let key of Object.keys(this.tableData)) {
|
||
if (res[key] + this.tableData[key] > max) {
|
||
if (setValue === 0 || setValue) {
|
||
const data = {}
|
||
Object.keys(this.tableData).map(item => {data[item] = setValue})
|
||
await this.mysql.models[this.tableName].update(
|
||
data,
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
res = {
|
||
...res.dataValues,
|
||
...data
|
||
}
|
||
return {res: true, value: res}
|
||
}
|
||
return {res: false, value: res}
|
||
}
|
||
}
|
||
|
||
await this.mysql.models[this.tableName].increment(
|
||
this.tableData,
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
|
||
res = await this.mysql.models[this.tableName].findOne(
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
return {res: true, value: res.dataValues}
|
||
|
||
}
|
||
|
||
/**
|
||
* 字段值减小
|
||
* @param {string} data 要减小的字段和值{xxx:1,xxxx:2}.
|
||
* @param {number} min 字段减小后的值不能小于最小值
|
||
* @param {number} setValue 如果减小后的字段小于min,设置字段值为n
|
||
*/
|
||
async setDec(min, setValue) {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
if (!this.tableData) { this.errorException.HttpException("请数据") }
|
||
if (!this.tableWhere) { this.errorException.HttpException("请筛选值") }
|
||
|
||
let res = await this.mysql.models[this.tableName].findOne(
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
|
||
for (let key of Object.keys(this.tableData)) {
|
||
if (res[key] - this.tableData[key] < min) {
|
||
if (setValue === 0 || setValue) {
|
||
const data = {}
|
||
Object.keys(this.tableData).map(item => {data[item] = setValue})
|
||
await this.mysql.models[this.tableName].update(
|
||
data,
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
res = {
|
||
...res.dataValues,
|
||
...data
|
||
}
|
||
return {res: true, value: res}
|
||
}
|
||
return {res: false, value: res}
|
||
}
|
||
}
|
||
|
||
await this.mysql.models[this.tableName].decrement(
|
||
this.tableData,
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
|
||
res = await this.mysql.models[this.tableName].findOne(
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
return {res: true, value: res.dataValues}
|
||
}
|
||
|
||
/**
|
||
* 常用时间筛选
|
||
* @param {string|array|Number} value 时间内容:按时间段:['2000-1-1','2000-1-2'],按常用时间:day,按最近60分钟:60.
|
||
* @param {string} field 时间字段(默认createdAt字段)
|
||
*/
|
||
time(value, field) {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
if (!field) { field = "createdAt"}
|
||
if (!this.tableWhere) { this.tableWhere = {} }
|
||
if (!this.tableWhere['$and']) {
|
||
this.tableWhere['$and'] = []
|
||
}
|
||
const {fn, col, where, literal} = Sequelize
|
||
switch (value) {
|
||
case 'yday': // 昨天
|
||
this.tableWhere['$and'].push(where(fn('TO_DAYS', col(this.tableName + '.' + field)), '-', fn('TO_DAYS', fn('NOW')), '<=', 1))
|
||
break;
|
||
case 'day': //当天
|
||
this.tableWhere['$and'].push(where(fn('TO_DAYS', col(this.tableName + '.' + field)), '=', fn('TO_DAYS', fn('NOW'))))
|
||
break;
|
||
case 'week': //本周
|
||
this.tableWhere['$and'].push(where(fn('YEARWEEK', fn('date_format', col(this.tableName + '.' + field), '%Y-%m-%d')), '=', fn('YEARWEEK', fn('now'))))
|
||
break;
|
||
case 'month': //当月
|
||
this.tableWhere['$and'].push(where(fn('DATE_FORMAT', col(this.tableName + '.' + field), '%Y%m'), '=', fn('DATE_FORMAT', fn('CURDATE'), '%Y%m')))
|
||
break;
|
||
case 'lmonth': //上个月
|
||
this.tableWhere['$and'].push(where(fn('PERIOD_DIFF', fn('date_format', fn('now'), '%Y%m'), fn('date_format', col(this.tableName + '.' + field), '%Y%m')), '=', 1))
|
||
break;
|
||
case 'year': //当年
|
||
this.tableWhere['$and'].push(where(fn('YEAR', col(this.tableName + '.' + field)), '=', fn('YEAR', fn('NOW'))))
|
||
break;
|
||
default:
|
||
if (xe.isArray(value)) { //时间范围筛选
|
||
const data = {}
|
||
data[field] = {"$between": value}
|
||
this.tableWhere['$and'].push(data)
|
||
}
|
||
if (xe.isNumber(value)) {
|
||
const minute = {}
|
||
minute[field] = {"$lt": new Date(), "$gt": new Date(new Date() - value * 60 * 1000)}
|
||
this.tableWhere['$and'].push(minute)
|
||
}
|
||
break;
|
||
}
|
||
|
||
return this
|
||
}
|
||
|
||
/**
|
||
* 查询1条数据
|
||
* @param {Transaction} options.transaction 运行查询的事务.
|
||
*/
|
||
async find(options) {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
const res = await this.mysql.models[this.tableName].findOne(
|
||
{
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
this.resetDbData()
|
||
return res && res.dataValues || null
|
||
}
|
||
|
||
/**
|
||
* 统计查询结果数
|
||
*/
|
||
async count() {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
const res = await this.mysql.models[this.tableName].count(
|
||
this.tableData,
|
||
{
|
||
where : this.tableWhere,
|
||
order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序
|
||
group : this.tableGroup,
|
||
attributes : this.tableAttributes,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
this.resetDbData()
|
||
return res
|
||
}
|
||
|
||
/**
|
||
* 求和
|
||
*/
|
||
async sum() {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
const res = await this.mysql.models[this.tableName].sum(
|
||
this.tableData,
|
||
{
|
||
where : this.tableWhere,
|
||
order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序
|
||
group : this.tableGroup,
|
||
attributes : this.tableAttributes,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
this.resetDbData()
|
||
return res
|
||
}
|
||
|
||
/**
|
||
* 查询最大值
|
||
*/
|
||
async max() {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
const res = await this.mysql.models[this.tableName].max(
|
||
this.tableData,
|
||
{
|
||
where : this.tableWhere,
|
||
order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序
|
||
group : this.tableGroup,
|
||
attributes : this.tableAttributes,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
this.resetDbData()
|
||
return res
|
||
}
|
||
|
||
/**
|
||
* 查询最小值
|
||
*/
|
||
async min() {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
const res = await this.mysql.models[this.tableName].min(
|
||
this.tableData,
|
||
{
|
||
where : this.tableWhere,
|
||
order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序
|
||
group : this.tableGroup,
|
||
attributes : this.tableAttributes,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
this.resetDbData()
|
||
return res
|
||
}
|
||
|
||
/**
|
||
* 更新数据
|
||
* @param {object} data 数据.
|
||
* @param {boolean} options.paranoid 如果为 true,则只会更新未删除的记录。如果为 false,将更新已删除和未删除的记录。仅适用于模型的 options.paranoid 为真。.
|
||
* @param {Array} options.fields 要更新的字段(默认为所有字段)
|
||
* @param {boolean} options.validate 每一行在插入之前是否应该经过验证。如果一行未通过验证,则整个插入将失败
|
||
* @param {boolean} options.hooks 在批量更新挂钩之后运行?
|
||
* @param {boolean} options.sideEffects 是否更新任何虚拟二传手的副作用。
|
||
* @param {boolean} options.individualHooks 在更新挂钩之前运行?如果为真,这将执行一个 SELECT,然后执行单独的 UPDATE。需要一个选择,因为需要将行数据传递给钩子
|
||
* @param {boolean | Array} options.returning 如果为真,则附加 RETURNING <model columns> 以取回所有定义的值;如果是列名数组,则附加 RETURNING <columns> 以获取特定列(仅限 Postgres)
|
||
* @param {number} options.limit 要更新多少行(仅适用于 mysql 和 mariadb,对于 MSSQL 实现为 TOP(n);对于 sqlite,仅当存在 rowid 时才支持)
|
||
* @param {Function} options.logging在运行查询以记录 sql 时执行的函数。
|
||
* @param {boolean} options.benchmark 将查询执行时间(以毫秒为单位)作为第二个参数传递给日志记录函数(options.logging)。
|
||
* @param {Transaction} options.transaction 运行查询的事务
|
||
* @param {boolean} options.silent 如果为 true,则不会更新 updatedAt 时间戳。
|
||
*/
|
||
async update(options) {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
if (!this.tableData) { this.errorException.HttpException("请数据") }
|
||
const res = await this.mysql.models[this.tableName].update(
|
||
this.tableData,
|
||
{
|
||
where: this.tableWhere,
|
||
...options
|
||
}
|
||
)
|
||
return res
|
||
}
|
||
|
||
/**
|
||
* 删除数据
|
||
* @param {object} options 参数.
|
||
* @param {boolean} options.hooks 在批量销毁挂钩之前运行.
|
||
* @param {boolean} options.individualHooks 如果设置为 true,destroy 将选择与 where 参数匹配的所有记录,并将在每行上的 destroy 钩子之前执行.
|
||
* @param {number} options.limit 要删除多少行.
|
||
* @param {boolean} options.force 删除而不是将 deletedAt 设置为当前时间戳(仅在启用偏执狂时适用).
|
||
* @param {boolean} options.truncate 如果设置为 true,支持它的方言将使用 TRUNCATE 而不是 DELETE FROM。如果表被截断,则忽略 where 和 limit 选项.
|
||
* @param {boolean} options.cascade 仅与 TRUNCATE 一起使用。截断所有具有对命名表的外键引用的表,或者截断由于 CASCADE 而添加到组中的任何表.
|
||
* @param {transaction} options.transaction 运行查询的事务.
|
||
* @param {Function} options.logging 在运行查询以记录 sql 时执行的函数。
|
||
* @param {boolean} options.benchmark 将查询执行时间(以毫秒为单位)作为第二个参数传递给日志记录函数(options.logging)。
|
||
*/
|
||
async delete(options) {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
const res = await this.mysql.models[this.tableName].destroy(
|
||
{
|
||
where: this.tableWhere,
|
||
...options
|
||
}
|
||
)
|
||
return res
|
||
}
|
||
|
||
/**
|
||
* 保存数据,如果数据已存在就更新,否则创建数据,可以传对象或数组,如果是需要更新数据,必须包含id
|
||
*/
|
||
async save() {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
if (!this.tableData) { this.errorException.HttpException("请传要保存的数据") }
|
||
let data = this.tableData
|
||
let updateOnDuplicate = []
|
||
let keyData = {}
|
||
if (xe.isArray(data)) {
|
||
if (!data.length) { this.errorException.HttpException("请传要保存的数据") }
|
||
keyData = data[0]
|
||
}
|
||
else {
|
||
keyData = data
|
||
data = [data]
|
||
}
|
||
|
||
for (let key of Object.keys(keyData)) {
|
||
if (key !== 'id') { updateOnDuplicate.push(key) }
|
||
}
|
||
|
||
const res = await this.mysql.models[this.tableName].bulkCreate(data,
|
||
{returning: true, updateOnDuplicate: updateOnDuplicate, transaction: this.t}
|
||
)
|
||
this.resetDbData()
|
||
return res
|
||
}
|
||
|
||
|
||
/**
|
||
* 查询所有符合条件的数据
|
||
* @return {array} dataValues 查询结果.
|
||
*/
|
||
async findAll() {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
const res = await this.mysql.models[this.tableName].findAll(
|
||
{
|
||
where : this.tableWhere,
|
||
offset : (this.tablePage && this.tableLimit) ? this.tablePage - 1 * this.tableLimit : null,
|
||
limit : this.tableLimit,
|
||
order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序
|
||
group : this.tableGroup,
|
||
attributes : this.tableAttributes,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
this.resetDbData()
|
||
return res.map(item => item.dataValues)
|
||
}
|
||
|
||
/**
|
||
* 如果数据不存在就创建数据,否则反查询结果
|
||
* @return {object} dataValues 查询结果.
|
||
*/
|
||
async findOrCreate() {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
if (!this.tableData) { this.errorException.HttpException("请传data") }
|
||
const res = await this.mysql.models[this.tableName].findOrCreate(
|
||
{
|
||
defaults : this.tableData,
|
||
where : this.tableWhere,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
this.resetDbData()
|
||
return res
|
||
}
|
||
|
||
|
||
/**
|
||
* 分页查询数据
|
||
* @param {array} args.include 关联查询.
|
||
* @return {int} count 总行数.
|
||
* @return {array} rows 数据列表.
|
||
*/
|
||
async findAndCountAll(args = {}) {
|
||
if (!this.tableName) { this.errorException.HttpException("请传表名") }
|
||
const {count, rows} = await this.mysql.models[this.tableName].findAndCountAll(
|
||
{
|
||
where : this.tableWhere,
|
||
offset : (this.tablePage && this.tableLimit) ? this.tablePage - 1 * this.tableLimit : null,
|
||
limit : this.tableLimit,
|
||
order : this.tableOrder || [['updatedAt', 'DESC']], // 时间排序
|
||
group : this.tableGroup,
|
||
attributes : this.tableAttributes,
|
||
transaction: this.t
|
||
}
|
||
)
|
||
this.resetDbData()
|
||
return {count, rows: rows.map(item => item.dataValues)}
|
||
}
|
||
|
||
/**
|
||
* json批量任务
|
||
* @param {array} taskList json格式的任务列表.
|
||
*/
|
||
async jsonTasks(taskList) {
|
||
const res = []
|
||
for (let tk of taskList) {
|
||
const tkKeys = Object.keys(tk)
|
||
for (let key of tkKeys) {
|
||
if (key === "as") { continue; }
|
||
if (key === "where" || key === "data") {
|
||
const findJsonRes = this.findJsonValue(tk[key], (n) => {
|
||
if (typeof n === "string") {
|
||
// console.log(tk[key],n);
|
||
if (n.indexOf("$as") !== -1) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
})
|
||
if (findJsonRes) {
|
||
const jsonPath = findJsonRes.path.join(".")
|
||
const jsonPath2 = objectPath.get(tk[key], jsonPath);//$as.user_data.id
|
||
const asName = jsonPath2.split(".")[1]
|
||
const asFilter = res.filter(item => {
|
||
// console.log(Object.keys(item), asName);
|
||
return Object.keys(item)[0] === asName
|
||
})
|
||
// console.log(asFilter);
|
||
// console.log(jsonPath2);
|
||
if (asFilter.length > 0) {
|
||
// console.log(objectPath.get(asFilter[0], jsonPath2.split(".").slice(1).join("."))); //999
|
||
// console.log(jsonPath2.split(".").slice(1).join("."));//user_data.id
|
||
// console.log(tk[key]);//user_data.id
|
||
objectPath.set(tk[key], jsonPath, objectPath.get(asFilter[0], jsonPath2.split(".").slice(1).join(".")));
|
||
}
|
||
}
|
||
}
|
||
console.log(tk[key]);
|
||
const tkRes = await this[key](tk[key])
|
||
if (
|
||
key === "save" ||
|
||
key === "count" ||
|
||
key === "sum" ||
|
||
key === "max" ||
|
||
key === "min" ||
|
||
key === "find" ||
|
||
key === "findAll" ||
|
||
key === "findOrCreate" ||
|
||
key === "jsonTasks" ||
|
||
key === "findAndCountAll"
|
||
) {
|
||
console.log(tk["as"]);
|
||
res.push(tk["as"] ? {[tk["as"]]: tkRes} : tkRes)
|
||
}
|
||
|
||
}
|
||
}
|
||
return res
|
||
}
|
||
|
||
//深度遍历JSON,搜索值
|
||
findJsonValue(n, value, path) {
|
||
if (n === value || (xe.isFunction(value) && value(n))) {
|
||
return {value: n, path}
|
||
}
|
||
path = path || []
|
||
// 获取所有的子节点,并遍历
|
||
if (typeof n === "object") {
|
||
const nkeys = Object.keys(n)
|
||
for (let k of nkeys) {
|
||
// concat() 方法用于连接两个或多个数组
|
||
const res = this.findJsonValue(n[k], value, path.concat(k));
|
||
if (res) {
|
||
return res
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
get application() {
|
||
return {
|
||
config : this.config,
|
||
utils : this.utils,
|
||
log : this.logger,
|
||
err : this.errorException,
|
||
event : this.event,
|
||
sequelize : this.Sequelize,
|
||
mysql : this.mysql,
|
||
sqlite : this.sqlite,
|
||
table : this.table,
|
||
where : this.where,
|
||
data : this.data,
|
||
search : this.search,
|
||
group : this.group,
|
||
attributes : this.attributes,
|
||
page : this.page,
|
||
setDec : this.setDec,
|
||
setInc : this.setInc,
|
||
limit : this.limit,
|
||
time : this.time,
|
||
setTransaction : this.setTransaction,
|
||
commitTransaction: this.commitTransaction,
|
||
save : this.save,
|
||
count : this.count,
|
||
sum : this.sum,
|
||
max : this.max,
|
||
min : this.min,
|
||
find : this.find,
|
||
findAll : this.findAll,
|
||
findOrCreate : this.findOrCreate,
|
||
findAndCountAll : this.findAndCountAll,
|
||
resetDbData : this.resetDbData,
|
||
jsonTasks : this.jsonTasks,
|
||
findJsonValue : this.findJsonValue,
|
||
//======= 简写函数
|
||
// S: this.sqlite.models,
|
||
// M: this.mysql.models,
|
||
C: this.config,
|
||
U: this.utils,
|
||
}
|
||
}
|
||
}
|