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.
- package/core/app.js +1813 -0
- package/core/config.js +124 -0
- package/core/file/plugins/administrator/config.js +17 -0
- package/core/file/plugins/administrator/controller.js +264 -0
- package/core/file/plugins/administrator/model.js +53 -0
- package/core/file/plugins/administrator/validate.js +41 -0
- package/core/file/plugins/alipay/controller.js +68 -0
- package/core/file/plugins/alipay/validate.js +9 -0
- package/core/file/plugins/cache/service.js +16 -0
- package/core/file/plugins/cloudfunction/config.js +4 -0
- package/core/file/plugins/cloudfunction/model.js +15 -0
- package/core/file/plugins/common/controller.js +398 -0
- package/core/file/plugins/common/validate.js +47 -0
- package/core/file/plugins/douyin/config.js +3 -0
- package/core/file/plugins/douyin/controller.js +521 -0
- package/core/file/plugins/douyin/validate.js +40 -0
- package/core/file/plugins/douyin_user/config.js +15 -0
- package/core/file/plugins/douyin_user/model.js +72 -0
- package/core/file/plugins/good_sku/controller.js +80 -0
- package/core/file/plugins/h5_user/config.js +19 -0
- package/core/file/plugins/h5_user/model.js +71 -0
- package/core/file/plugins/lang/config.js +4 -0
- package/core/file/plugins/lang/model.js +15 -0
- package/core/file/plugins/language/config.js +5 -0
- package/core/file/plugins/language/model.js +54 -0
- package/core/file/plugins/log/config.js +25 -0
- package/core/file/plugins/log/controller.js +31 -0
- package/core/file/plugins/log/model.js +51 -0
- package/core/file/plugins/model/config.js +12 -0
- package/core/file/plugins/model/controller.js +69 -0
- package/core/file/plugins/model/model.js +169 -0
- package/core/file/plugins/model/service.js +218 -0
- package/core/file/plugins/model/validate.js +42 -0
- package/core/file/plugins/model_attributes/config.js +12 -0
- package/core/file/plugins/model_attributes/model.js +114 -0
- package/core/file/plugins/mp_user/config.js +19 -0
- package/core/file/plugins/mp_user/model.js +59 -0
- package/core/file/plugins/permission/config.js +15 -0
- package/core/file/plugins/permission/model.js +91 -0
- package/core/file/plugins/role/config.js +27 -0
- package/core/file/plugins/role/controller.js +26 -0
- package/core/file/plugins/role/model.js +58 -0
- package/core/file/plugins/role_permission/config.js +12 -0
- package/core/file/plugins/role_permission/controller.js +27 -0
- package/core/file/plugins/role_permission/model.js +24 -0
- package/core/file/plugins/routes/config.js +17 -0
- package/core/file/plugins/routes/controller.js +153 -0
- package/core/file/plugins/routes/model.js +70 -0
- package/core/file/plugins/routes/service.js +22 -0
- package/core/file/plugins/setting/afterExecute.js +14 -0
- package/core/file/plugins/setting/config.js +14 -0
- package/core/file/plugins/setting/controller.js +50 -0
- package/core/file/plugins/setting/model.js +118 -0
- package/core/file/plugins/setting/validate.js +42 -0
- package/core/file/plugins/system/controller.js +501 -0
- package/core/file/plugins/system/service.js +148 -0
- package/core/file/plugins/system/validate.js +40 -0
- package/core/file/plugins/todolist/config.js +31 -0
- package/core/file/plugins/todolist/model.js +69 -0
- package/core/file/plugins/toutiao/controller.js +201 -0
- package/core/file/plugins/toutiao_user/config.js +15 -0
- package/core/file/plugins/toutiao_user/model.js +66 -0
- package/core/file/plugins/user/afterExecute.js +38 -0
- package/core/file/plugins/user/config.js +9 -0
- package/core/file/plugins/user/controller.js +329 -0
- package/core/file/plugins/user/model.js +96 -0
- package/core/file/plugins/user/test.js +71 -0
- package/core/file/plugins/user/validate.js +44 -0
- package/core/file/plugins/video/config.js +3 -0
- package/core/file/plugins/video/controller.js +15 -0
- package/core/file/plugins/video/validate.js +12 -0
- package/core/file/plugins/weixin/config.js +3 -0
- package/core/file/plugins/weixin/controller.js +994 -0
- package/core/file/plugins/weixin/service.js +105 -0
- package/core/file/plugins/weixin/validate.js +111 -0
- package/core/file/services/aliSms.js +45 -0
- package/core/file/services/alipay.js +123 -0
- package/core/file/services/amap.js +95 -0
- package/core/file/services/card.js +24 -0
- package/core/file/services/config.js +38 -0
- package/core/file/services/douyin.js +151 -0
- package/core/file/services/email.js +45 -0
- package/core/file/services/express.js +37 -0
- package/core/file/services/geo.js +71 -0
- package/core/file/services/qqVideo.js +64 -0
- package/core/file/services/toutiao.js +102 -0
- package/core/file/services/weixin.js +79 -0
- package/core/file/services/weixinArticle.js +53 -0
- package/core/file/services/weixinCrypt.js +34 -0
- package/core/file/services/weixinMP.js +435 -0
- package/core/file/services/weixinPay.js +35 -0
- package/core/file/services/xml.js +33 -0
- package/core/file/task/shop/index.js +589 -0
- package/core/file/task/shop/static/562e45760a44632de6fa7219bab78cce.png +0 -0
- package/core/file/task/shop/static/d7aeaeb6bfd68f71a00a83c0f5548363.png +0 -0
- package/core/file/utils/index.js +61 -0
- package/core/middlewares.js +120 -0
- package/core/restc/.npminstall.done +1 -0
- package/core/restc/LICENSE +21 -0
- package/core/restc/README.md +48 -0
- package/core/restc/faas/index.html +1112 -0
- package/core/restc/faas/index.txt +31 -0
- package/core/restc/faas/install_production.sh +6 -0
- package/core/restc/index.d.ts +7 -0
- package/core/restc/index.js +9 -0
- package/core/restc/lib/express.js +7 -0
- package/core/restc/lib/hapi.js +17 -0
- package/core/restc/lib/hapiLegacy.js +15 -0
- package/core/restc/lib/index.js +46 -0
- package/core/restc/lib/koa.js +9 -0
- package/core/restc/lib/koa2.js +9 -0
- package/core/restc/lib/utils/gateway.js +51 -0
- package/core/restc/package.json +41 -0
- package/core/validator.js +15 -0
- package/index.js +1 -0
- 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
|
+
}
|