q-koa 13.8.44 → 13.8.46
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.
|
@@ -543,22 +543,21 @@ exports.initDataWithCache = exports.initData = async (ctx) => {
|
|
|
543
543
|
const { app } = getAppByCtx(ctx)
|
|
544
544
|
const appConfig = getConfig(app)
|
|
545
545
|
|
|
546
|
-
const { is_cache } = await appConfig.getObject('base')
|
|
546
|
+
const { is_cache, version: cacheVersion } = await appConfig.getObject('base')
|
|
547
547
|
const { includes, excludes = [] } = ctx.request.body
|
|
548
548
|
if (!includes) {
|
|
549
549
|
return ctx.SUCCESS(null)
|
|
550
550
|
}
|
|
551
|
-
const cacheTarget = JSON.stringify(ctx.request.body)
|
|
551
|
+
const cacheTarget = is_cache ? JSON.stringify(ctx.request.body) : ''
|
|
552
552
|
|
|
553
|
-
let result = null
|
|
554
|
-
if (
|
|
555
|
-
result = cache.get(cacheTarget)
|
|
556
|
-
} else {
|
|
553
|
+
let result = is_cache ? cache.get(cacheTarget) : null
|
|
554
|
+
if (!result) {
|
|
557
555
|
result = await app.service.system.initData({
|
|
558
556
|
app,
|
|
559
557
|
includes,
|
|
560
558
|
excludes,
|
|
561
559
|
ctx,
|
|
560
|
+
cacheVersion,
|
|
562
561
|
})
|
|
563
562
|
if (is_cache && cacheTarget.includes('setting/findAll')) {
|
|
564
563
|
cache.set(cacheTarget, result)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const lodash = require('lodash')
|
|
2
|
-
const {
|
|
2
|
+
const { Sequelize } = require('q-koa')
|
|
3
3
|
const { formatLiteralObj, filterFunction } = require('../../utils')
|
|
4
4
|
const formatPostFunction = (str) => `eval(${str})`
|
|
5
5
|
const nodeVm = require('vm')
|
|
@@ -17,19 +17,17 @@ const getLiteral = (str) => {
|
|
|
17
17
|
}
|
|
18
18
|
return ''
|
|
19
19
|
}
|
|
20
|
-
exports.initData = async ({ includes, excludes, app, ctx }) => {
|
|
21
|
-
const appConfig = getConfig(app)
|
|
22
|
-
const { version: cacheVersion } = await appConfig.getObject('base')
|
|
23
|
-
|
|
20
|
+
exports.initData = async ({ includes, excludes, app, ctx, cacheVersion }) => {
|
|
24
21
|
const apiVersion = ctx.request.header.version
|
|
25
22
|
const config = ctx.request.header.config
|
|
26
23
|
|
|
27
24
|
if (!(excludes instanceof Array)) {
|
|
28
25
|
excludes = [excludes]
|
|
29
26
|
}
|
|
27
|
+
const excludeSet = new Set(excludes)
|
|
30
28
|
const result = await Promise.all(
|
|
31
29
|
Object.keys(includes)
|
|
32
|
-
.filter((item) =>
|
|
30
|
+
.filter((item) => excludeSet.has(item) || excludes.length === 0)
|
|
33
31
|
.map((item) => {
|
|
34
32
|
const route = includes[item]
|
|
35
33
|
if (lodash.isObject(route)) {
|
|
@@ -125,14 +123,17 @@ exports.initData = async ({ includes, excludes, app, ctx }) => {
|
|
|
125
123
|
Array.isArray(excludeInclude) &&
|
|
126
124
|
excludeInclude.length > 0
|
|
127
125
|
) {
|
|
126
|
+
const excludeNameSet = new Set(excludeInclude)
|
|
127
|
+
const excludeModelSet = new Set(
|
|
128
|
+
excludeInclude
|
|
129
|
+
.map((m) => app.model[m])
|
|
130
|
+
.filter((m) => m !== undefined)
|
|
131
|
+
)
|
|
128
132
|
myInclude = myInclude.filter((i) => {
|
|
129
|
-
if (
|
|
130
|
-
if (
|
|
133
|
+
if (excludeNameSet.has(model)) return false
|
|
134
|
+
if (excludeNameSet.has(i.as)) return false
|
|
131
135
|
return (
|
|
132
|
-
(
|
|
133
|
-
return app.model[m] !== i.model
|
|
134
|
-
}) ||
|
|
135
|
-
i.as) &&
|
|
136
|
+
(!excludeModelSet.has(i.model) || i.as) &&
|
|
136
137
|
!(
|
|
137
138
|
i.as &&
|
|
138
139
|
data.attributes &&
|
|
@@ -254,7 +255,9 @@ exports.initData = async ({ includes, excludes, app, ctx }) => {
|
|
|
254
255
|
}).then((res) => {
|
|
255
256
|
app.cache && app.cache.set(`${model}`, res)
|
|
256
257
|
const cacheModel = lodash.get(app, 'appConfig.cacheModel', [])
|
|
257
|
-
|
|
258
|
+
if (!cacheModel.includes(model)) {
|
|
259
|
+
app.appConfig.cacheModel = [...cacheModel, model]
|
|
260
|
+
}
|
|
258
261
|
return {
|
|
259
262
|
[item]: res,
|
|
260
263
|
}
|
|
@@ -275,14 +278,24 @@ exports.initData = async ({ includes, excludes, app, ctx }) => {
|
|
|
275
278
|
}
|
|
276
279
|
}
|
|
277
280
|
|
|
281
|
+
const settingById = new Map()
|
|
282
|
+
const codeCount = new Map()
|
|
283
|
+
for (const s of obj.setting) {
|
|
284
|
+
settingById.set(s.id, s)
|
|
285
|
+
codeCount.set(s.code, (codeCount.get(s.code) || 0) + 1)
|
|
286
|
+
}
|
|
287
|
+
|
|
278
288
|
return {
|
|
279
289
|
...obj,
|
|
280
290
|
setting: obj.setting.map((i) => {
|
|
281
291
|
let reset = true
|
|
282
292
|
if (i.parent_id) {
|
|
283
|
-
const parent =
|
|
284
|
-
|
|
285
|
-
|
|
293
|
+
const parent = settingById.get(i.parent_id)
|
|
294
|
+
if (
|
|
295
|
+
parent &&
|
|
296
|
+
parent.code !== config &&
|
|
297
|
+
(codeCount.get(i.code) || 0) > 1
|
|
298
|
+
) {
|
|
286
299
|
reset = false
|
|
287
300
|
}
|
|
288
301
|
}
|
|
@@ -39,9 +39,12 @@ class AllinPay {
|
|
|
39
39
|
options.isTest !== undefined ? options.isTest : config.allinpay.isTest
|
|
40
40
|
this.signType = options.signType || config.allinpay.signType
|
|
41
41
|
this.privateKey = options.privateKey || config.allinpay.privateKey
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
// 优先使用外部传入的公钥(如商户配置的 union_publicKey),未传则用内置平台公钥
|
|
43
|
+
this.publicKey =
|
|
44
|
+
options.publicKey ||
|
|
45
|
+
(this.isTest
|
|
46
|
+
? config.allinpay.publicKey.test
|
|
47
|
+
: config.allinpay.publicKey.production)
|
|
45
48
|
this.baseUrl = this.isTest
|
|
46
49
|
? config.allinpay.apiUrl.test
|
|
47
50
|
: config.allinpay.apiUrl.production
|
|
@@ -105,25 +108,52 @@ class AllinPay {
|
|
|
105
108
|
const signStr = this.buildSignStr(paramsCopy)
|
|
106
109
|
|
|
107
110
|
// PEM 标准化:无头尾则包 SPKI 头尾 + 内容单换行;已有头尾若一行无换行补换行
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
pem
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
const normalizePem = (key) => {
|
|
112
|
+
let pem = String(key)
|
|
113
|
+
if (!pem.includes('-----BEGIN')) {
|
|
114
|
+
pem = `-----BEGIN PUBLIC KEY-----\n${pem}\n-----END PUBLIC KEY-----`
|
|
115
|
+
} else if (!pem.includes('\n')) {
|
|
116
|
+
pem = pem
|
|
117
|
+
.replace('-----BEGIN PUBLIC KEY-----', '-----BEGIN PUBLIC KEY-----\n')
|
|
118
|
+
.replace('-----END PUBLIC KEY-----', '\n-----END PUBLIC KEY-----')
|
|
119
|
+
}
|
|
120
|
+
return pem
|
|
115
121
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
|
|
123
|
+
// 候选公钥:先用实例公钥(可能是商户配置的 union_publicKey),失败再用内置平台公钥兜底。
|
|
124
|
+
// 通联平台响应用平台私钥签名,商户配置的应用公钥验不过时用平台公钥补一轮。
|
|
125
|
+
const candidates = [this.publicKey]
|
|
126
|
+
const platformKey = this.isTest
|
|
127
|
+
? config.allinpay.publicKey.test
|
|
128
|
+
: config.allinpay.publicKey.production
|
|
129
|
+
if (platformKey !== this.publicKey) candidates.push(platformKey)
|
|
130
|
+
|
|
131
|
+
for (const pem of candidates) {
|
|
132
|
+
// RSA 双算法兜底:先 SHA256(新 2048-bit 证书),失败再 SHA1(老 1024-bit 证书)
|
|
133
|
+
for (const alg of ['RSA-SHA256', 'RSA-SHA1']) {
|
|
134
|
+
try {
|
|
135
|
+
const verify = crypto.createVerify(alg)
|
|
136
|
+
verify.update(signStr, 'utf8')
|
|
137
|
+
if (verify.verify(normalizePem(pem), sign, 'base64')) return true
|
|
138
|
+
} catch (e) {}
|
|
139
|
+
}
|
|
123
140
|
}
|
|
124
141
|
return false
|
|
125
142
|
}
|
|
126
143
|
|
|
144
|
+
// 统一响应处理:retcode=SUCCESS 时验签(失败仅告警),否则抛出业务错误
|
|
145
|
+
_handleResult(result) {
|
|
146
|
+
if (result && result.retcode === 'SUCCESS') {
|
|
147
|
+
if (result.sign && !this.verifySign(result)) {
|
|
148
|
+
console.warn(
|
|
149
|
+
'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
return result
|
|
153
|
+
}
|
|
154
|
+
throw new Error(result.retmsg || 'Unknown error')
|
|
155
|
+
}
|
|
156
|
+
|
|
127
157
|
async request(url, params) {
|
|
128
158
|
const _params = new URLSearchParams()
|
|
129
159
|
Object.keys(params).forEach((key) => {
|
|
@@ -156,7 +186,8 @@ class AllinPay {
|
|
|
156
186
|
console.error('Response status:', error.response.status)
|
|
157
187
|
console.error('Response data:', JSON.stringify(error.response.data))
|
|
158
188
|
}
|
|
159
|
-
|
|
189
|
+
// 直接抛出原始错误,保留错误栈便于排查
|
|
190
|
+
throw error
|
|
160
191
|
}
|
|
161
192
|
}
|
|
162
193
|
|
|
@@ -216,17 +247,7 @@ class AllinPay {
|
|
|
216
247
|
const url = `${this.baseUrl}/pay`
|
|
217
248
|
const result = await this.request(url, params)
|
|
218
249
|
|
|
219
|
-
|
|
220
|
-
if (result.sign && this.verifySign(result)) {
|
|
221
|
-
return result
|
|
222
|
-
} else {
|
|
223
|
-
console.warn(
|
|
224
|
-
'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
|
|
225
|
-
)
|
|
226
|
-
return result
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
throw new Error(result.retmsg || 'Unknown error')
|
|
250
|
+
return this._handleResult(result)
|
|
230
251
|
}
|
|
231
252
|
|
|
232
253
|
async queryOrder(reqsn) {
|
|
@@ -243,17 +264,7 @@ class AllinPay {
|
|
|
243
264
|
const url = `${this.baseUrl}/query`
|
|
244
265
|
const result = await this.request(url, params)
|
|
245
266
|
|
|
246
|
-
|
|
247
|
-
if (result.sign && this.verifySign(result)) {
|
|
248
|
-
return result
|
|
249
|
-
} else {
|
|
250
|
-
console.warn(
|
|
251
|
-
'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
|
|
252
|
-
)
|
|
253
|
-
return result
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
throw new Error(result.retmsg || 'Unknown error')
|
|
267
|
+
return this._handleResult(result)
|
|
257
268
|
}
|
|
258
269
|
|
|
259
270
|
async checkOrder(reqsn) {
|
|
@@ -273,17 +284,7 @@ class AllinPay {
|
|
|
273
284
|
: 'https://vsp.allinpay.com/apiweb/tranx/query'
|
|
274
285
|
const result = await this.request(url, params)
|
|
275
286
|
|
|
276
|
-
|
|
277
|
-
if (result.sign && this.verifySign(result)) {
|
|
278
|
-
return result
|
|
279
|
-
} else {
|
|
280
|
-
console.warn(
|
|
281
|
-
'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
|
|
282
|
-
)
|
|
283
|
-
return result
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
throw new Error(result.retmsg || 'Unknown error')
|
|
287
|
+
return this._handleResult(result)
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
async checkBalance(accttype = '01') {
|
|
@@ -304,17 +305,7 @@ class AllinPay {
|
|
|
304
305
|
: 'https://cus.allinpay.com/cusapi/cusacct/querybalance'
|
|
305
306
|
const result = await this.request(url, params)
|
|
306
307
|
|
|
307
|
-
|
|
308
|
-
if (result.sign && this.verifySign(result)) {
|
|
309
|
-
return result
|
|
310
|
-
} else {
|
|
311
|
-
console.warn(
|
|
312
|
-
'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
|
|
313
|
-
)
|
|
314
|
-
return result
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
throw new Error(result.retmsg || 'Unknown error')
|
|
308
|
+
return this._handleResult(result)
|
|
318
309
|
}
|
|
319
310
|
|
|
320
311
|
async refundOrder(orderData) {
|
|
@@ -331,7 +322,6 @@ class AllinPay {
|
|
|
331
322
|
}
|
|
332
323
|
if (remark) params.remark = remark
|
|
333
324
|
if (acct) params.acct = acct
|
|
334
|
-
params.signtype = this.signType
|
|
335
325
|
if (this.notifyUrl) params.notify_url = this.notifyUrl
|
|
336
326
|
|
|
337
327
|
params.sign = this.sign(params)
|
|
@@ -341,17 +331,7 @@ class AllinPay {
|
|
|
341
331
|
: 'https://vsp.allinpay.com/apiweb/tranx/refund'
|
|
342
332
|
const result = await this.request(url, params)
|
|
343
333
|
|
|
344
|
-
|
|
345
|
-
if (result.sign && this.verifySign(result)) {
|
|
346
|
-
return result
|
|
347
|
-
} else {
|
|
348
|
-
console.warn(
|
|
349
|
-
'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
|
|
350
|
-
)
|
|
351
|
-
return result
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
throw new Error(result.retmsg || 'Unknown error')
|
|
334
|
+
return this._handleResult(result)
|
|
355
335
|
}
|
|
356
336
|
|
|
357
337
|
async cancelOrder(reqsn) {
|
|
@@ -368,17 +348,7 @@ class AllinPay {
|
|
|
368
348
|
const url = `${this.baseUrl}/cancel`
|
|
369
349
|
const result = await this.request(url, params)
|
|
370
350
|
|
|
371
|
-
|
|
372
|
-
if (result.sign && this.verifySign(result)) {
|
|
373
|
-
return result
|
|
374
|
-
} else {
|
|
375
|
-
console.warn(
|
|
376
|
-
'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
|
|
377
|
-
)
|
|
378
|
-
return result
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
throw new Error(result.retmsg || 'Unknown error')
|
|
351
|
+
return this._handleResult(result)
|
|
382
352
|
}
|
|
383
353
|
|
|
384
354
|
async downloadBill({ date }) {
|
|
@@ -390,7 +360,6 @@ class AllinPay {
|
|
|
390
360
|
filetype: '1',
|
|
391
361
|
signtype: this.signType,
|
|
392
362
|
}
|
|
393
|
-
params.signtype = this.signType
|
|
394
363
|
|
|
395
364
|
params.sign = this.sign(params)
|
|
396
365
|
|
|
@@ -399,17 +368,7 @@ class AllinPay {
|
|
|
399
368
|
: 'https://cus.allinpay.com/cusapi/trxfile/get'
|
|
400
369
|
const result = await this.request(url, params)
|
|
401
370
|
|
|
402
|
-
|
|
403
|
-
if (result.sign && this.verifySign(result)) {
|
|
404
|
-
return result
|
|
405
|
-
} else {
|
|
406
|
-
console.warn(
|
|
407
|
-
'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
|
|
408
|
-
)
|
|
409
|
-
return result
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
throw new Error(result.retmsg || 'Unknown error')
|
|
371
|
+
return this._handleResult(result)
|
|
413
372
|
}
|
|
414
373
|
|
|
415
374
|
generateRandomStr(length = 32) {
|