@be-link/request 1.45.1-beta.8 → 1.45.1-beta.9

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 (80) hide show
  1. package/README.md +288 -365
  2. package/dist/BeLinkRequest.d.ts +207 -0
  3. package/dist/BeLinkRequest.d.ts.map +1 -0
  4. package/dist/index.cjs.js +721 -5356
  5. package/dist/index.d.ts +88 -33
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.esm.js +717 -5331
  8. package/dist/interceptors/index.d.ts +10 -0
  9. package/dist/interceptors/index.d.ts.map +1 -0
  10. package/dist/interceptors/request.d.ts +50 -0
  11. package/dist/interceptors/request.d.ts.map +1 -0
  12. package/dist/interceptors/response.d.ts +52 -0
  13. package/dist/interceptors/response.d.ts.map +1 -0
  14. package/dist/services/EncryptionService.d.ts +120 -0
  15. package/dist/services/EncryptionService.d.ts.map +1 -0
  16. package/dist/services/TimeSyncService.d.ts +117 -0
  17. package/dist/services/TimeSyncService.d.ts.map +1 -0
  18. package/dist/services/index.d.ts +10 -0
  19. package/dist/services/index.d.ts.map +1 -0
  20. package/dist/types.d.ts +268 -0
  21. package/dist/types.d.ts.map +1 -0
  22. package/package.json +1 -2
  23. package/dist/application/clients/BeLinkClient.d.ts +0 -82
  24. package/dist/application/clients/BeLinkClient.d.ts.map +0 -1
  25. package/dist/application/clients/HttpClient.d.ts +0 -64
  26. package/dist/application/clients/HttpClient.d.ts.map +0 -1
  27. package/dist/application/clients/LiveClient.d.ts +0 -52
  28. package/dist/application/clients/LiveClient.d.ts.map +0 -1
  29. package/dist/application/interceptors/AuthInterceptor.d.ts +0 -23
  30. package/dist/application/interceptors/AuthInterceptor.d.ts.map +0 -1
  31. package/dist/application/interceptors/TimeInterceptor.d.ts +0 -21
  32. package/dist/application/interceptors/TimeInterceptor.d.ts.map +0 -1
  33. package/dist/application/interceptors/TokenInterceptor.d.ts +0 -26
  34. package/dist/application/interceptors/TokenInterceptor.d.ts.map +0 -1
  35. package/dist/application/services/UserInfoService.d.ts +0 -33
  36. package/dist/application/services/UserInfoService.d.ts.map +0 -1
  37. package/dist/cloudbase/index.d.ts +0 -27
  38. package/dist/cloudbase/index.d.ts.map +0 -1
  39. package/dist/cloudbase/types.d.ts +0 -63
  40. package/dist/cloudbase/types.d.ts.map +0 -1
  41. package/dist/core/constants/index.d.ts +0 -64
  42. package/dist/core/constants/index.d.ts.map +0 -1
  43. package/dist/core/errors/index.d.ts +0 -42
  44. package/dist/core/errors/index.d.ts.map +0 -1
  45. package/dist/core/interfaces/IConfigManager.d.ts +0 -31
  46. package/dist/core/interfaces/IConfigManager.d.ts.map +0 -1
  47. package/dist/core/interfaces/IEncryptionService.d.ts +0 -28
  48. package/dist/core/interfaces/IEncryptionService.d.ts.map +0 -1
  49. package/dist/core/interfaces/IHttpClient.d.ts +0 -43
  50. package/dist/core/interfaces/IHttpClient.d.ts.map +0 -1
  51. package/dist/core/interfaces/index.d.ts +0 -4
  52. package/dist/core/interfaces/index.d.ts.map +0 -1
  53. package/dist/core/types/config.types.d.ts +0 -65
  54. package/dist/core/types/config.types.d.ts.map +0 -1
  55. package/dist/core/types/index.d.ts +0 -5
  56. package/dist/core/types/index.d.ts.map +0 -1
  57. package/dist/core/types/interceptor.types.d.ts +0 -45
  58. package/dist/core/types/interceptor.types.d.ts.map +0 -1
  59. package/dist/core/types/request.types.d.ts +0 -65
  60. package/dist/core/types/request.types.d.ts.map +0 -1
  61. package/dist/core/types/response.types.d.ts +0 -29
  62. package/dist/core/types/response.types.d.ts.map +0 -1
  63. package/dist/infrastructure/adapters/AxiosAdapter.d.ts +0 -51
  64. package/dist/infrastructure/adapters/AxiosAdapter.d.ts.map +0 -1
  65. package/dist/infrastructure/services/ConfigManager.d.ts +0 -52
  66. package/dist/infrastructure/services/ConfigManager.d.ts.map +0 -1
  67. package/dist/infrastructure/services/EncryptionService.d.ts +0 -34
  68. package/dist/infrastructure/services/EncryptionService.d.ts.map +0 -1
  69. package/dist/infrastructure/services/TimeSyncService.d.ts +0 -43
  70. package/dist/infrastructure/services/TimeSyncService.d.ts.map +0 -1
  71. package/dist/infrastructure/utils/env.utils.d.ts +0 -13
  72. package/dist/infrastructure/utils/env.utils.d.ts.map +0 -1
  73. package/dist/presets/belink.preset.d.ts +0 -4
  74. package/dist/presets/belink.preset.d.ts.map +0 -1
  75. package/dist/presets/index.d.ts +0 -4
  76. package/dist/presets/index.d.ts.map +0 -1
  77. package/dist/presets/live.preset.d.ts +0 -4
  78. package/dist/presets/live.preset.d.ts.map +0 -1
  79. package/dist/presets/request.preset.d.ts +0 -9
  80. package/dist/presets/request.preset.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,499 +1,422 @@
