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 (is_cache && cache.get(cacheTarget)) {
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 { getConfig, Sequelize } = require('q-koa')
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) => excludes.includes(item) || excludes.length === 0)
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 (excludeInclude.includes(model)) return false
130
- if (excludeInclude.includes(i.as)) return false
133
+ if (excludeNameSet.has(model)) return false
134
+ if (excludeNameSet.has(i.as)) return false
131
135
  return (
132
- (excludeInclude.every((m) => {
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
- app.appConfig.cacheModel = Array.from(new Set([...cacheModel, model]))
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 = obj.setting.find((s) => s.id === i.parent_id)
284
- const same = obj.setting.filter((s) => s.code === i.code)
285
- if (parent && parent.code !== config && same.length > 1) {
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
  }
@@ -2362,7 +2362,7 @@ exports.union_checkPay = async (ctx) => {
2362
2362
  }
2363
2363
 
2364
2364
  exports.union_check_balance = async (ctx) => {
2365
- const { app, appName } = getAppByCtx(ctx)
2365
+ const { app } = getAppByCtx(ctx)
2366
2366
 
2367
2367
  const {
2368
2368
  pay_config = 'weixin_pay',
@@ -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
- this.publicKey = this.isTest
43
- ? config.allinpay.publicKey.test
44
- : config.allinpay.publicKey.production
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
- let pem = String(this.publicKey)
109
- if (!pem.includes('-----BEGIN')) {
110
- pem = `-----BEGIN PUBLIC KEY-----\n${pem}\n-----END PUBLIC KEY-----`
111
- } else if (!pem.includes('\n')) {
112
- pem = pem
113
- .replace('-----BEGIN PUBLIC KEY-----', '-----BEGIN PUBLIC KEY-----\n')
114
- .replace('-----END PUBLIC KEY-----', '\n-----END PUBLIC KEY-----')
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
- // RSA 双算法兜底:先 SHA256(新 2048-bit 证书),失败再 SHA1(老 1024-bit 证书)
117
- for (const alg of ['RSA-SHA256', 'RSA-SHA1']) {
118
- try {
119
- const verify = crypto.createVerify(alg)
120
- verify.update(signStr, 'utf8')
121
- if (verify.verify(pem, sign, 'base64')) return true
122
- } catch (e) {}
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
- throw new Error(error.message)
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
- if (result && result.retcode === 'SUCCESS') {
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
- if (result && result.retcode === 'SUCCESS') {
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
- if (result && result.retcode === 'SUCCESS') {
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
- if (result && result.retcode === 'SUCCESS') {
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
- if (result && result.retcode === 'SUCCESS') {
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
- if (result && result.retcode === 'SUCCESS') {
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
- if (result && result.retcode === 'SUCCESS') {
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q-koa",
3
- "version": "13.8.44",
3
+ "version": "13.8.46",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {