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,105 @@
1
+ const { getAppByCtx, getConfig } = require('multiple-quick-koa')
2
+ const { Pay } = require('@sigodenjs/wechatpay')
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+
6
+ exports.refund = async ({
7
+ ctx,
8
+ id,
9
+ prefix = '',
10
+ total_fee,
11
+ refund_fee,
12
+ price,
13
+ ...rest
14
+ }) => {
15
+ const { app, appName } = getAppByCtx(ctx)
16
+ const appConfig = getConfig(app)
17
+ const { mchId, key, appId } = await appConfig.getObject('weixin_pay')
18
+ const { site_host } = await appConfig.getObject('base')
19
+
20
+ const payObj = new Pay({
21
+ appId: appId,
22
+ mchId: mchId,
23
+ key: key, // 微信商户平台API密钥,
24
+ pfx: fs.readFileSync(
25
+ path.resolve(
26
+ __dirname,
27
+ `${process.cwd()}/app/${appName}/plugins/weixin/apiclient_cert.p12`
28
+ )
29
+ ), // 微信商户平台证书
30
+ })
31
+ const orderModel = prefix ? `${prefix}_order` : 'order'
32
+ const out_trade_no = prefix
33
+ ? `${appName}_${prefix}_${id}_MP-WEIXIN`
34
+ : `${appName}_${id}_MP-WEIXIN`
35
+ const orderDetail = await app.model[orderModel].findOne({
36
+ where: {
37
+ id,
38
+ },
39
+ })
40
+ if (!orderDetail) throw new Error('不存在该订单')
41
+ try {
42
+ const { result_code, err_code_des } = await payObj.refund({
43
+ ...rest,
44
+ out_trade_no,
45
+ out_refund_no: id + '',
46
+ total_fee: Math.round((total_fee || refund_fee || price) * 100),
47
+ refund_fee: Math.round((refund_fee || price) * 100),
48
+ notify_url: `https://${
49
+ site_host || 'api.kuashou.com'
50
+ }/${appName}/weixin/refund_notify`,
51
+ })
52
+ if (result_code === 'SUCCESS') {
53
+ return true
54
+ } else {
55
+ throw new Error(err_code_des)
56
+ }
57
+ } catch (e) {
58
+ throw new Error(e)
59
+ }
60
+ }
61
+
62
+ exports.cash = async ({ ctx, id, user_id, number }) => {
63
+ const { app, appName } = getAppByCtx(ctx)
64
+
65
+ const appConfig = getConfig(app)
66
+ const { mchId, key, appId } = await appConfig.getObject('weixin_pay')
67
+
68
+ const { is_dev } = await appConfig.getObject('base')
69
+
70
+ const payObj = new Pay({
71
+ appId: appId,
72
+ mchId: mchId,
73
+ key: key, // 微信商户平台API密钥,
74
+ pfx: fs.readFileSync(
75
+ path.resolve(
76
+ __dirname,
77
+ `${process.cwd()}/app/${appName}/plugins/weixin/apiclient_cert.p12`
78
+ )
79
+ ), // 微信商户平台证书
80
+ })
81
+
82
+ const user = await app.model.user.findOne({
83
+ where: {
84
+ id: user_id,
85
+ },
86
+ include: app.model.mp_user,
87
+ })
88
+
89
+ if (!user) throw new Error('找不到该用户')
90
+ if (!user.mp_user.openid) throw new Error('找不到该用户openid')
91
+
92
+ const amount = Math.abs(number) * 100
93
+
94
+ const result = await payObj.transfers({
95
+ partner_trade_no: `${id}`,
96
+ openid: user.mp_user.openid,
97
+ check_name: 'NO_CHECK',
98
+ amount,
99
+ desc: '提现',
100
+ spbill_create_ip: '192.168.0.1',
101
+ })
102
+
103
+ if (result.err_code_des) throw new Error(result.err_code_des)
104
+ return result
105
+ }
@@ -0,0 +1,111 @@
1
+ const { Validator } = require('multiple-quick-koa');
2
+
3
+ exports.getConfig = async (ctx) => {
4
+ const params = {
5
+ url: {
6
+ type: 'string',
7
+ required: true,
8
+ message: '请输入url',
9
+ },
10
+ };
11
+ await new Validator(params).validate(ctx.request.body);
12
+ };
13
+
14
+ exports.h5_pay = async (ctx) => {
15
+ /**
16
+ * 判断微信
17
+ */
18
+ const userAgent = ctx.userAgent._agent.source;
19
+ const isWeixin = /MicroMessenger/i.test(userAgent);
20
+ if (isWeixin) throw new Error('请用手机浏览器打开');
21
+ };
22
+
23
+ exports.wx_pay = async (ctx) => {
24
+ /**
25
+ * 判断微信
26
+ */
27
+ const userAgent = ctx.userAgent._agent.source;
28
+ const isWeixin = /MicroMessenger/i.test(userAgent);
29
+ if (!isWeixin) throw new Error('请在微信里打开');
30
+ };
31
+
32
+ exports.pay = async (ctx) => {
33
+ const params = {
34
+ body: {
35
+ type: 'string',
36
+ required: true,
37
+ message: '请输入支付标题',
38
+ },
39
+ out_trade_no: {
40
+ type: 'string',
41
+ required: true,
42
+ message: '请输入订单号',
43
+ },
44
+ total_fee: {
45
+ type: 'number',
46
+ required: 1,
47
+ message: '请输入支付金额',
48
+ },
49
+ };
50
+ await new Validator(params).validate(ctx.request.body);
51
+ };
52
+
53
+ exports.mp_login = async (ctx) => {
54
+ const params = {
55
+ code: {
56
+ type: 'string',
57
+ required: true,
58
+ message: '需要code登录',
59
+ },
60
+ };
61
+ await new Validator(params).validate(ctx.request.body);
62
+ };
63
+
64
+
65
+ exports.qr_code = async (ctx) => {
66
+ const params = {
67
+ page: {
68
+ type: 'string',
69
+ required: true,
70
+ message: '请输入页面链接:pages/index/index',
71
+ },
72
+ is_hyaline: {
73
+ type: 'boolean',
74
+ required: false,
75
+ message: 'is_hyaline 是否透明 boolean',
76
+ },
77
+ scene: {
78
+ type: 'object',
79
+ required: false,
80
+ message: 'scene 参数 object',
81
+ },
82
+ };
83
+ await new Validator(params).validate(ctx.request.body);
84
+ };
85
+
86
+
87
+ exports.sendMessage = async (ctx) => {
88
+ const params = {
89
+ template_id: {
90
+ type: 'string',
91
+ required: true,
92
+ message: '请输入模版|qyq3BJ4sMhOiBwJOjR3iTiyk8fX0vmC_R7d2PnHySks',
93
+ },
94
+ openid: {
95
+ type: 'string',
96
+ required: true,
97
+ message: 'openid | oETg_5eggr_QA8bBzcEZ_A31ZRkk',
98
+ },
99
+ data: {
100
+ type: 'object',
101
+ required: true,
102
+ message: 'data | {name1:{value:\'苏晓光\'},number2:{value:\'32323\'},thing5:{value:\'xxx\'}}',
103
+ },
104
+ page: {
105
+ type: 'string',
106
+ required: false,
107
+ message: '请输入页面链接:pages/index/index',
108
+ },
109
+ };
110
+ await new Validator(params).validate(ctx.request.body);
111
+ };
@@ -0,0 +1,45 @@
1
+ const Core = require('@alicloud/pop-core');
2
+ const {
3
+ lodash
4
+ } = require('multiple-quick-koa')
5
+ module.exports = class Sms {
6
+ constructor (config) {
7
+ this.config = {
8
+ accessKeyId: config.accessKeyId, // 开发者账号id
9
+ accessKeySecret: config.accessKeySecret, // 开发者token
10
+ SignName: config.SignName, // 应用id
11
+ param: Array.from({
12
+ length: config.number || 4
13
+ }, () => lodash.random(0, 9)).join(''),
14
+ };
15
+ this.client = new Core({
16
+ accessKeyId: this.config.accessKeyId,
17
+ accessKeySecret: this.config.accessKeySecret,
18
+ endpoint: 'https://dysmsapi.aliyuncs.com',
19
+ apiVersion: '2017-05-25'
20
+ });
21
+ }
22
+
23
+ async send ({
24
+ mobile,
25
+ TemplateCode,
26
+ TemplateParam
27
+ }) {
28
+ // if(!mobile) throw new Error('请输入手机号码')
29
+ const result = await this.client.request('SendSms', {
30
+ PhoneNumbers: mobile,
31
+ SignName: this.config.SignName,
32
+ TemplateCode,
33
+ TemplateParam: JSON.stringify({
34
+ code: this.config.param
35
+ })
36
+ }, {
37
+ method: 'POST'
38
+ })
39
+ return result
40
+ }
41
+
42
+ getCode () {
43
+ return this.config.param
44
+ }
45
+ };
@@ -0,0 +1,123 @@
1
+ const crypto = require('crypto')
2
+ const { lodash } = require('multiple-quick-koa')
3
+ module.exports = class Singleton {
4
+ constructor(config) {
5
+ this.config = {
6
+ service: 'alipay.wap.create.direct.pay.by.user',
7
+ partner: '',
8
+ _input_charset: 'utf-8',
9
+ sign_type: 'MD5',
10
+ key: '',
11
+ out_trade_no: '9890879868657',
12
+ subject: '商品',
13
+ total_fee: '',
14
+ seller_id: '',
15
+ payment_type: '1',
16
+ show_url: 'https://www.kuashou.com',
17
+ notify_url:
18
+ 'http://www.test.com/create_direct_pay_by_user-JAVA-UTF-8/notify_url.jsp',
19
+ return_url: 'http://www.baidu.com',
20
+ ...config,
21
+ }
22
+ /**
23
+ * 单例模式
24
+ */
25
+ if (!Singleton.instance) {
26
+ Singleton.instance = this
27
+ }
28
+ const previous = Singleton.instance.getConfig()
29
+ if (!lodash.isEqual(previous, config)) {
30
+ Singleton.instance = this
31
+ }
32
+ return Singleton.instance
33
+ }
34
+
35
+ async create() {
36
+ const paraFilterResult = this.paraFilter(this.config)
37
+ const paraSort = this.argSort(paraFilterResult)
38
+ const prestr = this.createLinkstring(paraSort)
39
+ const sign = this.md5Sign(prestr, this.config.key)
40
+ const { key, ...rest } = this.config
41
+ const postData = {
42
+ ...rest,
43
+ sign,
44
+ }
45
+
46
+ const url = require('url').format({
47
+ protocol: 'https',
48
+ hostname: 'mapi.alipay.com',
49
+ pathname: '/gateway.do',
50
+ query: {
51
+ ...postData,
52
+ },
53
+ })
54
+
55
+ return url
56
+ }
57
+
58
+ // async getConfig() {
59
+ // const config = (await system.findOne({
60
+ // raw: true,
61
+ // where: {
62
+ // code: 'alipay',
63
+ // },
64
+ // })).value;
65
+
66
+ // const partner = getSetting(config)('partner');
67
+ // const alipay_key = getSetting(config)('alipay_key');
68
+ // const total_fee = getSetting(config)('total_fee');
69
+ // this.config = {
70
+ // ...this.config,
71
+ // partner,
72
+ // key: alipay_key,
73
+ // total_fee,
74
+ // seller_id: partner,
75
+ // };
76
+ // }
77
+
78
+ paraFilter(para) {
79
+ const para_filter = new Object()
80
+ for (const key in para) {
81
+ if (
82
+ key == 'sign' ||
83
+ key == 'sign_type' ||
84
+ para[key] == '' ||
85
+ key == 'key'
86
+ ) {
87
+ continue
88
+ } else {
89
+ para_filter[key] = para[key]
90
+ }
91
+ }
92
+
93
+ return para_filter
94
+ }
95
+
96
+ argSort(para) {
97
+ const result = new Object()
98
+ const keys = Object.keys(para).sort()
99
+ for (let i = 0; i < keys.length; i++) {
100
+ const k = keys[i]
101
+ result[k] = para[k]
102
+ }
103
+ return result
104
+ }
105
+
106
+ createLinkstring(para) {
107
+ let ls = ''
108
+ for (const k in para) {
109
+ ls = `${ls + k}=${para[k]}&`
110
+ }
111
+ ls = ls.substring(0, ls.length - 1)
112
+ return ls
113
+ }
114
+
115
+ md5Sign(prestr, key) {
116
+ prestr += key
117
+ return crypto.createHash('md5').update(prestr, 'utf8').digest('hex') // crypto.createHash('md5').update(prestr).digest("hex");
118
+ }
119
+
120
+ getConfig() {
121
+ return this.config
122
+ }
123
+ }
@@ -0,0 +1,95 @@
1
+ const axios = require('axios')
2
+ const { lodash } = require('multiple-quick-koa')
3
+
4
+ const queryFn = (obj) => {
5
+ const query = Object.keys(obj)
6
+ .filter((item) => {
7
+ return obj[item]
8
+ })
9
+ .reduce((a, b) => `${a}&${b}=${obj[b]}`, '')
10
+ return query.replace('&', '?')
11
+ }
12
+ module.exports = class Singleton {
13
+ constructor (key) {
14
+ this.key = key
15
+
16
+ /**
17
+ * 单例模式
18
+ */
19
+ if (!Singleton.instance) {
20
+ Singleton.instance = this
21
+ }
22
+ const previous = Singleton.instance.getConfig()
23
+ if (!lodash.isEqual(previous, key)) {
24
+ Singleton.instance = this
25
+ }
26
+ return Singleton.instance
27
+ }
28
+
29
+ async geo ({ address }) {
30
+ if (!this.key) throw new Error('没有key')
31
+
32
+ const obj = {
33
+ key: this.key,
34
+ address,
35
+ }
36
+
37
+ const url = encodeURI(
38
+ `https://restapi.amap.com/v3/geocode/geo${queryFn(obj)}`
39
+ )
40
+
41
+ const data = await axios.get(url).then((res) => res.data)
42
+ if (data.status !== '1') throw new Error(data.info)
43
+ return data.geocodes
44
+ }
45
+
46
+ async regeo ({ longitude, latitude }) {
47
+ if (!this.key) throw new Error('没有key')
48
+
49
+ const obj = {
50
+ key: this.key,
51
+ location: `${longitude},${latitude}`,
52
+ }
53
+
54
+ const url = encodeURI(
55
+ `https://restapi.amap.com/v3/geocode/regeo${queryFn(obj)}`
56
+ )
57
+
58
+ const data = await axios.get(url).then((res) => res.data)
59
+
60
+ return data
61
+ }
62
+
63
+ async getDistrict ({
64
+ keywords,
65
+ subdistrict,
66
+ page,
67
+ offset,
68
+ extensions,
69
+ filter,
70
+ }) {
71
+ if (!this.key) throw new Error('没有key')
72
+
73
+ const obj = {
74
+ key: this.key,
75
+ keywords,
76
+ subdistrict,
77
+ page,
78
+ offset,
79
+ extensions,
80
+ filter,
81
+ }
82
+
83
+ const url = encodeURI(
84
+ `https://restapi.amap.com/v3/config/district${queryFn(obj)}`
85
+ )
86
+
87
+ const data = await axios.get(url).then((res) => res.data)
88
+
89
+ return data
90
+ }
91
+
92
+ getConfig () {
93
+ return this.key
94
+ }
95
+ }
@@ -0,0 +1,24 @@
1
+ const axios = require('axios');
2
+
3
+ const qs = require('qs');
4
+
5
+
6
+ module.exports = class Card {
7
+ constructor(config) {
8
+ this.config = {
9
+ app_code: config.app_code,
10
+ };
11
+ this.send_url = 'http://businesscard.sinosecu.com.cn/api/recogliu.do';
12
+ }
13
+
14
+ send(img) {
15
+ return axios.post(this.send_url, qs.stringify({
16
+ img,
17
+ }), {
18
+ headers: {
19
+ Authorization: 'APPCODE 59f7e9da22e84913a5414cb959cef1e4',
20
+ 'Content-Type': 'application/x-www-form-urlencoded',
21
+ },
22
+ }).then(res => res.data);
23
+ }
24
+ };
@@ -0,0 +1,38 @@
1
+ const { getObject } = require('../utils')
2
+
3
+ module.exports = class Config {
4
+ constructor(app) {
5
+ this.app = app
6
+ this.configList = []
7
+ }
8
+
9
+ async init() {
10
+ if (this.app.cache && this.app.cache.get('configList')) {
11
+ this.configList = this.app.cache.get('configList')
12
+ return
13
+ }
14
+ const configList = await this.app.model.setting.findAll({
15
+ where: {
16
+ type: {
17
+ excludes: ['textarea', 'rich-text'],
18
+ },
19
+ },
20
+ })
21
+ this.app.cache && this.app.cache.set('configList', configList)
22
+ this.configList = configList
23
+ }
24
+
25
+ async getObject(type) {
26
+ if (this.app.cache && this.app.cache.get(type))
27
+ return this.app.cache.get(type)
28
+ await this.init()
29
+ const result = getObject(this.configList)(type)
30
+ if (!result) throw new Error('没有这个type')
31
+ this.app.cache && this.app.cache.set(type, result)
32
+ return result
33
+ }
34
+
35
+ update() {
36
+ this.app.cache.set('configList', null)
37
+ }
38
+ }
@@ -0,0 +1,151 @@
1
+ const util = require('util')
2
+ const axios = require('axios')
3
+ const { lodash } = require('multiple-quick-koa')
4
+
5
+ const commonTokenUrl =
6
+ 'https://open.douyin.com/oauth/client_token/?client_key=%s&client_secret=%s&grant_type=client_credential'
7
+ const hotSearchUrl =
8
+ 'https://open.douyin.com/hotsearch/sentences/?access_token=%s'
9
+ const hotSearchTrendingUrl =
10
+ 'https://open.douyin.com/hotsearch/trending/sentences/?access_token=%s&cursor=%s&count=%s'
11
+ const hotVideoUrl =
12
+ 'https://open.douyin.com/hotsearch/videos/?access_token=%s&hot_sentence=%s&dateType=%s'
13
+ const starHostListUrl =
14
+ 'https://open.douyin.com/star/hot_list/?access_token=%s&hot_list_type=%s'
15
+
16
+ const LRU = require('lru-cache')
17
+ const cache = new LRU({
18
+ max: 100,
19
+ maxAge: 1000 * 60 * 60,
20
+ })
21
+
22
+ module.exports = class Singleton {
23
+ constructor(config) {
24
+ this.config = {
25
+ ...config,
26
+ }
27
+
28
+ /**
29
+ * 单例模式
30
+ */
31
+ if (!Singleton.instance) {
32
+ Singleton.instance = this
33
+ }
34
+ const previous = Singleton.instance.getConfig()
35
+ if (!lodash.isEqual(previous, config)) {
36
+ Singleton.instance = this
37
+ }
38
+ return Singleton.instance
39
+ }
40
+
41
+ init() {
42
+ if (!this.config.client_key || !this.config.client_secret) {
43
+ throw new Error('没有配置client_key或client_secret')
44
+ }
45
+ }
46
+
47
+ async getAccessToken() {
48
+ const { client_key, client_secret } = this.config
49
+
50
+ if (cache.get(client_key)) {
51
+ return cache.get(client_key)
52
+ }
53
+ const url = util.format(commonTokenUrl, client_key, client_secret)
54
+ const tokenResult = await axios.get(url).then((res) => res.data)
55
+ if (tokenResult.message !== 'success') {
56
+ throw new Error(tokenResult.data.description)
57
+ }
58
+ cache.set(client_key, tokenResult.data.access_token)
59
+ return tokenResult.data.access_token
60
+ }
61
+
62
+ async getHotSearch() {
63
+ const access_token = await this.getAccessToken()
64
+ try {
65
+ const hotsearchResult = await axios
66
+ .get(util.format(hotSearchUrl, access_token))
67
+ .then((res) => res.data)
68
+ if (hotsearchResult.data.error_code) {
69
+ if (hotsearchResult.data.error_code === 2190008) {
70
+ cache.reset()
71
+ return await this.getHotSearch()
72
+ }
73
+ throw new Error(hotsearchResult.data.description)
74
+ }
75
+
76
+ return hotsearchResult.data
77
+ } catch (e) {
78
+ console.log(e)
79
+ throw new Error('请求抖音失败')
80
+ }
81
+ }
82
+
83
+ async getHotSearchTrending(offset, limit) {
84
+ const access_token = await this.getAccessToken()
85
+ offset = offset || 0
86
+ limit = limit || 20
87
+ try {
88
+ const hotSearchTrendingResult = await axios
89
+ .get(util.format(hotSearchTrendingUrl, access_token, offset, limit))
90
+ .then((res) => res.data)
91
+ if (hotSearchTrendingResult.data.error_code) {
92
+ if (hotSearchTrendingResult.data.error_code === 2190008) {
93
+ cache.reset()
94
+ return await this.getHotSearchTrending(offset, limit)
95
+ }
96
+ throw new Error(hotSearchTrendingResult.data.description)
97
+ }
98
+
99
+ return hotSearchTrendingResult.data
100
+ } catch (e) {
101
+ console.log(e)
102
+ throw new Error('请求抖音失败')
103
+ }
104
+ }
105
+
106
+ async getHotVideo(word, day = 7) {
107
+ if (!word) throw new Error('没有热点词')
108
+ const access_token = await this.getAccessToken()
109
+ const url = util.format(hotVideoUrl, access_token, encodeURI(word), day)
110
+ try {
111
+ const hotVideoResult = await axios.get(url).then((res) => res.data)
112
+
113
+ if (hotVideoResult.data.error_code) {
114
+ if (hotVideoResult.data.error_code === 2190008) {
115
+ cache.reset()
116
+ return await this.getHotVideo(word, day)
117
+ }
118
+ throw new Error(hotVideoResult.data.description)
119
+ }
120
+ if (!hotVideoResult.data.list) throw new Error(`${word}不是热词`)
121
+ return hotVideoResult.data
122
+ } catch (e) {
123
+ console.log(e)
124
+ throw new Error('请求抖音失败')
125
+ }
126
+ }
127
+
128
+ async getStarHot(hot_list_type) {
129
+ const access_token = await this.getAccessToken()
130
+ const url = util.format(starHostListUrl, access_token, hot_list_type)
131
+ try {
132
+ const result = await axios.get(url).then((res) => res.data)
133
+
134
+ if (result.data.error_code) {
135
+ if (result.data.error_code === 2190008) {
136
+ cache.reset()
137
+ return await this.getStarHot(hot_list_type)
138
+ }
139
+ throw new Error(result.data.description)
140
+ }
141
+ return result.data
142
+ } catch (e) {
143
+ console.log(e)
144
+ throw new Error('请求抖音失败')
145
+ }
146
+ }
147
+
148
+ getConfig() {
149
+ return this.config
150
+ }
151
+ }