1
1
  # @be-link/request
2
2
 
3
- 共比邻 HTTP 请求库,提供企业级的 HTTP 请求封装和云函数调用功能。
3
+ 共比邻 HTTP 请求库,提供简洁的 HTTP 请求封装,支持时间同步和 Token 加密。
4
4
 
5
5
  ## 特性
6
6
 
7
- - 企业级 HTTP 请求客户端(认证、加密、时间同步)
8
- - 云函数调用封装(腾讯云 CloudBase)
9
- - 拦截器机制(请求/响应拦截)
7
+ - 单例模式,全局统一配置
8
+ - 服务器时间同步(解决客户端时间不准问题)
9
+ - Token AES 加密(防重放攻击)
10
+ - 请求/响应拦截器
10
11
  - TypeScript 类型支持
11
- - 多环境适配(Browser/Node.js)
12
- - 支持多服务实例配置
12
+ - 所有参数由应用方传递,灵活配置
13
+ - 自定义 Header 名称(Token、UserId)
14
+ - 并发请求时自动合并时间同步(防止重复请求)
15
+ - 使用 `performance.now()` 防止客户端系统时间篡改
13
16
 
14
17
  ## 安装
15
18
 
16
19
  ```bash
17
- # 基础安装(包含所有依赖)
18
20
  pnpm add @be-link/request axios crypto-js
19
-
20
- # 如果需要使用云函数调用 (callFunction)
21
- pnpm add @be-link/request axios crypto-js @cloudbase/js-sdk
22
21
  ```
23
22
 
24
23
  ### 依赖说明
25
24
 
26
- | 依赖 | 是否必须 | 说明 |
27
- | ------------------- | -------- | ---------------------- |
28
- | `axios` | ✅ 必须 | HTTP 客户端 |
29
- | `crypto-js` | ✅ 必须 | Token 加密(AES) |
30
- | `@cloudbase/js-sdk` | ⚠️ 可选 | 仅 `callFunction` 需要 |
25
+ | 依赖 | 是否必须 | 说明 |
26
+ | ----------- | -------- | ----------------- |
27
+ | `axios` | ✅ 必须 | HTTP 客户端 |
28
+ | `crypto-js` | ✅ 必须 | Token 加密(AES) |
31
29
 
32
- > **注意**:所有依赖作为 external 不打包进产物,需要用户安装。
30
+ ## 快速开始
33
31
 
34
- ## 架构设计
32
+ ### 基本用法
35
33
 
36
- ```
37
- ┌─────────────────────────────────────────────────────────────────────┐
38
- │ 应用层 (Application) │
39
- │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
40
- │ │ beLinkRequest │ │ beLinkLiveRequest│ │ request │ │
41
- │ │ (企业级客户端) │ │ (直播客户端) │ │ (基础请求) │ │
42
- │ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ │
43
- └───────────│─────────────────────│─────────────────────│─────────────┘
44
- │ │ │
45
- ▼ ▼ │
46
- ┌─────────────────────────────────────────────────────────────────────┐
47
- │ 客户端层 (Clients) │
48
- │ ┌──────────────────┐ ┌──────────────────┐ │
49
- │ │ BeLinkClient │ │ LiveClient │ │
50
- │ │ - Token 加密 │ │ - 简化版 │ │
51
- │ │ - 时间同步 │ │ - 无认证逻辑 │ │
52
- │ │ - 401 处理 │ │ │ │
53
- │ └────────┬─────────┘ └────────┬─────────┘ │
54
- └───────────│─────────────────────│───────────────────────────────────┘
55
- │ │
56
- ▼ ▼
57
- ┌─────────────────────────────────────────────────────────────────────┐
58
- │ 基类层 (HttpClient) │
59
- │ ┌─────────────────────────────────────────────────────────────┐ │
60
- │ │ - get/post/put/delete 方法 │ │
61
- │ │ - 拦截器管理 (addRequestInterceptor/addResponseInterceptor) │ │
62
- │ │ - 请求上下文管理 │ │
63
- │ └──────────────────────────────┬──────────────────────────────┘ │
64
- └─────────────────────────────────│───────────────────────────────────┘
65
-
66
-
67
- ┌─────────────────────────────────────────────────────────────────────┐
68
- │ 适配器层 (AxiosAdapter) │
69
- │ ┌─────────────────────────────────────────────────────────────┐ │
70
- │ │ - 封装 Axios 实例 │ │
71
- │ │ - 统一错误处理 (HttpError/NetworkError/TimeoutError) │ │
72
- │ │ - 请求/响应转换 │ │
73
- │ └─────────────────────────────────────────────────────────────┘ │
74
- └─────────────────────────────────────────────────────────────────────┘
75
- ```
34
+ ```typescript
35
+ import { beLinkRequest } from '@be-link/request';
76
36
 
77
- ### Token 加密流程
37
+ // 1. 初始化(应用启动时调用一次)
38
+ beLinkRequest.init({
39
+ baseURL: 'https://api.example.com',
40
+ timeout: 30000,
41
+ });
78
42
 
43
+ // 2. 发起请求
44
+ const users = await beLinkRequest.get('/api/users');
45
+ const result = await beLinkRequest.post('/api/users', { name: '张三' });
79
46
  ```
