q-koa 7.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/core/app.js +1813 -0
  2. package/core/config.js +124 -0
  3. package/core/file/plugins/administrator/config.js +17 -0
  4. package/core/file/plugins/administrator/controller.js +264 -0
  5. package/core/file/plugins/administrator/model.js +53 -0
  6. package/core/file/plugins/administrator/validate.js +41 -0
  7. package/core/file/plugins/alipay/controller.js +68 -0
  8. package/core/file/plugins/alipay/validate.js +9 -0
  9. package/core/file/plugins/cache/service.js +16 -0
  10. package/core/file/plugins/cloudfunction/config.js +4 -0
  11. package/core/file/plugins/cloudfunction/model.js +15 -0
  12. package/core/file/plugins/common/controller.js +398 -0
  13. package/core/file/plugins/common/validate.js +47 -0
  14. package/core/file/plugins/douyin/config.js +3 -0
  15. package/core/file/plugins/douyin/controller.js +521 -0
  16. package/core/file/plugins/douyin/validate.js +40 -0
  17. package/core/file/plugins/douyin_user/config.js +15 -0
  18. package/core/file/plugins/douyin_user/model.js +72 -0
  19. package/core/file/plugins/good_sku/controller.js +80 -0
  20. package/core/file/plugins/h5_user/config.js +19 -0
  21. package/core/file/plugins/h5_user/model.js +71 -0
  22. package/core/file/plugins/lang/config.js +4 -0
  23. package/core/file/plugins/lang/model.js +15 -0
  24. package/core/file/plugins/language/config.js +5 -0
  25. package/core/file/plugins/language/model.js +54 -0
  26. package/core/file/plugins/log/config.js +25 -0
  27. package/core/file/plugins/log/controller.js +31 -0
  28. package/core/file/plugins/log/model.js +51 -0
  29. package/core/file/plugins/model/config.js +12 -0
  30. package/core/file/plugins/model/controller.js +69 -0
  31. package/core/file/plugins/model/model.js +169 -0
  32. package/core/file/plugins/model/service.js +218 -0
  33. package/core/file/plugins/model/validate.js +42 -0
  34. package/core/file/plugins/model_attributes/config.js +12 -0
  35. package/core/file/plugins/model_attributes/model.js +114 -0
  36. package/core/file/plugins/mp_user/config.js +19 -0
  37. package/core/file/plugins/mp_user/model.js +59 -0
  38. package/core/file/plugins/permission/config.js +15 -0
  39. package/core/file/plugins/permission/model.js +91 -0
  40. package/core/file/plugins/role/config.js +27 -0
  41. package/core/file/plugins/role/controller.js +26 -0
  42. package/core/file/plugins/role/model.js +58 -0
  43. package/core/file/plugins/role_permission/config.js +12 -0
  44. package/core/file/plugins/role_permission/controller.js +27 -0
  45. package/core/file/plugins/role_permission/model.js +24 -0
  46. package/core/file/plugins/routes/config.js +17 -0
  47. package/core/file/plugins/routes/controller.js +153 -0
  48. package/core/file/plugins/routes/model.js +70 -0
  49. package/core/file/plugins/routes/service.js +22 -0
  50. package/core/file/plugins/setting/afterExecute.js +14 -0
  51. package/core/file/plugins/setting/config.js +14 -0
  52. package/core/file/plugins/setting/controller.js +50 -0
  53. package/core/file/plugins/setting/model.js +118 -0
  54. package/core/file/plugins/setting/validate.js +42 -0
  55. package/core/file/plugins/system/controller.js +501 -0
  56. package/core/file/plugins/system/service.js +148 -0
  57. package/core/file/plugins/system/validate.js +40 -0
  58. package/core/file/plugins/todolist/config.js +31 -0
  59. package/core/file/plugins/todolist/model.js +69 -0
  60. package/core/file/plugins/toutiao/controller.js +201 -0
  61. package/core/file/plugins/toutiao_user/config.js +15 -0
  62. package/core/file/plugins/toutiao_user/model.js +66 -0
  63. package/core/file/plugins/user/afterExecute.js +38 -0
  64. package/core/file/plugins/user/config.js +9 -0
  65. package/core/file/plugins/user/controller.js +329 -0
  66. package/core/file/plugins/user/model.js +96 -0
  67. package/core/file/plugins/user/test.js +71 -0
  68. package/core/file/plugins/user/validate.js +44 -0
  69. package/core/file/plugins/video/config.js +3 -0
  70. package/core/file/plugins/video/controller.js +15 -0
  71. package/core/file/plugins/video/validate.js +12 -0
  72. package/core/file/plugins/weixin/config.js +3 -0
  73. package/core/file/plugins/weixin/controller.js +994 -0
  74. package/core/file/plugins/weixin/service.js +105 -0
  75. package/core/file/plugins/weixin/validate.js +111 -0
  76. package/core/file/services/aliSms.js +45 -0
  77. package/core/file/services/alipay.js +123 -0
  78. package/core/file/services/amap.js +95 -0
  79. package/core/file/services/card.js +24 -0
  80. package/core/file/services/config.js +38 -0
  81. package/core/file/services/douyin.js +151 -0
  82. package/core/file/services/email.js +45 -0
  83. package/core/file/services/express.js +37 -0
  84. package/core/file/services/geo.js +71 -0
  85. package/core/file/services/qqVideo.js +64 -0
  86. package/core/file/services/toutiao.js +102 -0
  87. package/core/file/services/weixin.js +79 -0
  88. package/core/file/services/weixinArticle.js +53 -0
  89. package/core/file/services/weixinCrypt.js +34 -0
  90. package/core/file/services/weixinMP.js +435 -0
  91. package/core/file/services/weixinPay.js +35 -0
  92. package/core/file/services/xml.js +33 -0
  93. package/core/file/task/shop/index.js +589 -0
  94. package/core/file/task/shop/static/562e45760a44632de6fa7219bab78cce.png +0 -0
  95. package/core/file/task/shop/static/d7aeaeb6bfd68f71a00a83c0f5548363.png +0 -0
  96. package/core/file/utils/index.js +61 -0
  97. package/core/middlewares.js +120 -0
  98. package/core/restc/.npminstall.done +1 -0
  99. package/core/restc/LICENSE +21 -0
  100. package/core/restc/README.md +48 -0
  101. package/core/restc/faas/index.html +1112 -0
  102. package/core/restc/faas/index.txt +31 -0
  103. package/core/restc/faas/install_production.sh +6 -0
  104. package/core/restc/index.d.ts +7 -0
  105. package/core/restc/index.js +9 -0
  106. package/core/restc/lib/express.js +7 -0
  107. package/core/restc/lib/hapi.js +17 -0
  108. package/core/restc/lib/hapiLegacy.js +15 -0
  109. package/core/restc/lib/index.js +46 -0
  110. package/core/restc/lib/koa.js +9 -0
  111. package/core/restc/lib/koa2.js +9 -0
  112. package/core/restc/lib/utils/gateway.js +51 -0
  113. package/core/restc/package.json +41 -0
  114. package/core/validator.js +15 -0
  115. package/index.js +1 -0
  116. package/package.json +65 -0
