This commit is contained in:
robin
2022-04-12 00:51:40 +08:00
parent c2f8473fb5
commit 344562baf4
18 changed files with 940 additions and 39 deletions
+4 -6
View File
@@ -10,11 +10,9 @@
]
],
"plugins": [
[
"babel-plugin-webpack-alias",
{
"config": "./webpack.config.js"
}
]
["@babel/plugin-proposal-decorators",{"legacy": true}],
// ["@babel/plugin-proposal-class-properties",{"loose": true}],
// ["babel-plugin-webpack-alias", { "config": "./webpack.config.js" } ]
]
}
+44 -2
View File
@@ -1,14 +1,56 @@
'use strict';
module.exports = {
doc : "",
doc : "默认页面",
path : "/",
method : ["get", "post"],
middleware: [],
params : {},
return : {},
controller: async (ctx, app) => {
// app.err.ParameterException()
app.event.emit('test.event2')
const where = {
// id: 4
}
const data = {
// id: 1,
uid: "77",
}
ctx.body = 'index,xe:' + app.utils.VERSION
// const User = await app.table("User").where(where).findAll()
// const User = await app.table("User").time(["2022-04-9", "2022-04-10"]).findAll()
// const User = await app.table("User").where(where).findOrCreate(data)
// const {count, rows} = await app.table("User").where(where).findAndCountAll()
// const User = await app.table("User").save(data)
// const res = {
// "创建单个数据" : await app.table("User").data({uid: "6666"}).save(),
// "查询单个数据" : await app.table("User").where({id: 2}).find(),
// "查询单个数据,不存在就创建" : await app.table("User").where({id: 999}).data({uid: "6666"}).findOrCreate(),
// "查询所有数据" : await app.table("User").where({uid: "6666"}).findAll(),
// "查询所有数据(时间区间)" : await app.table("User").time(['2022-04-09', '2022-04-10']).findAll(),
// "查询所有数据(当月数据)" : await app.table("User").time('month').findAll(),
// "查询所有数据(30分钟内的数据)": await app.table("User").time(30).findAll(),
// "查询所有数据(分页)" : await app.table("User").where({uid: "6666"}).page(1).limit(1).findAll(),
// "查询所有数据+总行数(分页)" : await app.table("User").where({uid: "6666"}).page(1).limit(1).findAndCountAll(),
// }
/*
await app.setTransaction() // 设置事务
await app.table("User").data({uid: "6666"}).save()
await app.table("User").where({id: 2}).find()
await app.table("User").data({uid: "6666"}).save()
await app.table("User").where({id: 2}).find()
await app.commitTransaction() //提交事务,如果设置了事务不提交,任务不会执行
*/
// await app.table("User").where({id: 1032}).data({age: 1}).setInc() // 字段值+1
// await app.table("User").where({id: 1032}).data({age: 2}).setInc() // 字段值+2
// const {res, value} = await app.table("User").where({id: 1032}).data({age: 2}).setDec(0) // 字段值-2,不能低于0,如果低于0返回false
// const {res, value} = await app.table("User").where({id: 1032}).data({age: 2}).setDec(0,10) // 字段值-2,不能低于0,如果低于0设置为10
const {res, value} = await app.table("User").where({id: 1032}).data({age: 2}).setInc(20, 10) // 字段值+2,不能大于20,如果大于20设置为10
ctx.body = {res, value}
}
}
@@ -1,44 +1,44 @@
class HttpException extends Error {
// message为异常信息,errorCode为错误码(开发人员内部约定)code为HTTP状态码
constructor(message = '服务器异常', errorCode = 10000, code = 400) {
// message为异常信息,code 为错误码(开发人员内部约定)status 为HTTP状态码
constructor(message, code, status) {
super()
this.errorCode = errorCode || 10000
this.code = code || 400
this.status = status || 500
this.code = code || 500
this.message = message || '服务器异常'
}
}
class ParameterException extends HttpException {
constructor(message, errorCode) {
constructor(message, code, status) {
super()
this.errorCode = errorCode || 10000
this.code = 400
this.status = status || 402
this.code = code
this.message = message || '参数错误'
}
}
class NotFound extends HttpException {
constructor(message, errorCode) {
constructor(message, code, status) {
super()
this.errorCode = errorCode || 10001
this.status = status || 404
this.code = 404
this.message = message || '资源未找到'
}
}
class AuthFailed extends HttpException {
constructor(message, errorCode) {
constructor(message, code, status) {
super()
this.errorCode = errorCode || 10002
this.status = status || 401
this.message = message || '授权失败'
this.code = 401
}
}
class Forbidden extends HttpException {
constructor(message, errorCode) {
constructor(message, code, status) {
super()
this.errorCode = errorCode || 10003
this.status = status || 403
this.message = message || '禁止访问'
this.code = 403
}
+10
View File
@@ -0,0 +1,10 @@
'use strict';
// 监听事件
module.exports = {
"event1": (args) => {
console.log(args || 'event1')
},
"event2": (args) => {
console.log(args || 'event2')
},
}
+4
View File
@@ -0,0 +1,4 @@
/*系统启动后要做初始化*/
module.exports = (app) => {
console.log("启动完成");
}
+12
View File
@@ -0,0 +1,12 @@
'use strict';
const error = require('koa-json-error')
// 错误处理
module.exports = error((err) => {
return {
status : err.status,
message : err.message,
code : err.code,
res : false,
// postFormat: (e, obj) => process.env.NODE_ENV === 'production' ? _.omit(obj, 'stack') : obj
}
})
-1
View File
@@ -5,7 +5,6 @@ module.exports = async (ctx, next, app) => {
// const timetaken = `${ctx.request.method}${ctx.request.url} 响应时间`
// console.time(timetaken)
// app.logger.debug("123")
throw new app.err.HttpException
await next()
// console.timeEnd(timetaken)
}
+8
View File
@@ -0,0 +1,8 @@
module.exports = {
doc : "用户",
model: {
uid: {type: "STRING", comment: '用户id'},
age: {type: "INTEGER", comment: '年龄', defaultValue: 0,},
}
}
+9
View File
@@ -0,0 +1,9 @@
'use strict';
// 监听事件
module.exports = {
time : "*/3 * * * * *",
run : true,//系统启动时立即执行一次
schedule: (app) => {
// console.log('每3秒我执行一次' + app.utils.VERSION)
}
}
View File
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
module.exports = {
doc : "mysql模型文件的m5记录",
model: {
fileName: {type: "STRING", comment: '文件名称'},
md5 : {type: "TEXT", comment: 'md5记录'},
md5json : {type: "JSON", comment: 'md5记录'},
}
}
+56
View File
@@ -1 +1,57 @@
/*数据库配置*/
module.exports = {
mysql: {
database: "bamboo",
username: "bamboo",
password: "bamboo",
options : {
dialect: 'mysql',
host : "192.168.1.26",
port : 3306,
// 禁用日志记录或提供自定义日志记录功能;默认值:console.log
// logging: false,
// model的全局配置
define: {
// 添加create,update,delete时间戳
timestamps: true,
// 添加软删除
paranoid: false,
// 防止修改表名为复数
freezeTableName: true,
// 防止驼峰式字段被默认转为下划线
underscored: false,
},
// 由于orm用的UTC时间,这里必须加上东八区,否则取出来的时间相差8小时
timezone: '+08:00',
// 连接数 = ((核心数 * 2) + 有效磁盘数)
pool : {// 连接池
max : require('os').cpus().length * 2 + 1,
min : 0,
acquire: 60000,
idle : 100000,
},
dialectOptions: {
charset : "utf8mb4",
collate : "utf8mb4_general_ci",
supportBigNumbers: true,
bigNumberStrings : true,
dateStrings : true,
typeCast(field, next) {// 让读取date类型数据时返回字符串而不是UTC时间
if (field.type === 'DATETIME') {
// console.log(field);
return field.string();
}
return next();
},
},
},
},
redis:{},
sqlite:{
dialect: 'sqlite',
storage: 'app/sqlite/database.sqlite',
logging: false,
},
}
-4
View File
@@ -1,4 +0,0 @@
/*redis配置*/
module.exports = {
port: 3000,
}
+8
View File
@@ -0,0 +1,8 @@
const Bamboo = require("./index")
module.exports = class DB extends Bamboo {
constructor() {super();}
}
+756 -12
View File
@@ -4,25 +4,66 @@ 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 error = require('./errorException')
const requireDirectory = require("require-directory");
const Router = require("koa-router");
const EventEmitter = require('events');
const schedule = require("node-schedule");
const Sequelize = require("sequelize");
module.exports = class Bamboo extends Koa {
constructor(agrs, options) {
super(options)
super.on('error', (err, ctx) => this.serverError(err, ctx));
this.asyncStatus = {} //异步启动状态
this.fulfill = false //启动完成
this.modelAmend = false //模型文件是否有改动
this.config = {}
this.utils = this.registeredContextUtils(xe)
this.logger = null
this.Sequelize = null
this.mysql = null
this.sqlite = 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) {
@@ -42,26 +83,240 @@ module.exports = class Bamboo extends Koa {
this.config = hash
}
//注册 错误
registeredError() {
console.log('注册 错误');
this.errorException = {}
const hash = requireDirectory(module, this.path('app/err'), {
visit: (obj) => {
for (let key of Object.keys(obj)) {
this.errorException[key] = (message, code) => {
throw new obj[key](message, code)
}
}
return obj
}
});
}
//注册 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: (c) => {
for (let methodElement of c.method) {
router[methodElement](c.path, async (ctx, next) => {
visit: (obj) => {
for (let methodElement of obj.method) {
router[methodElement](obj.path, async (ctx, next) => {
ctx['logger'] = this.logger
await c.controller(ctx, this.application)
await obj.controller(ctx, this.application)
next();
});
}
return c
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}`, obj[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');
@@ -78,7 +333,7 @@ module.exports = class Bamboo extends Koa {
return obj
}
});
console.log(hash);
// console.log(hash);
}
//注册 utils
@@ -119,11 +374,500 @@ module.exports = class Bamboo extends Koa {
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
}
/**
* 更新数据
* @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 如果设置为 truedestroy 将选择与 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)}
}
get application() {
return {
utils : this.utils,
logger: this.logger,
err : error,
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,
find : this.find,
findAll : this.findAll,
findOrCreate : this.findOrCreate,
findAndCountAll : this.findAndCountAll,
resetDbData : this.resetDbData,
//======= 简写函数
// S: this.sqlite.models,
// M: this.mysql.models,
C: this.config,
}
}
}
+7
View File
@@ -13,6 +13,8 @@
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-decorators": "^7.17.9",
"@babel/plugin-transform-runtime": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
@@ -44,13 +46,18 @@
"clean-webpack-plugin": "^4.0.0",
"cross-env": "^7.0.3",
"koa-bodyparser": "^4.3.0",
"koa-json-error": "^3.1.2",
"koa-logger": "^2.0.1",
"koa-redis": "^4.0.1",
"log4js": "^6.4.4",
"mysql2": "^2.3.3",
"node-schedule": "^2.1.0",
"pm2": "^5.1.2",
"require-all": "^3.0.0",
"require-directory": "^2.1.1",
"sequelize": "^6.18.0",
"shelljs": "^0.8.5",
"sqlite3": "^5.0.2",
"webpack": "^4.41.5",
"webpack-cli": "3.3.10",
"webpack-node-externals": "^3.0.0",