80
- 每次请求时:
81
- ┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
82
- │ TokenProvider│────>│ EncryptionService│────>│ 请求头 │
83
- │ () => token │ │ AES(token|+|time)│ │ x-belink-auth.. │
84
- └─────────────┘ └──────────────────┘ └─────────────────┘
85
-
86
-
87
- TimeSyncService
88
- (服务器时间校正)
89
- ```
90
-
91
- ## 客户端选择指南
92
-
93
- | 客户端 | 使用场景 | Token 加密 | 时间同步 | 401 处理 |
94
- | ------------------- | -------------------- | :--------: | :------: | :------: |
95
- | `beLinkRequest` | 需要认证的业务 API | ✅ | ✅ | ✅ |
96
- | `beLinkLiveRequest` | 直播、无需认证的场景 | ❌ | ❌ | ❌ |
97
- | `request` | 第三方 API、简单请求 | ❌ | ❌ | ❌ |
98
-
99
- ## 快速开始
100
47
 
101
- ### 1. 初始化配置
48
+ ### 完整配置
102
49
 
103
50
  ```typescript
104
51
  import { beLinkRequest } from '@be-link/request';
105
52
 
106
- // 初始化客户端
107
53
  beLinkRequest.init({
108
- mode: 'production', // 'development' | 'testing' | 'production'
109
- baseUrl: 'https://api.example.com', // API 基础地址
110
- loginPath: '/login', // 401 时跳转的登录页路径
111
- tokenHeaderName: 'x-belink-authorization', // Token 请求头名称(可选)
112
- tokenStorageKey: 'belink_token', // localStorage 存储键名(可选)
113
- });
54
+ // 基础配置
55
+ baseURL: 'https://api.example.com',
56
+ timeout: 30000,
57
+ headers: {
58
+ 'X-Custom-Header': 'value',
59
+ },
114
60
 
115
- // 设置 Token(通常在登录后调用)
116
- beLinkRequest.setToken('your-auth-token');
61
+ // 时间同步配置
62
+ timeSync: {
63
+ enabled: true,
64
+ syncUrl: 'https://api.example.com/api/time',
65
+ syncGapTime: 50000, // 同步间隔阈值(毫秒)
66
+ storagePrefix: 'myapp', // 存储 key 前缀
67
+ },
117
68
 
118
- // 或监听 localStorage 中的 Token 变化
119
- beLinkRequest.setupTokenListener('belink_token');
120
- ```
69
+ // 加密配置
70
+ encryption: {
71
+ enabled: true,
72
+ key: 'your-aes-key-16ch', // 16 位 AES 密钥
73
+ iv: 'your-aes-iv-16ch', // 16 位 AES IV
74
+ },
121
75
 
122
- ### 2. 发起请求
76
+ // Token 获取函数
77
+ getToken: () => localStorage.getItem('token'),
123
78
 
124
- ```typescript
125
- import { beLinkRequest } from '@be-link/request';
79
+ // 用户 ID 获取函数
80
+ getUserId: () => localStorage.getItem('userId'),
126
81
 
127
- // GET 请求
128
- const users = await beLinkRequest.get('/api/users');
82
+ // 自定义 Header 名称(可选)
83
+ tokenHeaderName: 'X-BeLink-Token', // 默认值
84
+ userIdHeaderName: 'X-BeLink-UserId', // 默认值
129
85
 
130
- // POST 请求
131
- const result = await beLinkRequest.post('/api/users', {
132
- name: '张三',
133
- age: 25,
134
- });
86
+ // 自定义请求拦截器
87
+ onRequest: (config) => {
88
+ console.log('请求发出:', config.url);
89
+ return config;
90
+ },
135
91
 
136
- // 带配置的请求
137
- const data = await beLinkRequest.request({
138
- url: '/api/data',
139
- method: 'post',
140
- data: { key: 'value' },
141
- headers: { 'X-Custom-Header': 'custom-value' },
142
- timeout: 10000,
92
+ // 自定义响应拦截器
93
+ onResponse: (response) => {
94
+ console.log('响应收到:', response);
95
+ return response;
96
+ },
97
+
98
+ // 自定义错误处理
99
+ onError: (error) => {
100
+ if (error.response?.status === 401) {
101
+ window.location.href = '/login';
102
+ }
103
+ throw error;
104
+ },
143
105
  });
144
106
  ```
145
107
 
146
108
  ## API 文档
147
109
 
148
- ### beLinkRequest - 企业级请求客户端
110
+ ### beLinkRequest
149
111
 
150
- **使用场景**: 需要用户认证的业务 API,如用户信息、订单、支付等。
112
+ 单例请求客户端,提供以下方法:
151
113
 
152
- **特性**:
114
+ #### init(options)
153
115
 
154
- - Token 自动加密(AES + 时间戳,防重放攻击)
155
- - 服务器时间同步(解决客户端时间不准问题)
156
- - 401 自动处理(跳转登录页或执行回调)
116
+ 初始化请求客户端,必须在使用其他方法之前调用。
157
117
 
158
118
  ```typescript
159
- import { beLinkRequest } from '@be-link/request';
160
-
161
- // 初始化(必须在使用前调用)
162
119
  beLinkRequest.init({
163
- mode: 'production',
164
- baseUrl: 'https://api.example.com',
165
- loginPath: '/login',
166
- tokenHeaderName: 'x-belink-authorization', // 请求头名称
167
- tokenStorageKey: 'belink_token', // localStorage 键名
120
+ baseURL: 'https://api.example.com',
121
+ // ... 其他配置
168
122
  });
123
+ ```
169
124
 
170
- // Token 管理
171
- beLinkRequest.setToken(token: string): void // 设置 Token
172
- beLinkRequest.getToken(): string | null // 获取 Token
173
- beLinkRequest.setupTokenListener(tokenKey): () => void // 监听 Token 变化
125
+ #### HTTP 方法
174
126
 
175
- // HTTP 方法
176
- beLinkRequest.get<T>(url: string, config?): Promise<ApiResponse<T>>
177
- beLinkRequest.post<T>(url: string, data?, config?): Promise<ApiResponse<T>>
178
- beLinkRequest.put<T>(url: string, data?, config?): Promise<ApiResponse<T>>
179
- beLinkRequest.delete<T>(url: string, config?): Promise<ApiResponse<T>>
127
+ ```typescript
128
+ // GET 请求
129
+ beLinkRequest.get<T>(url: string, config?: AxiosRequestConfig): Promise<T>
180
130
 
181
- // 通用请求
182
- beLinkRequest.request<T>(config: RequestConfig): Promise<ApiResponse<T>>
131
+ // POST 请求
132
+ beLinkRequest.post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
183
133
 
184
- // 时间同步(向后兼容)
185
- beLinkRequest.setupRequestTime(config): Promise<void> // 设置请求时间
186
- beLinkRequest.removeRequestTime(): Promise<void> // 清除请求时间
187
- ```
134
+ // PUT 请求
135
+ beLinkRequest.put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
188
136
 
189
- ### beLinkLiveRequest - 直播请求客户端
137
+ // PATCH 请求
138
+ beLinkRequest.patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
190
139
 
191
- **使用场景**: 直播、公开 API、不需要用户认证的场景。
140
+ // DELETE 请求
141
+ beLinkRequest.delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>
192
142
 
193
- **特性**:
143
+ // 通用请求
144
+ beLinkRequest.request<T>(config: AxiosRequestConfig): Promise<T>
145
+ ```
194
146
 
195
- - 简化版客户端,无 Token 加密
196
- - 无时间同步
197
- - 无 401 处理
198
- - 支持配置 baseUrl 和 tokenHeaderName
147
+ #### 其他方法
199
148
 
200
149
  ```typescript
201
- import { beLinkLiveRequest } from '@be-link/request';
150
+ // 获取时间同步服务实例
151
+ beLinkRequest.getTimeSyncService(): TimeSyncService | null
202
152
 
203
- // 初始化
204
- beLinkLiveRequest.init({
205
- baseUrl: 'https://live-api.example.com',
206
- tokenHeaderName: 'x-live-authorization', // 可选
207
- });
153
+ // 获取加密服务实例
154
+ beLinkRequest.getEncryptionService(): EncryptionService | null
208
155
 
209
- // 发起请求
210
- const data = await beLinkLiveRequest.post('/api/live/room', { roomId: '123' });
211
- const info = await beLinkLiveRequest.get('/api/live/info');
156
+ // 获取 Axios 实例
157
+ beLinkRequest.getAxiosInstance(): AxiosInstance | null
158
+
159
+ // 重置客户端(清除配置,需重新 init)
160
+ beLinkRequest.reset(): void
212
161
  ```
