q-koa 13.4.3 → 13.4.5
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 +20 -15
- package/core/file/plugins/common/controller.js +7 -2
- package/core/file/plugins/log/controller.js +2 -1
- package/core/file/plugins/permission/config.js +6 -2
- package/core/file/plugins/system/controller.js +40 -6
- package/core/file/plugins/user/controller.js +6 -0
- package/core/file/plugins/weixin/controller.js +15 -4
- package/core/file/plugins/weixin/service.js +64 -51
- package/core/file/services/weixinMP.js +14 -0
- package/core/file/services/weixinOpen.js +11 -0
- package/package.json +1 -1
package/core/app.js
CHANGED
|
@@ -222,6 +222,7 @@ class APP {
|
|
|
222
222
|
this.app[appName].sign = async (user) => {
|
|
223
223
|
const result = await jwt.sign(user, secret, {
|
|
224
224
|
expiresIn,
|
|
225
|
+
algorithm: 'HS256',
|
|
225
226
|
})
|
|
226
227
|
return result
|
|
227
228
|
}
|
|
@@ -248,7 +249,9 @@ class APP {
|
|
|
248
249
|
}
|
|
249
250
|
|
|
250
251
|
try {
|
|
251
|
-
const { user } = await verify(token, secret
|
|
252
|
+
const { user } = await verify(token, secret, {
|
|
253
|
+
algorithms: ['HS256'],
|
|
254
|
+
})
|
|
252
255
|
if (user) {
|
|
253
256
|
if (this.config.log.request) {
|
|
254
257
|
console.log('user id ---> ', appName, '--->', user.id, user.name)
|
|
@@ -893,15 +896,11 @@ class APP {
|
|
|
893
896
|
const modelService = cloneTarget[b]
|
|
894
897
|
const newB = Object.keys(modelService).reduce((_a, _b) => {
|
|
895
898
|
const bindFn = modelService[_b].bind(this.app[appName])
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
[_b]: bindFn,
|
|
899
|
-
}
|
|
899
|
+
_a[_b] = bindFn
|
|
900
|
+
return _a
|
|
900
901
|
}, {})
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
[b]: newB,
|
|
904
|
-
}
|
|
902
|
+
a[b] = newB
|
|
903
|
+
return a
|
|
905
904
|
}, {})
|
|
906
905
|
}
|
|
907
906
|
|
|
@@ -914,10 +913,8 @@ class APP {
|
|
|
914
913
|
const modelList = Object.keys(this.app[appName].model)
|
|
915
914
|
|
|
916
915
|
const include = modelList.reduce((a, b) => {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
[b]: [],
|
|
920
|
-
}
|
|
916
|
+
a[b] = []
|
|
917
|
+
return a
|
|
921
918
|
}, {})
|
|
922
919
|
this.app[appName] = _.defaultsDeep(
|
|
923
920
|
{
|
|
@@ -1308,10 +1305,18 @@ class APP {
|
|
|
1308
1305
|
typeof where.id === 'string' &&
|
|
1309
1306
|
where.id.includes('/')
|
|
1310
1307
|
) {
|
|
1308
|
+
const arr = where.id.split('?')
|
|
1309
|
+
|
|
1310
|
+
let obj = {}
|
|
1311
|
+
if (arr[1] && arr[1].includes('id=')) {
|
|
1312
|
+
obj = {
|
|
1313
|
+
id: JSON.parse(arr[1].split('=')[1]),
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1311
1316
|
const _result = await _.get(
|
|
1312
1317
|
app[appName].controller,
|
|
1313
|
-
|
|
1314
|
-
)(ctx)
|
|
1318
|
+
arr[0].replace('/', '.')
|
|
1319
|
+
)(ctx, obj)
|
|
1315
1320
|
where.id = _result
|
|
1316
1321
|
}
|
|
1317
1322
|
|
|
@@ -431,12 +431,17 @@ exports.syncModel = async (ctx) => {
|
|
|
431
431
|
ctx.SUCCESS('ok')
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
-
exports.verify = async (ctx) => {
|
|
434
|
+
exports.verify = async (ctx, _data) => {
|
|
435
435
|
const { app, appName } = getAppByCtx(ctx)
|
|
436
|
-
const { code } = ctx.request.body
|
|
436
|
+
const { code } = _data || ctx.request.body
|
|
437
|
+
if (!code) {
|
|
438
|
+
ctx.SUCCESS(null)
|
|
439
|
+
return null
|
|
440
|
+
}
|
|
437
441
|
const verify = util.promisify(jwt.verify)
|
|
438
442
|
const result = await verify(code, 'token')
|
|
439
443
|
ctx.SUCCESS(result)
|
|
444
|
+
return result
|
|
440
445
|
}
|
|
441
446
|
|
|
442
447
|
const expressFn = async ({ app, express_number: _express_number, mobile }) => {
|
|
@@ -33,11 +33,12 @@ exports.clearWeixin = async (ctx) => {
|
|
|
33
33
|
exports.push = async (ctx) => {
|
|
34
34
|
const { app, appName } = getAppByCtx(ctx)
|
|
35
35
|
|
|
36
|
-
const { message = '测试通知' } = ctx.request.body
|
|
36
|
+
const { message = '测试通知', url } = ctx.request.body
|
|
37
37
|
|
|
38
38
|
await app.service.log.push({
|
|
39
39
|
app,
|
|
40
40
|
message,
|
|
41
|
+
url,
|
|
41
42
|
})
|
|
42
43
|
|
|
43
44
|
ctx.SUCCESS('ok')
|
|
@@ -4,7 +4,11 @@ module.exports = {
|
|
|
4
4
|
multiple: false,
|
|
5
5
|
availableSort: false,
|
|
6
6
|
order: [],
|
|
7
|
-
select: [
|
|
7
|
+
select: [
|
|
8
|
+
{
|
|
9
|
+
key: 'controller',
|
|
10
|
+
},
|
|
11
|
+
],
|
|
8
12
|
excludes: [],
|
|
9
13
|
limit: 20,
|
|
10
14
|
defaultOrder: [],
|
|
@@ -12,4 +16,4 @@ module.exports = {
|
|
|
12
16
|
sortOrder: 1,
|
|
13
17
|
reference: [],
|
|
14
18
|
excludeAuth: [],
|
|
15
|
-
}
|
|
19
|
+
}
|
|
@@ -78,13 +78,13 @@ exports.countAndMaxId = async (ctx, _data) => {
|
|
|
78
78
|
ctx.SUCCESS(null)
|
|
79
79
|
return null
|
|
80
80
|
}
|
|
81
|
-
const count = await app.model[model].count()
|
|
82
|
-
const current = await app.model[model].findOne({
|
|
83
|
-
attributes: ['id', 'created_at'],
|
|
84
|
-
order: [['id', 'DESC']],
|
|
85
|
-
})
|
|
86
81
|
|
|
87
|
-
const
|
|
82
|
+
const result = await app.sequelize.query(
|
|
83
|
+
`select * from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA = '${app.sequelize.config.database}' and TABLE_NAME = '${model}'`
|
|
84
|
+
)
|
|
85
|
+
const count = result[0][0].TABLE_ROWS
|
|
86
|
+
|
|
87
|
+
const current_max_id = result[0][0].AUTO_INCREMENT
|
|
88
88
|
|
|
89
89
|
const lastMonth = await app.model[model].findOne({
|
|
90
90
|
attributes: ['id'],
|
|
@@ -933,6 +933,40 @@ exports.checkIndex = async (ctx) => {
|
|
|
933
933
|
}
|
|
934
934
|
}
|
|
935
935
|
|
|
936
|
+
exports.checkAttribute = async (ctx) => {
|
|
937
|
+
const { app, appName } = getAppByCtx(ctx)
|
|
938
|
+
const { model } = ctx.request.body
|
|
939
|
+
|
|
940
|
+
if (!model) return ctx.ERROR('model不能为空')
|
|
941
|
+
|
|
942
|
+
const result = await app.sequelize.getQueryInterface().describeTable(model)
|
|
943
|
+
|
|
944
|
+
const attributeList = Object.keys(result).filter(
|
|
945
|
+
(item) => !['id', 'created_at', 'createdid', 'updated_at'].includes(item)
|
|
946
|
+
)
|
|
947
|
+
|
|
948
|
+
const currentAttributeList = Object.keys(app.attributes[model]).filter(
|
|
949
|
+
(item) => {
|
|
950
|
+
return app.attributes[model][item].type !== Sequelize.VIRTUAL
|
|
951
|
+
}
|
|
952
|
+
)
|
|
953
|
+
|
|
954
|
+
let flag = true
|
|
955
|
+
if (attributeList.length !== currentAttributeList.length) {
|
|
956
|
+
flag = false
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
const current = attributeList.filter(
|
|
960
|
+
(item) => !currentAttributeList.includes(item)
|
|
961
|
+
)
|
|
962
|
+
|
|
963
|
+
ctx.SUCCESS({
|
|
964
|
+
isSame: flag,
|
|
965
|
+
current,
|
|
966
|
+
remote: [],
|
|
967
|
+
})
|
|
968
|
+
}
|
|
969
|
+
|
|
936
970
|
exports.dropIndex = async (ctx) => {
|
|
937
971
|
const { app, appName } = getAppByCtx(ctx)
|
|
938
972
|
const { model, key } = ctx.request.body
|
|
@@ -342,14 +342,18 @@ exports.checkLogin = async (ctx) => {
|
|
|
342
342
|
const mp_user_include = config.includes('mp')
|
|
343
343
|
? {
|
|
344
344
|
model: app.model.mp_user,
|
|
345
|
+
attributes: ['openid', 'unionid'],
|
|
345
346
|
where: {
|
|
346
347
|
appid,
|
|
347
348
|
},
|
|
348
349
|
}
|
|
349
350
|
: {
|
|
350
351
|
model: app.model.mp_user,
|
|
352
|
+
attributes: ['openid', 'unionid'],
|
|
351
353
|
}
|
|
354
|
+
|
|
352
355
|
let result
|
|
356
|
+
|
|
353
357
|
const includeDefault = [
|
|
354
358
|
{
|
|
355
359
|
model: app.model.github_user,
|
|
@@ -364,6 +368,7 @@ exports.checkLogin = async (ctx) => {
|
|
|
364
368
|
|
|
365
369
|
if (app.appConfig.loginData) {
|
|
366
370
|
const application = await getAppConfig({ app, config })
|
|
371
|
+
|
|
367
372
|
const loginData = lodash.mergeWith(
|
|
368
373
|
lodash.cloneDeep(app.appConfig.loginData),
|
|
369
374
|
lodash.get(application, 'loginData', {}),
|
|
@@ -390,6 +395,7 @@ exports.checkLogin = async (ctx) => {
|
|
|
390
395
|
])
|
|
391
396
|
),
|
|
392
397
|
}
|
|
398
|
+
|
|
393
399
|
result = await app.model.user.findOne({
|
|
394
400
|
where: {
|
|
395
401
|
id: ctx.request[`${appName}-user`].id,
|
|
@@ -94,8 +94,17 @@ exports.mp_getPhone = async (ctx) => {
|
|
|
94
94
|
(!exist.mp_user.appid || exist.mp_user.appid === app_id)
|
|
95
95
|
|
|
96
96
|
if (mobileExist) {
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
const formatMobile = (mobile) => {
|
|
98
|
+
if (!mobile) return ''
|
|
99
|
+
return mobile.slice(0, 3) + '*****' + mobile.slice(8, mobile.length)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const user_id = getUserByCtx(ctx)
|
|
103
|
+
app.service.log.push({
|
|
104
|
+
app,
|
|
105
|
+
message: `系统已存在该手机号${phoneNumber};${user_id}`,
|
|
106
|
+
})
|
|
107
|
+
return ctx.ERROR(`系统已存在该手机号`)
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
const userResult = await app.model.mp_user.findOne({
|
|
@@ -1043,6 +1052,7 @@ exports.mp_pay = async (ctx) => {
|
|
|
1043
1052
|
config = 'weixin_mp',
|
|
1044
1053
|
pay_config = 'weixin_pay',
|
|
1045
1054
|
profit_sharing = 'N',
|
|
1055
|
+
mch_id = '',
|
|
1046
1056
|
} = ctx.request.body
|
|
1047
1057
|
if (price === 0) throw new Error('价格不能为0')
|
|
1048
1058
|
|
|
@@ -1066,7 +1076,7 @@ exports.mp_pay = async (ctx) => {
|
|
|
1066
1076
|
key,
|
|
1067
1077
|
pay_key: _pay_key,
|
|
1068
1078
|
partner_key,
|
|
1069
|
-
} = await appConfig.getObject(pay_config)
|
|
1079
|
+
} = await appConfig.getObject(mch_id || pay_config)
|
|
1070
1080
|
|
|
1071
1081
|
const pay_key = _pay_key || key || appName
|
|
1072
1082
|
const { is_dev, site_host } = await appConfig.getObject('base')
|
|
@@ -1078,7 +1088,7 @@ exports.mp_pay = async (ctx) => {
|
|
|
1078
1088
|
|
|
1079
1089
|
const wxpay = WXPay({
|
|
1080
1090
|
appid: app_id,
|
|
1081
|
-
mch_id: mchId,
|
|
1091
|
+
mch_id: mchId || mch_id,
|
|
1082
1092
|
partner_key, // 微信商户平台API密钥
|
|
1083
1093
|
})
|
|
1084
1094
|
|
|
@@ -1675,6 +1685,7 @@ exports.checkPayOnly = async (ctx, _data) => {
|
|
|
1675
1685
|
})
|
|
1676
1686
|
|
|
1677
1687
|
ctx.SUCCESS({ prefix, out_trade_no, ...payResult })
|
|
1688
|
+
return { prefix, out_trade_no, ...payResult }
|
|
1678
1689
|
}
|
|
1679
1690
|
|
|
1680
1691
|
exports.checkRefund = async (ctx, _data) => {
|
|
@@ -866,6 +866,60 @@ exports.removereceiver = async ({
|
|
|
866
866
|
return res
|
|
867
867
|
}
|
|
868
868
|
|
|
869
|
+
const handleBillData = (result) => {
|
|
870
|
+
const arr = result.split('\r\n')
|
|
871
|
+
const list = arr.slice(1, arr.length - 3)
|
|
872
|
+
return {
|
|
873
|
+
header: arr[0],
|
|
874
|
+
list,
|
|
875
|
+
result: list.map((item) => {
|
|
876
|
+
const array = item.split(',')
|
|
877
|
+
const orderdate = array[20].substring(1).slice(2, 10)
|
|
878
|
+
const orderPrefix = array[6].substring(1).split('_')[1]
|
|
879
|
+
let order_id = 0
|
|
880
|
+
let prefix = ''
|
|
881
|
+
if (orderPrefix.includes('-')) {
|
|
882
|
+
order_id = Number(orderPrefix.split('-')[1])
|
|
883
|
+
prefix = orderPrefix.split('-')[0]
|
|
884
|
+
} else {
|
|
885
|
+
order_id = Number(orderPrefix)
|
|
886
|
+
}
|
|
887
|
+
let order_date
|
|
888
|
+
if (prefix) {
|
|
889
|
+
order_date = moment(array[0].substring(1)).format('YYYY-MM-DD') + ''
|
|
890
|
+
} else {
|
|
891
|
+
order_date = moment(
|
|
892
|
+
`${orderdate.slice(0, 4)}-${orderdate.slice(4, 6)}-${orderdate.slice(
|
|
893
|
+
6,
|
|
894
|
+
8
|
|
895
|
+
)}`
|
|
896
|
+
).format('YYYY-MM-DD')
|
|
897
|
+
}
|
|
898
|
+
return {
|
|
899
|
+
created_at: array[0].substring(1),
|
|
900
|
+
transactionid: array[5].substring(1),
|
|
901
|
+
[`${[prefix, 'order_id'].filter(Boolean).join('_')}`]: order_id,
|
|
902
|
+
openid: array[7].substring(1),
|
|
903
|
+
price: Number(array[12].substring(1)),
|
|
904
|
+
refund_price: Number(array[16].substring(1)),
|
|
905
|
+
remark: array[20].substring(1),
|
|
906
|
+
rate_price: Number(array[22].substring(1)),
|
|
907
|
+
type: array[9].substring(1),
|
|
908
|
+
order_date,
|
|
909
|
+
}
|
|
910
|
+
}),
|
|
911
|
+
totalHeader: arr[arr.length - 3],
|
|
912
|
+
total: arr[arr.length - 2],
|
|
913
|
+
totalResult: {
|
|
914
|
+
number: Number(arr[arr.length - 2].split(',')[0].substring(1)),
|
|
915
|
+
total_price: Number(arr[arr.length - 2].split(',')[1].substring(1)),
|
|
916
|
+
refund_price: Number(arr[arr.length - 2].split(',')[2].substring(1)),
|
|
917
|
+
rate_price: Number(arr[arr.length - 2].split(',')[4].substring(1)),
|
|
918
|
+
price: Number(arr[arr.length - 2].split(',')[5].substring(1)),
|
|
919
|
+
},
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
869
923
|
exports.downloadBill = async ({ ctx, pay_config = 'weixin_pay', ...rest }) => {
|
|
870
924
|
if (!ctx) throw new Error('?ctx')
|
|
871
925
|
const { app, appName } = getAppByCtx(ctx)
|
|
@@ -887,58 +941,17 @@ exports.downloadBill = async ({ ctx, pay_config = 'weixin_pay', ...rest }) => {
|
|
|
887
941
|
|
|
888
942
|
try {
|
|
889
943
|
const result = await payObj.downloadBill(rest)
|
|
944
|
+
return handleBillData(result)
|
|
945
|
+
} catch {
|
|
946
|
+
return []
|
|
947
|
+
}
|
|
948
|
+
}
|
|
890
949
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
result: list.map((item) => {
|
|
897
|
-
const array = item.split(',')
|
|
898
|
-
const orderdate = array[20].substring(1).slice(2, 10)
|
|
899
|
-
const orderPrefix = array[6].substring(1).split('_')[1]
|
|
900
|
-
let order_id = 0
|
|
901
|
-
let prefix = ''
|
|
902
|
-
if (orderPrefix.includes('-')) {
|
|
903
|
-
order_id = Number(orderPrefix.split('-')[1])
|
|
904
|
-
prefix = orderPrefix.split('-')[0]
|
|
905
|
-
} else {
|
|
906
|
-
order_id = Number(orderPrefix)
|
|
907
|
-
}
|
|
908
|
-
let order_date
|
|
909
|
-
if (prefix) {
|
|
910
|
-
order_date = moment(array[0].substring(1)).format('YYYY-MM-DD') + ''
|
|
911
|
-
} else {
|
|
912
|
-
order_date = moment(
|
|
913
|
-
`${orderdate.slice(0, 4)}-${orderdate.slice(
|
|
914
|
-
4,
|
|
915
|
-
6
|
|
916
|
-
)}-${orderdate.slice(6, 8)}`
|
|
917
|
-
).format('YYYY-MM-DD')
|
|
918
|
-
}
|
|
919
|
-
return {
|
|
920
|
-
created_at: array[0].substring(1),
|
|
921
|
-
transactionid: array[5].substring(1),
|
|
922
|
-
[`${[prefix, 'order_id'].filter(Boolean).join('_')}`]: order_id,
|
|
923
|
-
openid: array[7].substring(1),
|
|
924
|
-
price: Number(array[12].substring(1)),
|
|
925
|
-
refund_price: Number(array[16].substring(1)),
|
|
926
|
-
remark: array[20].substring(1),
|
|
927
|
-
rate_price: Number(array[22].substring(1)),
|
|
928
|
-
type: array[9].substring(1),
|
|
929
|
-
order_date,
|
|
930
|
-
}
|
|
931
|
-
}),
|
|
932
|
-
totalHeader: arr[arr.length - 3],
|
|
933
|
-
total: arr[arr.length - 2],
|
|
934
|
-
totalResult: {
|
|
935
|
-
number: Number(arr[arr.length - 2].split(',')[0].substring(1)),
|
|
936
|
-
total_price: Number(arr[arr.length - 2].split(',')[1].substring(1)),
|
|
937
|
-
refund_price: Number(arr[arr.length - 2].split(',')[2].substring(1)),
|
|
938
|
-
rate_price: Number(arr[arr.length - 2].split(',')[4].substring(1)),
|
|
939
|
-
price: Number(arr[arr.length - 2].split(',')[5].substring(1)),
|
|
940
|
-
},
|
|
941
|
-
}
|
|
950
|
+
exports.uploadBill = async ({ ctx, content }) => {
|
|
951
|
+
if (!ctx) throw new Error('?ctx')
|
|
952
|
+
if (!content) return []
|
|
953
|
+
try {
|
|
954
|
+
return handleBillData(content)
|
|
942
955
|
} catch {
|
|
943
956
|
return []
|
|
944
957
|
}
|
|
@@ -123,6 +123,20 @@ const uploadPromise = (options) => {
|
|
|
123
123
|
})
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
const https = require('https')
|
|
127
|
+
|
|
128
|
+
// 1. 全局 HTTPS 连接池(微信专用)
|
|
129
|
+
const wxHttpsAgent = new https.Agent({
|
|
130
|
+
keepAlive: true, // 开启长连接
|
|
131
|
+
keepAliveMsecs: 60000, // 空闲连接保留 60 秒
|
|
132
|
+
maxSockets: 30, // 最大并发连接(适配微信QPS,不超限)
|
|
133
|
+
maxFreeSockets: 10, // 保留空闲连接数
|
|
134
|
+
timeout: 8000, // 底层套接字超时
|
|
135
|
+
rejectUnauthorized: true,
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
axios.defaults.httpsAgent = wxHttpsAgent
|
|
139
|
+
|
|
126
140
|
module.exports = class Singleton {
|
|
127
141
|
constructor(config) {
|
|
128
142
|
this.config = {
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
const util = require('util')
|
|
2
2
|
const axios = require('axios')
|
|
3
|
+
const https = require('https')
|
|
4
|
+
// 1. 全局 HTTPS 连接池(微信专用)
|
|
5
|
+
const wxHttpsAgent = new https.Agent({
|
|
6
|
+
keepAlive: true, // 开启长连接
|
|
7
|
+
keepAliveMsecs: 60000, // 空闲连接保留 60 秒
|
|
8
|
+
maxSockets: 30, // 最大并发连接(适配微信QPS,不超限)
|
|
9
|
+
maxFreeSockets: 10, // 保留空闲连接数
|
|
10
|
+
timeout: 8000, // 底层套接字超时
|
|
11
|
+
rejectUnauthorized: true,
|
|
12
|
+
})
|
|
3
13
|
|
|
14
|
+
axios.defaults.httpsAgent = wxHttpsAgent
|
|
4
15
|
const { lodash } = require('q-koa')
|
|
5
16
|
const getAccessTokenUrl =
|
|
6
17
|
'https://api.weixin.qq.com/cgi-bin/token?grant_type=%s&appid=%s&secret=%s'
|