package/core/app.js ADDED
@@ -0,0 +1,1813 @@
1
+ const Koa = require('koa')
2
+ const Router = require('koa-router')
3
+ const _ = require('lodash')
4
+ const path = require('path')
5
+ const fs = require('fs')
6
+ const Sequelize = require('sequelize')
7
+ const moment = require('moment')
8
+ const { EventEmitter } = require('events')
9
+ const nodeVm = require('vm')
10
+ const LRU = require('lru-cache')
11
+
12
+ const { Serve } = require('static-koa-router')
13
+
14
+ const rootDirectory = process.cwd()
15
+ const ora = require('ora')
16
+ const chalk = require('chalk')
17
+ const { Random } = require('mockjs')
18
+ const util = require('util')
19
+ const jwt = require('jsonwebtoken')
20
+ const fsExtra = require('fs-extra')
21
+ const { getObject } = require('./file/utils')
22
+ const restc = require('./restc')
23
+ const APP_DIR = 'app'
24
+ const PLUGINS_DIR = 'plugins'
25
+ const {
26
+ miErrors,
27
+ miSend,
28
+ miStatic,
29
+ // miProxy,
30
+ miAgent,
31
+ miResponse,
32
+ miCors,
33
+ miBody,
34
+ miXmlBody,
35
+ miCompress,
36
+ miBodyParser,
37
+ miWebsocket,
38
+ miLimit,
39
+ } = require('./middlewares')
40
+ const Validator = require('./validator')
41
+
42
+ let spinner
43
+
44
+ const starter = new EventEmitter()
45
+
46
+ const formatPostFunction = (str) => `eval(${str})`
47
+
48
+ const connectDatabase = (database) => (config) => {
49
+ if (!database) throw new Error('请配置数据库名称')
50
+ if (!config.username) throw new Error('请配置数据库账号')
51
+ if (!config.password) throw new Error('请配置密码')
52
+ if (!config.host) throw new Error('请配置数据库地址')
53
+ // @ts-ignore
54
+ const db = new Sequelize(database, config.username, config.password, {
55
+ host: config.host,
56
+ dialect: config.dialect,
57
+ port: config.port,
58
+ logging: config.logging,
59
+ timezone: config.timezone,
60
+ operatorsAliases: config.operatorsAliases,
61
+ pool: config.pool,
62
+ })
63
+ return db
64
+ }
65
+
66
+ const defaultConfig = require('./config')
67
+
68
+ class APP {
69
+ constructor(_config) {
70
+ const config = _.defaultsDeep(_config, defaultConfig)
71
+ const { port } = config
72
+ this.config = config
73
+ this.port = port
74
+ this.app = new Koa()
75
+ this.server = null
76
+ this.middlewares = [
77
+ ..._.get(config, 'middlewares', []),
78
+ miLimit(config.rateLimit),
79
+ miStatic(config.static),
80
+ miAgent(),
81
+ miWebsocket(),
82
+ miCors(config.includes, config.ip),
83
+ // miRestc(config),
84
+ miBody(config.koaBody),
85
+ miXmlBody(config.xmlBody),
86
+ miBodyParser(config.bodyParser),
87
+
88
+ miCompress(),
89
+ // miProxy(),
90
+ miSend(),
91
+ miResponse(config.response),
92
+ // miToken(),
93
+ miErrors(),
94
+ ]
95
+ }
96
+
97
+ async start() {
98
+ console.log(chalk.green('==================== start ===================='))
99
+ spinner = ora('Loading unicorns').start()
100
+ spinner.color = 'yellow'
101
+ spinner.text = ' '
102
+ this.middlewares.forEach((middleware) => {
103
+ this.app.use(middleware)
104
+ })
105
+ // this.app.use(cors({
106
+ // origin: 'http://0.0.0.0:3078',
107
+ // credentials: true,
108
+ // }));
109
+ const { is_off = false } = this.config.restc
110
+ if (!is_off) {
111
+ this.app.use((ctx, next) => {
112
+ const { url } = ctx.request
113
+ const appName = url.split('/')[1]
114
+ const excludes = [
115
+ ...this.config.restc.excludes,
116
+ ..._.get(ctx.app[appName], 'appConfig.restc.excludes', []).map(
117
+ (item) => `/${appName}/${item}`
118
+ ),
119
+ ]
120
+ this.app.use(
121
+ restc.koa2({
122
+ excludes,
123
+ })
124
+ )
125
+ return next()
126
+ })
127
+ }
128
+
129
+ /**
130
+ * jwt中间件
131
+ */
132
+ this.app.use(async (ctx, next) => {
133
+ const verify = util.promisify(jwt.verify)
134
+ const { url, host, ip, method } = ctx.request
135
+ const appName = url.split('/')[1]
136
+ const { secret, whiteList, _whiteList, tokenKey, expiresIn } =
137
+ ctx.app[appName] && ctx.app[appName].appConfig
138
+ ? ctx.app[appName].appConfig.jwt
139
+ : {
140
+ secret: 'token',
141
+ tokenKey: 'token',
142
+ expiresIn: '200000000',
143
+ _whiteList: [],
144
+ whiteList: [
145
+ 'find',
146
+ 'administrator/login',
147
+ 'administrator/checkLogin',
148
+ 'user/checkLogin',
149
+ 'user/login',
150
+ 'user/register',
151
+ 'common',
152
+ 'good/search',
153
+ 'category/tree',
154
+ 'system',
155
+ ],
156
+ }
157
+
158
+ /**
159
+ * 挂在jwt解密方法
160
+ */
161
+ if (this.app[appName]) {
162
+ this.app[appName].sign = async (user) => {
163
+ const result = await jwt.sign(user, secret, {
164
+ expiresIn,
165
+ })
166
+ return result
167
+ }
168
+ }
169
+ // eslint-disable-next-line no-nested-ternary
170
+ ctx.request.clientType = ctx.header['client-type']
171
+ ? Number(ctx.header['client-type'])
172
+ : host.startsWith('localhost')
173
+ ? 0
174
+ : 99
175
+
176
+ const token =
177
+ ctx.header[`${appName}-${tokenKey}`] ||
178
+ ctx.cookies.get(`${appName}-${tokenKey}`) ||
179
+ ctx.query[`${appName}-${tokenKey}`]
180
+
181
+ if (url.length > 2) {
182
+ console.log(
183
+ moment().format('HH:mm:ss'),
184
+ url,
185
+ host,
186
+ ctx.request.clientType
187
+ )
188
+ }
189
+
190
+ try {
191
+ const { user } = await verify(token, secret)
192
+ if (user) {
193
+ console.log('user id ---> ', appName, '--->', user.id, user.name)
194
+ ctx.request[`${appName}-user`] = user
195
+ }
196
+ } catch (e) {
197
+ const combineWhiteList = Array.from(
198
+ new Set([...whiteList, ..._whiteList])
199
+ )
200
+ const pass = combineWhiteList.some(
201
+ (link) => link !== '/' && url.includes(link)
202
+ )
203
+
204
+ if (
205
+ pass ||
206
+ url === `/${appName}/` ||
207
+ method === 'GET' ||
208
+ url.includes('.') ||
209
+ ctx.request.clientType === 0
210
+ ) {
211
+ return await next()
212
+ }
213
+ throw new Error('没有权限或登录失效,请重新登录')
214
+ }
215
+ await next()
216
+ })
217
+
218
+ this.initApp()
219
+
220
+ this.app.on('error', async (err, ctx) => {
221
+ const { url } = ctx.request
222
+ const appName = url.split('/')[1]
223
+ const errorData = {
224
+ url: ctx.request.href,
225
+ body: ctx.request.body,
226
+ message: err.message,
227
+ content: err.stack,
228
+ header: ctx.request.header,
229
+ }
230
+ if (
231
+ this.app &&
232
+ this.app[appName] &&
233
+ this.app[appName].model &&
234
+ this.app[appName].model.log
235
+ ) {
236
+ this.app[appName].model.log.upsert(errorData)
237
+ }
238
+ throw new Error(err)
239
+ })
240
+
241
+ // this.app.on('message', async (content) => {
242
+ // console.log(content);
243
+ // });
244
+
245
+ starter.on('loadAll', () => {
246
+ const appName = this.config.includes[0]
247
+ this.server = this.app.listen(this.port, () => {
248
+ console.log(`server is running at http://localhost:${this.port}`)
249
+ console.log(
250
+ `first app is http://localhost:3001/${appName}/setting/findOne`
251
+ )
252
+ })
253
+
254
+ if (process.env.NODE_ENV !== 'production') {
255
+ if (
256
+ this.app[appName].service.model &&
257
+ this.app[appName].service.model.loadModel
258
+ ) {
259
+ this.app[appName].service.model.loadModel({
260
+ app: this.app[appName],
261
+ appName: appName,
262
+ })
263
+ } else {
264
+ fs.writeFileSync(
265
+ `${rootDirectory}/${APP_DIR}/${appName}/${PLUGINS_DIR}/model/service.js`,
266
+ ''
267
+ )
268
+ this.start()
269
+ }
270
+ }
271
+
272
+ process.on('exit', (code) => {
273
+ console.log(`[exit custom], About to exit with code: ${code}`)
274
+ })
275
+ if (process.env.NODE_ENV === 'production') {
276
+ if (
277
+ this.app[appName].service.cache &&
278
+ this.app[appName].service.cache.init
279
+ ) {
280
+ this.app[appName].service.cache.init({
281
+ app: this.app[appName],
282
+ appName: appName,
283
+ })
284
+ }
285
+ process.on('SIGINT', () => {
286
+ console.log('Closing server...')
287
+ this.server.close(() => {
288
+ console.log('Server closed !!! ')
289
+ process.exit()
290
+ })
291
+ })
292
+ }
293
+ })
294
+ }
295
+
296
+ async initApp() {
297
+ let dirList = fs.readdirSync(
298
+ path.resolve(__dirname, `${rootDirectory}/${APP_DIR}`)
299
+ )
300
+ dirList = dirList.filter((item) => this.config.includes.includes(item))
301
+
302
+ let dbFlag = false
303
+ for (let i = 0; i < dirList.length; i++) {
304
+ const appName = dirList[i]
305
+
306
+ console.log(
307
+ chalk.green(`==================== 启动 ${appName} ====================`)
308
+ )
309
+ const routerConfig = {
310
+ prefix: `/${appName}`,
311
+ }
312
+ const router = new Router(routerConfig)
313
+ Serve(
314
+ path.resolve(
315
+ __dirname,
316
+ `${rootDirectory}/${APP_DIR}/${appName}/public`
317
+ ),
318
+ router
319
+ )
320
+
321
+ try {
322
+ const db = connectDatabase(appName)(this.config.db)
323
+ if (process.env.NODE_ENV === 'production') {
324
+ if (!dbFlag) {
325
+ console.log(chalk.green(`检查${appName}数据库`))
326
+ await db.authenticate()
327
+ dbFlag = true
328
+ }
329
+ } else {
330
+ console.log(chalk.green(`检查${appName}数据库`))
331
+ await db.authenticate()
332
+ }
333
+ const configPath = `${rootDirectory}/${APP_DIR}/${appName}/config.js`
334
+ const configFile = path.resolve(__dirname, configPath)
335
+ const configExist = fs.existsSync(configFile)
336
+
337
+ this.app[appName] = {
338
+ sequelize: db,
339
+ appConfig: _.defaultsDeep(
340
+ configExist ? require(path.resolve(__dirname, configPath)) : {},
341
+ this.config.app
342
+ ),
343
+ }
344
+
345
+ // const proxyConfig = _.get(this.app[appName], 'appConfig.proxy');
346
+ const taskConfig = _.get(this.app[appName], 'appConfig.task', {
347
+ is_available: false,
348
+ })
349
+ // if (proxyConfig) {
350
+ // this.app.use(proxy(`/${appName}`, proxyConfig));
351
+ // }
352
+
353
+ const initFileConfig = _.get(
354
+ this.app[appName],
355
+ 'appConfig.initFile',
356
+ false
357
+ )
358
+ if (initFileConfig) {
359
+ this.initFile(appName)
360
+ }
361
+
362
+ this.initPlugin(appName)
363
+ this.app[appName].event = new EventEmitter()
364
+
365
+ const cacheConfig = _.get(this.app[appName], 'appConfig.cache', {
366
+ max: 100,
367
+ maxAge: 1000 * 60 * 60 * 24,
368
+ })
369
+
370
+ this.app[appName].cache = new LRU({
371
+ max: cacheConfig.max,
372
+ maxAge: cacheConfig.maxAge,
373
+ })
374
+
375
+ this.initModel(appName)
376
+ // const models = Object.keys(this.app[appName].model).join(' / ')
377
+ if (taskConfig.is_available) {
378
+ this.initTask(appName)
379
+ }
380
+ } catch (e) {
381
+ console.log(`${appName}数据库连接失败`)
382
+ console.log(e)
383
+ throw new Error(e)
384
+ }
385
+ if (
386
+ this.app[appName] &&
387
+ this.app[appName].appConfig &&
388
+ this.app[appName].appConfig.router
389
+ ) {
390
+ this.app[appName].appConfig.router(router)(this.app[appName])
391
+ }
392
+ this.initRouter(router)
393
+ this.app.use(router.routes()).use(router.allowedMethods())
394
+ }
395
+
396
+ const router = new Router()
397
+ if (this.config.router) {
398
+ this.config.router(router)
399
+ }
400
+ router.post('*', () => {
401
+ throw new Error('接口路径不符合规范')
402
+ })
403
+ this.app.use(router.routes()).use(router.allowedMethods())
404
+
405
+ spinner.stop()
406
+
407
+ console.log(chalk.green('==================== end ===================='))
408
+ starter.emit('loadAll')
409
+ }
410
+
411
+ initFile(appName) {
412
+ const dirExist = fsExtra.existsSync(path.resolve(__dirname, './file'))
413
+ if (dirExist) {
414
+ fsExtra.copySync(
415
+ path.resolve(__dirname, './file'),
416
+ path.resolve(__dirname, `${rootDirectory}/${APP_DIR}/${appName}`),
417
+ {
418
+ clobber: false,
419
+ }
420
+ )
421
+ }
422
+ }
423
+
424
+ initTask(appName) {
425
+ console.log(chalk.red(`启动任务${appName}`))
426
+ const taskDir = path.resolve(
427
+ __dirname,
428
+ `${rootDirectory}/${APP_DIR}/${appName}/task`
429
+ )
430
+ const publicExist = fs.existsSync(taskDir)
431
+ if (!publicExist) fs.mkdirSync(taskDir)
432
+ fs.readdirSync(taskDir).forEach((folder) => {
433
+ const isFolder = fs.lstatSync(path.resolve(taskDir, folder)).isDirectory()
434
+ if (isFolder) {
435
+ fs.readdirSync(path.resolve(taskDir, folder)).forEach((filename) => {
436
+ if (filename === 'index.js') {
437
+ const task = require(path.resolve(
438
+ __dirname,
439
+ `${rootDirectory}/${APP_DIR}/${appName}/task/${folder}`
440
+ ))
441
+ this.app[appName] = _.defaultsDeep(
442
+ {
443
+ task,
444
+ },
445
+ this.app[appName]
446
+ )
447
+ }
448
+ })
449
+ }
450
+ })
451
+ }
452
+
453
+ initPlugin(appName) {
454
+ const pluginDir = path.resolve(
455
+ __dirname,
456
+ `${rootDirectory}/${APP_DIR}/${appName}/${PLUGINS_DIR}`
457
+ )
458
+ const publicExist = fs.existsSync(pluginDir)
459
+ if (!publicExist) fs.mkdirSync(pluginDir)
460
+
461
+ fs.readdirSync(pluginDir).forEach((folder) => {
462
+ const isFolder = fs
463
+ .lstatSync(path.resolve(pluginDir, folder))
464
+ .isDirectory()
465
+ if (isFolder) {
466
+ fs.readdirSync(path.resolve(pluginDir, folder)).forEach((filename) => {
467
+ const extname = path.extname(filename)
468
+ if (extname === '.js') {
469
+ const n = path.basename(filename, extname)
470
+ if (
471
+ [
472
+ 'model',
473
+ 'controller',
474
+ 'service',
475
+ 'validate',
476
+ 'config',
477
+ 'beforeValidate',
478
+ 'afterValidate',
479
+ 'afterExecute',
480
+ ].includes(n)
481
+ ) {
482
+ if (n === 'model') {
483
+ const defaultModelPath = path.resolve(
484
+ __dirname,
485
+ `./file/${PLUGINS_DIR}/${folder}`,
486
+ `${filename}`
487
+ )
488
+ const defaultModelExist = fs.existsSync(defaultModelPath)
489
+ const currentAttributes = require(path.resolve(
490
+ pluginDir,
491
+ folder,
492
+ filename
493
+ ))
494
+ const _attributes =
495
+ Object.keys(currentAttributes).length === 0 &&
496
+ defaultModelExist
497
+ ? require(defaultModelPath)
498
+ : currentAttributes
499
+
500
+ const belongs = _.get(
501
+ this.app[appName].config[folder],
502
+ 'belongs',
503
+ 'page'
504
+ )
505
+
506
+ const _id =
507
+ belongs === 'setting'
508
+ ? {
509
+ id: {
510
+ type: Sequelize.TINYINT.UNSIGNED,
511
+ allowNull: false,
512
+ primaryKey: true,
513
+ autoIncrement: true,
514
+ },
515
+ }
516
+ : {
517
+ id: {
518
+ type: Sequelize.MEDIUMINT.UNSIGNED,
519
+ allowNull: false,
520
+ primaryKey: true,
521
+ autoIncrement: true,
522
+ },
523
+ }
524
+
525
+ const attributes = _.defaultsDeep(_.cloneDeep(_attributes), {
526
+ ..._id,
527
+ createdid: {
528
+ type: Sequelize.MEDIUMINT.UNSIGNED,
529
+ allowNull: false,
530
+ defaultValue: 0,
531
+ },
532
+ created_at: {
533
+ type: Sequelize.DATE,
534
+ allowNull: false,
535
+ // defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
536
+ comment: '生成时间',
537
+ },
538
+ updated_at: {
539
+ type: Sequelize.DATE,
540
+ allowNull: false,
541
+ // defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
542
+ comment: '修改时间',
543
+ },
544
+ })
545
+
546
+ this.app[appName].attributes = {
547
+ ...this.app[appName].attributes,
548
+ [folder]: _attributes,
549
+ }
550
+
551
+ const instance = this.app[appName].sequelize.define(
552
+ folder,
553
+ attributes,
554
+ {
555
+ freezeTableName: true,
556
+ comment: folder,
557
+ // timestamps: !modelConfig.belongs === 'setting',
558
+ createdAt: 'created_at',
559
+ updatedAt: 'updated_at',
560
+ name: {
561
+ singular: folder,
562
+ plural: `${folder}s`,
563
+ },
564
+ hooks: {
565
+ beforeCreate: (res, t) => {
566
+ try {
567
+ if (res.toJSON().user_id) {
568
+ res.createdid = res.toJSON().user_id
569
+ }
570
+ } catch (e) {
571
+ console.log(e)
572
+ }
573
+ },
574
+ },
575
+ }
576
+ )
577
+ instance.getName = function () {
578
+ return folder
579
+ }
580
+ this.app[appName] = _.defaultsDeep(
581
+ {
582
+ [n]: {
583
+ [folder]: instance,
584
+ },
585
+ },
586
+ this.app[appName]
587
+ )
588
+ const mockObj = _.pickBy(
589
+ _attributes,
590
+ (item) => item.mock && typeof item.mock === 'function'
591
+ )
592
+ this.app[appName] = _.defaultsDeep(
593
+ {
594
+ mock: {
595
+ [folder]: () =>
596
+ _.zipObject(
597
+ Object.keys(mockObj),
598
+ Object.keys(mockObj).map((key) =>
599
+ _.result(mockObj, `${key}.mock`)
600
+ )
601
+ ),
602
+ },
603
+ },
604
+ this.app[appName]
605
+ )
606
+ } else if (
607
+ ['controller', 'validate', 'afterExecute', 'service'].includes(
608
+ n
609
+ )
610
+ ) {
611
+ const defaultPath = path.resolve(
612
+ __dirname,
613
+ `./file/${PLUGINS_DIR}/${folder}`,
614
+ `${filename}`
615
+ )
616
+ const defaultExist = fs.existsSync(defaultPath)
617
+ const defaultN = !defaultExist
618
+ ? Object.create(null)
619
+ : {
620
+ [n]: {
621
+ [folder]: defaultExist
622
+ ? require(path.resolve(
623
+ __dirname,
624
+ `./file/${PLUGINS_DIR}/${folder}`,
625
+ `${filename}`
626
+ ))
627
+ : null,
628
+ },
629
+ }
630
+ this.app[appName] = _.defaultsDeep(
631
+ {
632
+ [n]: {
633
+ [folder]: require(path.resolve(
634
+ pluginDir,
635
+ `${folder}`,
636
+ `${filename}`
637
+ )),
638
+ },
639
+ },
640
+ defaultN,
641
+ this.app[appName]
642
+ )
643
+ } else {
644
+ this.app[appName] = _.defaultsDeep(
645
+ {
646
+ [n]: {
647
+ [folder]: require(path.resolve(
648
+ pluginDir,
649
+ `${folder}`,
650
+ `${filename}`
651
+ )),
652
+ },
653
+ },
654
+ this.app[appName]
655
+ )
656
+ }
657
+ }
658
+ }
659
+ })
660
+ }
661
+ })
662
+ }
663
+
664
+ initModel(appName) {
665
+ for (let i = 0; i < Object.keys(this.app[appName].model).length; i++) {
666
+ const model = Object.keys(this.app[appName].model)[i]
667
+
668
+ if (!this.app[appName].include || !this.app[appName].include[model]) {
669
+ this.app[appName] = _.defaultsDeep(
670
+ {
671
+ include: {
672
+ [model]: [],
673
+ },
674
+ },
675
+ this.app[appName]
676
+ )
677
+ }
678
+ const db = this.app[appName].attributes[model]
679
+
680
+ const list = Object.keys(db)
681
+ .filter((item) => item.endsWith('_id'))
682
+ .map((item) => item.match(/(.*)_id/)[1])
683
+
684
+ list.forEach((model1) => {
685
+ if (this.app[appName].attributes[model][`${model1}_id`].belongsTo) {
686
+ const myModel = this.app[appName].attributes[model][`${model1}_id`]
687
+ .belongsTo
688
+ this.app[appName].model[model].belongsTo(
689
+ this.app[appName].model[myModel],
690
+ {
691
+ as: model1,
692
+ foreignKey: `${model1}_id`,
693
+ constraints: false,
694
+ }
695
+ )
696
+ this.app[appName].include[model].push({
697
+ model: this.app[appName].model[myModel],
698
+ as: model1,
699
+ })
700
+ }
701
+
702
+ if (!this.app[appName].include[model1]) {
703
+ this.app[appName].include[model1] = []
704
+ }
705
+ if (!this.app[appName].include[model]) {
706
+ this.app[appName].include[model] = []
707
+ }
708
+ if (model1 === 'parent') {
709
+ this.app[appName].model[model].belongsTo(
710
+ this.app[appName].model[model],
711
+ {
712
+ as: 'parent',
713
+ foreignKey: 'parent_id',
714
+ constraints: false,
715
+ }
716
+ )
717
+ this.app[appName].include[model].push({
718
+ model: this.app[appName].model[model],
719
+ as: 'parent',
720
+ })
721
+ }
722
+
723
+ if (model1 && this.app[appName].model[model1]) {
724
+ this.app[appName].model[model].hasOne(
725
+ this.app[appName].model[model1],
726
+ {
727
+ foreignKey: 'id',
728
+ sourceKey: `${model1}_id`,
729
+ constraints: false,
730
+ }
731
+ )
732
+ this.app[appName].include[model].push({
733
+ model: this.app[appName].model[model1],
734
+ })
735
+ if (
736
+ this.app[appName].attributes[model][`${model1}_id`].hasOne &&
737
+ this.app[appName].attributes[model][`${model1}_id`].hasOne === true
738
+ ) {
739
+ this.app[appName].model[model1].hasOne(
740
+ this.app[appName].model[model],
741
+ {
742
+ foreignKey: `${model1}_id`,
743
+ targetKey: 'id',
744
+ constraints: false,
745
+ }
746
+ )
747
+ } else {
748
+ this.app[appName].model[model1].hasMany(
749
+ this.app[appName].model[model],
750
+ {
751
+ foreignKey: `${model1}_id`,
752
+ targetKey: 'id',
753
+ constraints: false,
754
+ }
755
+ )
756
+ }
757
+ // this.app.include[model1].push({ model: app.model[model] });
758
+ }
759
+ })
760
+ }
761
+ }
762
+
763
+ initRouter(router) {
764
+ router.post('/:controller', async (ctx, next) => {
765
+ const appName = router.opts.prefix.substring(1)
766
+ const { app } = ctx
767
+ const { controller } = ctx.params
768
+ if (
769
+ !_.get(app, `${appName}.model.${controller}`) ||
770
+ !_.get(app, `${appName}.controller.${controller}`)
771
+ ) {
772
+ throw new Error('路由model定义缺失')
773
+ }
774
+ await next()
775
+ })
776
+ router.post('/:controller/:fn', async (ctx) => {
777
+ const appName = router.opts.prefix.substring(1)
778
+ const { app } = ctx
779
+
780
+ const { controller, fn } = ctx.params
781
+ if (_.get(app, `${appName}.model.${controller}`) && fn === 'mock') {
782
+ return ctx.SUCCESS(await app[appName].mock[controller]())
783
+ }
784
+
785
+ if (
786
+ !_.get(app, `${appName}.model.${controller}.${fn}`) &&
787
+ !_.get(app, `${appName}.controller.${controller}.${fn}`)
788
+ ) {
789
+ throw new Error('路由fn定义缺失')
790
+ }
791
+
792
+ const cacheModel = _.get(ctx.app[appName], 'appConfig.cacheModel', [])
793
+
794
+ if (fn === 'updateSort') {
795
+ if (cacheModel.includes(controller)) {
796
+ ctx.app[appName].cache.set(`${controller}`, null)
797
+ }
798
+ }
799
+
800
+ if (_.get(app, `${appName}.controller.${controller}.${fn}`)) {
801
+ const hooksList = [
802
+ 'beforeValidate',
803
+ 'validate',
804
+ 'afterValidate',
805
+ 'controller',
806
+ 'afterExecute',
807
+ ]
808
+ for (let i = 0; i < hooksList.length; i++) {
809
+ if (_.get(app, `${appName}.${hooksList[i]}.${controller}.${fn}`)) {
810
+ await app[appName][hooksList[i]][controller][fn](ctx)
811
+ }
812
+ }
813
+
814
+ return true
815
+ }
816
+
817
+ if (
818
+ _.get(app, `${appName}.model.${controller}`) &&
819
+ [
820
+ 'findOne',
821
+ 'findAll',
822
+ 'findAndCountAll',
823
+ 'create',
824
+ 'upsert',
825
+ 'destroy',
826
+ 'bulkCreate',
827
+ 'count',
828
+ ].includes(fn)
829
+ ) {
830
+ const excludeAuth =
831
+ _.get(app, `${appName}.config.${controller}.excludeAuth`) || []
832
+ if (excludeAuth.includes(fn) && ctx.request.clientType > 1) {
833
+ throw new Error('没有权限')
834
+ }
835
+
836
+ const hooksList = ['beforeValidate', 'validate', 'afterValidate']
837
+ for (let i = 0; i < hooksList.length; i++) {
838
+ if (_.get(app, `${appName}.${hooksList[i]}.${controller}.${fn}`)) {
839
+ await app[appName][hooksList[i]][controller][fn](ctx)
840
+ }
841
+ }
842
+ if (['findOne', 'findAll', 'findAndCountAll', 'count'].includes(fn)) {
843
+ const {
844
+ where,
845
+ limit,
846
+ offset,
847
+ includes,
848
+ order,
849
+ union,
850
+ include,
851
+ referenceInclude,
852
+ relate,
853
+ attributes,
854
+ excludeInclude,
855
+ raw = false,
856
+ is_split = false,
857
+ omit = [],
858
+ ...other
859
+ } = ctx.request.body
860
+ if (['findOne', 'findAll', 'findAndCountAll'].includes(fn)) {
861
+ if (!_.isEmpty(other)) {
862
+ throw new Error('参数不合理,条件需要放在where里')
863
+ }
864
+ }
865
+
866
+ let myInclude
867
+ if (include) {
868
+ if (relate) {
869
+ /**
870
+ * 如果像user_distribute里有两个user,需要用这个来重新建立关系
871
+ */
872
+ const _str = formatPostFunction(relate)
873
+ nodeVm.runInNewContext(_str)(app[appName])
874
+ // relate.toFunction()(app[appName]);
875
+ }
876
+ const _str = formatPostFunction(include)
877
+ const postInclude = nodeVm.runInNewContext(_str)
878
+ // const postInclude = include.toFunction();
879
+ myInclude =
880
+ app[appName] &&
881
+ app[appName].include &&
882
+ app[appName].include[controller]
883
+ ? _.uniqWith(
884
+ [
885
+ ...postInclude(app[appName]),
886
+ ...app[appName].include[controller],
887
+ ],
888
+ _.isEqual
889
+ )
890
+ : postInclude(app[appName])
891
+ } else if (include === null) {
892
+ myInclude = null
893
+ } else {
894
+ myInclude = app[appName].include
895
+ ? app[appName].include[controller]
896
+ : null
897
+ }
898
+
899
+ if (referenceInclude) {
900
+ const _referenceInclude = Object.keys(referenceInclude).map(
901
+ (model) => ({
902
+ model: app[appName].model[model],
903
+ where: {
904
+ ...referenceInclude[model],
905
+ },
906
+ })
907
+ )
908
+ myInclude = [...myInclude, ..._referenceInclude]
909
+ }
910
+ myInclude = myInclude.filter(
911
+ (item) => item.model && item.model.getName()
912
+ )
913
+
914
+ if (
915
+ excludeInclude &&
916
+ Array.isArray(excludeInclude) &&
917
+ excludeInclude.length > 0
918
+ ) {
919
+ myInclude = myInclude.filter((i) => {
920
+ return excludeInclude.every((m) => {
921
+ return app[appName].model[m] !== i.model
922
+ })
923
+ })
924
+ }
925
+
926
+ let _where
927
+
928
+ if (where) {
929
+ _where = _.cloneDeep(where)
930
+ // if (util.isObject(where)) {
931
+ // _where = _.cloneDeep(where);
932
+ // } else if (util.isString(where)) {
933
+ // const _fn = app[appName].vm.run(where);
934
+ // _where = _fn(Sequelize.Op);
935
+ // // _where = where.toFunction()(Sequelize.Op);
936
+ // }
937
+ } else {
938
+ _where = {}
939
+ }
940
+ if (fn === 'count') {
941
+ const requiredInclude = myInclude.filter(
942
+ (item) =>
943
+ (item.where && !item.hasOwnProperty('required')) ||
944
+ (item.hasOwnProperty('required') && item.required)
945
+ )
946
+ const result = await app[appName].model[controller][fn]({
947
+ where: _where,
948
+ include: requiredInclude,
949
+ attributes,
950
+ group: other.group,
951
+ })
952
+ ctx.SUCCESS(result)
953
+ }
954
+ if (['findOne', 'findAll', 'findAndCountAll'].includes(fn)) {
955
+ // 拆分
956
+ if (is_split) {
957
+ if (fn === 'findAndCountAll') {
958
+ const requiredInclude = myInclude.filter(
959
+ (item) =>
960
+ (item.where && !item.hasOwnProperty('required')) ||
961
+ (item.hasOwnProperty('required') && item.required)
962
+ )
963
+ const { count, rows } = await app[appName].model[controller][
964
+ fn
965
+ ]({
966
+ where: _where,
967
+ limit,
968
+ offset,
969
+ includes,
970
+ include: requiredInclude,
971
+ order,
972
+ distinct: true,
973
+ attributes,
974
+ raw,
975
+ })
976
+ if (rows.length === 0) {
977
+ return ctx.SUCCESS({
978
+ count,
979
+ rows: [],
980
+ })
981
+ }
982
+
983
+ let objList = {}
984
+ for (let j = 0; j < myInclude.length; j++) {
985
+ if (
986
+ (myInclude[j].where &&
987
+ !myInclude[j].hasOwnProperty('required')) ||
988
+ (myInclude[j].hasOwnProperty('required') &&
989
+ myInclude[j].required)
990
+ ) {
991
+ continue
992
+ }
993
+ const model = myInclude[j].model
994
+ const modelName = model.getName()
995
+
996
+ if (app[appName].attributes[controller][modelName + '_id']) {
997
+ const idList = rows
998
+ .map((r) => r[modelName + '_id'])
999
+ .filter((item) => item)
1000
+
1001
+ if (idList.length) {
1002
+ const list =
1003
+ !myInclude[j].include &&
1004
+ app[appName].cache.get(modelName)
1005
+ ? app[appName].cache.get(modelName)
1006
+ : await model.findAll({
1007
+ where: {
1008
+ id: idList,
1009
+ ...myInclude[j].where,
1010
+ },
1011
+ include: myInclude[j].include,
1012
+ })
1013
+ objList = {
1014
+ ...objList,
1015
+ [modelName]: list,
1016
+ }
1017
+ } else {
1018
+ objList = {
1019
+ ...objList,
1020
+ [modelName]: [],
1021
+ }
1022
+ }
1023
+ } else {
1024
+ const idList = rows.map((r) => r.id).filter((item) => item)
1025
+ if (idList.length) {
1026
+ const list = await model.findAll({
1027
+ where: {
1028
+ [controller + '_id']: idList,
1029
+ ...myInclude[j].where,
1030
+ },
1031
+ include: myInclude[j].include,
1032
+ })
1033
+ objList = {
1034
+ ...objList,
1035
+ [modelName]: list,
1036
+ }
1037
+ } else {
1038
+ objList = {
1039
+ ...objList,
1040
+ [modelName]: [],
1041
+ }
1042
+ }
1043
+ }
1044
+ }
1045
+
1046
+ let result = []
1047
+ for (let i = 0; i < rows.length; i++) {
1048
+ for (let j = 0; j < myInclude.length; j++) {
1049
+ if (
1050
+ (myInclude[j].where &&
1051
+ !myInclude[j].hasOwnProperty('required')) ||
1052
+ (myInclude[j].hasOwnProperty('required') &&
1053
+ myInclude[j].required)
1054
+ ) {
1055
+ continue
1056
+ }
1057
+ const model = myInclude[j].model
1058
+ const modelName = model.getName()
1059
+ if (
1060
+ app[appName].attributes[controller][modelName + '_id']
1061
+ ) {
1062
+ const target = objList[modelName].find(
1063
+ (b) => b.id === rows[i][modelName + '_id']
1064
+ )
1065
+ rows[i].setDataValue(modelName, target)
1066
+ } else {
1067
+ const hasOne =
1068
+ app[appName].attributes[modelName][controller + '_id']
1069
+ .hasOne
1070
+ if (hasOne) {
1071
+ rows[i].setDataValue(
1072
+ modelName,
1073
+ objList[modelName].find(
1074
+ (b) => b[controller + '_id'] === rows[i].id
1075
+ ) || null
1076
+ )
1077
+ } else {
1078
+ rows[i].setDataValue(
1079
+ [modelName + 's'],
1080
+ objList[modelName].filter(
1081
+ (b) => b[controller + '_id'] === rows[i].id
1082
+ )
1083
+ )
1084
+ }
1085
+ }
1086
+ }
1087
+ result = [...result, rows[i]]
1088
+ }
1089
+ ctx.SUCCESS({ count, rows: result })
1090
+ } else if (fn === 'findAll') {
1091
+ const requiredInclude = myInclude.filter(
1092
+ (item) =>
1093
+ (item.where && !item.hasOwnProperty('required')) ||
1094
+ (item.hasOwnProperty('required') && item.required)
1095
+ )
1096
+ const rows = await app[appName].model[controller][fn]({
1097
+ where: _where,
1098
+ limit,
1099
+ offset,
1100
+ includes,
1101
+ include: requiredInclude,
1102
+ order,
1103
+ distinct: true,
1104
+ attributes,
1105
+ raw,
1106
+ })
1107
+
1108
+ if (rows.length === 0) ctx.SUCCESS([])
1109
+ let objList = {}
1110
+ for (let j = 0; j < myInclude.length; j++) {
1111
+ const model = myInclude[j].model
1112
+ const modelName = model.getName()
1113
+
1114
+ if (app[appName].attributes[controller][modelName + '_id']) {
1115
+ const modelIdList = _.uniq(
1116
+ rows
1117
+ .reduce((a, b) => {
1118
+ const next = Array.isArray(b[modelName + '_id'])
1119
+ ? b[modelName + '_id']
1120
+ : [b[modelName + '_id']]
1121
+ return [...a, ...next]
1122
+ }, [])
1123
+ .filter((item) => item)
1124
+ )
1125
+ if (modelIdList.length) {
1126
+ const list =
1127
+ !myInclude[j].include &&
1128
+ !myInclude[j].where &&
1129
+ app[appName].cache.get(modelName)
1130
+ ? app[appName].cache.get(modelName)
1131
+ : await model.findAll({
1132
+ where: {
1133
+ id: modelIdList,
1134
+ ...(myInclude[j].where
1135
+ ? myInclude[j].where
1136
+ : {}),
1137
+ },
1138
+ include: myInclude[j].include,
1139
+ })
1140
+ objList = {
1141
+ ...objList,
1142
+ [modelName]: list,
1143
+ }
1144
+ } else {
1145
+ objList = {
1146
+ ...objList,
1147
+ [modelName]: [],
1148
+ }
1149
+ }
1150
+ } else {
1151
+ const modelIdList = rows
1152
+ .map((r) => r.id)
1153
+ .filter((item) => item)
1154
+ if (
1155
+ modelIdList.length &&
1156
+ app[appName].attributes[modelName] &&
1157
+ app[appName].attributes[modelName][controller + '_id']
1158
+ ) {
1159
+ const { required, include, where, ...rest } = myInclude[j]
1160
+ const list = await model.findAll({
1161
+ where: {
1162
+ [controller + '_id']: modelIdList,
1163
+ ...(where || {}),
1164
+ },
1165
+ ...rest,
1166
+ include,
1167
+ })
1168
+ objList = {
1169
+ ...objList,
1170
+ [modelName]: list,
1171
+ }
1172
+ } else {
1173
+ objList = {
1174
+ ...objList,
1175
+ [modelName]: [],
1176
+ }
1177
+ }
1178
+ }
1179
+ }
1180
+
1181
+ let result = []
1182
+ for (let i = 0; i < rows.length; i++) {
1183
+ for (let j = 0; j < myInclude.length; j++) {
1184
+ const model = myInclude[j].model
1185
+ const modelName = model.getName()
1186
+ if (
1187
+ app[appName].attributes[controller][modelName + '_id']
1188
+ ) {
1189
+ const is_single = !(
1190
+ (app[appName].attributes[controller][modelName + '_id']
1191
+ .defaultValue &&
1192
+ Array.isArray(
1193
+ app[appName].attributes[controller][
1194
+ modelName + '_id'
1195
+ ].defaultValue
1196
+ )) ||
1197
+ (app[appName].attributes[controller][modelName + '_id']
1198
+ .type &&
1199
+ app[appName].attributes[controller][modelName + '_id']
1200
+ .type === 'JSON') ||
1201
+ (app[appName].attributes[controller][modelName + '_id']
1202
+ .type &&
1203
+ app[appName].attributes[controller][modelName + '_id']
1204
+ .type.key &&
1205
+ app[appName].attributes[controller][modelName + '_id']
1206
+ .type.key === 'JSON')
1207
+ )
1208
+ if (is_single) {
1209
+ rows[i].setDataValue(
1210
+ modelName,
1211
+ objList[modelName].find(
1212
+ (b) => b.id === rows[i][modelName + '_id']
1213
+ )
1214
+ )
1215
+ } else {
1216
+ if (rows[i][modelName + '_id']) {
1217
+ rows[i].setDataValue(
1218
+ [modelName + 's'],
1219
+ rows[i][modelName + '_id'].map((modelId) => {
1220
+ return objList[modelName].find(
1221
+ (b) => b.id === modelId
1222
+ )
1223
+ })
1224
+ )
1225
+ }
1226
+ }
1227
+ } else {
1228
+ const hasOne =
1229
+ app[appName].attributes[modelName][
1230
+ controller + '_id'
1231
+ ] &&
1232
+ app[appName].attributes[modelName][controller + '_id']
1233
+ .hasOne
1234
+
1235
+ if (hasOne) {
1236
+ rows[i].setDataValue(
1237
+ modelName,
1238
+ objList[modelName].find(
1239
+ (b) => b[controller + '_id'] === rows[i].id
1240
+ ) || null
1241
+ )
1242
+ } else {
1243
+ rows[i].setDataValue(
1244
+ [modelName + 's'],
1245
+ objList[modelName].filter(
1246
+ (b) => b[controller + '_id'] === rows[i].id
1247
+ )
1248
+ )
1249
+ }
1250
+ }
1251
+ }
1252
+ result = [...result, rows[i]]
1253
+ }
1254
+ ctx.SUCCESS(result)
1255
+ } else if (fn === 'findOne') {
1256
+ const target = await app[appName].model[controller][fn]({
1257
+ where: _where,
1258
+ limit,
1259
+ offset,
1260
+ includes,
1261
+ order,
1262
+ distinct: true,
1263
+ attributes,
1264
+ raw,
1265
+ })
1266
+
1267
+ if (target === null) {
1268
+ return ctx.SUCCESS(null)
1269
+ } else {
1270
+ let objList = {}
1271
+ for (let j = 0; j < myInclude.length; j++) {
1272
+ const model = myInclude[j].model
1273
+ const modelName = model.getName()
1274
+ if (
1275
+ app[appName].attributes[controller][modelName + '_id']
1276
+ ) {
1277
+ const modelIdList = _.uniq(
1278
+ [target]
1279
+ .reduce((a, b) => {
1280
+ const next = Array.isArray(b[modelName + '_id'])
1281
+ ? b[modelName + '_id']
1282
+ : [b[modelName + '_id']]
1283
+ return [...a, ...next]
1284
+ }, [])
1285
+ .filter((item) => item)
1286
+ )
1287
+ if (modelIdList.length) {
1288
+ const list =
1289
+ !myInclude[j].include &&
1290
+ !myInclude[j].where &&
1291
+ app[appName].cache.get(modelName)
1292
+ ? app[appName].cache.get(modelName)
1293
+ : await model.findAll({
1294
+ where: {
1295
+ id: modelIdList,
1296
+ ...(myInclude[j].where
1297
+ ? myInclude[j].where
1298
+ : {}),
1299
+ },
1300
+ include: myInclude[j].include,
1301
+ })
1302
+ objList = {
1303
+ ...objList,
1304
+ [modelName]: list,
1305
+ }
1306
+ } else {
1307
+ objList = {
1308
+ ...objList,
1309
+ [modelName]: [],
1310
+ }
1311
+ }
1312
+ } else {
1313
+ if (
1314
+ target.id &&
1315
+ app[appName].attributes[modelName] &&
1316
+ app[appName].attributes[modelName][controller + '_id']
1317
+ ) {
1318
+ const { required, include, where, ...rest } = myInclude[
1319
+ j
1320
+ ]
1321
+ const list = await model.findAll({
1322
+ where: {
1323
+ [controller + '_id']: target.id,
1324
+ ...(where || {}),
1325
+ },
1326
+ ...rest,
1327
+ include,
1328
+ })
1329
+ objList = {
1330
+ ...objList,
1331
+ [modelName]: list,
1332
+ }
1333
+ } else {
1334
+ objList = {
1335
+ ...objList,
1336
+ [modelName]: [],
1337
+ }
1338
+ }
1339
+ }
1340
+ }
1341
+
1342
+ for (let j = 0; j < myInclude.length; j++) {
1343
+ const model = myInclude[j].model
1344
+ const modelName = model.getName()
1345
+ if (
1346
+ app[appName].attributes[controller][modelName + '_id']
1347
+ ) {
1348
+ const is_single = !(
1349
+ (app[appName].attributes[controller][modelName + '_id']
1350
+ .defaultValue &&
1351
+ Array.isArray(
1352
+ app[appName].attributes[controller][
1353
+ modelName + '_id'
1354
+ ].defaultValue
1355
+ )) ||
1356
+ (app[appName].attributes[controller][modelName + '_id']
1357
+ .type &&
1358
+ app[appName].attributes[controller][modelName + '_id']
1359
+ .type === 'JSON') ||
1360
+ (app[appName].attributes[controller][modelName + '_id']
1361
+ .type &&
1362
+ app[appName].attributes[controller][modelName + '_id']
1363
+ .type.key &&
1364
+ app[appName].attributes[controller][modelName + '_id']
1365
+ .type.key === 'JSON')
1366
+ )
1367
+
1368
+ if (is_single) {
1369
+ target.setDataValue(
1370
+ modelName,
1371
+ objList[modelName].find(
1372
+ (b) => b.id === target[modelName + '_id']
1373
+ )
1374
+ )
1375
+ } else {
1376
+ if (target[modelName + '_id']) {
1377
+ target.setDataValue(
1378
+ [modelName + 's'],
1379
+ target[modelName + '_id'].map((modelId) => {
1380
+ return objList[modelName].find(
1381
+ (b) => b.id === modelId
1382
+ )
1383
+ })
1384
+ )
1385
+ }
1386
+ }
1387
+ } else {
1388
+ const hasOne =
1389
+ app[appName].attributes[modelName][
1390
+ controller + '_id'
1391
+ ] &&
1392
+ app[appName].attributes[modelName][controller + '_id']
1393
+ .hasOne
1394
+
1395
+ if (hasOne) {
1396
+ target.setDataValue(
1397
+ modelName,
1398
+ objList[modelName].find(
1399
+ (b) => b[controller + '_id'] === target.id
1400
+ ) || null
1401
+ )
1402
+ } else {
1403
+ target.setDataValue(
1404
+ [modelName + 's'],
1405
+ objList[modelName].filter(
1406
+ (b) => b[controller + '_id'] === target.id
1407
+ )
1408
+ )
1409
+ }
1410
+ }
1411
+ }
1412
+
1413
+ ctx.SUCCESS(target)
1414
+ }
1415
+ }
1416
+ } else {
1417
+ let needInclude = []
1418
+ let specialInclude = []
1419
+ for (let i = 0; i < myInclude.length; i++) {
1420
+ const model = myInclude[i].model
1421
+ const modelName = model.getName()
1422
+
1423
+ const is_single = !(
1424
+ (app[appName].attributes[controller][modelName + '_id'] &&
1425
+ app[appName].attributes[controller][modelName + '_id']
1426
+ .defaultValue &&
1427
+ Array.isArray(
1428
+ app[appName].attributes[controller][modelName + '_id']
1429
+ .defaultValue
1430
+ )) ||
1431
+ (app[appName].attributes[controller][modelName + '_id'] &&
1432
+ app[appName].attributes[controller][modelName + '_id']
1433
+ .type &&
1434
+ app[appName].attributes[controller][modelName + '_id']
1435
+ .type === 'JSON') ||
1436
+ (app[appName].attributes[controller][modelName + '_id'] &&
1437
+ app[appName].attributes[controller][modelName + '_id']
1438
+ .type &&
1439
+ app[appName].attributes[controller][modelName + '_id'].type
1440
+ .key &&
1441
+ app[appName].attributes[controller][modelName + '_id'].type
1442
+ .key === 'JSON')
1443
+ )
1444
+
1445
+ if (is_single) {
1446
+ needInclude = [...needInclude, myInclude[i]]
1447
+ } else {
1448
+ specialInclude = [...specialInclude, myInclude[i]]
1449
+ }
1450
+ }
1451
+
1452
+ if (specialInclude.length === 0) {
1453
+ const result = await app[appName].model[controller][fn]({
1454
+ where: _where,
1455
+ limit,
1456
+ offset,
1457
+ includes,
1458
+ order,
1459
+ distinct: true,
1460
+ include: needInclude,
1461
+ attributes,
1462
+ raw,
1463
+ })
1464
+ if (omit.length === 0) {
1465
+ ctx.SUCCESS(result)
1466
+ } else {
1467
+ ctx.SUCCESS(
1468
+ result.map((target) => {
1469
+ return _.omit(target.toJSON(), omit)
1470
+ })
1471
+ )
1472
+ }
1473
+ } else {
1474
+ const rows = await app[appName].model[controller][fn]({
1475
+ where: _where,
1476
+ limit,
1477
+ offset,
1478
+ includes,
1479
+ order,
1480
+ distinct: true,
1481
+ include: needInclude,
1482
+ attributes,
1483
+ raw,
1484
+ })
1485
+ if (fn === 'findAndCountAll') {
1486
+ ctx.SUCCESS(rows)
1487
+ } else if (fn === 'findAll') {
1488
+ if (rows.length === 0) ctx.SUCCESS([])
1489
+ let objList = {}
1490
+ for (let j = 0; j < specialInclude.length; j++) {
1491
+ const model = specialInclude[j].model
1492
+ const modelName = model.getName()
1493
+ const modelIdList = _.uniq(
1494
+ rows
1495
+ .reduce((a, b) => {
1496
+ const next = Array.isArray(b[modelName + '_id'])
1497
+ ? b[modelName + '_id']
1498
+ : [b[modelName + '_id']]
1499
+ return [...a, ...next]
1500
+ }, [])
1501
+ .filter((item) => item)
1502
+ )
1503
+ if (modelIdList.length) {
1504
+ const list = await model.findAll({
1505
+ where: {
1506
+ id: modelIdList,
1507
+ },
1508
+ })
1509
+ objList = {
1510
+ ...objList,
1511
+ [modelName]: list,
1512
+ }
1513
+ } else {
1514
+ objList = {
1515
+ ...objList,
1516
+ [modelName]: [],
1517
+ }
1518
+ }
1519
+ }
1520
+
1521
+ let result = []
1522
+ for (let i = 0; i < rows.length; i++) {
1523
+ for (let j = 0; j < specialInclude.length; j++) {
1524
+ const model = specialInclude[j].model
1525
+
1526
+ const modelName = model.getName()
1527
+
1528
+ if (rows[i][modelName + '_id']) {
1529
+ rows[i].setDataValue(
1530
+ modelName + 's',
1531
+ rows[i][modelName + '_id'] === null
1532
+ ? []
1533
+ : rows[i][modelName + '_id']
1534
+ .map((modelId) => {
1535
+ return objList[modelName].find(
1536
+ (b) => b.id === modelId
1537
+ )
1538
+ })
1539
+ .filter((item) => item)
1540
+ )
1541
+ }
1542
+ }
1543
+
1544
+ result = [
1545
+ ...result,
1546
+ omit.length === 0
1547
+ ? rows[i]
1548
+ : _.omit(rows[i].toJSON(), omit),
1549
+ ]
1550
+ }
1551
+ ctx.SUCCESS(result)
1552
+ } else {
1553
+ if (rows == null) return ctx.SUCCESS(null)
1554
+ let objList = {}
1555
+ for (let j = 0; j < specialInclude.length; j++) {
1556
+ const model = specialInclude[j].model
1557
+ const modelName = model.getName()
1558
+ const modelIdList = _.uniq(
1559
+ [rows]
1560
+ .reduce((a, b) => {
1561
+ const next = Array.isArray(b[modelName + '_id'])
1562
+ ? b[modelName + '_id']
1563
+ : [b[modelName + '_id']]
1564
+ return [...a, ...next]
1565
+ }, [])
1566
+ .filter((item) => item)
1567
+ )
1568
+ if (modelIdList.length) {
1569
+ const list = await model.findAll({
1570
+ where: {
1571
+ id: modelIdList,
1572
+ },
1573
+ })
1574
+ objList = {
1575
+ ...objList,
1576
+ [modelName]: list,
1577
+ }
1578
+ } else {
1579
+ objList = {
1580
+ ...objList,
1581
+ [modelName]: [],
1582
+ }
1583
+ }
1584
+ }
1585
+ for (let j = 0; j < specialInclude.length; j++) {
1586
+ const model = specialInclude[j].model
1587
+ const modelName = model.getName()
1588
+
1589
+ if (rows[modelName + '_id']) {
1590
+ rows.setDataValue(
1591
+ modelName + 's',
1592
+ rows[modelName + '_id'] === null
1593
+ ? []
1594
+ : rows[modelName + '_id']
1595
+ .map((modelId) => {
1596
+ return objList[modelName].find(
1597
+ (b) => b.id === modelId
1598
+ )
1599
+ })
1600
+ .filter((item) => item)
1601
+ )
1602
+ }
1603
+ }
1604
+
1605
+ ctx.SUCCESS(omit.length === 0 ? rows : _.omit(rows, omit))
1606
+ }
1607
+ }
1608
+ }
1609
+ }
1610
+ }
1611
+
1612
+ if (fn === 'bulkCreate') {
1613
+ const { list, key = [] } = ctx.request.body
1614
+ if (!list || !key) throw new Error('缺少list/key')
1615
+ if (key.length) {
1616
+ const result = await app[appName].model[controller][fn](list, {
1617
+ updateOnDuplicate: key,
1618
+ })
1619
+ ctx.SUCCESS(result)
1620
+ } else {
1621
+ const result = await app[appName].model[controller][fn](list)
1622
+ ctx.SUCCESS(result)
1623
+ }
1624
+ }
1625
+
1626
+ if (fn === 'upsert' || fn === 'create') {
1627
+ if (_.isEmpty(ctx.request.body)) {
1628
+ throw new Error('新增不能为空')
1629
+ }
1630
+ const _exclude =
1631
+ ctx.request[`${appName}-user`] &&
1632
+ ctx.request[`${appName}-user`].name &&
1633
+ ctx.request[`${appName}-user`].name === 'admin'
1634
+ ? ['updated_at']
1635
+ : ['created_at', 'updated_at']
1636
+ const newData = _.omit(_.omitBy(ctx.request.body, _.isNull), _exclude)
1637
+
1638
+ const flag = await app[appName].model[controller][fn]({
1639
+ createdid: ctx.request[`${appName}-user`]
1640
+ ? ctx.request[`${appName}-user`].id
1641
+ : 0,
1642
+ ...newData,
1643
+ })
1644
+ ctx.SUCCESS(flag)
1645
+
1646
+ if (cacheModel.includes(controller)) {
1647
+ ctx.app[appName].cache.set(`${controller}`, null)
1648
+ }
1649
+ }
1650
+
1651
+ if (fn === 'destroy') {
1652
+ if (_.isEmpty(ctx.request.body)) throw new Error('请输入删除条件')
1653
+ const where =
1654
+ ctx.request.clientType > 1
1655
+ ? {
1656
+ ...ctx.request.body,
1657
+ createdid:
1658
+ ctx.request.clientType > 1
1659
+ ? ctx.request[`${appName}-user`]
1660
+ ? ctx.request[`${appName}-user`].id
1661
+ : 0
1662
+ : 0,
1663
+ }
1664
+ : ctx.request.body
1665
+ const result = await app[appName].model[controller][fn]({
1666
+ where,
1667
+ })
1668
+ if (result === 0) throw new Error('删除失败')
1669
+ ctx.SUCCESS(result)
1670
+
1671
+ if (cacheModel.includes(controller)) {
1672
+ ctx.app[appName].cache.set(`${controller}`, null)
1673
+ }
1674
+ }
1675
+
1676
+ if (_.get(app, `${appName}.afterExecute.${controller}.${fn}`)) {
1677
+ app[appName].afterExecute[controller][fn](ctx)
1678
+ .then((res) => {
1679
+ console.log('afterUpdate', res)
1680
+ })
1681
+ .catch((e) => {
1682
+ console.log('错误', e)
1683
+ const errorData = {
1684
+ url: ctx.request.href,
1685
+ body: ctx.request.body,
1686
+ message: e.message,
1687
+ content: e.stack,
1688
+ header: ctx.request.header,
1689
+ }
1690
+ if (
1691
+ app[appName] &&
1692
+ app[appName].model &&
1693
+ app[appName].model.log
1694
+ ) {
1695
+ app[appName].model.log.upsert(errorData)
1696
+ }
1697
+ })
1698
+ }
1699
+ }
1700
+ })
1701
+ }
1702
+ }
1703
+
1704
+ APP.Sequelize = Sequelize
1705
+ APP.lodash = _
1706
+ APP.Random = Random
1707
+ APP.Validator = Validator
1708
+ APP.moment = moment
1709
+ global.moment = moment
1710
+ global.Sequelize = Sequelize
1711
+ global.lodash = _
1712
+ APP.getService = (name) => {
1713
+ const target = require(path.resolve(__dirname, `./file/services/${name}`))
1714
+ return target
1715
+ }
1716
+ APP.getAppByCtx = (ctx) => {
1717
+ const appName = ctx.router.opts.prefix.substring(1)
1718
+
1719
+ let controller = ''
1720
+ try {
1721
+ const url = ctx.request.url
1722
+ const _split = url.split('/')
1723
+ controller = _split[2]
1724
+ } catch (e) {
1725
+ console.warn(e)
1726
+ }
1727
+ const { [appName]: app } = ctx.app
1728
+ return {
1729
+ appName,
1730
+ app,
1731
+ controller,
1732
+ }
1733
+ }
1734
+
1735
+ APP.getClientTypeByCtx = (ctx) => {
1736
+ const typeEnum = {
1737
+ 1: 'admin',
1738
+ 2: 'h5',
1739
+ 3: 'mp',
1740
+ 4: 'toutiao',
1741
+ 5: 'alipay',
1742
+ }
1743
+ if (ctx && ctx.request && ctx.request.clientType) {
1744
+ const type = ctx.request.clientType
1745
+ if (typeEnum[type]) {
1746
+ return typeEnum[type]
1747
+ }
1748
+ return null
1749
+ }
1750
+ return null
1751
+ }
1752
+
1753
+ APP.getConfig = (app) => ({
1754
+ async getObject(type) {
1755
+ let configList = await app.cache.get('configList')
1756
+ if (!configList) {
1757
+ configList = await app.model.setting.findAll({
1758
+ raw: true,
1759
+ where: {
1760
+ is_cache: true,
1761
+ },
1762
+ attributes: {
1763
+ exclude: [
1764
+ 'name',
1765
+ 'created_at',
1766
+ 'updated_at',
1767
+ 'is_cache',
1768
+ 'is_front',
1769
+ 'is_control',
1770
+ 'createdid',
1771
+ ],
1772
+ },
1773
+ include: [
1774
+ {
1775
+ model: app.model.setting,
1776
+ as: 'parent',
1777
+ attributes: {
1778
+ exclude: [
1779
+ 'name',
1780
+ 'type',
1781
+ 'value',
1782
+ 'created_at',
1783
+ 'updated_at',
1784
+ 'is_cache',
1785
+ 'is_front',
1786
+ 'is_control',
1787
+ 'createdid',
1788
+ ],
1789
+ },
1790
+ },
1791
+ ],
1792
+ })
1793
+ await app.cache.set(
1794
+ 'configList',
1795
+ configList.map((item) => ({
1796
+ ...item,
1797
+ }))
1798
+ )
1799
+
1800
+ const result = getObject(configList)(type)
1801
+ if (!result) throw new Error('没有这个type')
1802
+ return result
1803
+ }
1804
+ const result = getObject(configList)(type)
1805
+ if (!result) throw new Error('没有这个type')
1806
+ return result
1807
+ },
1808
+ update() {
1809
+ app.cache.set('configList', null)
1810
+ },
1811
+ })
1812
+
1813
+ module.exports = APP