213
162
 
214
- ### request - 基础请求
163
+ ### 配置选项 RequestOptions
215
164
 
216
- **使用场景**: 调用第三方 API、简单的 HTTP 请求、不需要任何认证处理的场景。
165
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
166
+ | ---------------- | ----------------------------------- | :--: | --------------- | ------------------ |
167
+ | baseURL | `string` | ✅ | - | API 基础地址 |
168
+ | timeout | `number` | ❌ | 30000 | 请求超时时间(ms) |
169
+ | headers | `Record<string, string>` | ❌ | {} | 默认请求头 |
170
+ | timeSync | `TimeSyncConfig` | ❌ | - | 时间同步配置 |
171
+ | encryption | `EncryptionConfig` | ❌ | - | 加密配置 |
172
+ | getToken | `() => string \| null \| undefined` | ❌ | - | Token 获取函数 |
173
+ | getUserId | `() => string \| null \| undefined` | ❌ | - | 用户 ID 获取函数 |
174
+ | tokenHeaderName | `string` | ❌ | X-BeLink-Token | Token Header 名称 |
175
+ | userIdHeaderName | `string` | ❌ | X-BeLink-UserId | UserId Header 名称 |
176
+ | onRequest | `(config) => config` | ❌ | - | 自定义请求拦截器 |
177
+ | onResponse | `(response) => response` | ❌ | - | 自定义响应拦截器 |
178
+ | onError | `(error) => any` | ❌ | - | 自定义错误处理 |
217
179
 
218
- ```typescript
219
- import { request } from '@be-link/request';
180
+ ### TimeSyncConfig
220
181
 
221
- const result = await request({
222
- url: 'https://api.example.com/data',
223
- method: 'post',
224
- data: { key: 'value' },
225
- });
226
- ```
182
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
183
+ | ------------- | --------- | :--: | ------ | --------------------- |
184
+ | enabled | `boolean` | ❌ | true | 是否启用时间同步 |
185
+ | syncUrl | `string` | ✅ | - | 时间同步接口 URL |
186
+ | syncGapTime | `number` | ❌ | 50000 | 同步间隔阈值(毫秒) |
187
+ | storagePrefix | `string` | ❌ | - | localStorage key 前缀 |
188
+
189
+ ### EncryptionConfig
190
+
191
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
192
+ | ------- | --------- | :--: | ------ | -------------- |
193
+ | enabled | `boolean` | ❌ | true | 是否启用加密 |
194
+ | key | `string` | ✅ | - | 16 位 AES 密钥 |
195
+ | iv | `string` | ✅ | - | 16 位 AES IV |
227
196
 
228
- ### callFunction - 云函数调用
197
+ ## 使用示例
229
198
 
230
- **使用场景**: 调用腾讯云 CloudBase 云函数。
199
+ ### 基础请求
231
200
 
232
201
  ```typescript
233
- import { callFunction } from '@be-link/request';
234
-
235
- // 调用云函数
236
- const result = await callFunction({
237
- name: 'cloud-function-name',
238
- data: {
239
- moduleName: 'biz',
240
- serviceName: 'main',
241
- funcName: 'getUserInfo',
242
- param: { userId: '123' },
243
- },
244
- });
202
+ import { beLinkRequest } from '@be-link/request';
245
203
 
246
- // 带操作用户信息(会自动注入)
247
- const result = await callFunction({
248
- name: 'cloud-function-name',
249
- data: {
250
- moduleName: 'biz',
251
- serviceName: 'main',
252
- funcName: 'createOrder',
253
- param: { productId: '456' },
254
- operateUser: { name: 'staffName' }, // 可选,会自动注入
255
- },
204
+ // 初始化
205
+ beLinkRequest.init({
206
+ baseURL: 'https://api.example.com',
256
207
  });
257
- ```
258
-
259
- ## 多服务配置
260
208
 
261
- 当页面需要调用多个不同域名的服务时,可以配置不同的 `tokenStorageKey` 避免冲突:
209
+ // GET 请求
210
+ const users = await beLinkRequest.get('/api/users');
262
211
 
