node-easywechat 3.0.0-alpha.2 → 3.0.0-beta.1

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/CHANGELOG.md CHANGED
@@ -1,8 +1,27 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v3.0.0-beta.1 (2022-06-21)
5
+
6
+ - Feat: 新增设置预置参数相关方法
7
+ - Feat: 增加设置日志处理器的方法
8
+ - Feat: 增加统一的对象调用入口
9
+
10
+ - Fix: 调整获取响应内容时的返回类型
11
+ - Fix: 修复微信小程序access_token键名前缀与公众号相同的问题
12
+ - Fix: 优化并导出配置项声明
13
+ - Fix: 修复请求响应结果非字符串类型时转化数据格式报错的问题
14
+ - Fix: 未设置ServerRequest实例就调用getRequest时提示异常
15
+ - Fix: 修复未自动创建客户端实例的问题
16
+
17
+ - Perf: 调整接口请求返回的HttpResponse对象toObject时的数据类型
18
+
4
19
  ## v3.0.0-alpha.2 (2022-06-08)
5
20
 
21
+ - Feat: 新增小程序相关接口
22
+
23
+ - Fix: 修复content-type非json时,错误判断不正确问题
24
+
6
25
  - Perf: 优化消息对象的提示信息
7
26
 
8
27
  - Refactor: 简化构造函数的写法
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  **注:3.x分支针对 EasyWechat 的 6.x版本。**
7
7
 
8
- ~~若您需要 EasyWechat 的 5.x版本,请切换到 [2.x](https://github.com/hpyer/node-easywechat/tree/2.x) 分支。~~
8
+ 若您需要 EasyWechat 的 5.x版本,请切换到 [2.x](https://github.com/hpyer/node-easywechat/tree/2.x) 分支。
9
9
 
10
10
  [EasyWechat](https://github.com/w7corp/easywechat) 是一个由 `安正超` 大神用 PHP 开发的开源的微信非官方 SDK(现由微擎团队团队维护)。其功能强大,使用方便,个人一直很喜欢,所以近日将其在 Node.js 上实现。本人会尽量还原其配置项以及接口的调用方式,但毕竟语言环境不同,具体的实现方式会有些许差别,还请各位开发者见谅。
11
11
 
@@ -20,16 +20,25 @@
20
20
 
21
21
  绝大部分API都可以根据 [EasyWechat 的文档](https://www.easywechat.com/5.x/) 来使用。小部分(如获取请求相关数据、返回响应数据、支付证书等)的操作,由于语言环境的不同,会有不同处理。具体可以查看 [node-easywechat-demo](https://github.com/hpyer/node-easywechat-demo/) 以及下方的[自定义模块说明](#自定义模块模块替换使用方法) 。如果仍有疑问,请提issue,谢谢~
22
22
 
23
+ 从 `3.x` 起 SDK 中不再内置具体业务的接口,仅封装底层基础部分,如认证、授权和 API 客户端。至于为什么不再封装业务接口,可以查看 [EasyWechat 给出说明](https://easywechat.com/6.x/introduction.html#不再封装业务接口)。
24
+
23
25
  ```js
24
- // 按需引入所需要的模块
25
26
  // 公众号
26
- const Application = require('node-easywechat/OfficialAccount/Application');
27
+ const { OfficialAccount } = require('node-easywechat');
28
+ // 实例化应用
29
+ let app = new OfficialAccount({
30
+ // 配置项
31
+ });
27
32
 
33
+ // 小程序
34
+ const { MiniApp } = require('node-easywechat');
28
35
  // 实例化应用
29
- let app = new Application({
30
- // ...
36
+ let app = new MiniApp({
37
+ // 配置项
31
38
  });
32
39
 
40
+ // ----- 以下为通用的 api 调用方法 -----
41
+
33
42
  // 获取 api 调用客户端
34
43
  let client = app.getClient();
35
44
 
@@ -43,26 +52,32 @@ let response = await client.post('/cgi-bin/user/info/updateremark', {
43
52
  ### 配置项示例
44
53
 
45
54
  ``` js
46
- // 公众号配置
55
+ // 通用配置,即所有模块都可以设置
47
56
  {
48
- // 微信公众号的 app key
49
- app_id: '',
50
- // 微信公众号的 app secret
51
- secret: '',
52
- // 微信公众号的 token
53
- token: '',
54
- // EncodingAESKey
55
- aes_key: '',
57
+ // axios 请求参数
58
+ // 详见:https://github.com/axios/axios#request-config
59
+ http: {},
56
60
 
57
61
  // 缓存以文件(默认设置)存储时,需要的配置项
58
62
  file_cache: {
59
63
  path: './cache/', // 文件存储目录(请确保该目录有读写权限)
60
64
  fileMode: 0o666, // 文件权限
61
65
  ext: '.cache' // 文件扩展名
62
- },
63
- // 自定义缓存驱动
64
- // 您需要实现一个继承 EasyWechat.CacheInterface 的缓存驱动类
65
- // 实例化以后利用 “模块替换” 功能赋值给 app.cache
66
+ }
67
+ }
68
+ ```
69
+
70
+ ``` js
71
+ // 公众号配置
72
+ {
73
+ // 公众号的 app key
74
+ app_id: '',
75
+ // 公众号的 app secret
76
+ secret: '',
77
+ // 公众号的 token
78
+ token: '',
79
+ // EncodingAESKey
80
+ aes_key: '',
66
81
 
67
82
  // 网页授权认证
68
83
  oauth: {
@@ -74,11 +89,133 @@ let response = await client.post('/cgi-bin/user/info/updateremark', {
74
89
  }
75
90
  ```
76
91
 
92
+ ``` js
93
+ // 小程序配置
94
+ {
95
+ // 小程序的 app key
96
+ app_id: '',
97
+ // 小程序的 app secret
98
+ secret: '',
99
+ // 小程序的 token
100
+ token: '',
101
+ // EncodingAESKey
102
+ aes_key: ''
103
+ }
104
+ ```
105
+
106
+ ### 自定义模块(模块替换)使用方法
107
+
108
+ #### 日志模块
109
+
110
+ 框架默认不记录任何日志。如果需要,可以通过 `client` 实例的 `setLogger` 方法设置日志处理回调方法。
111
+
112
+ ```js
113
+ // 以公众号为例
114
+ const { OfficialAccount } = require('node-easywechat');
115
+ // 实例化应用
116
+ let app = new OfficialAccount({
117
+ // ...
118
+ });
119
+
120
+ // 获取 api 调用客户端
121
+ let client = app.getClient();
122
+
123
+ /**
124
+ * 该回调方法会接收两种日志信息
125
+ * 1、接口请求调用前的参数:
126
+ * string 固定值,'before'
127
+ * object 请求参数
128
+ * 2、接口请求调用后的参数:
129
+ * string 固定值,'after'
130
+ * object 请求参数
131
+ * number 请求耗时(ms)
132
+ * Response 请求响应对象
133
+ */
134
+ client.setLogger((...args) => {
135
+ console.log(args)
136
+ });
137
+ ```
138
+
139
+ #### 缓存模块
140
+
141
+ 框架默认使用文件缓存读取到的 `access_token` 等值,如需要改用其他缓存方式(如:redis),请实现接口 `CacheInterface` 并通过 `app.setCacher` 方法进行模块替换。
142
+
143
+ ```js
144
+ const { OfficialAccount, CacheInterface } = require('node-easywechat');
145
+ const Redis = require("ioredis");
146
+
147
+ class RedisCacher extends CacheInterface {
148
+ constructor(redis) {
149
+ this.redis = redis;
150
+ }
151
+
152
+ get(id)
153
+ {
154
+ return this.redis.get(id);
155
+ }
156
+
157
+ async has(id)
158
+ {
159
+ return (await this.redis.exists(id)) > 0;
160
+ }
161
+
162
+ async set(id, data = null, lifeTime = 0)
163
+ {
164
+ if (lifeTime > 0) {
165
+ await this.redis.set(id, data, 'EX', lifeTime);
166
+ }
167
+ else {
168
+ await this.redis.set(id, data);
169
+ }
170
+ return true;
171
+ }
172
+
173
+ async delete(id)
174
+ {
175
+ await this.redis.del(id);
176
+ return true;
177
+ }
178
+ }
179
+
180
+ // 实例化应用
181
+ let app = new OfficialAccount({
182
+ // ...
183
+ });
184
+
185
+ // 替换缓存实例
186
+ app.setCacher(new RedisCacher(new Redis));
187
+ ```
188
+
189
+ #### 请求模块
190
+
191
+ 通常,如果你的应用不需要处理服务端回调、支付回调等逻辑的话,是不需要传递请求对象的。
192
+
193
+ ```js
194
+ const { OfficialAccount, ServerRequest } = require('node-easywechat');
195
+
196
+ // 实例化应用
197
+ let app = new OfficialAccount({
198
+ // ...
199
+ });
200
+
201
+ // 根据 IncomingMessage 实例(即 Node.js 原始请求对象)创建 ServerRequest 实例
202
+ //
203
+ // 由于 IncomingMessage 的 body 流的特殊性,某些框架(目前已知:fastify)
204
+ // 可能会自动读取后挂载到上下文中,从而导致 node-easywechat 去尝试读取时报错。
205
+ // 这时可以选择传入第二个参数,即 body 的内容
206
+ //
207
+ // 如果此方法 不能解决你的问题,可以选择继承 ServerRequest,重写相关方法
208
+ let request = ServerRequest.createFromIncomingMessage(req);
209
+
210
+ // 设置请求实例
211
+ app.setRequest(request);
212
+ ```
213
+
77
214
  ### 模块支持情况
78
215
 
79
216
  - [x] 公众号模块
80
217
  - [ ] 微信支付
81
- - [ ] 小程序
218
+ - [x] 小程序
82
219
  - [ ] 开放平台
83
220
  - [ ] 企业微信
84
221
  - [ ] 企业微信开放平台
@@ -8,6 +8,6 @@ declare abstract class AccessTokenInterface {
8
8
  * 转成url参数
9
9
  * @returns
10
10
  */
11
- toQuery(): Promise<object>;
11
+ toQuery(): Promise<Record<string, any>>;
12
12
  }
13
13
  export = AccessTokenInterface;
@@ -3,17 +3,19 @@ import AccessTokenAwareHttpClientInterface from "./Contracts/AccessTokenAwareHtt
3
3
  import AccessTokenInterface from "../Contracts/AccessTokenInterface";
4
4
  import HttpClientInterface from "./Contracts/HttpClientInterface";
5
5
  import HttpClientMethodsMixin from './Mixins/HttpClientMethodsMixin';
6
- import { HttpClientFailureJudgeClosure } from '../../Types/global';
6
+ import { HttpClientFailureJudgeClosure, LogHandler } from '../../Types/global';
7
7
  import HttpClientResponse from './HttpClientResponse';
8
+ import PresetMixin from './Mixins/PresetMixin';
8
9
  declare class AccessTokenAwareClient implements AccessTokenAwareHttpClientInterface, HttpClientInterface {
9
10
  protected client: HttpClientInterface;
10
11
  protected accessToken: AccessTokenInterface;
11
- constructor(client: HttpClientInterface, accessToken: AccessTokenInterface, failureJudge?: HttpClientFailureJudgeClosure, throwError?: boolean);
12
+ constructor(client: HttpClientInterface, accessToken?: AccessTokenInterface, failureJudge?: HttpClientFailureJudgeClosure, throwError?: boolean);
12
13
  withAccessToken(accessToken: AccessTokenInterface): this;
13
14
  getInstance(): AxiosInstance;
14
15
  setInstance(instance: AxiosInstance): this;
16
+ setLogger(logger: LogHandler): this;
15
17
  request(method: Method, url: string, payload?: AxiosRequestConfig<any>): Promise<HttpClientResponse>;
16
18
  }
17
- interface AccessTokenAwareClient extends HttpClientMethodsMixin {
19
+ interface AccessTokenAwareClient extends HttpClientMethodsMixin, PresetMixin {
18
20
  }
19
21
  export = AccessTokenAwareClient;
@@ -15,8 +15,9 @@ const merge_1 = __importDefault(require("merge"));
15
15
  const Utils_1 = require("../Support/Utils");
16
16
  const HttpClient_1 = __importDefault(require("./HttpClient"));
17
17
  const HttpClientMethodsMixin_1 = __importDefault(require("./Mixins/HttpClientMethodsMixin"));
18
+ const PresetMixin_1 = __importDefault(require("./Mixins/PresetMixin"));
18
19
  class AccessTokenAwareClient {
19
- constructor(client, accessToken, failureJudge = null, throwError = true) {
20
+ constructor(client, accessToken = null, failureJudge = null, throwError = true) {
20
21
  this.client = null;
21
22
  this.accessToken = null;
22
23
  this.client = client || HttpClient_1.default.create(null, failureJudge, throwError);
@@ -33,15 +34,20 @@ class AccessTokenAwareClient {
33
34
  this.client.setInstance(instance);
34
35
  return this;
35
36
  }
37
+ setLogger(logger) {
38
+ this.client.setLogger(logger);
39
+ return this;
40
+ }
36
41
  request(method, url, payload = {}) {
37
42
  return __awaiter(this, void 0, void 0, function* () {
38
43
  if (this.accessToken) {
39
44
  payload.params = (0, merge_1.default)(true, payload.params || {}, yield this.accessToken.toQuery());
40
45
  }
41
- return this.client.request(method, (0, Utils_1.ltrim)(url, '\\/+'), payload);
46
+ let options = this.mergeThenResetPrepends(payload, method);
47
+ return this.client.request(method, (0, Utils_1.ltrim)(url, '\\/+'), options);
42
48
  });
43
49
  }
44
50
  }
45
51
  ;
46
- (0, Utils_1.applyMixins)(AccessTokenAwareClient, [HttpClientMethodsMixin_1.default]);
52
+ (0, Utils_1.applyMixins)(AccessTokenAwareClient, [HttpClientMethodsMixin_1.default, PresetMixin_1.default]);
47
53
  module.exports = AccessTokenAwareClient;
@@ -1,4 +1,5 @@
1
1
  import { Method, AxiosRequestConfig, AxiosInstance } from 'axios';
2
+ import { LogHandler } from '../../../Types/global';
2
3
  import HttpClientResponse from '../HttpClientResponse';
3
4
  declare abstract class HttpClientInterface {
4
5
  /**
@@ -10,6 +11,10 @@ declare abstract class HttpClientInterface {
10
11
  * 设置axios实例
11
12
  */
12
13
  setInstance(instance: AxiosInstance): this;
14
+ /**
15
+ * 设置日志方法
16
+ */
17
+ setLogger(logger: LogHandler): this;
13
18
  /**
14
19
  * 发起http请求
15
20
  */
@@ -18,6 +18,10 @@ class HttpClientInterface {
18
18
  * 设置axios实例
19
19
  */
20
20
  setInstance(instance) { return this; }
21
+ /**
22
+ * 设置日志方法
23
+ */
24
+ setLogger(logger) { return this; }
21
25
  /**
22
26
  * 发起http请求
23
27
  */
@@ -1,12 +1,21 @@
1
1
  import { AxiosInstance, AxiosRequestConfig, Method } from 'axios';
2
2
  import HttpClientInterface from './Contracts/HttpClientInterface';
3
3
  import HttpClientResponse from './HttpClientResponse';
4
- import { HttpClientFailureJudgeClosure } from '../../Types/global';
4
+ import { HttpClientFailureJudgeClosure, LogHandler } from '../../Types/global';
5
5
  declare class HttpClient implements HttpClientInterface {
6
6
  protected axios: AxiosInstance;
7
7
  protected failureJudge: HttpClientFailureJudgeClosure;
8
8
  protected throwError: boolean;
9
+ /**
10
+ * 日志处理方法
11
+ */
12
+ protected logger: LogHandler;
9
13
  constructor(axios: AxiosInstance, failureJudge?: HttpClientFailureJudgeClosure, throwError?: boolean);
14
+ /**
15
+ * 设置日志处理方法
16
+ * @param logger
17
+ */
18
+ setLogger(logger: LogHandler): this;
10
19
  request(method: Method, url: string, payload?: AxiosRequestConfig<any>): Promise<HttpClientResponse>;
11
20
  getInstance(): AxiosInstance;
12
21
  setInstance(instance: AxiosInstance): this;
@@ -20,6 +20,18 @@ class HttpClient {
20
20
  this.axios = axios;
21
21
  this.failureJudge = failureJudge;
22
22
  this.throwError = throwError;
23
+ /**
24
+ * 日志处理方法
25
+ */
26
+ this.logger = null;
27
+ }
28
+ /**
29
+ * 设置日志处理方法
30
+ * @param logger
31
+ */
32
+ setLogger(logger) {
33
+ this.logger = logger;
34
+ return this;
23
35
  }
24
36
  request(method, url, payload = {}) {
25
37
  return __awaiter(this, void 0, void 0, function* () {
@@ -66,7 +78,15 @@ class HttpClient {
66
78
  options['json'] = undefined;
67
79
  delete options['json'];
68
80
  }
81
+ let starttime = Date.now();
82
+ if (typeof this.logger === 'function') {
83
+ yield this.logger('before', options);
84
+ }
69
85
  let response = yield this.axios.request(options);
86
+ if (typeof this.logger === 'function') {
87
+ let usedTime = Date.now() - starttime;
88
+ yield this.logger('after', options, usedTime, response);
89
+ }
70
90
  return new HttpClientResponse_1.default(response, this.failureJudge, this.throwError);
71
91
  });
72
92
  }
@@ -1,5 +1,5 @@
1
1
  import { AxiosResponse } from "axios";
2
- import { HttpClientFailureJudgeClosure } from "../../Types/global";
2
+ import { HttpClientFailureJudgeClosure, WeixinResponse } from "../../Types/global";
3
3
  import HttpClientResponseInterface from "./Contracts/HttpClientResponseInterface";
4
4
  declare class HttpClientResponse implements HttpClientResponseInterface {
5
5
  protected response: AxiosResponse;
@@ -30,7 +30,7 @@ declare class HttpClientResponse implements HttpClientResponseInterface {
30
30
  * @param throwError
31
31
  * @returns
32
32
  */
33
- toObject(throwError?: boolean): Promise<Record<string, any>>;
33
+ toObject<T = WeixinResponse>(throwError?: boolean): Promise<T>;
34
34
  /**
35
35
  * 返回json字符串
36
36
  * @param throwError
@@ -73,7 +73,7 @@ declare class HttpClientResponse implements HttpClientResponseInterface {
73
73
  getHeader(key: string): any;
74
74
  getStatusCode(): number;
75
75
  getHeaders(throwError?: boolean): Record<string, any>;
76
- getContent(throwError?: boolean): string;
76
+ getContent(throwError?: boolean): any;
77
77
  cancel(): void;
78
78
  getInfo(type?: string): import("axios").AxiosRequestConfig<any>;
79
79
  offsetExists(key: any): Promise<boolean>;
@@ -73,9 +73,10 @@ class HttpClientResponse {
73
73
  if (!content) {
74
74
  throw new Error('Response body is empty.');
75
75
  }
76
- let contentType = this.getHeader('content-type') || '';
77
- if (contentType && (contentType.indexOf('text/xml') > -1 || contentType.indexOf('application/xml') > -1 || content.indexOf('<xml>') > -1)) {
78
- return (0, Utils_1.parseXml)(content);
76
+ if (typeof content === 'string') {
77
+ if (this.is('xml') && content.indexOf('<xml>') > -1) {
78
+ return (0, Utils_1.parseXml)(content);
79
+ }
79
80
  }
80
81
  return content;
81
82
  });
@@ -0,0 +1,79 @@
1
+ import { AxiosRequestConfig, Method } from "axios";
2
+ declare class PresetMixin {
3
+ /**
4
+ * 存储预置参数
5
+ */
6
+ protected presets: Record<string, any>;
7
+ /**
8
+ * 存储预置headers数据
9
+ */
10
+ protected prependHeaders: Record<string, any>;
11
+ /**
12
+ * 存储预置数据
13
+ */
14
+ protected prependData: Record<string, any>;
15
+ /**
16
+ * 设置预置参数
17
+ * @param presets
18
+ * @returns
19
+ */
20
+ setPresets(presets: Record<string, any>): this;
21
+ /**
22
+ * 设置单个预置header
23
+ * @param key
24
+ * @param value
25
+ * @returns
26
+ */
27
+ withHeader(key: string, value: any): this;
28
+ /**
29
+ * 批量设置预置headers
30
+ * @param headers
31
+ * @returns
32
+ */
33
+ withHeaders(headers: Record<string, any>): this;
34
+ /**
35
+ * 设置预置数据
36
+ * @param key 参数名
37
+ * @param value 参数值,不设置则尝试从预置参数中获取
38
+ * @returns
39
+ */
40
+ with(key: string | string[] | Record<string, any>, value?: string): this;
41
+ /**
42
+ * 预设置app_id(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
43
+ * @param new_appid
44
+ * @returns
45
+ */
46
+ withAppId(new_appid?: string): this;
47
+ /**
48
+ * 预设置app_id的别名(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
49
+ * @param new_alias
50
+ * @returns
51
+ */
52
+ withAppIdAs(new_alias: string): this;
53
+ /**
54
+ * 预设置secret(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
55
+ * @param new_secret
56
+ * @returns
57
+ */
58
+ withSecret(new_secret?: string): this;
59
+ /**
60
+ * 预设置mch_id(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
61
+ * @param new_mch_id
62
+ * @returns
63
+ */
64
+ withMchId(new_mch_id?: string): this;
65
+ /**
66
+ * 预设置mch_id别名(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
67
+ * @param new_alias
68
+ * @returns
69
+ */
70
+ withMchIdAs(new_alias?: string): this;
71
+ /**
72
+ * 合并预置参数并清空预置数据
73
+ * @param payload
74
+ * @param method
75
+ * @returns
76
+ */
77
+ mergeThenResetPrepends(payload: AxiosRequestConfig, method?: Method): any;
78
+ }
79
+ export = PresetMixin;
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ const merge_1 = __importDefault(require("merge"));
6
+ class PresetMixin {
7
+ constructor() {
8
+ /**
9
+ * 存储预置参数
10
+ */
11
+ this.presets = {};
12
+ /**
13
+ * 存储预置headers数据
14
+ */
15
+ this.prependHeaders = {};
16
+ /**
17
+ * 存储预置数据
18
+ */
19
+ this.prependData = {};
20
+ }
21
+ /**
22
+ * 设置预置参数
23
+ * @param presets
24
+ * @returns
25
+ */
26
+ setPresets(presets) {
27
+ this.presets = presets;
28
+ return this;
29
+ }
30
+ /**
31
+ * 设置单个预置header
32
+ * @param key
33
+ * @param value
34
+ * @returns
35
+ */
36
+ withHeader(key, value) {
37
+ if (typeof this.prependHeaders !== 'object') {
38
+ this.prependHeaders = {};
39
+ }
40
+ this.prependHeaders[key] = value;
41
+ return this;
42
+ }
43
+ /**
44
+ * 批量设置预置headers
45
+ * @param headers
46
+ * @returns
47
+ */
48
+ withHeaders(headers) {
49
+ headers.map((value, key) => {
50
+ this.withHeader(key, value);
51
+ });
52
+ return this;
53
+ }
54
+ /**
55
+ * 设置预置数据
56
+ * @param key 参数名
57
+ * @param value 参数值,不设置则尝试从预置参数中获取
58
+ * @returns
59
+ */
60
+ with(key, value = null) {
61
+ if (Array.isArray(key)) {
62
+ key.map((k) => {
63
+ var _a;
64
+ this.with(k, (_a = this.presets[k]) !== null && _a !== void 0 ? _a : null);
65
+ });
66
+ return this;
67
+ }
68
+ else if (typeof key === 'object') {
69
+ key.map((v, k) => {
70
+ var _a;
71
+ this.with(k, (_a = v !== null && v !== void 0 ? v : this.presets[k]) !== null && _a !== void 0 ? _a : null);
72
+ });
73
+ return this;
74
+ }
75
+ if (typeof this.prependData !== 'object') {
76
+ this.prependData = {};
77
+ }
78
+ this.prependData[key] = value || this.presets[key] || null;
79
+ return this;
80
+ }
81
+ /**
82
+ * 预设置app_id(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
83
+ * @param new_appid
84
+ * @returns
85
+ */
86
+ withAppId(new_appid = null) {
87
+ this.with('app_id', new_appid);
88
+ return this;
89
+ }
90
+ /**
91
+ * 预设置app_id的别名(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
92
+ * @param new_alias
93
+ * @returns
94
+ */
95
+ withAppIdAs(new_alias) {
96
+ this.with(new_alias, this.presets['app_id']);
97
+ return this;
98
+ }
99
+ /**
100
+ * 预设置secret(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
101
+ * @param new_secret
102
+ * @returns
103
+ */
104
+ withSecret(new_secret = null) {
105
+ this.with('secret', new_secret);
106
+ return this;
107
+ }
108
+ /**
109
+ * 预设置mch_id(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
110
+ * @param new_mch_id
111
+ * @returns
112
+ */
113
+ withMchId(new_mch_id = null) {
114
+ this.with('mch_id', new_mch_id);
115
+ return this;
116
+ }
117
+ /**
118
+ * 预设置mch_id别名(因nodejs不支持魔术方法,只好预先设置几个常用的方法)
119
+ * @param new_alias
120
+ * @returns
121
+ */
122
+ withMchIdAs(new_alias = null) {
123
+ this.with(new_alias, this.presets['mch_id']);
124
+ return this;
125
+ }
126
+ /**
127
+ * 合并预置参数并清空预置数据
128
+ * @param payload
129
+ * @param method
130
+ * @returns
131
+ */
132
+ mergeThenResetPrepends(payload, method = 'get') {
133
+ var _a, _b, _c, _d;
134
+ let field = method.toLowerCase() === 'get' ? 'params' : 'data';
135
+ let options = merge_1.default.recursive(true, payload);
136
+ if (!options.headers)
137
+ options.headers = {};
138
+ if (((_b = (_a = options.headers['Content-Type']) !== null && _a !== void 0 ? _a : options.headers['content-type']) !== null && _b !== void 0 ? _b : null) === 'application/json' || !!options.json) {
139
+ field = 'json';
140
+ }
141
+ if (((_d = (_c = options.headers['Content-Type']) !== null && _c !== void 0 ? _c : options.headers['content-type']) !== null && _d !== void 0 ? _d : null) === 'text/xml' || !!options.xml) {
142
+ field = 'xml';
143
+ }
144
+ if (this.prependData && Object.keys(this.prependData).length > 0) {
145
+ options[field] = Object.assign(Object.assign({}, this.prependData), options[field]);
146
+ }
147
+ if (this.prependHeaders && Object.keys(this.prependHeaders).length > 0) {
148
+ options.headers = Object.assign(Object.assign({}, this.prependHeaders), options.headers);
149
+ }
150
+ this.prependData = {};
151
+ this.prependHeaders = {};
152
+ return options;
153
+ }
154
+ }
155
+ module.exports = PresetMixin;
@@ -1,6 +1,11 @@
1
1
  import AccessTokenAwareClient from "../HttpClient/AccessTokenAwareClient";
2
- declare class ClientMixin {
2
+ declare abstract class ClientMixin {
3
3
  protected client: AccessTokenAwareClient;
4
+ /**
5
+ * 创建客户端实例
6
+ * @returns
7
+ */
8
+ abstract createClient(): AccessTokenAwareClient;
4
9
  /**
5
10
  * 获取客户端实例
6
11
  * @returns
@@ -5,6 +5,9 @@ class ClientMixin {
5
5
  * @returns
6
6
  */
7
7
  getClient() {
8
+ if (!this.client) {
9
+ this.client = this.createClient();
10
+ }
8
11
  return this.client;
9
12
  }
10
13
  /**
@@ -5,6 +5,9 @@ class ServerRequestMixin {
5
5
  * @returns
6
6
  */
7
7
  getRequest() {
8
+ if (!this.request) {
9
+ throw new Error('Please set request instance before use.');
10
+ }
8
11
  return this.request;
9
12
  }
10
13
  /**
@@ -68,6 +68,12 @@ export declare const strStudly: (value: string) => string;
68
68
  * @returns
69
69
  */
70
70
  export declare const strCamel: (value: string) => string;
71
+ /**
72
+ * 蛇形(下划线分隔,全小写),'helloWorld' => 'hello_world'
73
+ * @param value
74
+ * @returns
75
+ */
76
+ export declare const strSnake: (value: string) => string;
71
77
  /**
72
78
  * 如果只有一个同名、同级节点,则不当作数组
73
79
  * @param obj
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.buildXml = exports.parseXml = exports.singleItem = exports.strCamel = exports.strStudly = exports.strLcwords = exports.strUcwords = exports.rtrim = exports.ltrim = exports.trim = exports.applyMixins = exports.inArray = exports.isIp = exports.isIpv6 = exports.isIpv4 = exports.isFunction = exports.isObject = exports.isNumber = exports.isArray = exports.isString = exports.makeSignature = exports.randomString = exports.parseQueryString = exports.buildQueryString = exports.getTimestamp = exports.md5File = exports.createHmac = exports.createHash = void 0;
15
+ exports.buildXml = exports.parseXml = exports.singleItem = exports.strSnake = exports.strCamel = exports.strStudly = exports.strLcwords = exports.strUcwords = exports.rtrim = exports.ltrim = exports.trim = exports.applyMixins = exports.inArray = exports.isIp = exports.isIpv6 = exports.isIpv4 = exports.isFunction = exports.isObject = exports.isNumber = exports.isArray = exports.isString = exports.makeSignature = exports.randomString = exports.parseQueryString = exports.buildQueryString = exports.getTimestamp = exports.md5File = exports.createHmac = exports.createHash = void 0;
16
16
  const crypto_1 = __importDefault(require("crypto"));
17
17
  const qs_1 = __importDefault(require("qs"));
18
18
  const xml2js_1 = __importDefault(require("xml2js"));
@@ -252,6 +252,15 @@ const strCamel = function (value) {
252
252
  return (0, exports.strLcwords)((0, exports.strStudly)(value));
253
253
  };
254
254
  exports.strCamel = strCamel;
255
+ /**
256
+ * 蛇形(下划线分隔,全小写),'helloWorld' => 'hello_world'
257
+ * @param value
258
+ * @returns
259
+ */
260
+ const strSnake = function (value) {
261
+ return value.replace(/([A-Z])/g, "_$1").toLowerCase().substring(1);
262
+ };
263
+ exports.strSnake = strSnake;
255
264
  /**
256
265
  * 如果只有一个同名、同级节点,则不当作数组
257
266
  * @param obj
@@ -1,4 +1,9 @@
1
1
  import BaseAccessToken from "../OfficialAccount/AccessToken";
2
2
  declare class AccessToken extends BaseAccessToken {
3
+ /**
4
+ * 获取access_token的缓存名称
5
+ * @returns
6
+ */
7
+ getKey(): string;
3
8
  }
4
9
  export = AccessToken;
@@ -4,5 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  const AccessToken_1 = __importDefault(require("../OfficialAccount/AccessToken"));
6
6
  class AccessToken extends AccessToken_1.default {
7
+ /**
8
+ * 获取access_token的缓存名称
9
+ * @returns
10
+ */
11
+ getKey() {
12
+ if (!this.key) {
13
+ this.key = `mini_app.access_token.${this.appId}`;
14
+ }
15
+ return this.key;
16
+ }
7
17
  }
8
18
  module.exports = AccessToken;
@@ -104,8 +104,8 @@ class Application {
104
104
  return new Utils_2.default(this);
105
105
  }
106
106
  createClient() {
107
- let httpClient = this.getHttpClient();
108
- return new AccessTokenAwareClient_1.default(httpClient, this.getAccessToken());
107
+ return (new AccessTokenAwareClient_1.default(this.getHttpClient(), this.getAccessToken()))
108
+ .setPresets(this.getConfig().all());
109
109
  }
110
110
  /**
111
111
  * 获取请求默认配置
@@ -144,8 +144,8 @@ class Application {
144
144
  return new Utils_2.default(this);
145
145
  }
146
146
  createClient() {
147
- let httpClient = this.getHttpClient();
148
- return new AccessTokenAwareClient_1.default(httpClient, this.getAccessToken());
147
+ return (new AccessTokenAwareClient_1.default(this.getHttpClient(), this.getAccessToken()))
148
+ .setPresets(this.getConfig().all());
149
149
  }
150
150
  /**
151
151
  * 获取请求默认配置
@@ -17,6 +17,21 @@ declare module 'axios' {
17
17
  }
18
18
  }
19
19
 
20
+ /**
21
+ * 微信接口响应数据格式
22
+ */
23
+ export declare interface WeixinResponse extends Record<string, any> {
24
+ /**
25
+ * 错误代码
26
+ */
27
+ errcode?: string,
28
+
29
+ /**
30
+ * 错误信息
31
+ */
32
+ errmsg?: string,
33
+ }
34
+
20
35
  /**
21
36
  * 公众号网页授权相关配置
22
37
  */
@@ -54,10 +69,25 @@ export declare interface CacheFileConfig {
54
69
  ext: string
55
70
  }
56
71
 
72
+ /**
73
+ * 基础配置
74
+ */
75
+ export declare interface BaseConfig {
76
+ /**
77
+ * 网络请求相关配置
78
+ */
79
+ http?: AxiosRequestConfig;
80
+
81
+ /**
82
+ * 文件缓存相关配置
83
+ */
84
+ file_cache?: CacheFileConfig;
85
+ }
86
+
57
87
  /**
58
88
  * 公众号配置
59
89
  */
60
- export declare interface OfficialAccountConfig {
90
+ export declare interface OfficialAccountConfig extends BaseConfig {
61
91
  /**
62
92
  * 公众号 app_id
63
93
  */
@@ -82,22 +112,12 @@ export declare interface OfficialAccountConfig {
82
112
  * 网页授权相关配置
83
113
  */
84
114
  oauth?: OauthConfig;
85
-
86
- /**
87
- * 网络请求相关配置
88
- */
89
- http?: AxiosRequestConfig;
90
-
91
- /**
92
- * 文件缓存相关配置
93
- */
94
- file_cache?: CacheFileConfig;
95
115
  }
96
116
 
97
117
  /**
98
118
  * 小程序配置
99
119
  */
100
- export declare interface MiniAppConfig {
120
+ export declare interface MiniAppConfig extends BaseConfig {
101
121
  /**
102
122
  * 小程序 app_id
103
123
  */
@@ -117,22 +137,12 @@ export declare interface MiniAppConfig {
117
137
  * 服务端消息加解密密钥 aes_key
118
138
  */
119
139
  aes_key?: string;
120
-
121
- /**
122
- * 网络请求相关配置
123
- */
124
- http?: AxiosRequestConfig;
125
-
126
- /**
127
- * 文件缓存相关配置
128
- */
129
- file_cache?: CacheFileConfig;
130
140
  }
131
141
 
132
142
  /**
133
143
  * 微信支付配置
134
144
  */
135
- export declare interface PayConfig {
145
+ export declare interface PayConfig extends BaseConfig {
136
146
  /**
137
147
  * 商户号
138
148
  */
@@ -152,22 +162,12 @@ export declare interface PayConfig {
152
162
  * v2 API密钥
153
163
  */
154
164
  v2_secret_key?: string;
155
-
156
- /**
157
- * 网络请求相关配置
158
- */
159
- http?: AxiosRequestConfig;
160
-
161
- /**
162
- * 文件缓存相关配置
163
- */
164
- file_cache?: CacheFileConfig;
165
165
  }
166
166
 
167
167
  /**
168
168
  * 开发平台配置
169
169
  */
170
- export declare interface OpenPlatformConfig {
170
+ export declare interface OpenPlatformConfig extends BaseConfig {
171
171
  /**
172
172
  * 开发平台 app_id
173
173
  */
@@ -187,22 +187,12 @@ export declare interface OpenPlatformConfig {
187
187
  * 开发平台服务端消息加解密密钥 aes_key
188
188
  */
189
189
  aes_key?: string;
190
-
191
- /**
192
- * 网络请求相关配置
193
- */
194
- http?: AxiosRequestConfig;
195
-
196
- /**
197
- * 文件缓存相关配置
198
- */
199
- file_cache?: CacheFileConfig;
200
190
  }
201
191
 
202
192
  /**
203
193
  * 企业微信配置
204
194
  */
205
- export declare interface WorkConfig {
195
+ export declare interface WorkConfig extends BaseConfig {
206
196
  /**
207
197
  * 企业微信 corp_id
208
198
  */
@@ -222,22 +212,12 @@ export declare interface WorkConfig {
222
212
  * 企业微信服务端消息加解密密钥 aes_key
223
213
  */
224
214
  aes_key?: string;
225
-
226
- /**
227
- * 网络请求相关配置
228
- */
229
- http?: AxiosRequestConfig;
230
-
231
- /**
232
- * 文件缓存相关配置
233
- */
234
- file_cache?: CacheFileConfig;
235
215
  }
236
216
 
237
217
  /**
238
218
  * 企业微信开放平台配置
239
219
  */
240
- export declare interface OpenWorkConfig {
220
+ export declare interface OpenWorkConfig extends BaseConfig {
241
221
  /**
242
222
  * 企业微信 corp_id
243
223
  */
@@ -257,16 +237,6 @@ export declare interface OpenWorkConfig {
257
237
  * 企业微信服务端消息加解密密钥 aes_key
258
238
  */
259
239
  aes_key?: string;
260
-
261
- /**
262
- * 网络请求相关配置
263
- */
264
- http?: AxiosRequestConfig;
265
-
266
- /**
267
- * 文件缓存相关配置
268
- */
269
- file_cache?: CacheFileConfig;
270
240
  }
271
241
 
272
242
  /**
@@ -341,3 +311,12 @@ export declare type PaymentRefundedHandler = (message: Message, reqInfo: object,
341
311
  * @param alert 业务错误处理函数
342
312
  */
343
313
  export declare type PaymentScannedHandler = (message: Message, fail: PaymentFailHandler, alert: PaymentAlertHandler) => void;
314
+
315
+ /**
316
+ * 日志处理方法
317
+ * @param type before:请求前,after:请求后
318
+ * @param options 请求选项
319
+ * @param usedTime 请求耗时,单位ms,仅在 type 为 after 时返回
320
+ * @param response 响应对象,仅在 type 为 after 时返回
321
+ */
322
+ export declare type LogHandler = (type: 'before' | 'after', options: AxiosRequestConfig, usedTime?: number, response?: AxiosResponse) => void | Promise<void>;
@@ -0,0 +1,6 @@
1
+ import { OfficialAccountConfig, MiniAppConfig, LogHandler } from './Types/global';
2
+ import OfficialAccount from './OfficialAccount/Application';
3
+ import MiniApp from './MiniApp/Application';
4
+ import CacheInterface from './Core/Contracts/CacheInterface';
5
+ import ServerRequest from './Core/Http/ServerRequest';
6
+ export { OfficialAccount, OfficialAccountConfig, MiniApp, MiniAppConfig, CacheInterface, ServerRequest, LogHandler, };
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ServerRequest = exports.CacheInterface = exports.MiniApp = exports.OfficialAccount = void 0;
7
+ const Application_1 = __importDefault(require("./OfficialAccount/Application"));
8
+ exports.OfficialAccount = Application_1.default;
9
+ const Application_2 = __importDefault(require("./MiniApp/Application"));
10
+ exports.MiniApp = Application_2.default;
11
+ const CacheInterface_1 = __importDefault(require("./Core/Contracts/CacheInterface"));
12
+ exports.CacheInterface = CacheInterface_1.default;
13
+ const ServerRequest_1 = __importDefault(require("./Core/Http/ServerRequest"));
14
+ exports.ServerRequest = ServerRequest_1.default;
@@ -0,0 +1 @@
1
+ {"data":"mock-access_token","lifeTime":1655744193}
@@ -1 +1 @@
1
- {"data":"mock-access_token","lifeTime":1654698919}
1
+ {"data":"mock-access_token","lifeTime":1655744193}
@@ -1 +1 @@
1
- {"data":"mock-ticket","lifeTime":1654704619}
1
+ {"data":"mock-ticket","lifeTime":1655749893}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-easywechat",
3
- "version": "3.0.0-alpha.2",
3
+ "version": "3.0.0-beta.1",
4
4
  "description": "EasyWechat SDK for Node.js (NOT OFFICIAL)",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {