@be-link/ecommerce-backend-bff-service-node-sdk 0.0.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/README.md +115 -0
- package/bff/modules/BaseService.d.ts +41 -0
- package/bff/modules/BaseService.js +41 -0
- package/bff/modules/demo/service.d.ts +22 -0
- package/bff/modules/demo/service.js +49 -0
- package/bff/modules/demo/types.d.ts +28 -0
- package/bff/modules/demo/types.js +2 -0
- package/bff/modules/example/service.d.ts +46 -0
- package/bff/modules/example/service.js +89 -0
- package/bff/modules/example/types.d.ts +133 -0
- package/bff/modules/example/types.js +2 -0
- package/bff/request/client.d.ts +45 -0
- package/bff/request/client.js +62 -0
- package/bff/request/strategy.d.ts +51 -0
- package/bff/request/strategy.js +188 -0
- package/errors/index.d.ts +27 -0
- package/errors/index.js +52 -0
- package/index.d.ts +54 -0
- package/index.js +58 -0
- package/package.json +30 -0
- package/types/index.d.ts +57 -0
- package/types/index.js +2 -0
- package/utils/env.d.ts +23 -0
- package/utils/env.js +40 -0
- package/utils/http.d.ts +5 -0
- package/utils/http.js +39 -0
- package/utils/string.d.ts +12 -0
- package/utils/string.js +20 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 请求策略的抽象基类
|
|
3
|
+
* 定义所有请求策略的公共契约和共享实现
|
|
4
|
+
*/
|
|
5
|
+
export declare abstract class BaseRequestStrategy {
|
|
6
|
+
/**
|
|
7
|
+
* 运行环境配置
|
|
8
|
+
*/
|
|
9
|
+
protected environment: 'production' | 'test';
|
|
10
|
+
/**
|
|
11
|
+
* 创建请求策略实例
|
|
12
|
+
* @param environment - 运行环境 ('production' 或 'test')
|
|
13
|
+
*/
|
|
14
|
+
constructor(environment: 'production' | 'test');
|
|
15
|
+
/**
|
|
16
|
+
* 子类必须实现的请求方法
|
|
17
|
+
*/
|
|
18
|
+
abstract request<T>(path: string, data?: any, headers?: Record<string, string>): Promise<T>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Web/H5 请求策略,使用 axios 进行 HTTP 请求
|
|
22
|
+
* 为浏览器环境实现请求策略
|
|
23
|
+
*/
|
|
24
|
+
export declare class WebRequestStrategy extends BaseRequestStrategy {
|
|
25
|
+
/**
|
|
26
|
+
* 使用 axios 执行 HTTP POST 请求
|
|
27
|
+
* @param path - API 端点路径
|
|
28
|
+
* @param data - 请求负载(可选)
|
|
29
|
+
* @param headers - 要包含在请求中的额外请求头(可选)
|
|
30
|
+
* @returns Promise,解析为响应数据
|
|
31
|
+
* @throws BizError 如果是 4xx 客户端错误
|
|
32
|
+
* @throws SystemError 如果是 5xx 服务器错误或网络错误
|
|
33
|
+
*/
|
|
34
|
+
request<T>(path: string, data?: any, headers?: Record<string, string>): Promise<T>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 微信小程序请求策略,使用 wx.cloud.callContainer
|
|
38
|
+
* 为微信小程序环境实现请求策略
|
|
39
|
+
*/
|
|
40
|
+
export declare class MiniProgramRequestStrategy extends BaseRequestStrategy {
|
|
41
|
+
/**
|
|
42
|
+
* 使用微信云托管容器 API 执行请求
|
|
43
|
+
* @param path - API 端点路径
|
|
44
|
+
* @param data - 请求负载(可选)
|
|
45
|
+
* @param headers - 要包含在请求中的额外请求头(可选)
|
|
46
|
+
* @returns Promise,解析为响应数据
|
|
47
|
+
* @throws BizError 如果是 4xx 客户端错误
|
|
48
|
+
* @throws SystemError 如果是 5xx 服务器错误或微信 API 错误
|
|
49
|
+
*/
|
|
50
|
+
request<T>(path: string, data?: any, headers?: Record<string, string>): Promise<T>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
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.MiniProgramRequestStrategy = exports.WebRequestStrategy = exports.BaseRequestStrategy = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const errors_1 = require("../../errors");
|
|
9
|
+
const env_1 = require("../../utils/env");
|
|
10
|
+
/**
|
|
11
|
+
* 请求策略的抽象基类
|
|
12
|
+
* 定义所有请求策略的公共契约和共享实现
|
|
13
|
+
*/
|
|
14
|
+
class BaseRequestStrategy {
|
|
15
|
+
/**
|
|
16
|
+
* 创建请求策略实例
|
|
17
|
+
* @param environment - 运行环境 ('production' 或 'test')
|
|
18
|
+
*/
|
|
19
|
+
constructor(environment) {
|
|
20
|
+
this.environment = environment;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.BaseRequestStrategy = BaseRequestStrategy;
|
|
24
|
+
/**
|
|
25
|
+
* Web/H5 请求策略,使用 axios 进行 HTTP 请求
|
|
26
|
+
* 为浏览器环境实现请求策略
|
|
27
|
+
*/
|
|
28
|
+
class WebRequestStrategy extends BaseRequestStrategy {
|
|
29
|
+
/**
|
|
30
|
+
* 使用 axios 执行 HTTP POST 请求
|
|
31
|
+
* @param path - API 端点路径
|
|
32
|
+
* @param data - 请求负载(可选)
|
|
33
|
+
* @param headers - 要包含在请求中的额外请求头(可选)
|
|
34
|
+
* @returns Promise,解析为响应数据
|
|
35
|
+
* @throws BizError 如果是 4xx 客户端错误
|
|
36
|
+
* @throws SystemError 如果是 5xx 服务器错误或网络错误
|
|
37
|
+
*/
|
|
38
|
+
async request(path, data, headers = {}) {
|
|
39
|
+
const requestId = headers['X-Request-Id'] || 'unknown';
|
|
40
|
+
const url = `${(0, env_1.getContainerHost)(this.environment)}${path}`;
|
|
41
|
+
try {
|
|
42
|
+
if (console?.info) {
|
|
43
|
+
console.info(`[SDK][${requestId}] 发起请求: ${url}`, data ? `参数: ${JSON.stringify(data)}` : '');
|
|
44
|
+
}
|
|
45
|
+
const response = await axios_1.default.post(url, data, {
|
|
46
|
+
headers,
|
|
47
|
+
timeout: 60000, // 60 秒超时
|
|
48
|
+
});
|
|
49
|
+
// 检查响应是否表示成功
|
|
50
|
+
if (response.status >= 200 && response.status < 300) {
|
|
51
|
+
const responseData = response.data;
|
|
52
|
+
// 成功响应:返回完整响应对象(包含 data, message, success, requestId)
|
|
53
|
+
// 前端可以访问 result.data, result.message, result.success, result.requestId
|
|
54
|
+
return responseData;
|
|
55
|
+
}
|
|
56
|
+
// 非 2xx 状态码
|
|
57
|
+
const errorMessage = response.data?.message || '请求失败';
|
|
58
|
+
if (console?.error) {
|
|
59
|
+
console.error(`[SDK][${requestId}] HTTP 错误: ${errorMessage}`, `状态码: ${response.status}`);
|
|
60
|
+
}
|
|
61
|
+
const ErrorClass = response.status >= 400 && response.status < 500 ? errors_1.BizError : errors_1.SystemError;
|
|
62
|
+
throw new ErrorClass(errorMessage, response.status);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// 如果已经是 SdkError(包括 BizError 和 SystemError),直接重新抛出
|
|
66
|
+
if (error instanceof errors_1.SdkError) {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
// 处理 axios 错误
|
|
70
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
71
|
+
const axiosError = error;
|
|
72
|
+
const statusCode = axiosError.response?.status || 500;
|
|
73
|
+
const message = axiosError.response?.data?.message || axiosError.message || '请求失败';
|
|
74
|
+
if (console?.error) {
|
|
75
|
+
console.error(`[SDK][${requestId}] Axios 错误: ${message}`);
|
|
76
|
+
if (axiosError.response) {
|
|
77
|
+
console.error(`[SDK][${requestId}] 响应状态: ${axiosError.response.status}`);
|
|
78
|
+
console.error(`[SDK][${requestId}] 响应数据:`, JSON.stringify(axiosError.response.data));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.error(`[SDK][${requestId}] 错误信息: ${axiosError.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// 根据状态码选择错误类型
|
|
85
|
+
const ErrorClass = axiosError.response
|
|
86
|
+
? axiosError.response.status >= 400 && axiosError.response.status < 500
|
|
87
|
+
? errors_1.BizError
|
|
88
|
+
: errors_1.SystemError
|
|
89
|
+
: errors_1.SystemError;
|
|
90
|
+
throw new ErrorClass(message, statusCode);
|
|
91
|
+
}
|
|
92
|
+
// 处理其他未知错误
|
|
93
|
+
const errorMessage = error instanceof Error ? error.message : '发生未知错误';
|
|
94
|
+
if (console?.error) {
|
|
95
|
+
console.error(`[SDK][${requestId}] 未知错误: ${errorMessage}`);
|
|
96
|
+
}
|
|
97
|
+
throw new errors_1.SystemError(errorMessage, 500);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.WebRequestStrategy = WebRequestStrategy;
|
|
102
|
+
/**
|
|
103
|
+
* 微信小程序请求策略,使用 wx.cloud.callContainer
|
|
104
|
+
* 为微信小程序环境实现请求策略
|
|
105
|
+
*/
|
|
106
|
+
class MiniProgramRequestStrategy extends BaseRequestStrategy {
|
|
107
|
+
/**
|
|
108
|
+
* 使用微信云托管容器 API 执行请求
|
|
109
|
+
* @param path - API 端点路径
|
|
110
|
+
* @param data - 请求负载(可选)
|
|
111
|
+
* @param headers - 要包含在请求中的额外请求头(可选)
|
|
112
|
+
* @returns Promise,解析为响应数据
|
|
113
|
+
* @throws BizError 如果是 4xx 客户端错误
|
|
114
|
+
* @throws SystemError 如果是 5xx 服务器错误或微信 API 错误
|
|
115
|
+
*/
|
|
116
|
+
async request(path, data, headers = {}) {
|
|
117
|
+
const requestId = headers['X-Request-Id'] || 'unknown';
|
|
118
|
+
try {
|
|
119
|
+
// 类型守卫,确保 wx 可用
|
|
120
|
+
// 使用 any 类型避免类型定义冲突
|
|
121
|
+
const wxGlobal = typeof wx !== 'undefined' ? wx : null;
|
|
122
|
+
if (!wxGlobal || !wxGlobal.cloud) {
|
|
123
|
+
if (console?.error) {
|
|
124
|
+
console.error(`[SDK][${requestId}] 微信小程序环境不可用`);
|
|
125
|
+
}
|
|
126
|
+
throw new errors_1.SystemError('微信小程序环境不可用', 500);
|
|
127
|
+
}
|
|
128
|
+
// 构造包含微信特定字段的请求头
|
|
129
|
+
const requestHeaders = {
|
|
130
|
+
...headers,
|
|
131
|
+
'X-WX-SERVICE': (0, env_1.getContainerService)(this.environment),
|
|
132
|
+
};
|
|
133
|
+
if (console?.info) {
|
|
134
|
+
console.info(`[SDK][${requestId}] 发起小程序云调用: ${path}`, `环境: ${(0, env_1.getContainerEnv)(this.environment)}`, data ? `参数: ${JSON.stringify(data)}` : '');
|
|
135
|
+
}
|
|
136
|
+
// 调用微信云托管容器
|
|
137
|
+
const response = await wxGlobal.cloud.callContainer({
|
|
138
|
+
config: {
|
|
139
|
+
env: (0, env_1.getContainerEnv)(this.environment),
|
|
140
|
+
},
|
|
141
|
+
path,
|
|
142
|
+
method: 'POST',
|
|
143
|
+
header: requestHeaders,
|
|
144
|
+
data,
|
|
145
|
+
timeout: 100000, // 100 秒超时
|
|
146
|
+
});
|
|
147
|
+
// 记录微信云调用 ID
|
|
148
|
+
if (console?.info && response.callID) {
|
|
149
|
+
console.info(`[SDK][${requestId}] 微信云调用 ID: ${response.callID}`);
|
|
150
|
+
}
|
|
151
|
+
// 检查响应状态码是否表示成功
|
|
152
|
+
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
153
|
+
const responseData = response.data;
|
|
154
|
+
// 成功响应:返回完整响应对象(包含 data, message, success, requestId)
|
|
155
|
+
// 前端可以访问 result.data, result.message, result.success, result.requestId
|
|
156
|
+
return responseData;
|
|
157
|
+
}
|
|
158
|
+
// 非 2xx 状态码
|
|
159
|
+
const errorMessage = response.data?.message || '请求失败';
|
|
160
|
+
if (console?.error) {
|
|
161
|
+
console.error(`[SDK][${requestId}] HTTP 错误: ${errorMessage}`, `状态码: ${response.statusCode}`, `微信云调用 ID: ${response.callID || 'unknown'}`, `响应数据: ${JSON.stringify(response.data)}`);
|
|
162
|
+
}
|
|
163
|
+
const ErrorClass = response.statusCode >= 400 && response.statusCode < 500 ? errors_1.BizError : errors_1.SystemError;
|
|
164
|
+
throw new ErrorClass(errorMessage, response.statusCode);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
// 如果已经是 SdkError(包括 BizError 和 SystemError),直接重新抛出
|
|
168
|
+
if (error instanceof errors_1.SdkError) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
// 处理微信 API 错误
|
|
172
|
+
if (error && typeof error === 'object' && 'errMsg' in error) {
|
|
173
|
+
const errorMessage = error.errMsg || '微信 API 调用失败';
|
|
174
|
+
if (console?.error) {
|
|
175
|
+
console.error(`[SDK][${requestId}] 微信 API 错误: ${errorMessage}`);
|
|
176
|
+
}
|
|
177
|
+
throw new errors_1.SystemError(errorMessage, 500);
|
|
178
|
+
}
|
|
179
|
+
// 处理其他未知错误
|
|
180
|
+
const errorMessage = error instanceof Error ? error.message : '发生未知错误';
|
|
181
|
+
if (console?.error) {
|
|
182
|
+
console.error(`[SDK][${requestId}] 未知错误: ${errorMessage}`);
|
|
183
|
+
}
|
|
184
|
+
throw new errors_1.SystemError(errorMessage, 500);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.MiniProgramRequestStrategy = MiniProgramRequestStrategy;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK 相关错误的基础错误类
|
|
3
|
+
* 扩展标准 Error 类,添加 HTTP 状态码
|
|
4
|
+
*/
|
|
5
|
+
export declare class SdkError extends Error {
|
|
6
|
+
readonly statusCode: number;
|
|
7
|
+
/**
|
|
8
|
+
* 创建新的 SdkError 实例
|
|
9
|
+
* @param message - 人类可读的错误描述
|
|
10
|
+
* @param statusCode - 失败请求的 HTTP 状态码
|
|
11
|
+
*/
|
|
12
|
+
constructor(message: string, statusCode: number);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 业务错误类 - 用于 4xx 客户端错误
|
|
16
|
+
* 通常表示请求参数错误、权限不足等业务层面的问题
|
|
17
|
+
*/
|
|
18
|
+
export declare class BizError extends SdkError {
|
|
19
|
+
constructor(message: string, statusCode?: number);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 系统错误类 - 用于 5xx 服务器错误
|
|
23
|
+
* 通常表示服务器内部错误、网络问题等系统层面的问题
|
|
24
|
+
*/
|
|
25
|
+
export declare class SystemError extends SdkError {
|
|
26
|
+
constructor(message: string, statusCode?: number);
|
|
27
|
+
}
|
package/errors/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SystemError = exports.BizError = exports.SdkError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* SDK 相关错误的基础错误类
|
|
6
|
+
* 扩展标准 Error 类,添加 HTTP 状态码
|
|
7
|
+
*/
|
|
8
|
+
class SdkError extends Error {
|
|
9
|
+
/**
|
|
10
|
+
* 创建新的 SdkError 实例
|
|
11
|
+
* @param message - 人类可读的错误描述
|
|
12
|
+
* @param statusCode - 失败请求的 HTTP 状态码
|
|
13
|
+
*/
|
|
14
|
+
constructor(message, statusCode) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.statusCode = statusCode;
|
|
17
|
+
this.name = 'SdkError';
|
|
18
|
+
// 维护错误抛出位置的正确堆栈跟踪(仅在 V8 上可用)
|
|
19
|
+
if (Error.captureStackTrace) {
|
|
20
|
+
Error.captureStackTrace(this, SdkError);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.SdkError = SdkError;
|
|
25
|
+
/**
|
|
26
|
+
* 业务错误类 - 用于 4xx 客户端错误
|
|
27
|
+
* 通常表示请求参数错误、权限不足等业务层面的问题
|
|
28
|
+
*/
|
|
29
|
+
class BizError extends SdkError {
|
|
30
|
+
constructor(message, statusCode = 400) {
|
|
31
|
+
super(message, statusCode);
|
|
32
|
+
this.name = 'BizError';
|
|
33
|
+
if (Error.captureStackTrace) {
|
|
34
|
+
Error.captureStackTrace(this, BizError);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.BizError = BizError;
|
|
39
|
+
/**
|
|
40
|
+
* 系统错误类 - 用于 5xx 服务器错误
|
|
41
|
+
* 通常表示服务器内部错误、网络问题等系统层面的问题
|
|
42
|
+
*/
|
|
43
|
+
class SystemError extends SdkError {
|
|
44
|
+
constructor(message, statusCode = 500) {
|
|
45
|
+
super(message, statusCode);
|
|
46
|
+
this.name = 'SystemError';
|
|
47
|
+
if (Error.captureStackTrace) {
|
|
48
|
+
Error.captureStackTrace(this, SystemError);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.SystemError = SystemError;
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ExampleService } from './bff/modules/example/service';
|
|
2
|
+
import DemoService from './bff/modules/demo/service';
|
|
3
|
+
import type { SdkOptions } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* EcommerceBackendBffSDK - 统一平台 SDK 的主入口
|
|
6
|
+
* 支持 Web/H5 和微信小程序环境
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* // Web/H5 环境 - 生产环境
|
|
11
|
+
* const sdk = new EcommerceBackendBffSDK({
|
|
12
|
+
* getToken: async () => localStorage.getItem('token') || '',
|
|
13
|
+
* getUserId: async () => localStorage.getItem('userId') || '',
|
|
14
|
+
* environment: 'production'
|
|
15
|
+
* })
|
|
16
|
+
*
|
|
17
|
+
* // 小程序环境 - 测试环境
|
|
18
|
+
* const sdk = new EcommerceBackendBffSDK({
|
|
19
|
+
* getToken: async () => wx.getStorageSync('token') || '',
|
|
20
|
+
* getUserId: async () => wx.getStorageSync('userId') || '',
|
|
21
|
+
* environment: 'test'
|
|
22
|
+
* })
|
|
23
|
+
*
|
|
24
|
+
* // 使用服务模块
|
|
25
|
+
* const profile = await sdk.example.getUserProfile('user123')
|
|
26
|
+
* const demoResult = await sdk.demo.demoFunc({ demoParam: 'test' })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class EcommerceBackendBffSDK {
|
|
30
|
+
/**
|
|
31
|
+
* HttpClient 实例,管理平台特定的请求策略
|
|
32
|
+
*/
|
|
33
|
+
private http;
|
|
34
|
+
/**
|
|
35
|
+
* 示例服务模块,演示 SDK 使用模式
|
|
36
|
+
*/
|
|
37
|
+
example: ExampleService;
|
|
38
|
+
/**
|
|
39
|
+
* Demo 服务模块(从旧版 config 服务迁移而来)
|
|
40
|
+
*/
|
|
41
|
+
demo: DemoService;
|
|
42
|
+
/**
|
|
43
|
+
* 使用配置选项初始化 SDK
|
|
44
|
+
* @param options - SDK 配置,包含凭证回调函数
|
|
45
|
+
*/
|
|
46
|
+
constructor(options: SdkOptions);
|
|
47
|
+
}
|
|
48
|
+
export { default as DemoService } from './bff/modules/demo/service';
|
|
49
|
+
export { ExampleService } from './bff/modules/example/service';
|
|
50
|
+
export type { Service as DemoControllerTypes } from './bff/modules/demo/types';
|
|
51
|
+
export type { Service as EcommerceBackendBffServiceControllerTypes } from './bff/modules/demo/types';
|
|
52
|
+
export type { Service as ExampleServiceTypes } from './bff/modules/example/types';
|
|
53
|
+
export { SdkError, BizError, SystemError } from './errors';
|
|
54
|
+
export type { SdkOptions, IRequestStrategy, StandardResponse } from './types';
|
package/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
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.SystemError = exports.BizError = exports.SdkError = exports.ExampleService = exports.DemoService = exports.EcommerceBackendBffSDK = void 0;
|
|
7
|
+
const client_1 = require("./bff/request/client");
|
|
8
|
+
const service_1 = require("./bff/modules/example/service");
|
|
9
|
+
const service_2 = __importDefault(require("./bff/modules/demo/service"));
|
|
10
|
+
/**
|
|
11
|
+
* EcommerceBackendBffSDK - 统一平台 SDK 的主入口
|
|
12
|
+
* 支持 Web/H5 和微信小程序环境
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // Web/H5 环境 - 生产环境
|
|
17
|
+
* const sdk = new EcommerceBackendBffSDK({
|
|
18
|
+
* getToken: async () => localStorage.getItem('token') || '',
|
|
19
|
+
* getUserId: async () => localStorage.getItem('userId') || '',
|
|
20
|
+
* environment: 'production'
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* // 小程序环境 - 测试环境
|
|
24
|
+
* const sdk = new EcommerceBackendBffSDK({
|
|
25
|
+
* getToken: async () => wx.getStorageSync('token') || '',
|
|
26
|
+
* getUserId: async () => wx.getStorageSync('userId') || '',
|
|
27
|
+
* environment: 'test'
|
|
28
|
+
* })
|
|
29
|
+
*
|
|
30
|
+
* // 使用服务模块
|
|
31
|
+
* const profile = await sdk.example.getUserProfile('user123')
|
|
32
|
+
* const demoResult = await sdk.demo.demoFunc({ demoParam: 'test' })
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
class EcommerceBackendBffSDK {
|
|
36
|
+
/**
|
|
37
|
+
* 使用配置选项初始化 SDK
|
|
38
|
+
* @param options - SDK 配置,包含凭证回调函数
|
|
39
|
+
*/
|
|
40
|
+
constructor(options) {
|
|
41
|
+
// 使用提供的选项初始化 HttpClient
|
|
42
|
+
this.http = new client_1.HttpClient(options);
|
|
43
|
+
// 使用 HttpClient 实例化所有服务模块
|
|
44
|
+
this.example = new service_1.ExampleService(this.http);
|
|
45
|
+
this.demo = new service_2.default(this.http);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.EcommerceBackendBffSDK = EcommerceBackendBffSDK;
|
|
49
|
+
// 导出服务类以便直接实例化(如需要)
|
|
50
|
+
var service_3 = require("./bff/modules/demo/service");
|
|
51
|
+
Object.defineProperty(exports, "DemoService", { enumerable: true, get: function () { return __importDefault(service_3).default; } });
|
|
52
|
+
var service_4 = require("./bff/modules/example/service");
|
|
53
|
+
Object.defineProperty(exports, "ExampleService", { enumerable: true, get: function () { return service_4.ExampleService; } });
|
|
54
|
+
// 错误类
|
|
55
|
+
var errors_1 = require("./errors");
|
|
56
|
+
Object.defineProperty(exports, "SdkError", { enumerable: true, get: function () { return errors_1.SdkError; } });
|
|
57
|
+
Object.defineProperty(exports, "BizError", { enumerable: true, get: function () { return errors_1.BizError; } });
|
|
58
|
+
Object.defineProperty(exports, "SystemError", { enumerable: true, get: function () { return errors_1.SystemError; } });
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@be-link/ecommerce-backend-bff-service-node-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "EcommerceBackendBffService Node.js SDK",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"author": "",
|
|
8
|
+
"license": "ISC",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"axios": "1.13.2"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/jest": "^29.5.0",
|
|
17
|
+
"@types/node": "^20.0.0",
|
|
18
|
+
"jest": "^29.5.0",
|
|
19
|
+
"ts-jest": "^29.1.0",
|
|
20
|
+
"tsoa": "^6.6.0",
|
|
21
|
+
"typescript": "^5.0.0"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "rm -rf ./dist && tsc && cp package.json README.md ./dist/ 2>/dev/null || true",
|
|
25
|
+
"test": "jest --passWithNoTests",
|
|
26
|
+
"test:coverage": "jest --coverage --passWithNoTests",
|
|
27
|
+
"swagger": "tsoa spec",
|
|
28
|
+
"build:swagger": "npm run swagger && npm run build && npm run swagger"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK 初始化的配置选项
|
|
3
|
+
*/
|
|
4
|
+
export interface SdkOptions {
|
|
5
|
+
/**
|
|
6
|
+
* 获取认证令牌的回调函数
|
|
7
|
+
* 可以是同步或异步的
|
|
8
|
+
*/
|
|
9
|
+
getToken: () => string | Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* 获取用户 ID 的回调函数
|
|
12
|
+
* 可以是同步或异步的
|
|
13
|
+
*/
|
|
14
|
+
getUserId: () => string | Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* 运行环境配置(必传)
|
|
17
|
+
* - 'production': 生产环境
|
|
18
|
+
* - 'test': 测试环境
|
|
19
|
+
*/
|
|
20
|
+
environment: 'production' | 'test';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 后端标准响应格式
|
|
24
|
+
* 所有 API 响应都遵循此格式
|
|
25
|
+
*/
|
|
26
|
+
export interface StandardResponse<T> {
|
|
27
|
+
/**
|
|
28
|
+
* 业务数据
|
|
29
|
+
*/
|
|
30
|
+
data: T;
|
|
31
|
+
/**
|
|
32
|
+
* 响应消息
|
|
33
|
+
*/
|
|
34
|
+
message: string;
|
|
35
|
+
/**
|
|
36
|
+
* 是否成功
|
|
37
|
+
*/
|
|
38
|
+
success: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* 请求 ID,用于追踪和调试
|
|
41
|
+
*/
|
|
42
|
+
requestId: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 平台特定请求策略的接口
|
|
46
|
+
* 实现处理 Web/H5 HTTP 请求或微信小程序云托管容器调用
|
|
47
|
+
*/
|
|
48
|
+
export interface IRequestStrategy {
|
|
49
|
+
/**
|
|
50
|
+
* 向后端服务执行请求
|
|
51
|
+
* @param path - API 端点路径(例如 '/api/v1/user/get-profile')
|
|
52
|
+
* @param data - 请求负载(可选)
|
|
53
|
+
* @param headers - 额外的自定义请求头(可选)
|
|
54
|
+
* @returns Promise,解析为响应数据
|
|
55
|
+
*/
|
|
56
|
+
request<T>(path: string, data?: any, headers?: Record<string, string>): Promise<T>;
|
|
57
|
+
}
|
package/types/index.js
ADDED
package/utils/env.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 检测运行时环境是否为微信小程序
|
|
3
|
+
* @returns 如果在微信小程序环境中运行则返回 true
|
|
4
|
+
*/
|
|
5
|
+
export declare function isMiniProgram(): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* 根据环境获取容器主机 URL
|
|
8
|
+
* @param environment - 运行环境 ('production' 或 'test')
|
|
9
|
+
* @returns 容器主机 URL
|
|
10
|
+
*/
|
|
11
|
+
export declare function getContainerHost(environment: 'production' | 'test'): string;
|
|
12
|
+
/**
|
|
13
|
+
* 根据环境获取微信云环境标识符
|
|
14
|
+
* @param environment - 运行环境 ('production' 或 'test')
|
|
15
|
+
* @returns 云环境标识符
|
|
16
|
+
*/
|
|
17
|
+
export declare function getContainerEnv(environment: 'production' | 'test'): string;
|
|
18
|
+
/**
|
|
19
|
+
* 根据环境获取微信云服务名称
|
|
20
|
+
* @param environment - 运行环境 ('production' 或 'test')
|
|
21
|
+
* @returns 云服务名称
|
|
22
|
+
*/
|
|
23
|
+
export declare function getContainerService(environment: 'production' | 'test'): string;
|
package/utils/env.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isMiniProgram = isMiniProgram;
|
|
4
|
+
exports.getContainerHost = getContainerHost;
|
|
5
|
+
exports.getContainerEnv = getContainerEnv;
|
|
6
|
+
exports.getContainerService = getContainerService;
|
|
7
|
+
/**
|
|
8
|
+
* 检测运行时环境是否为微信小程序
|
|
9
|
+
* @returns 如果在微信小程序环境中运行则返回 true
|
|
10
|
+
*/
|
|
11
|
+
function isMiniProgram() {
|
|
12
|
+
return typeof wx !== 'undefined' && !!wx.cloud;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 根据环境获取容器主机 URL
|
|
16
|
+
* @param environment - 运行环境 ('production' 或 'test')
|
|
17
|
+
* @returns 容器主机 URL
|
|
18
|
+
*/
|
|
19
|
+
function getContainerHost(environment) {
|
|
20
|
+
// 生产环境和测试环境使用不同的主机地址
|
|
21
|
+
return environment === 'production'
|
|
22
|
+
? 'http://bxbvjnca.tfs.cwl9ok0a.mk2u3r3l.com:8090' // 生产环境
|
|
23
|
+
: 'http://bxbvjnca.tfs.cwl9ok0a.mk2u3r3l.com:8090'; // 测试环境
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 根据环境获取微信云环境标识符
|
|
27
|
+
* @param environment - 运行环境 ('production' 或 'test')
|
|
28
|
+
* @returns 云环境标识符
|
|
29
|
+
*/
|
|
30
|
+
function getContainerEnv(environment) {
|
|
31
|
+
return environment === 'production' ? 'prod-1g8x9y7z' : 'test-2h3j4k5l';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 根据环境获取微信云服务名称
|
|
35
|
+
* @param environment - 运行环境 ('production' 或 'test')
|
|
36
|
+
* @returns 云服务名称
|
|
37
|
+
*/
|
|
38
|
+
function getContainerService(environment) {
|
|
39
|
+
return environment === 'production' ? 'bff-container-prod' : 'bff-container-test';
|
|
40
|
+
}
|
package/utils/http.d.ts
ADDED
package/utils/http.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
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.callApi = callApi;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
/**
|
|
9
|
+
* 生成简单的请求 ID
|
|
10
|
+
*/
|
|
11
|
+
function generateRequestId() {
|
|
12
|
+
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 旧版 callApi 函数,用于向后兼容
|
|
16
|
+
* @deprecated 请改用新的 HttpClient 和服务模块
|
|
17
|
+
*/
|
|
18
|
+
async function callApi(url, request) {
|
|
19
|
+
const requestId = generateRequestId();
|
|
20
|
+
try {
|
|
21
|
+
console.info(`准备发起ecommerce-backend-bff-service请求[${requestId}]: ${url}, 参数: ${JSON.stringify(request)}`);
|
|
22
|
+
const response = await axios_1.default.post(url, request, { headers: { 'x-request-id': requestId } });
|
|
23
|
+
const responseData = response.data;
|
|
24
|
+
return responseData.data;
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const axiosError = error;
|
|
28
|
+
if (axiosError.response) {
|
|
29
|
+
const response = axiosError.response;
|
|
30
|
+
const data = response.data;
|
|
31
|
+
console.error(`ecommerce-backend-bff-service 异常: ${axiosError.message},requestId: ${requestId}`);
|
|
32
|
+
console.info('响应信息', data.message);
|
|
33
|
+
console.error('异常堆栈', JSON.stringify(error.stack));
|
|
34
|
+
throw new Error(data.errorType + ' - ' + data.message);
|
|
35
|
+
}
|
|
36
|
+
console.error(`ecommerce-backend-bff-service 未知异常: ${axiosError.message}`, error.stack);
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将驼峰命名字符串转换为短横线命名
|
|
3
|
+
* @param str 驼峰命名字符串
|
|
4
|
+
* @returns 短横线命名字符串
|
|
5
|
+
* @example
|
|
6
|
+
* camelToKebabCase('fetchConfig') // 'fetch-config'
|
|
7
|
+
* camelToKebabCase('getCosTempSecret') // 'get-cos-temp-secret'
|
|
8
|
+
* camelToKebabCase('getUserProfile') // 'get-user-profile'
|
|
9
|
+
* camelToKebabCase('API') // 'api'
|
|
10
|
+
* camelToKebabCase('myHTTPRequest') // 'my-http-request'
|
|
11
|
+
*/
|
|
12
|
+
export declare function camelToKebabCase(str: string): string;
|
package/utils/string.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.camelToKebabCase = camelToKebabCase;
|
|
4
|
+
/**
|
|
5
|
+
* 将驼峰命名字符串转换为短横线命名
|
|
6
|
+
* @param str 驼峰命名字符串
|
|
7
|
+
* @returns 短横线命名字符串
|
|
8
|
+
* @example
|
|
9
|
+
* camelToKebabCase('fetchConfig') // 'fetch-config'
|
|
10
|
+
* camelToKebabCase('getCosTempSecret') // 'get-cos-temp-secret'
|
|
11
|
+
* camelToKebabCase('getUserProfile') // 'get-user-profile'
|
|
12
|
+
* camelToKebabCase('API') // 'api'
|
|
13
|
+
* camelToKebabCase('myHTTPRequest') // 'my-http-request'
|
|
14
|
+
*/
|
|
15
|
+
function camelToKebabCase(str) {
|
|
16
|
+
return str
|
|
17
|
+
.replace(/([A-Z])/g, '-$1')
|
|
18
|
+
.toLowerCase()
|
|
19
|
+
.replace(/^-/, ''); // 如果存在则移除前导连字符
|
|
20
|
+
}
|