263
- ```typescript
264
- import { BeLinkClient } from '@be-link/request';
212
+ // 带参数的 GET 请求
213
+ const user = await beLinkRequest.get('/api/users/1', {
214
+ params: { include: 'profile' },
215
+ });
265
216
 
266
- // 服务 A
267
- const serviceA = new BeLinkClient();
268
- serviceA.init({
269
- mode: 'production',
270
- baseUrl: 'https://api-a.example.com',
271
- tokenHeaderName: 'x-service-a-auth',
272
- tokenStorageKey: 'service_a_token',
217
+ // POST 请求
218
+ const newUser = await beLinkRequest.post('/api/users', {
219
+ name: '张三',
220
+ age: 25,
273
221
  });
274
222
 
275
- // 服务 B
276
- const serviceB = new BeLinkClient();
277
- serviceB.init({
278
- mode: 'production',
279
- baseUrl: 'https://api-b.example.com',
280
- tokenHeaderName: 'x-service-b-auth',
281
- tokenStorageKey: 'service_b_token',
223
+ // PUT 请求
224
+ const updated = await beLinkRequest.put('/api/users/1', {
225
+ name: '李四',
282
226
  });
283
227
 
284
- // localStorage 结构:
285
- // {
286
- // "service_a_token": "token_for_service_a",
287
- // "service_b_token": "token_for_service_b"
288
- // }
228
+ // DELETE 请求
229
+ await beLinkRequest.delete('/api/users/1');
289
230
  ```
290
231
 
291
- ## 初始化配置
232
+ ### 带类型的请求
292
233
 
293
- ### init 配置项
234
+ ```typescript
235
+ interface User {
236
+ id: number;
237
+ name: string;
238
+ age: number;
239
+ }
294
240
 
295
- | 参数 | 类型 | 必填 | 默认值 | 说明 |
296
- | --------------- | -------------------------------------------- | :--: | -------------------------- | --------------------- |
297
- | mode | `'development' \| 'testing' \| 'production'` | ✅ | - | 运行模式 |
298
- | baseUrl | `string` | ❌ | - | API 基础地址 |
299
- | loginPath | `string \| (() => void)` | ❌ | - | 401 时跳转路径或回调 |
300
- | tokenHeaderName | `string` | ❌ | `'x-belink-authorization'` | Token 请求头名称 |
301
- | tokenStorageKey | `string` | ❌ | `'token'` | localStorage 存储键名 |
241
+ interface ApiResult<T> {
242
+ success: boolean;
243
+ data: T;
244
+ message?: string;
245
+ }
302
246
 
303
- ### RequestConfig
247
+ // 指定响应类型
248
+ const result = await beLinkRequest.get<ApiResult<User[]>>('/api/users');
249
+ console.log(result.data); // User[]
304
250
 
305
- ```typescript
306
- interface RequestConfig<D = any> {
307
- url: string; // 请求 URL
308
- method?: 'get' | 'post' | 'put' | 'delete' | 'patch'; // HTTP 方法
309
- headers?: Record<string, string>; // 请求头
310
- params?: Record<string, any>; // URL 参数
311
- data?: D; // 请求体
312
- timeout?: number; // 超时时间(毫秒)
313
- withCredentials?: boolean; // 是否携带凭证
314
- customToken?: string; // 自定义 Token
315
- }
251
+ const user = await beLinkRequest.post<ApiResult<User>>('/api/users', {
252
+ name: '张三',
253
+ age: 25,
254
+ });
255
+ console.log(user.data); // User
316
256
  ```
317
257
 
318
- ### 响应格式
258
+ ### 错误处理
319
259
 
320
260
  ```typescript
321
- interface ApiResponse<T = any> {
322
- data: T; // 响应数据
323
- code: number; // 状态码
324
- msg: string; // 消息
325
- success: boolean; // 是否成功
326
- }
261
+ beLinkRequest.init({
262
+ baseURL: 'https://api.example.com',
263
+ onError: (error) => {
264
+ // 处理 401 未授权
265
+ if (error.response?.status === 401) {
266
+ localStorage.removeItem('token');
267
+ window.location.href = '/login';
268
+ return;
269
+ }
270
+
271
+ // 处理业务错误
272
+ if (error.data?.message) {
273
+ alert(error.data.message);
274
+ }
275
+
276
+ throw error;
277
+ },
278
+ });
327
279
  ```
328
280
 
329
- ## 拦截器
281
+ ### 多实例场景
330
282
 
331
- ### 添加自定义拦截器
283
+ 如果需要创建多个独立的请求实例,可以使用 `BeLinkRequest` 类:
332
284
 
333
285
  ```typescript
334
- import { beLinkRequest } from '@be-link/request';
286
+ import { BeLinkRequest } from '@be-link/request';
335
287
 
