q-koa 13.8.57 → 13.8.59
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.
- package/core/app.js +424 -403
- package/core/file/plugins/weixin/controller.js +14 -17
- package/package.json +1 -1
package/core/app.js
CHANGED
|
@@ -458,154 +458,88 @@ class APP {
|
|
|
458
458
|
})
|
|
459
459
|
dirList = dirList.filter((item) => includeList.some((i) => i.app === item))
|
|
460
460
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
`${rootDirectory}/${APP_DIR}/${appName}/public`
|
|
474
|
-
),
|
|
475
|
-
router
|
|
476
|
-
)
|
|
477
|
-
|
|
478
|
-
const { push } = require('./file/plugins/log/service')
|
|
461
|
+
const { push } = require('./file/plugins/log/service')
|
|
462
|
+
|
|
463
|
+
// 所有 app 共用同一份 redis 配置,全局只连接一对 client+subscriber 供各 app 复用,
|
|
464
|
+
// 避免每个 app 各自建一套连接(2N 条连接 -> 2 条)
|
|
465
|
+
let sharedRedisClient = null
|
|
466
|
+
let sharedSubscriber = null
|
|
467
|
+
let redisError = null
|
|
468
|
+
if (
|
|
469
|
+
this.config.redis &&
|
|
470
|
+
this.config.redis.host &&
|
|
471
|
+
this.config.redis.port
|
|
472
|
+
) {
|
|
479
473
|
try {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
await db.authenticate()
|
|
493
|
-
}
|
|
494
|
-
const configPath = `${rootDirectory}/${APP_DIR}/${appName}/config.js`
|
|
495
|
-
const configFile = path.resolve(__dirname, configPath)
|
|
496
|
-
const configExist = fs.existsSync(configFile)
|
|
497
|
-
|
|
498
|
-
if (
|
|
499
|
-
this.config.redis &&
|
|
500
|
-
this.config.redis.host &&
|
|
501
|
-
this.config.redis.port
|
|
502
|
-
) {
|
|
503
|
-
let subscriber
|
|
504
|
-
let client
|
|
505
|
-
try {
|
|
506
|
-
client = redis.createClient({
|
|
507
|
-
socket: {
|
|
508
|
-
host: this.config.redis.host,
|
|
509
|
-
port: this.config.redis.port,
|
|
510
|
-
reconnectStrategy: false,
|
|
511
|
-
},
|
|
512
|
-
...(this.config.redis.database
|
|
513
|
-
? {
|
|
514
|
-
database: this.config.redis.database,
|
|
515
|
-
}
|
|
516
|
-
: {}),
|
|
517
|
-
})
|
|
518
|
-
|
|
519
|
-
client.on('error', (err) => console.log('Redis Client Error', err))
|
|
520
|
-
|
|
521
|
-
await client.connect()
|
|
522
|
-
|
|
523
|
-
subscriber = client.duplicate()
|
|
524
|
-
await subscriber.connect()
|
|
525
|
-
|
|
526
|
-
if (!is_dev) {
|
|
527
|
-
push({
|
|
528
|
-
appName,
|
|
529
|
-
message: `${appName}连接redis成功,${this.config.ip}:${this.port}`,
|
|
530
|
-
})
|
|
531
|
-
}
|
|
532
|
-
} catch (e) {
|
|
533
|
-
client = null
|
|
534
|
-
|
|
535
|
-
push({
|
|
536
|
-
appName,
|
|
537
|
-
message: `${appName}连接redis失败,${e.message}`,
|
|
538
|
-
})
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
this.app[appName] = {
|
|
542
|
-
sequelize: db,
|
|
543
|
-
...(client
|
|
544
|
-
? {
|
|
545
|
-
redisClient: client,
|
|
546
|
-
subscriber,
|
|
547
|
-
}
|
|
548
|
-
: {}),
|
|
549
|
-
appConfig: _.defaultsDeep(
|
|
550
|
-
configExist ? require(path.resolve(__dirname, configPath)) : {},
|
|
551
|
-
this.config.app,
|
|
552
|
-
{ appName, ip: this.config.ip, port: this.port }
|
|
553
|
-
),
|
|
554
|
-
}
|
|
555
|
-
} else {
|
|
556
|
-
this.app[appName] = {
|
|
557
|
-
sequelize: db,
|
|
558
|
-
redisClient: redisVirtual,
|
|
559
|
-
appConfig: _.defaultsDeep(
|
|
560
|
-
configExist ? require(path.resolve(__dirname, configPath)) : {},
|
|
561
|
-
this.config.app,
|
|
562
|
-
{ appName, ip: this.config.ip, port: this.port }
|
|
563
|
-
),
|
|
564
|
-
}
|
|
565
|
-
}
|
|
474
|
+
sharedRedisClient = redis.createClient({
|
|
475
|
+
socket: {
|
|
476
|
+
host: this.config.redis.host,
|
|
477
|
+
port: this.config.redis.port,
|
|
478
|
+
reconnectStrategy: false,
|
|
479
|
+
},
|
|
480
|
+
...(this.config.redis.database
|
|
481
|
+
? {
|
|
482
|
+
database: this.config.redis.database,
|
|
483
|
+
}
|
|
484
|
+
: {}),
|
|
485
|
+
})
|
|
566
486
|
|
|
567
|
-
|
|
487
|
+
sharedRedisClient.on('error', (err) =>
|
|
488
|
+
console.log('Redis Client Error', err)
|
|
489
|
+
)
|
|
568
490
|
|
|
569
|
-
|
|
570
|
-
// this.app.use(proxy(`/${appName}`, proxyConfig));
|
|
571
|
-
// }
|
|
491
|
+
await sharedRedisClient.connect()
|
|
572
492
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
await this.initPlugin(appName)
|
|
493
|
+
sharedSubscriber = sharedRedisClient.duplicate()
|
|
494
|
+
await sharedSubscriber.connect()
|
|
495
|
+
} catch (e) {
|
|
496
|
+
sharedRedisClient = null
|
|
497
|
+
sharedSubscriber = null
|
|
498
|
+
redisError = e
|
|
499
|
+
}
|
|
500
|
+
}
|
|
582
501
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
502
|
+
// 数据库连接校验:生产只校验一次(保持原有行为);开发环境按 dbName 去重,同库只校验一次
|
|
503
|
+
let prodAuthPromise = null
|
|
504
|
+
const dbAuthCache = new Map()
|
|
505
|
+
const authenticateDb = (db, dbName, appName) => {
|
|
506
|
+
if (!is_dev) {
|
|
507
|
+
if (!prodAuthPromise) {
|
|
508
|
+
console.log(chalk.green(`${appName}检查数据库${dbName}`))
|
|
509
|
+
prodAuthPromise = db.authenticate()
|
|
586
510
|
}
|
|
511
|
+
return prodAuthPromise
|
|
512
|
+
}
|
|
513
|
+
if (!dbAuthCache.has(dbName)) {
|
|
514
|
+
console.log(chalk.green(`${appName}检查数据库${dbName}`))
|
|
515
|
+
dbAuthCache.set(dbName, db.authenticate())
|
|
516
|
+
}
|
|
517
|
+
return dbAuthCache.get(dbName)
|
|
518
|
+
}
|
|
587
519
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
this.
|
|
594
|
-
|
|
595
|
-
|
|
520
|
+
const routers = new Map()
|
|
521
|
+
|
|
522
|
+
// 各 app 的重初始化(数据库校验/插件/模型等)互相独立,并行执行
|
|
523
|
+
const results = await Promise.allSettled(
|
|
524
|
+
dirList.map((appName) =>
|
|
525
|
+
this._initSingleApp(appName, includeList, {
|
|
526
|
+
push,
|
|
527
|
+
routers,
|
|
528
|
+
sharedRedisClient,
|
|
529
|
+
sharedSubscriber,
|
|
530
|
+
redisError,
|
|
531
|
+
authenticateDb,
|
|
596
532
|
})
|
|
533
|
+
)
|
|
534
|
+
)
|
|
535
|
+
const failed = results.find((r) => r.status === 'rejected')
|
|
536
|
+
if (failed) {
|
|
537
|
+
throw failed.reason
|
|
538
|
+
}
|
|
597
539
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
push({
|
|
602
|
-
appName,
|
|
603
|
-
message: `${appName}数据库连接失败`,
|
|
604
|
-
})
|
|
605
|
-
console.log(`${appName}数据库连接失败`)
|
|
606
|
-
console.log(e)
|
|
607
|
-
throw new Error(e.message)
|
|
608
|
-
}
|
|
540
|
+
// 按原有顺序挂载路由,保证中间件注册顺序确定
|
|
541
|
+
for (const appName of dirList) {
|
|
542
|
+
const router = routers.get(appName)
|
|
609
543
|
if (
|
|
610
544
|
this.app[appName] &&
|
|
611
545
|
this.app[appName].appConfig &&
|
|
@@ -654,10 +588,121 @@ class APP {
|
|
|
654
588
|
starter.emit('loadAll')
|
|
655
589
|
}
|
|
656
590
|
|
|
657
|
-
|
|
591
|
+
async _initSingleApp(appName, includeList, ctx) {
|
|
592
|
+
const {
|
|
593
|
+
push,
|
|
594
|
+
routers,
|
|
595
|
+
sharedRedisClient,
|
|
596
|
+
sharedSubscriber,
|
|
597
|
+
redisError,
|
|
598
|
+
authenticateDb,
|
|
599
|
+
} = ctx
|
|
600
|
+
console.log(
|
|
601
|
+
chalk.green(`==================== 启动 ${appName} ====================`)
|
|
602
|
+
)
|
|
603
|
+
const routerConfig = {
|
|
604
|
+
prefix: `/${appName}`,
|
|
605
|
+
}
|
|
606
|
+
const router = new Router(routerConfig)
|
|
607
|
+
Serve(
|
|
608
|
+
path.resolve(__dirname, `${rootDirectory}/${APP_DIR}/${appName}/public`),
|
|
609
|
+
router
|
|
610
|
+
)
|
|
611
|
+
routers.set(appName, router)
|
|
612
|
+
|
|
613
|
+
try {
|
|
614
|
+
const target = includeList.find((i) => i.app === appName)
|
|
615
|
+
const dbName = (target ? target.db : '') || this.config.db.name || appName
|
|
616
|
+
const db = connectDatabase(dbName)(this.config.db)
|
|
617
|
+
await authenticateDb(db, dbName, appName)
|
|
618
|
+
|
|
619
|
+
const configPath = `${rootDirectory}/${APP_DIR}/${appName}/config.js`
|
|
620
|
+
const configFile = path.resolve(__dirname, configPath)
|
|
621
|
+
const configExist = fs.existsSync(configFile)
|
|
622
|
+
const appConfig = _.defaultsDeep(
|
|
623
|
+
configExist ? require(configFile) : {},
|
|
624
|
+
this.config.app,
|
|
625
|
+
{ appName, ip: this.config.ip, port: this.port }
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
if (sharedRedisClient) {
|
|
629
|
+
if (!is_dev) {
|
|
630
|
+
push({
|
|
631
|
+
appName,
|
|
632
|
+
message: `${appName}连接redis成功,${this.config.ip}:${this.port}`,
|
|
633
|
+
})
|
|
634
|
+
}
|
|
635
|
+
this.app[appName] = {
|
|
636
|
+
sequelize: db,
|
|
637
|
+
redisClient: sharedRedisClient,
|
|
638
|
+
subscriber: sharedSubscriber,
|
|
639
|
+
appConfig,
|
|
640
|
+
}
|
|
641
|
+
} else if (
|
|
642
|
+
this.config.redis &&
|
|
643
|
+
this.config.redis.host &&
|
|
644
|
+
this.config.redis.port
|
|
645
|
+
) {
|
|
646
|
+
// redis 配置了但连接失败:保持原有行为(不挂 redisClient),仅记录日志
|
|
647
|
+
push({
|
|
648
|
+
appName,
|
|
649
|
+
message: `${appName}连接redis失败,${
|
|
650
|
+
redisError ? redisError.message : '未知错误'
|
|
651
|
+
}`,
|
|
652
|
+
})
|
|
653
|
+
this.app[appName] = {
|
|
654
|
+
sequelize: db,
|
|
655
|
+
appConfig,
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
this.app[appName] = {
|
|
659
|
+
sequelize: db,
|
|
660
|
+
redisClient: redisVirtual,
|
|
661
|
+
appConfig,
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const initFileConfig = _.get(
|
|
666
|
+
this.app[appName],
|
|
667
|
+
'appConfig.initFile',
|
|
668
|
+
false
|
|
669
|
+
)
|
|
670
|
+
if (initFileConfig) {
|
|
671
|
+
await this.initFile(appName)
|
|
672
|
+
}
|
|
673
|
+
await this.initPlugin(appName)
|
|
674
|
+
|
|
675
|
+
const initEvent = _.get(this.app[appName], 'appConfig.initEvent', false)
|
|
676
|
+
if (initEvent) {
|
|
677
|
+
this.app[appName].event = new EventEmitter()
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const cacheConfig = _.get(this.app[appName], 'appConfig.cache', {
|
|
681
|
+
max: 100,
|
|
682
|
+
maxAge: 1000 * 60 * 60 * 24,
|
|
683
|
+
})
|
|
684
|
+
|
|
685
|
+
this.app[appName].cache = new LRUCache({
|
|
686
|
+
max: cacheConfig.max,
|
|
687
|
+
ttl: cacheConfig.maxAge,
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
await this.initModel(appName)
|
|
691
|
+
} catch (e) {
|
|
692
|
+
push({
|
|
693
|
+
appName,
|
|
694
|
+
message: `${appName}数据库连接失败`,
|
|
695
|
+
})
|
|
696
|
+
console.log(`${appName}数据库连接失败`)
|
|
697
|
+
console.log(e)
|
|
698
|
+
throw new Error(e.message)
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
async initFile(appName) {
|
|
658
703
|
const dirExist = fsExtra.existsSync(path.resolve(__dirname, './file'))
|
|
659
704
|
if (dirExist) {
|
|
660
|
-
fsExtra.
|
|
705
|
+
await fsExtra.copy(
|
|
661
706
|
path.resolve(__dirname, './file'),
|
|
662
707
|
path.resolve(__dirname, `${rootDirectory}/${APP_DIR}/${appName}`),
|
|
663
708
|
{
|
|
@@ -672,285 +717,265 @@ class APP {
|
|
|
672
717
|
__dirname,
|
|
673
718
|
`${rootDirectory}/${APP_DIR}/${appName}/${PLUGINS_DIR}`
|
|
674
719
|
)
|
|
675
|
-
|
|
676
|
-
if (!publicExist) await fsPromise.mkdir(pluginDir)
|
|
720
|
+
await fsPromise.mkdir(pluginDir, { recursive: true })
|
|
677
721
|
const excludesModel = _.get(
|
|
678
722
|
this.app[appName],
|
|
679
723
|
'appConfig.excludesModel',
|
|
680
724
|
[]
|
|
681
725
|
)
|
|
682
|
-
|
|
683
|
-
const
|
|
684
|
-
|
|
726
|
+
// withFileTypes 直接带出类型,省掉每个文件夹一次同步 lstatSync
|
|
727
|
+
const pluginEntries = await fsPromise.readdir(pluginDir, {
|
|
728
|
+
withFileTypes: true,
|
|
729
|
+
})
|
|
730
|
+
const folders = pluginEntries
|
|
731
|
+
.filter(
|
|
732
|
+
(item) => item.isDirectory() && !excludesModel.includes(item.name)
|
|
733
|
+
)
|
|
734
|
+
.map((item) => item.name)
|
|
735
|
+
|
|
736
|
+
// 各插件目录的 readdir 互相独立,并行读取
|
|
737
|
+
const folderFileList = await Promise.all(
|
|
738
|
+
folders.map(async (folder) => [
|
|
739
|
+
folder,
|
|
740
|
+
await fsPromise.readdir(path.resolve(pluginDir, folder)),
|
|
741
|
+
])
|
|
685
742
|
)
|
|
686
743
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
path.resolve(pluginDir, folder)
|
|
694
|
-
)
|
|
695
|
-
for (const filename of folderResult) {
|
|
696
|
-
const extname = path.extname(filename)
|
|
697
|
-
if (extname === '.js') {
|
|
698
|
-
const n = path.basename(filename, extname)
|
|
699
|
-
if (
|
|
700
|
-
[
|
|
701
|
-
'model',
|
|
702
|
-
'controller',
|
|
703
|
-
'service',
|
|
704
|
-
'validate',
|
|
705
|
-
'config',
|
|
706
|
-
'beforeValidate',
|
|
707
|
-
'afterValidate',
|
|
708
|
-
'afterExecute',
|
|
709
|
-
].includes(n)
|
|
710
|
-
) {
|
|
711
|
-
if (n === 'model') {
|
|
712
|
-
const defaultModelPath = path.resolve(
|
|
713
|
-
__dirname,
|
|
714
|
-
`./file/${PLUGINS_DIR}/${folder}`,
|
|
715
|
-
`${filename}`
|
|
716
|
-
)
|
|
717
|
-
const defaultModelExist = fs.existsSync(defaultModelPath)
|
|
718
|
-
const currentAttributes = require(path.resolve(
|
|
719
|
-
pluginDir,
|
|
720
|
-
folder,
|
|
721
|
-
filename
|
|
722
|
-
))
|
|
723
|
-
const _attributes =
|
|
724
|
-
Object.keys(currentAttributes).length === 0 &&
|
|
725
|
-
defaultModelExist
|
|
726
|
-
? require(defaultModelPath)
|
|
727
|
-
: currentAttributes
|
|
728
|
-
|
|
729
|
-
const belongs = _.get(
|
|
730
|
-
this.app[appName].config[folder],
|
|
731
|
-
'belongs',
|
|
732
|
-
'page'
|
|
733
|
-
)
|
|
744
|
+
// O(1) 直接挂载,替代对整个 app 对象反复 defaultsDeep(O(n²))
|
|
745
|
+
const mount = (n, folder, value) => {
|
|
746
|
+
const target = this.app[appName]
|
|
747
|
+
target[n] = target[n] || {}
|
|
748
|
+
target[n][folder] = value
|
|
749
|
+
}
|
|
734
750
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
751
|
+
for (const [folder, folderResult] of folderFileList) {
|
|
752
|
+
for (const filename of folderResult) {
|
|
753
|
+
const extname = path.extname(filename)
|
|
754
|
+
if (extname === '.js') {
|
|
755
|
+
const n = path.basename(filename, extname)
|
|
756
|
+
if (
|
|
757
|
+
[
|
|
758
|
+
'model',
|
|
759
|
+
'controller',
|
|
760
|
+
'service',
|
|
761
|
+
'validate',
|
|
762
|
+
'config',
|
|
763
|
+
'beforeValidate',
|
|
764
|
+
'afterValidate',
|
|
765
|
+
'afterExecute',
|
|
766
|
+
].includes(n)
|
|
767
|
+
) {
|
|
768
|
+
if (n === 'model') {
|
|
769
|
+
const defaultModelPath = path.resolve(
|
|
770
|
+
__dirname,
|
|
771
|
+
`./file/${PLUGINS_DIR}/${folder}`,
|
|
772
|
+
`${filename}`
|
|
773
|
+
)
|
|
774
|
+
const defaultModelExist = fs.existsSync(defaultModelPath)
|
|
775
|
+
const currentAttributes = require(path.resolve(
|
|
776
|
+
pluginDir,
|
|
777
|
+
folder,
|
|
778
|
+
filename
|
|
779
|
+
))
|
|
780
|
+
const _attributes =
|
|
781
|
+
Object.keys(currentAttributes).length === 0 &&
|
|
782
|
+
defaultModelExist
|
|
783
|
+
? require(defaultModelPath)
|
|
784
|
+
: currentAttributes
|
|
785
|
+
|
|
786
|
+
const belongs = _.get(
|
|
787
|
+
this.app[appName].config[folder],
|
|
788
|
+
'belongs',
|
|
789
|
+
'page'
|
|
790
|
+
)
|
|
740
791
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
autoIncrement: true,
|
|
747
|
-
},
|
|
748
|
-
}
|
|
792
|
+
const idType = _.get(
|
|
793
|
+
this.app[appName].config[folder],
|
|
794
|
+
'idType',
|
|
795
|
+
belongs === 'setting' ? 'TINYINT' : 'MEDIUMINT'
|
|
796
|
+
)
|
|
749
797
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
type: Sequelize.DATE,
|
|
759
|
-
allowNull: false,
|
|
760
|
-
// defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
|
|
761
|
-
comment: '生成时间',
|
|
762
|
-
},
|
|
763
|
-
updated_at: {
|
|
764
|
-
type: Sequelize.DATE,
|
|
765
|
-
allowNull: false,
|
|
766
|
-
// defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
|
|
767
|
-
comment: '修改时间',
|
|
768
|
-
},
|
|
769
|
-
})
|
|
798
|
+
const _id = {
|
|
799
|
+
id: {
|
|
800
|
+
type: Sequelize[idType].UNSIGNED,
|
|
801
|
+
allowNull: false,
|
|
802
|
+
primaryKey: true,
|
|
803
|
+
autoIncrement: true,
|
|
804
|
+
},
|
|
805
|
+
}
|
|
770
806
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
807
|
+
const attributes = _.defaultsDeep(_.cloneDeep(_attributes), {
|
|
808
|
+
..._id,
|
|
809
|
+
createdid: {
|
|
810
|
+
type: Sequelize.MEDIUMINT.UNSIGNED,
|
|
811
|
+
allowNull: false,
|
|
812
|
+
defaultValue: 0,
|
|
813
|
+
},
|
|
814
|
+
created_at: {
|
|
815
|
+
type: Sequelize.DATE,
|
|
816
|
+
allowNull: false,
|
|
817
|
+
// defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
|
|
818
|
+
comment: '生成时间',
|
|
819
|
+
},
|
|
820
|
+
updated_at: {
|
|
821
|
+
type: Sequelize.DATE,
|
|
822
|
+
allowNull: false,
|
|
823
|
+
// defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
|
|
824
|
+
comment: '修改时间',
|
|
825
|
+
},
|
|
826
|
+
})
|
|
827
|
+
|
|
828
|
+
this.app[appName].attributes = {
|
|
829
|
+
...this.app[appName].attributes,
|
|
830
|
+
[folder]: _attributes,
|
|
831
|
+
}
|
|
832
|
+
const indexes = _.cloneDeep(
|
|
833
|
+
_.get(this.app[appName].config[folder], 'indexList', [])
|
|
834
|
+
)
|
|
835
|
+
let hookList = _.cloneDeep(
|
|
836
|
+
_.get(this.app[appName].config[folder], 'hookList', [])
|
|
837
|
+
)
|
|
838
|
+
if (
|
|
839
|
+
hookList.some((h) =>
|
|
840
|
+
['afterDestroy', 'beforeDestroy'].includes(h)
|
|
841
|
+
) &&
|
|
842
|
+
!hookList.includes('beforeBulkDestroy')
|
|
843
|
+
) {
|
|
844
|
+
hookList = [...hookList, 'beforeBulkDestroy']
|
|
845
|
+
}
|
|
846
|
+
let reduceHook = {}
|
|
847
|
+
for (const hook of hookList) {
|
|
848
|
+
reduceHook = {
|
|
849
|
+
...reduceHook,
|
|
850
|
+
[hook]: (target, t) => {
|
|
851
|
+
if (['beforeBulkDestroy'].includes(hook)) {
|
|
852
|
+
target.individualHooks = true
|
|
853
|
+
}
|
|
854
|
+
try {
|
|
855
|
+
this.app[appName].service &&
|
|
856
|
+
this.app[appName].service[folder] &&
|
|
857
|
+
this.app[appName].service[folder][hook] &&
|
|
858
|
+
this.app[appName].service[folder][hook]({
|
|
859
|
+
app: this.app[appName],
|
|
860
|
+
model: folder,
|
|
861
|
+
hook,
|
|
862
|
+
...(target.toJSON
|
|
863
|
+
? {
|
|
864
|
+
target: target.toJSON(),
|
|
865
|
+
}
|
|
866
|
+
: {
|
|
867
|
+
target,
|
|
868
|
+
}),
|
|
869
|
+
...t,
|
|
870
|
+
})
|
|
871
|
+
} catch (e) {
|
|
872
|
+
console.log(e)
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
if (['beforeBulkDestroy'].includes(hook)) {
|
|
876
|
+
return target
|
|
877
|
+
}
|
|
878
|
+
},
|
|
788
879
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
880
|
+
}
|
|
881
|
+
const instance = this.app[appName].sequelize.define(
|
|
882
|
+
folder,
|
|
883
|
+
attributes,
|
|
884
|
+
{
|
|
885
|
+
freezeTableName: true,
|
|
886
|
+
comment: folder,
|
|
887
|
+
// timestamps: !modelConfig.belongs === 'setting',
|
|
888
|
+
createdAt: 'created_at',
|
|
889
|
+
updatedAt: 'updated_at',
|
|
890
|
+
name: {
|
|
891
|
+
singular: folder,
|
|
892
|
+
plural: `${folder}s`,
|
|
893
|
+
},
|
|
894
|
+
hooks: {
|
|
792
895
|
...reduceHook,
|
|
793
|
-
|
|
794
|
-
if (['beforeBulkDestroy'].includes(hook)) {
|
|
795
|
-
target.individualHooks = true
|
|
796
|
-
}
|
|
896
|
+
beforeCreate: (res, t) => {
|
|
797
897
|
try {
|
|
798
|
-
|
|
799
|
-
this.app[appName].service
|
|
800
|
-
|
|
801
|
-
this.app[appName].service[folder][hook]({
|
|
802
|
-
app: this.app[appName],
|
|
803
|
-
model: folder,
|
|
804
|
-
hook,
|
|
805
|
-
...(target.toJSON
|
|
806
|
-
? {
|
|
807
|
-
target: target.toJSON(),
|
|
808
|
-
}
|
|
809
|
-
: {
|
|
810
|
-
target,
|
|
811
|
-
}),
|
|
812
|
-
...t,
|
|
813
|
-
})
|
|
814
|
-
} catch (e) {
|
|
815
|
-
console.log(e)
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
if (['beforeBulkDestroy'].includes(hook)) {
|
|
819
|
-
return target
|
|
820
|
-
}
|
|
821
|
-
},
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
const instance = this.app[appName].sequelize.define(
|
|
825
|
-
folder,
|
|
826
|
-
attributes,
|
|
827
|
-
{
|
|
828
|
-
freezeTableName: true,
|
|
829
|
-
comment: folder,
|
|
830
|
-
// timestamps: !modelConfig.belongs === 'setting',
|
|
831
|
-
createdAt: 'created_at',
|
|
832
|
-
updatedAt: 'updated_at',
|
|
833
|
-
name: {
|
|
834
|
-
singular: folder,
|
|
835
|
-
plural: `${folder}s`,
|
|
836
|
-
},
|
|
837
|
-
hooks: {
|
|
838
|
-
...reduceHook,
|
|
839
|
-
beforeCreate: (res, t) => {
|
|
840
|
-
try {
|
|
841
|
-
if (folder === 'log') {
|
|
842
|
-
if (this.is_dev && !this.app[appName].service.log) {
|
|
843
|
-
console.error('缺少log/service.js,')
|
|
844
|
-
}
|
|
845
|
-
this.app[appName].service.log &&
|
|
846
|
-
this.app[appName].service.log.push({
|
|
847
|
-
app: this.app[appName],
|
|
848
|
-
appName,
|
|
849
|
-
message: res.toJSON().message,
|
|
850
|
-
url: res.toJSON().url,
|
|
851
|
-
})
|
|
898
|
+
if (folder === 'log') {
|
|
899
|
+
if (this.is_dev && !this.app[appName].service.log) {
|
|
900
|
+
console.error('缺少log/service.js,')
|
|
852
901
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
902
|
+
this.app[appName].service.log &&
|
|
903
|
+
this.app[appName].service.log.push({
|
|
904
|
+
app: this.app[appName],
|
|
905
|
+
appName,
|
|
906
|
+
message: res.toJSON().message,
|
|
907
|
+
url: res.toJSON().url,
|
|
908
|
+
})
|
|
858
909
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
...(indexes.length > 0
|
|
862
|
-
? {
|
|
863
|
-
indexes,
|
|
910
|
+
if (res.toJSON().user_id) {
|
|
911
|
+
res.createdid = res.toJSON().user_id
|
|
864
912
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
instance.getName = function () {
|
|
869
|
-
return folder
|
|
870
|
-
}
|
|
871
|
-
this.app[appName] = _.defaultsDeep(
|
|
872
|
-
{
|
|
873
|
-
[n]: {
|
|
874
|
-
[folder]: instance,
|
|
913
|
+
} catch (e) {
|
|
914
|
+
console.log(e)
|
|
915
|
+
}
|
|
875
916
|
},
|
|
876
917
|
},
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
(item) => item.mock && typeof item.mock === 'function'
|
|
883
|
-
)
|
|
884
|
-
this.app[appName] = _.defaultsDeep(
|
|
885
|
-
{
|
|
886
|
-
mock: {
|
|
887
|
-
[folder]: () =>
|
|
888
|
-
_.zipObject(
|
|
889
|
-
Object.keys(mockObj),
|
|
890
|
-
Object.keys(mockObj).map((key) =>
|
|
891
|
-
_.result(mockObj, `${key}.mock`)
|
|
892
|
-
)
|
|
893
|
-
),
|
|
894
|
-
},
|
|
895
|
-
},
|
|
896
|
-
this.app[appName]
|
|
897
|
-
)
|
|
918
|
+
...(indexes.length > 0
|
|
919
|
+
? {
|
|
920
|
+
indexes,
|
|
921
|
+
}
|
|
922
|
+
: {}),
|
|
898
923
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
const defaultPath = path.resolve(
|
|
909
|
-
__dirname,
|
|
910
|
-
`./file/${PLUGINS_DIR}/${folder}`,
|
|
911
|
-
`${filename}`
|
|
912
|
-
)
|
|
913
|
-
const defaultExist = fs.existsSync(defaultPath)
|
|
914
|
-
const defaultN = !defaultExist
|
|
915
|
-
? Object.create(null)
|
|
916
|
-
: {
|
|
917
|
-
[n]: {
|
|
918
|
-
[folder]: defaultExist
|
|
919
|
-
? require(path.resolve(
|
|
920
|
-
__dirname,
|
|
921
|
-
`./file/${PLUGINS_DIR}/${folder}`,
|
|
922
|
-
`${filename}`
|
|
923
|
-
))
|
|
924
|
-
: null,
|
|
925
|
-
},
|
|
926
|
-
}
|
|
927
|
-
this.app[appName] = _.defaultsDeep(
|
|
928
|
-
{
|
|
929
|
-
[n]: {
|
|
930
|
-
[folder]: require(path.resolve(
|
|
931
|
-
pluginDir,
|
|
932
|
-
`${folder}`,
|
|
933
|
-
`${filename}`
|
|
934
|
-
)),
|
|
935
|
-
},
|
|
936
|
-
},
|
|
937
|
-
defaultN,
|
|
938
|
-
this.app[appName]
|
|
924
|
+
)
|
|
925
|
+
instance.getName = function () {
|
|
926
|
+
return folder
|
|
927
|
+
}
|
|
928
|
+
mount(n, folder, instance)
|
|
929
|
+
if (is_dev) {
|
|
930
|
+
const mockObj = _.pickBy(
|
|
931
|
+
_attributes,
|
|
932
|
+
(item) => item.mock && typeof item.mock === 'function'
|
|
939
933
|
)
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
`${
|
|
948
|
-
)
|
|
949
|
-
|
|
950
|
-
},
|
|
951
|
-
this.app[appName]
|
|
934
|
+
mount(
|
|
935
|
+
'mock',
|
|
936
|
+
folder,
|
|
937
|
+
() =>
|
|
938
|
+
_.zipObject(
|
|
939
|
+
Object.keys(mockObj),
|
|
940
|
+
Object.keys(mockObj).map((key) =>
|
|
941
|
+
_.result(mockObj, `${key}.mock`)
|
|
942
|
+
)
|
|
943
|
+
)
|
|
952
944
|
)
|
|
953
945
|
}
|
|
946
|
+
} else if (
|
|
947
|
+
[
|
|
948
|
+
'config',
|
|
949
|
+
'controller',
|
|
950
|
+
'validate',
|
|
951
|
+
'afterExecute',
|
|
952
|
+
'service',
|
|
953
|
+
].includes(n)
|
|
954
|
+
) {
|
|
955
|
+
const defaultPath = path.resolve(
|
|
956
|
+
__dirname,
|
|
957
|
+
`./file/${PLUGINS_DIR}/${folder}`,
|
|
958
|
+
`${filename}`
|
|
959
|
+
)
|
|
960
|
+
const userModule = require(path.resolve(
|
|
961
|
+
pluginDir,
|
|
962
|
+
`${folder}`,
|
|
963
|
+
`${filename}`
|
|
964
|
+
))
|
|
965
|
+
// 用户模块缺失的方法/配置由框架内置同名模块补齐(仅合并当前模块,不波及整个 app)
|
|
966
|
+
if (
|
|
967
|
+
fs.existsSync(defaultPath) &&
|
|
968
|
+
_.isPlainObject(userModule)
|
|
969
|
+
) {
|
|
970
|
+
_.defaultsDeep(userModule, require(defaultPath))
|
|
971
|
+
}
|
|
972
|
+
mount(n, folder, userModule)
|
|
973
|
+
} else {
|
|
974
|
+
mount(
|
|
975
|
+
n,
|
|
976
|
+
folder,
|
|
977
|
+
require(path.resolve(pluginDir, `${folder}`, `${filename}`))
|
|
978
|
+
)
|
|
954
979
|
}
|
|
955
980
|
}
|
|
956
981
|
}
|
|
@@ -984,12 +1009,8 @@ class APP {
|
|
|
984
1009
|
a[b] = []
|
|
985
1010
|
return a
|
|
986
1011
|
}, {})
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
include,
|
|
990
|
-
},
|
|
991
|
-
this.app[appName]
|
|
992
|
-
)
|
|
1012
|
+
// 直接赋值,避免对整个 app 对象做 defaultsDeep 深合并(且保持对象引用不变)
|
|
1013
|
+
this.app[appName].include = include
|
|
993
1014
|
for (const model of modelList) {
|
|
994
1015
|
|
|
995
1016
|
// if (!this.app[appName].include || !this.app[appName].include[model]) {
|
|
@@ -1673,7 +1673,12 @@ exports.union_notify = async (ctx) => {
|
|
|
1673
1673
|
}
|
|
1674
1674
|
|
|
1675
1675
|
// 通联回调报文字段为全小写 signtype;兼容大小写两种 + 配置 union_signType;末位回退 RSA
|
|
1676
|
-
const rawSignType = (
|
|
1676
|
+
const rawSignType = (
|
|
1677
|
+
notifyData.signtype ||
|
|
1678
|
+
notifyData.signType ||
|
|
1679
|
+
union_signType ||
|
|
1680
|
+
'RSA'
|
|
1681
|
+
).toUpperCase()
|
|
1677
1682
|
const hasSign = notifyData.sign !== undefined && notifyData.sign !== ''
|
|
1678
1683
|
const trxstatus = notifyData.trxstatus
|
|
1679
1684
|
|
|
@@ -1698,22 +1703,17 @@ exports.union_notify = async (ctx) => {
|
|
|
1698
1703
|
if (!signOk) {
|
|
1699
1704
|
app.service.log.push({
|
|
1700
1705
|
app,
|
|
1701
|
-
message: `通联回调验签失败(type=${rawSignType}): ${JSON.stringify(
|
|
1706
|
+
message: `通联回调验签失败(type=${rawSignType}): ${JSON.stringify(
|
|
1707
|
+
notifyData
|
|
1708
|
+
)}`,
|
|
1702
1709
|
})
|
|
1703
1710
|
ctx.body = 'FAIL'
|
|
1704
1711
|
return
|
|
1705
1712
|
}
|
|
1706
1713
|
}
|
|
1707
1714
|
|
|
1708
|
-
const {
|
|
1709
|
-
|
|
1710
|
-
cusorderid,
|
|
1711
|
-
acct,
|
|
1712
|
-
trxid,
|
|
1713
|
-
trxamt,
|
|
1714
|
-
srctrxid,
|
|
1715
|
-
trxreserved,
|
|
1716
|
-
} = notifyData
|
|
1715
|
+
const { trxcode, cusorderid, acct, trxid, trxamt, srctrxid, trxreserved } =
|
|
1716
|
+
notifyData
|
|
1717
1717
|
|
|
1718
1718
|
if (trxstatus === '0000') {
|
|
1719
1719
|
if (['VSP501', 'VSP681'].includes(trxcode)) {
|
|
@@ -2439,6 +2439,7 @@ exports.union_checkPay = async (ctx) => {
|
|
|
2439
2439
|
const payResult = await allinpay.checkOrder(out_trade_no)
|
|
2440
2440
|
|
|
2441
2441
|
if (payResult) {
|
|
2442
|
+
console.log(payResult)
|
|
2442
2443
|
if (payResult.trxstatus === '0000' && payResult.trxcode === 'VSP501') {
|
|
2443
2444
|
const order_price = Number(payResult.trxamt) / 100
|
|
2444
2445
|
|
|
@@ -2477,12 +2478,8 @@ exports.union_check_balance = async (ctx) => {
|
|
|
2477
2478
|
} = ctx.request.body
|
|
2478
2479
|
|
|
2479
2480
|
const appConfig = getConfig(app)
|
|
2480
|
-
const {
|
|
2481
|
-
|
|
2482
|
-
union_appid,
|
|
2483
|
-
union_privateKey,
|
|
2484
|
-
union_publicKey,
|
|
2485
|
-
} = await appConfig.getObject(pay_config)
|
|
2481
|
+
const { union_mchId, union_appid, union_privateKey, union_publicKey } =
|
|
2482
|
+
await appConfig.getObject(pay_config)
|
|
2486
2483
|
|
|
2487
2484
|
const allinpay = new AllinPay({
|
|
2488
2485
|
cusid: mch_id || union_mchId,
|