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
@@ -0,0 +1,994 @@
1
+ const { lodash, getAppByCtx, getConfig } = require('multiple-quick-koa')
2
+
3
+ const path = require('path')
4
+ const axios = require('axios')
5
+ const OAuth = require('wechat-oauth')
6
+ const WXPay = require('weixin-pay')
7
+ const WeixinPay = require('../../services/weixinPay')
8
+ const WeixinMp = require('../../services/weixinMP')
9
+ const Weixin = require('../../services/weixin')
10
+ const { jsonToXml } = require('../../services/xml')
11
+ const WXBizDataCrypt = require('../../services/weixinCrypt')
12
+ const weixinArticle = require('../../services/weixinArticle')
13
+ const fxp = require('fast-xml-parser')
14
+ const crypto = require('crypto')
15
+ const OSS = require('ali-oss')
16
+
17
+ exports.getConfig = async (ctx) => {
18
+ const { app } = getAppByCtx(ctx)
19
+
20
+ const { url } = ctx.request.body
21
+
22
+ const appConfig = getConfig(app)
23
+ const { app_id, app_secrect } = await appConfig.getObject('weixin_h5')
24
+ const weixin = new Weixin({
25
+ appid: app_id,
26
+ secrect: app_secrect,
27
+ })
28
+
29
+ weixin.init()
30
+ const result = await weixin.sign(url)
31
+ ctx.SUCCESS({
32
+ ...result,
33
+ debugFlag: false,
34
+ appId: app_id,
35
+ jsApiList: [
36
+ 'updateAppMessageShareData',
37
+ 'updateTimelineShareData',
38
+ 'getLocation',
39
+ 'scanQRCode',
40
+ ], // 必填,需要使用的JS接口列表
41
+ })
42
+ }
43
+
44
+ exports.mp_getPhone = async (ctx) => {
45
+ const { app } = getAppByCtx(ctx)
46
+
47
+ const { code, iv, encryptedData, config = 'weixin_mp' } = ctx.request.body
48
+
49
+ const appConfig = getConfig(app)
50
+ const { app_id, app_secrect } = await appConfig.getObject(config)
51
+
52
+ const weixinLoginUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${app_id}&secret=${app_secrect}&js_code=${code}&grant_type=authorization_code`
53
+
54
+ const weixinResult = await axios.get(weixinLoginUrl).then((res) => res.data)
55
+
56
+ const { session_key, openid } = weixinResult
57
+
58
+ if (!openid) {
59
+ throw new Error('微信获取手机失败,请重试')
60
+ }
61
+
62
+ const pc = new WXBizDataCrypt(app_id, session_key)
63
+
64
+ const { phoneNumber } = pc.decryptData(encryptedData, iv)
65
+
66
+ const exist = await app.model.user.findOne({
67
+ where: {
68
+ mobile: phoneNumber,
69
+ },
70
+ include: [
71
+ {
72
+ model: app.model.mp_user,
73
+ },
74
+ ],
75
+ })
76
+
77
+ const mobileExist =
78
+ exist &&
79
+ exist.mp_user &&
80
+ (!exist.mp_user.appid || exist.mp_user.appid === app_id)
81
+
82
+ if (mobileExist) {
83
+ throw new Error('系统已存在该手机号')
84
+ }
85
+
86
+ const userResult = await app.model.mp_user.findOne({
87
+ where: {
88
+ openid,
89
+ },
90
+ })
91
+
92
+ if (!userResult || !userResult.user_id)
93
+ throw new Error('微信获取手机失败,请重试')
94
+
95
+ await app.model.user.upsert({
96
+ id: userResult.user_id,
97
+ mobile: phoneNumber,
98
+ })
99
+
100
+ const result = await app.model.user.findOne({
101
+ where: {
102
+ id: userResult.user_id,
103
+ },
104
+ include: [
105
+ {
106
+ model: app.model.h5_user,
107
+ },
108
+ {
109
+ model: app.model.mp_user,
110
+ },
111
+ {
112
+ model: app.model.toutiao_user,
113
+ },
114
+ ...app.include.user,
115
+ ].filter((item) => item.model),
116
+ })
117
+
118
+ const tokenResult = {
119
+ id: result.id,
120
+ name: result.name,
121
+ mobile: result.mobile,
122
+ h5_user: result.h5_user
123
+ ? {
124
+ openid: result.h5_user && result.h5_user.openid,
125
+ }
126
+ : null,
127
+ mp_user: result.mp_user
128
+ ? {
129
+ openid: result.mp_user && result.mp_user.openid,
130
+ }
131
+ : null,
132
+ toutiao_user: result.toutiao_user
133
+ ? {
134
+ openid: result.toutiao_user && result.toutiao_user.openid,
135
+ }
136
+ : null,
137
+ }
138
+
139
+ const token = await app.sign({
140
+ user: tokenResult,
141
+ })
142
+
143
+ return ctx.SUCCESS({
144
+ token,
145
+ user: result,
146
+ })
147
+ }
148
+
149
+ exports.getPhone = async (ctx) => {
150
+ const { app } = getAppByCtx(ctx)
151
+
152
+ const { code, iv, encryptedData } = ctx.request.body
153
+
154
+ const appConfig = getConfig(app)
155
+ const { app_id, app_secrect } = await appConfig.getObject('weixin_mp')
156
+
157
+ const weixinLoginUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${app_id}&secret=${app_secrect}&js_code=${code}&grant_type=authorization_code`
158
+
159
+ const weixinResult = await axios.get(weixinLoginUrl).then((res) => res.data)
160
+
161
+ const { session_key, openid } = weixinResult
162
+
163
+ if (!openid) {
164
+ throw new Error('微信获取手机失败,请重试')
165
+ }
166
+
167
+ const pc = new WXBizDataCrypt(app_id, session_key)
168
+
169
+ const { phoneNumber } = pc.decryptData(encryptedData, iv)
170
+
171
+ return ctx.SUCCESS({ phoneNumber })
172
+ }
173
+
174
+ /**
175
+ * 微信小程序登录
176
+ */
177
+ exports.mp_login = async (ctx) => {
178
+ const { app, appName } = getAppByCtx(ctx)
179
+ const { code, config = 'weixin_mp', ...rest } = ctx.request.body
180
+
181
+ const appConfig = getConfig(app)
182
+ const { app_id, app_secrect } = await appConfig.getObject(config)
183
+
184
+ const weixinLoginUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${app_id}&secret=${app_secrect}&js_code=${code}&grant_type=authorization_code`
185
+
186
+ const res = await axios
187
+ .get(weixinLoginUrl)
188
+ .then((weixinRes) => weixinRes.data)
189
+
190
+ if (!res) throw new Error('微信登录失败')
191
+ const { openid, unionid } = res
192
+ if (!openid) {
193
+ const msg = `找不到openid, errcode:${res.errcode} app_id:${app_id} app_secrect:${app_secrect}`
194
+
195
+ throw new Error('微信登录失败')
196
+ }
197
+
198
+ const post = lodash.pickBy(
199
+ {
200
+ nick_name: lodash.get(rest, 'nickName', null),
201
+ avatar: lodash.get(rest, 'avatarUrl', null),
202
+ openid,
203
+ unionid: lodash.get(res, 'unionid', null),
204
+ appid: app_id,
205
+ user_id: lodash.get(ctx.request, `${appName}-user.id`, null),
206
+ },
207
+ (item) => item !== null
208
+ )
209
+
210
+ /**
211
+ * 查询是否已经有openid
212
+ *
213
+ * 如果已经有openid
214
+ * 更新这条记录
215
+ * 更新token并返回这条记录
216
+ *
217
+ * 如果没有openid
218
+ * 1.如果token里有uid
219
+ * 将uid作为user_id 添加一条微信记录
220
+ *
221
+ * 2.如果token里没有uid
222
+ * user新增一条匿名记录
223
+ * 获得新匿名记录的uid
224
+ * 将uid作为user_id 添加一条微信记录
225
+ *
226
+ * 更新token并返回这条匿名记录
227
+ */
228
+
229
+ const openIdUser = await app.model.mp_user.findOne({
230
+ where: {
231
+ openid: post.openid,
232
+ },
233
+ })
234
+
235
+ let uid
236
+ if (openIdUser) {
237
+ await app.model.mp_user.update(post, {
238
+ where: {
239
+ openid: post.openid,
240
+ },
241
+ })
242
+ uid = openIdUser.user_id
243
+ } else {
244
+ if (post.user_id) {
245
+ uid = post.user_id
246
+ await app.model.mp_user.create({
247
+ ...post,
248
+ user_id: uid,
249
+ })
250
+ } else {
251
+ try {
252
+ await app.sequelize.transaction(async (transaction) => {
253
+ const user = await app.model.user.create(
254
+ {},
255
+ {
256
+ transaction,
257
+ }
258
+ )
259
+ uid = user.id
260
+ await app.model.mp_user.create(
261
+ {
262
+ ...post,
263
+ appid: app_id,
264
+ user_id: uid,
265
+ },
266
+ {
267
+ transaction,
268
+ }
269
+ )
270
+ })
271
+ } catch (e) {
272
+ console.log('mp_user.create出错了', { ...post })
273
+ throw new Error(e)
274
+ }
275
+ }
276
+ }
277
+
278
+ const result = await app.model.user.findOne({
279
+ where: {
280
+ id: uid,
281
+ },
282
+ include: [
283
+ {
284
+ model: app.model.h5_user,
285
+ },
286
+ {
287
+ model: app.model.mp_user,
288
+ },
289
+ {
290
+ model: app.model.toutiao_user,
291
+ },
292
+ ...app.include.user,
293
+ ].filter((item) => item.model),
294
+ })
295
+
296
+ if (!result.id) throw new Error('登录失败')
297
+
298
+ const tokenResult = {
299
+ id: result.id,
300
+ name: result.name,
301
+ mobile: result.mobile,
302
+ h5_user: result.h5_user
303
+ ? {
304
+ openid: result.h5_user && result.h5_user.openid,
305
+ }
306
+ : null,
307
+ mp_user: result.mp_user
308
+ ? {
309
+ openid: result.mp_user && result.mp_user.openid,
310
+ }
311
+ : null,
312
+ toutiao_user: result.toutiao_user
313
+ ? {
314
+ openid: result.toutiao_user && result.toutiao_user.openid,
315
+ }
316
+ : null,
317
+ }
318
+
319
+ const token = await app.sign({
320
+ user: tokenResult,
321
+ })
322
+
323
+ return ctx.SUCCESS({
324
+ token,
325
+ user: result,
326
+ })
327
+ }
328
+
329
+ /**
330
+ * 微信公众号H5登录
331
+ */
332
+ exports.h5_login = async (ctx) => {
333
+ const { app, appName } = getAppByCtx(ctx)
334
+ const { return_uri } = ctx.query
335
+ const scope = 'snsapi_userinfo'
336
+ // const scope = 'snsapi_base'
337
+ const h5_login_callback_url =
338
+ scope === 'snsapi_base' ? 'h5_login_callback' : 'h5_login_info_callback'
339
+ /**
340
+ * upyun要开启query
341
+ */
342
+ const appConfig = getConfig(app)
343
+ const { app_id, host_link } = await appConfig.getObject('weixin_h5')
344
+
345
+ console.log(
346
+ 'user_id========================',
347
+ lodash.get(ctx.request, `${appName}-user.id`, 0)
348
+ )
349
+ const redirect_uri = lodash.get(ctx.request, `${appName}-user.id`, 0)
350
+ ? encodeURIComponent(
351
+ `${host_link}/${appName}/weixin/${h5_login_callback_url}?url=${encodeURIComponent(
352
+ return_uri
353
+ )}&user_id=${ctx.request[`${appName}-user`].id}`
354
+ )
355
+ : encodeURIComponent(
356
+ `${host_link}/${appName}/weixin/${h5_login_callback_url}?url=${encodeURIComponent(
357
+ return_uri
358
+ )}`
359
+ )
360
+
361
+ const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${app_id}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}#wechat_redirect`
362
+
363
+ return ctx.redirect(url)
364
+ }
365
+
366
+ exports.h5_login_callback = async (ctx) => {
367
+ const { app, appName } = getAppByCtx(ctx)
368
+ const { code, url, user_id } = ctx.query
369
+
370
+ const appConfig = getConfig(app)
371
+ const { app_id, app_secrect } = await appConfig.getObject('weixin_h5')
372
+
373
+ const client = new OAuth(app_id, app_secrect)
374
+
375
+ const getAccessToken = (c) =>
376
+ new Promise((resolve, reject) => {
377
+ client.getAccessToken(c, (err, result) => {
378
+ if (err) {
379
+ console.log('err===', err)
380
+ return reject(err.data)
381
+ }
382
+ return resolve(result.data)
383
+ })
384
+ })
385
+ const data = await getAccessToken(`${code}`)
386
+
387
+ const post = lodash.pickBy(
388
+ {
389
+ nick_name: lodash.get(data, 'nickname', null),
390
+ avatar: lodash.get(data, 'headimgurl', null),
391
+ openid: data.openid,
392
+ appid: app_id,
393
+ user_id: user_id ? Number(user_id) : null,
394
+ },
395
+ (item) => item !== null
396
+ )
397
+ console.log('post', post)
398
+ /**
399
+ * 查询是否已经有openid
400
+ *
401
+ * 如果已经有openid
402
+ * 更新这条记录
403
+ * 更新token并返回这条记录
404
+ *
405
+ * 如果没有openid
406
+ * 1.如果token里有uid
407
+ * 将uid作为user_id 添加一条微信记录
408
+ *
409
+ * 2.如果token里没有uid
410
+ * user新增一条匿名记录
411
+ * 获得新匿名记录的uid
412
+ * 将uid作为user_id 添加一条微信记录
413
+ * 更新token并返回这条匿名记录
414
+ */
415
+
416
+ const openIdUser = await app.model.h5_user.findOne({
417
+ where: {
418
+ openid: post.openid,
419
+ },
420
+ })
421
+ let uid
422
+ if (openIdUser) {
423
+ await app.model.h5_user.update(post, {
424
+ where: {
425
+ openid: post.openid,
426
+ },
427
+ })
428
+ uid = openIdUser.user_id
429
+ } else {
430
+ if (post.user_id) {
431
+ uid = post.user_id
432
+ } else {
433
+ // 新增空记录
434
+ const user = await app.model.user.create()
435
+ uid = user.id
436
+ }
437
+
438
+ await app.model.h5_user.create({
439
+ ...post,
440
+ user_id: uid,
441
+ })
442
+ }
443
+
444
+ const result = await app.model.user.findOne({
445
+ where: {
446
+ id: uid,
447
+ },
448
+ include: [
449
+ {
450
+ model: app.model.h5_user,
451
+ },
452
+ {
453
+ model: app.model.mp_user,
454
+ },
455
+ {
456
+ model: app.model.toutiao_user,
457
+ },
458
+ ...app.include.user,
459
+ ].filter((item) => item.model),
460
+ })
461
+
462
+ const tokenResult = {
463
+ id: result.id,
464
+ name: result.name,
465
+ mobile: result.mobile,
466
+ h5_user: result.h5_user
467
+ ? {
468
+ openid: result.h5_user && result.h5_user.openid,
469
+ }
470
+ : null,
471
+ mp_user: result.mp_user
472
+ ? {
473
+ openid: result.mp_user && result.mp_user.openid,
474
+ }
475
+ : null,
476
+ toutiao_user: result.toutiao_user
477
+ ? {
478
+ openid: result.toutiao_user && result.toutiao_user.openid,
479
+ }
480
+ : null,
481
+ }
482
+
483
+ const token = await app.sign({
484
+ user: tokenResult,
485
+ })
486
+
487
+ ctx.cookies.set(`${appName}-token`, token, {
488
+ httpOnly: false,
489
+ })
490
+
491
+ return ctx.redirect(url)
492
+ }
493
+
494
+ exports.h5_login_info_callback = async (ctx) => {
495
+ const { app, appName } = getAppByCtx(ctx)
496
+ const { code, url, user_id } = ctx.query
497
+
498
+ const appConfig = getConfig(app)
499
+ const { app_id, app_secrect } = await appConfig.getObject('weixin_h5')
500
+
501
+ const client = new OAuth(app_id, app_secrect)
502
+
503
+ const getUserByCode = (c) =>
504
+ new Promise((resolve, reject) => {
505
+ client.getUserByCode(c, (err, result) => {
506
+ if (err) {
507
+ console.log('err===', err)
508
+ return reject(err.data)
509
+ }
510
+ return resolve(result)
511
+ })
512
+ })
513
+ const data = await getUserByCode(`${code}`)
514
+ console.log('========', data)
515
+
516
+ const post = lodash.pickBy(
517
+ {
518
+ nick_name: lodash.get(data, 'nickname', null),
519
+ avatar: lodash.get(data, 'headimgurl', null),
520
+ openid: data.openid,
521
+ appid: app_id,
522
+ user_id: user_id ? Number(user_id) : null,
523
+ },
524
+ (item) => item !== null
525
+ )
526
+ console.log('post', post)
527
+ /**
528
+ * 查询是否已经有openid
529
+ *
530
+ * 如果已经有openid
531
+ * 更新这条记录
532
+ * 更新token并返回这条记录
533
+ *
534
+ * 如果没有openid
535
+ * 1.如果token里有uid
536
+ * 将uid作为user_id 添加一条微信记录
537
+ *
538
+ * 2.如果token里没有uid
539
+ * user新增一条匿名记录
540
+ * 获得新匿名记录的uid
541
+ * 将uid作为user_id 添加一条微信记录
542
+ * 更新token并返回这条匿名记录
543
+ */
544
+
545
+ const openIdUser = await app.model.h5_user.findOne({
546
+ where: {
547
+ openid: post.openid,
548
+ },
549
+ })
550
+ let uid
551
+ if (openIdUser) {
552
+ await app.model.h5_user.update(post, {
553
+ where: {
554
+ openid: post.openid,
555
+ },
556
+ })
557
+ uid = openIdUser.user_id
558
+ } else {
559
+ if (post.user_id) {
560
+ uid = post.user_id
561
+ } else {
562
+ // 新增空记录
563
+ const user = await app.model.user.create({
564
+ nick_name: post.nick_name,
565
+ avatar: post.avatar,
566
+ })
567
+ uid = user.id
568
+ }
569
+
570
+ await app.model.h5_user.create({
571
+ ...post,
572
+ user_id: uid,
573
+ })
574
+ }
575
+ const result = await app.model.user.findOne({
576
+ where: {
577
+ id: uid,
578
+ },
579
+ include: [
580
+ {
581
+ model: app.model.h5_user,
582
+ },
583
+ {
584
+ model: app.model.mp_user,
585
+ },
586
+ {
587
+ model: app.model.toutiao_user,
588
+ },
589
+ ...app.include.user,
590
+ ].filter((item) => item.model),
591
+ })
592
+
593
+ const tokenResult = {
594
+ id: result.id,
595
+ name: result.name,
596
+ mobile: result.mobile,
597
+ h5_user: result.h5_user
598
+ ? {
599
+ openid: result.h5_user && result.h5_user.openid,
600
+ }
601
+ : null,
602
+ mp_user: result.mp_user
603
+ ? {
604
+ openid: result.mp_user && result.mp_user.openid,
605
+ }
606
+ : null,
607
+ toutiao_user: result.toutiao_user
608
+ ? {
609
+ openid: result.toutiao_user && result.toutiao_user.openid,
610
+ }
611
+ : null,
612
+ }
613
+
614
+ const token = await app.sign({
615
+ user: tokenResult,
616
+ })
617
+
618
+ ctx.cookies.set(`${appName}-token`, token, {
619
+ httpOnly: false,
620
+ })
621
+
622
+ return ctx.redirect(url)
623
+ }
624
+
625
+ exports.h5_pay = async (ctx) => {
626
+ const { app, appName } = getAppByCtx(ctx)
627
+ const {
628
+ name,
629
+ order_id,
630
+ price,
631
+ return_url,
632
+ type,
633
+ is_admin = false,
634
+ } = ctx.request.body
635
+
636
+ const appConfig = getConfig(app)
637
+ const { appId, key, mchId } = await appConfig.getObject('weixin_pay')
638
+ const { is_dev, site_host } = await appConfig.getObject('base')
639
+
640
+ const payObj = new WeixinPay({
641
+ appId,
642
+ key,
643
+ mchId,
644
+ })
645
+ const result = await payObj.run({
646
+ body: name,
647
+ out_trade_no: `${appName}_${order_id}_${type}`,
648
+ total_fee: is_admin || is_dev ? 1 : Math.round(price * 100),
649
+ notify_url: `https://${
650
+ site_host || 'api.kuashou.com'
651
+ }/${appName}/weixin/notify`,
652
+ scene_info: `{"h5_info": {"type":"Wap","wap_url": ${
653
+ site_host || 'https://api.kuashou.com'
654
+ },"wap_name": "腾讯充值"}}`,
655
+ })
656
+ if (result.result_code === 'FAIL') throw new Error(result.err_code_des)
657
+
658
+ ctx.SUCCESS(
659
+ return_url
660
+ ? `${result.mweb_url}&redirect_url=${encodeURIComponent(return_url)}`
661
+ : result.mweb_url
662
+ )
663
+ }
664
+
665
+ exports.mp_pay = async (ctx) => {
666
+ const { app, appName } = getAppByCtx(ctx)
667
+ const {
668
+ name,
669
+ order_id,
670
+ price,
671
+ return_url,
672
+ prefix = '',
673
+ type,
674
+ out_trade_no,
675
+ is_admin = false,
676
+ config = 'weixin_mp',
677
+ } = ctx.request.body
678
+
679
+ if (price === 0) throw new Error('价格不能为0')
680
+
681
+ if (type === 'H5-WEIXIN') {
682
+ if (
683
+ !ctx.request[`${appName}-user`] ||
684
+ !ctx.request[`${appName}-user`].h5_user
685
+ ) {
686
+ throw new Error('请先微信登录')
687
+ }
688
+ } else if (
689
+ !ctx.request[`${appName}-user`] ||
690
+ !ctx.request[`${appName}-user`].mp_user
691
+ ) {
692
+ throw new Error('请先小程序登录')
693
+ }
694
+ const appConfig = getConfig(app)
695
+
696
+ const { mchId, key } = await appConfig.getObject('weixin_pay')
697
+ const { is_dev, site_host } = await appConfig.getObject('base')
698
+ // 公众号支付用 服务号 appid,其他用小程序 appid
699
+ const app_id =
700
+ type === 'H5-WEIXIN'
701
+ ? (await appConfig.getObject('weixin_h5')).app_id
702
+ : (await appConfig.getObject(config)).app_id
703
+
704
+ const wxpay = WXPay({
705
+ appid: app_id,
706
+ mch_id: mchId,
707
+ partner_key: key, // 微信商户平台API密钥
708
+ })
709
+
710
+ const pay = (options) =>
711
+ new Promise((resolve, reject) => {
712
+ wxpay.getBrandWCPayRequestParams(options, (err, result) => {
713
+ if (err) return reject(err)
714
+ return resolve(result)
715
+ })
716
+ })
717
+
718
+ const userDetail =
719
+ type === 'H5-WEIXIN'
720
+ ? ctx.request[`${appName}-user`].h5_user
721
+ : ctx.request[`${appName}-user`].mp_user
722
+ const {
723
+ appId: appid,
724
+ nonceStr: nonce_str,
725
+ package: prepay_id,
726
+ paySign: sign,
727
+ signType,
728
+ timeStamp,
729
+ } = await pay({
730
+ openid: userDetail.openid,
731
+ body: name,
732
+ detail: '公众号支付测试',
733
+ out_trade_no: out_trade_no
734
+ ? out_trade_no
735
+ : prefix
736
+ ? `${appName}_${prefix}-${order_id}_${type}`
737
+ : `${appName}_${order_id}_${type}`,
738
+ total_fee: is_admin || is_dev ? 1 : Math.round(price * 100),
739
+ spbill_create_ip: '192.168.2.210',
740
+ notify_url: `http://${
741
+ site_host || 'api.kuashou.com'
742
+ }/${appName}/weixin/notify`,
743
+ })
744
+
745
+ if (prepay_id.includes('undefined')) {
746
+ const model = type === 'H5-WEIXIN' ? 'h5_user' : 'mp_user'
747
+ const userTarget = await app.model[model].findOne({
748
+ where: {
749
+ openid: userDetail.openid,
750
+ },
751
+ })
752
+ if (app_id !== userTarget.appid)
753
+ throw new Error(
754
+ `appid不符,用户appid${userTarget.appid},付款appid${app_id}`
755
+ )
756
+ throw new Error(`支付失败,请重试`)
757
+ }
758
+ return ctx.SUCCESS({
759
+ debug: false,
760
+ appid,
761
+ timeStamp,
762
+ nonce_str,
763
+ prepay_id,
764
+ sign,
765
+ signType,
766
+ jsApiList: ['chooseWXPay'],
767
+ return_url,
768
+ })
769
+ }
770
+
771
+ exports.notify = async (ctx) => {
772
+ const { app } = getAppByCtx(ctx)
773
+ const result = ctx.request.xmlBody
774
+ let prefix = ''
775
+ let order_id = result.xml.out_trade_no.split('_')[1]
776
+ const order_price = Number(result.xml.total_fee) / 100
777
+ if (order_id.includes('-')) {
778
+ const arr = order_id.split('-')
779
+ order_id = Number(arr[1])
780
+ ;[prefix] = arr
781
+ } else {
782
+ order_id = Number(order_id)
783
+ }
784
+ const model = prefix ? `${prefix}_order` : 'order'
785
+ let transactionid = ''
786
+ try {
787
+ transactionid = result.xml.transaction_id
788
+ } catch (e) {}
789
+
790
+ console.log(order_id, '微信支付回调-----', model, order_price, transactionid)
791
+ if (app.service[model] && app.service[model].notify) {
792
+ await app.service[model].notify({
793
+ app,
794
+ order_id,
795
+ order: model,
796
+ order_price,
797
+ transactionid,
798
+ })
799
+ }
800
+
801
+ ctx.status = 200
802
+ ctx.res.setHeader('Content-Type', 'application/xml')
803
+ const returnResult = jsonToXml({
804
+ xml: {
805
+ return_code: 'SUCCESS',
806
+ },
807
+ })
808
+ ctx.res.end(returnResult)
809
+ }
810
+
811
+ exports.qr_code = async (ctx) => {
812
+ const { app } = getAppByCtx(ctx)
813
+
814
+ const { page, is_hyaline, scene, is_oss = false } = ctx.request.body
815
+
816
+ // if (!lodash.get(ctx.request, `${appName}-user.id`, null)) throw new Error('没有登录')
817
+
818
+ const appConfig = getConfig(app)
819
+ const { app_id, app_secrect } = await appConfig.getObject('weixin_mp')
820
+
821
+ const { site_host } = await appConfig.getObject('base')
822
+ const weixinMp = new WeixinMp({
823
+ appid: app_id,
824
+ secrect: app_secrect,
825
+ targetPath: `${path.resolve(__dirname, `${process.cwd()}/public/upload`)}`,
826
+ })
827
+ weixinMp.init()
828
+ if (!is_oss) {
829
+ const result = await weixinMp.createQR({
830
+ page,
831
+ is_hyaline,
832
+ scene,
833
+ })
834
+ const qr_image = `https://${
835
+ site_host || 'api.kuashou.com'
836
+ }/upload/${result}`
837
+
838
+ ctx.SUCCESS(qr_image)
839
+ } else {
840
+ const {
841
+ accessKeyId,
842
+ accessKeySecret,
843
+ bucket,
844
+ region,
845
+ } = await appConfig.getObject('oss')
846
+ if (!(accessKeyId && accessKeySecret && bucket && region)) {
847
+ throw new Error('没有配置oss设置')
848
+ }
849
+ const buffer = await weixinMp.createQRBuffer({
850
+ page,
851
+ is_hyaline,
852
+ scene,
853
+ })
854
+ const client = new OSS({
855
+ accessKeyId,
856
+ accessKeySecret,
857
+ bucket,
858
+ region,
859
+ secure: true,
860
+ })
861
+ const fileName = `${
862
+ String(new Date().getTime()).split('').reverse().join('') + Math.random()
863
+ }.png`
864
+ const result = await client.put(fileName, buffer)
865
+ if (result.res && result.res.status === 200) {
866
+ const url = result.url
867
+ return ctx.SUCCESS(url)
868
+ } else {
869
+ throw new Error(result.res.statusMessage)
870
+ }
871
+ }
872
+ }
873
+
874
+ exports.sendMessage = async (ctx) => {
875
+ const { app } = getAppByCtx(ctx)
876
+
877
+ const { openid, template_id, data, page } = ctx.request.body
878
+
879
+ const appConfig = getConfig(app)
880
+ const { app_id, app_secrect } = await appConfig.getObject('weixin_mp')
881
+
882
+ const weixinMp = new WeixinMp({
883
+ appid: app_id,
884
+ secrect: app_secrect,
885
+ })
886
+ weixinMp.init()
887
+ await weixinMp.sendMessage({
888
+ touser: openid,
889
+ template_id,
890
+ miniprogram_state: 'developer',
891
+ data,
892
+ page,
893
+ })
894
+
895
+ ctx.SUCCESS('发送成功')
896
+ }
897
+
898
+ exports.refund = async (ctx) => {
899
+ const { app, appName } = getAppByCtx(ctx)
900
+ const appConfig = getConfig(app)
901
+ const { id, prefix = '', price } = ctx.request.body
902
+
903
+ await app.service.weixin.refund({
904
+ ctx,
905
+ id,
906
+ prefix: '',
907
+ price,
908
+ })
909
+ ctx.SUCCESS('ok')
910
+ }
911
+
912
+ const decryption = function (data, key, iv) {
913
+ if (!data) {
914
+ return ''
915
+ }
916
+ iv = iv || ''
917
+ var clearEncoding = 'utf8'
918
+ var cipherEncoding = 'base64'
919
+ var cipherChunks = []
920
+ var decipher = crypto.createDecipheriv('aes-256-ecb', key, iv)
921
+ decipher.setAutoPadding(true)
922
+ cipherChunks.push(decipher.update(data, cipherEncoding, clearEncoding))
923
+ cipherChunks.push(decipher.final(clearEncoding))
924
+ return cipherChunks.join('')
925
+ }
926
+
927
+ const getRefundResultJson = async ({ req_info, app_key }) => {
928
+ const result = Buffer.from(req_info, 'base64')
929
+
930
+ const key = await crypto.createHash('md5').update(app_key).digest('hex')
931
+ const iv = Buffer.alloc(0) // 设置偏移量
932
+ let decxml = decryption(result, key, iv)
933
+ const reg = new RegExp('root>', 'g')
934
+ decxml = decxml.replace(reg, 'xml>') // 转化为xml格式
935
+ const xml2json = fxp.parse(decxml) // xml转对象
936
+ return xml2json.xml
937
+ }
938
+
939
+ exports.refund_notify = async (ctx) => {
940
+ const { app } = getAppByCtx(ctx)
941
+ const result = ctx.request.xmlBody
942
+
943
+ const appConfig = getConfig(app)
944
+ const { key } = await appConfig.getObject('weixin_pay')
945
+
946
+ const { req_info } = result.xml
947
+ const refundResult = await getRefundResultJson({
948
+ req_info,
949
+ app_key: key,
950
+ })
951
+
952
+ const { out_refund_no, out_trade_no, total_fee, refund_fee } = refundResult
953
+
954
+ const refund_price = Number(refund_fee) / 100
955
+
956
+ const prefix =
957
+ out_trade_no.split('_').length === 4 ? out_trade_no.split('_')[1] : ''
958
+
959
+ const order_id = Number(out_refund_no)
960
+ const model = prefix ? `${prefix}_order` : 'order'
961
+ console.log(out_refund_no, '微信支付回调-----', model)
962
+ if (app.service[model] && app.service[model].refund_notify) {
963
+ await app.service[model].refund_notify({
964
+ app,
965
+ order_id,
966
+ order: model,
967
+ refund_price,
968
+ })
969
+ }
970
+
971
+ ctx.status = 200
972
+ ctx.res.setHeader('Content-Type', 'application/xml')
973
+ const returnResult = jsonToXml({
974
+ xml: {
975
+ return_code: 'SUCCESS',
976
+ },
977
+ })
978
+ ctx.res.end(returnResult)
979
+ }
980
+
981
+ exports.article = async (ctx) => {
982
+ const { app } = getAppByCtx(ctx)
983
+
984
+ const { url } = ctx.request.body
985
+
986
+ if (!url) throw new Error('没有url')
987
+ if (!url.includes('//mp.weixin.qq.com/s')) throw new Error('非法url')
988
+
989
+ const content = await axios.get(url).then((res) => res.data)
990
+
991
+ const result = await weixinArticle.handleHtml(content)
992
+
993
+ return ctx.SUCCESS(result)
994
+ }