336
- // 添加请求拦截器
337
- const removeInterceptor = beLinkRequest.addRequestInterceptor({
338
- name: 'CustomInterceptor',
339
- priority: 10, // 数字越小优先级越高
340
- onRequest: async (context) => {
341
- // 修改请求配置
342
- context.config.headers['X-Custom'] = 'value';
343
- return context;
344
- },
288
+ // 创建实例 A
289
+ const serviceA = new BeLinkRequest();
290
+ serviceA.init({
291
+ baseURL: 'https://api-a.example.com',
345
292
  });
346
293
 
347
- // 添加响应拦截器
348
- beLinkRequest.addResponseInterceptor({
349
- name: 'LogInterceptor',
350
- priority: 10,
351
- onResponse: async (context) => {
352
- console.log('Response:', context.response);
353
- return context;
354
- },
355
- onResponseError: async (error) => {
356
- console.error('Error:', error);
357
- throw error;
358
- },
294
+ // 创建实例 B
295
+ const serviceB = new BeLinkRequest();
296
+ serviceB.init({
297
+ baseURL: 'https://api-b.example.com',
359
298
  });
360
299
 
361
- // 移除拦截器
362
- removeInterceptor();
300
+ // 分别使用
301
+ const dataA = await serviceA.get('/api/data');
302
+ const dataB = await serviceB.get('/api/data');
363
303
  ```
364
304
 
365
- ### 内置拦截器(BeLinkClient)
366
-
367
- | 拦截器 | 优先级 | 功能 |
368
- | ---------------- | :----: | -------------------------- |
369
- | TimeInterceptor | 1 | 时间同步,添加服务器时间头 |
370
- | TokenInterceptor | 2 | Token 加密,添加认证头 |
371
- | AuthInterceptor | 1 | 401 响应处理,跳转登录 |
305
+ ## 工作流程
372
306
 
373
- ## 错误处理
307
+ ### Token 加密流程
374
308
 
375
- ```typescript
376
- import { beLinkRequest, HttpError, AuthError, TimeoutError, NetworkError } from '@be-link/request';
377
-
378
- try {
379
- const data = await beLinkRequest.get('/api/data');
380
- } catch (error) {
381
- if (error instanceof AuthError) {
382
- // 401 认证错误,会自动跳转登录页
383
- console.log('认证失败');
384
- } else if (error instanceof TimeoutError) {
385
- console.log('请求超时');
386
- } else if (error instanceof NetworkError) {
387
- console.log('网络错误');
388
- } else if (error instanceof HttpError) {
389
- console.log('HTTP 错误:', error.status);
390
- }
391
- }
309
+ ```
310
+ 请求发起时:
311
+ ┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
312
+ │ getToken() │────>│ EncryptionService│────>│ 请求头 │
313
+ 获取 Token │ │ AES(token|+|time)│ │ Authorization │
314
+ └─────────────┘ └──────────────────┘ └─────────────────┘
315
+
316
+
317
+ TimeSyncService
318
+ (服务器时间校正)
392
319
  ```
393
320
 
394
- ## 环境变量
321
+ ### 时间同步流程
395
322
 
396
- 支持通过环境变量配置加密密钥:
323
+ ```
324
+ 1. 请求发起前检查本地时间是否需要同步
325
+ 2. 如果超过同步间隔阈值(默认 50 秒),发起同步请求
326
+ 3. 将服务器时间和客户端时间存储到 localStorage
327
+ 4. 后续加密时使用校正后的时间戳
328
+ ```
397
329
 
398
- ```bash
399
- # Vite
400
- VITE_BELINK_ENCRYPTION_KEY=your-key
401
- VITE_BELINK_ENCRYPTION_IV=your-iv
330
+ #### 并发控制
402
331
 
403
- # Webpack/Node
404
- BELINK_ENCRYPTION_KEY=your-key
405
- BELINK_ENCRYPTION_IV=your-iv
406
- ```
332
+ 当多个请求同时发起时,时间同步使用 Promise 锁机制,确保只发起一次同步请求:
407
333
 
408
- ## 类型导出
334
+ ```
335
+ 请求1 ──┬── ensureSync() ──> 发起同步 ──> 等待完成 ──> 继续请求
336
+ 请求2 ──┤ ↓
337
+ 请求3 ──┼── ensureSync() ──> 检测到锁 ──> 等待同一个 Promise ──> 继续请求
338
+ 请求4 ──┤
339
+ 请求5 ──┘
340
+ ```
409
341
 
410
- ```typescript
411
- // 请求相关
412
- export type { RequestConfig, RequestContext, IConfig, HttpMethod } from '@be-link/request';
342
+ #### 防止系统时间篡改
413
343
 
414
- // 响应相关
415
- export type { ApiResponse, ResponseContext, IApiResponse } from '@be-link/request';
344
+ 使用 `performance.now()` 替代 `Date.now()` 计算时间流逝:
416
345
 
417
- // 拦截器
418
- export type { RequestInterceptor, ResponseInterceptor } from '@be-link/request';
346
+ - `performance.now()` 返回页面加载后的高精度时间,不受系统时间修改影响
347
+ - 即使用户修改系统时间,Token 加密的时间戳仍然准确
419
348
 
420
- // 配置
421
- export type { AppConfig, EncryptionConfig, TimeSyncConfig, CloudbaseConfig } from '@be-link/request';
349
+ ## 类型导出
422
350
 
423
- // 云函数
424
- export type { CloudFunctionOptions, CloudFunctionResponse, CloudFunctionData } from '@be-link/request';
351
+ ```typescript
352
+ // 单例实例
353
+ import { beLinkRequest } from '@be-link/request';
425
354
 
426
- // 错误类
427
- export { BaseError, HttpError, AuthError, TimeoutError, ConfigError, NetworkError } from '@be-link/request';
355
+ // 类(用于多实例)
356
+ import { BeLinkRequest } from '@be-link/request';
428
357
 
429
- // 客户端类
430
- export { HttpClient, BeLinkClient, LiveClient } from '@be-link/request';
358
+ // 服务类
359
+ import { TimeSyncService, EncryptionService } from '@be-link/request';
431
360
 
432
- // 工具函数
433
- export { isBrowser, isNode, detectEnvironment } from '@be-link/request';
361
+ // 配置类型
362
+ import type { RequestOptions, TimeSyncConfig, EncryptionConfig, ApiResponse } from '@be-link/request';
434
363
  ```
435
364
 
436
365
  ## 完整示例
437
366
 
438
367
  ```typescript
439
- import { beLinkRequest, beLinkLiveRequest, callFunction, HttpError, AuthError } from '@be-link/request';
368
+ import { beLinkRequest } from '@be-link/request';
440
369
 
441
- // 1. 初始化企业级客户端
370
+ // 应用入口初始化
442
371
  beLinkRequest.init({
443
- mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
444
- baseUrl: 'https://api.example.com',
445
- loginPath: () => {
446
- window.location.href = '/login';
372
+ baseURL: import.meta.env.VITE_API_URL,
373
+ timeout: 30000,
374
+
375
+ // 时间同步
376
+ timeSync: {
377
+ syncUrl: `${import.meta.env.VITE_API_URL}/api/time`,
447
378
  },
448
- tokenStorageKey: 'app_token',
449
- });
450
379
 
451
- // 2. 初始化直播客户端
452
- beLinkLiveRequest.init({
453
- baseUrl: 'https://live-api.example.com',
454
- });
380
+ // Token 加密
381
+ encryption: {
382
+ key: import.meta.env.VITE_ENCRYPTION_KEY,
383
+ iv: import.meta.env.VITE_ENCRYPTION_IV,
384
+ },
455
385
 
456
- // 3. 设置 Token(登录后)
457
- const token = localStorage.getItem('app_token');
458
- if (token) {
459
- beLinkRequest.setToken(token);
460
- }
386
+ // Token 获取
387
+ getToken: () => localStorage.getItem('token'),
388
+
389
+ // 用户 ID
390
+ getUserId: () => {
391
+ const user = JSON.parse(localStorage.getItem('user') || '{}');
392
+ return user.id;
393
+ },
461
394
 
462
- // 4. 监听 Token 变化
463
- beLinkRequest.setupTokenListener('app_token');
464
-
465
- // 5. 发起业务请求
466
- async function fetchUserInfo(userId: string) {
467
- try {
468
- const response = await beLinkRequest.get<{ name: string; age: number }>(`/api/users/${userId}`);
469
- return response.data;
470
- } catch (error) {
471
- if (error instanceof AuthError) {
472
- // 自动跳转登录页
473
- return null;
395
+ // 错误处理
396
+ onError: (error) => {
397
+ if (error.response?.status === 401) {
398
+ localStorage.removeItem('token');
399
+ window.location.href = '/login';
474
400
  }
475
401
  throw error;
476
- }
402
+ },
403
+ });
404
+
405
+ // API 调用
406
+ async function getUserInfo(userId: string) {
407
+ return beLinkRequest.get(`/api/users/${userId}`);
408
+ }
409
+
410
+ async function createUser(data: { name: string; age: number }) {
411
+ return beLinkRequest.post('/api/users', data);
477
412
  }
478
413
 
479
- // 6. 发起直播请求
480
- async function getLiveRoomInfo(roomId: string) {
481
- const response = await beLinkLiveRequest.get(`/api/room/${roomId}`);
482
- return response.data;
414
+ async function updateUser(userId: string, data: { name?: string; age?: number }) {
415
+ return beLinkRequest.put(`/api/users/${userId}`, data);
483
416
  }
484
417
 
485
- // 7. 调用云函数
486
- async function createOrder(productId: string) {
487
- const result = await callFunction({
488
- name: 'order-service',
489
- data: {
490
- moduleName: 'order',
491
- serviceName: 'main',
492
- funcName: 'create',
493
- param: { productId },
494
- },
495
- });
496
- return result;
418
+ async function deleteUser(userId: string) {
419
+ return beLinkRequest.delete(`/api/users/${userId}`);
497
420
  }
498
421
  ```
499
422