@be-link/ecommerce-promotion-service-node-sdk 0.1.23 → 0.1.25
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/modules/BaseService.d.ts +1 -1
- package/modules/BaseService.js +1 -1
- package/modules/award/service.d.ts +0 -1
- package/modules/award/service.js +0 -8
- package/modules/award/types.d.ts +2 -52
- package/modules/pointsMall/service.d.ts +1 -0
- package/modules/pointsMall/service.js +8 -0
- package/modules/pointsMall/types.d.ts +28 -0
- package/modules/pricingCalculation/types.d.ts +0 -6
- package/package.json +1 -1
- package/utils/http.d.ts +22 -0
- package/utils/http.js +140 -38
package/modules/BaseService.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export default abstract class BaseService {
|
|
|
8
8
|
protected abstract prefixUrl: string;
|
|
9
9
|
/** 子网域名 */
|
|
10
10
|
protected readonly natDevHost = "http://192.168.3.168:8090/promotion";
|
|
11
|
-
protected readonly natProdHost = "http://
|
|
11
|
+
protected readonly natProdHost = "http://192.168.87.191:8090/promotion";
|
|
12
12
|
/** 公网域名 */
|
|
13
13
|
protected readonly publicDevHost = "https://ecommerce-dev.wejourney.top/promotion";
|
|
14
14
|
protected readonly publicProdHost = "";
|
package/modules/BaseService.js
CHANGED
|
@@ -13,7 +13,7 @@ class BaseService {
|
|
|
13
13
|
constructor() {
|
|
14
14
|
/** 子网域名 */
|
|
15
15
|
this.natDevHost = 'http://192.168.3.168:8090/promotion';
|
|
16
|
-
this.natProdHost = 'http://
|
|
16
|
+
this.natProdHost = 'http://192.168.87.191:8090/promotion';
|
|
17
17
|
/** 公网域名 */
|
|
18
18
|
this.publicDevHost = 'https://ecommerce-dev.wejourney.top/promotion';
|
|
19
19
|
this.publicProdHost = '';
|
|
@@ -7,7 +7,6 @@ declare class AwardService extends BaseService implements Service.PrizeDistribut
|
|
|
7
7
|
liveRoomIssueCoupon(request: Service.Request.liveRoomIssueCoupon): Promise<Service.Response.distributePrize>;
|
|
8
8
|
queryLiveRoomDistributionStats(request: Service.Request.queryLiveRoomDistributionStats): Promise<Service.Response.queryLiveRoomDistributionStatsResponse>;
|
|
9
9
|
adminSingleIssueCoupon(request: Service.Request.adminSingleIssueCoupon): Promise<Service.Response.distributePrize>;
|
|
10
|
-
batchIssueCoupon(request: Service.Request.batchIssueCoupon): Promise<Service.Response.distributePrize>;
|
|
11
10
|
}
|
|
12
11
|
export declare const awardService: AwardService;
|
|
13
12
|
export default awardService;
|
package/modules/award/service.js
CHANGED
|
@@ -36,9 +36,6 @@ let AwardService = class AwardService extends BaseService_1.default {
|
|
|
36
36
|
adminSingleIssueCoupon(request) {
|
|
37
37
|
return (0, http_1.callApi)(this.getApiUrl(this.adminSingleIssueCoupon), request);
|
|
38
38
|
}
|
|
39
|
-
batchIssueCoupon(request) {
|
|
40
|
-
return (0, http_1.callApi)(this.getApiUrl(this.batchIssueCoupon), request);
|
|
41
|
-
}
|
|
42
39
|
};
|
|
43
40
|
__decorate([
|
|
44
41
|
(0, tsoa_1.OperationId)('发放奖励'),
|
|
@@ -65,11 +62,6 @@ __decorate([
|
|
|
65
62
|
(0, tsoa_1.Post)('admin-single-issue-coupon'),
|
|
66
63
|
__param(0, (0, tsoa_1.Body)())
|
|
67
64
|
], AwardService.prototype, "adminSingleIssueCoupon", null);
|
|
68
|
-
__decorate([
|
|
69
|
-
(0, tsoa_1.OperationId)('批量发放券'),
|
|
70
|
-
(0, tsoa_1.Post)('batch-issue-coupon'),
|
|
71
|
-
__param(0, (0, tsoa_1.Body)())
|
|
72
|
-
], AwardService.prototype, "batchIssueCoupon", null);
|
|
73
65
|
AwardService = __decorate([
|
|
74
66
|
(0, tsoa_1.Route)('award'),
|
|
75
67
|
(0, tsoa_1.Tags)('Award')
|
package/modules/award/types.d.ts
CHANGED
|
@@ -47,30 +47,8 @@ export declare namespace Service {
|
|
|
47
47
|
namespace Request {
|
|
48
48
|
/** 发放奖励 */
|
|
49
49
|
interface distributePrize {
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
/** 用户ID */
|
|
53
|
-
userId: string;
|
|
54
|
-
/** 任务ID */
|
|
55
|
-
taskId: string;
|
|
56
|
-
/** 任务名称 */
|
|
57
|
-
taskName: string;
|
|
58
|
-
/** 任务类型 */
|
|
59
|
-
taskType: string;
|
|
60
|
-
/** 奖品类型 */
|
|
61
|
-
prizeType: string;
|
|
62
|
-
/** 直播间id */
|
|
63
|
-
liveStreamRoomId: string;
|
|
64
|
-
/** 业务ID */
|
|
65
|
-
bizId: string;
|
|
66
|
-
/** 发放数量 */
|
|
67
|
-
quantity: number;
|
|
68
|
-
/** 轮次编号(观看任务专用) */
|
|
69
|
-
roundNumber?: number;
|
|
70
|
-
/** 订单ID(满赠/买赠专用) */
|
|
71
|
-
orderId?: string;
|
|
72
|
-
/** 订单明细ID(买赠专用) */
|
|
73
|
-
orderDetailId?: string;
|
|
50
|
+
/** 业务编号 */
|
|
51
|
+
bizNo: string;
|
|
74
52
|
}
|
|
75
53
|
/** 作废奖励 */
|
|
76
54
|
interface reversePrize {
|
|
@@ -124,32 +102,6 @@ export declare namespace Service {
|
|
|
124
102
|
couponId: string;
|
|
125
103
|
/** 用户ID */
|
|
126
104
|
userId: string;
|
|
127
|
-
/** 数量 */
|
|
128
|
-
quantity?: number;
|
|
129
|
-
}
|
|
130
|
-
/** 批量发放券请求项 */
|
|
131
|
-
interface batchIssueCouponItem {
|
|
132
|
-
/** 业务编号 */
|
|
133
|
-
bizNo: string;
|
|
134
|
-
/** 直播间ID */
|
|
135
|
-
liveRoomId: string;
|
|
136
|
-
/** 用户ID */
|
|
137
|
-
userId: string;
|
|
138
|
-
/** 优惠券ID */
|
|
139
|
-
couponId: string;
|
|
140
|
-
/** 数量 */
|
|
141
|
-
quantity: number;
|
|
142
|
-
/** 业务ID */
|
|
143
|
-
bizId: string;
|
|
144
|
-
/** 操作人ID */
|
|
145
|
-
operatorId: string;
|
|
146
|
-
}
|
|
147
|
-
/** 批量发放券 */
|
|
148
|
-
interface batchIssueCoupon {
|
|
149
|
-
/** 批量发放券请求列表 */
|
|
150
|
-
requests: batchIssueCouponItem[];
|
|
151
|
-
/** 发放渠道 */
|
|
152
|
-
getChannel: ENUM.AWARD_ENUM.DISTRIBUTION_CHANNEL;
|
|
153
105
|
}
|
|
154
106
|
}
|
|
155
107
|
namespace Response {
|
|
@@ -186,7 +138,5 @@ export declare namespace Service {
|
|
|
186
138
|
queryLiveRoomDistributionStats(request: Service.Request.queryLiveRoomDistributionStats, req: any): Promise<Service.Response.queryLiveRoomDistributionStatsResponse>;
|
|
187
139
|
/** 后台单个发券 */
|
|
188
140
|
adminSingleIssueCoupon(request: Service.Request.adminSingleIssueCoupon, req: any): Promise<Service.Response.distributePrize>;
|
|
189
|
-
/** 批量发放券 */
|
|
190
|
-
batchIssueCoupon(request: Service.Request.batchIssueCoupon, req: any): Promise<Service.Response.distributePrize>;
|
|
191
141
|
}
|
|
192
142
|
}
|
|
@@ -9,6 +9,7 @@ declare class PointsMallService extends BaseService implements Service.PointsMal
|
|
|
9
9
|
getProductDetail(request: Service.Request.getProductDetail): Promise<Service.Response.getProductDetail>;
|
|
10
10
|
queryEnabledAndOnlineProducts(request: Service.Request.queryEnabledAndOnlineProducts): Promise<Service.Response.queryEnabledAndOnlineProducts>;
|
|
11
11
|
getAvailableCouponCountForProduct(request: Service.Request.getAvailableCouponCountForProduct): Promise<Service.Response.getAvailableCouponCountForProduct>;
|
|
12
|
+
getConsumptionThresholdInfo(request: Service.Request.getConsumptionThresholdInfo): Promise<Service.Response.getConsumptionThresholdInfo>;
|
|
12
13
|
}
|
|
13
14
|
export declare const pointsMallService: PointsMallService;
|
|
14
15
|
export default pointsMallService;
|
|
@@ -42,6 +42,9 @@ let PointsMallService = class PointsMallService extends BaseService_1.default {
|
|
|
42
42
|
getAvailableCouponCountForProduct(request) {
|
|
43
43
|
return (0, http_1.callApi)(this.getApiUrl(this.getAvailableCouponCountForProduct), request);
|
|
44
44
|
}
|
|
45
|
+
getConsumptionThresholdInfo(request) {
|
|
46
|
+
return (0, http_1.callApi)(this.getApiUrl(this.getConsumptionThresholdInfo), request);
|
|
47
|
+
}
|
|
45
48
|
};
|
|
46
49
|
__decorate([
|
|
47
50
|
(0, tsoa_1.OperationId)('批量创建积分商城商品'),
|
|
@@ -78,6 +81,11 @@ __decorate([
|
|
|
78
81
|
(0, tsoa_1.Post)('get-available-coupon-count-for-product'),
|
|
79
82
|
__param(0, (0, tsoa_1.Body)())
|
|
80
83
|
], PointsMallService.prototype, "getAvailableCouponCountForProduct", null);
|
|
84
|
+
__decorate([
|
|
85
|
+
(0, tsoa_1.OperationId)('查询商品消费门槛校验信息'),
|
|
86
|
+
(0, tsoa_1.Post)('get-consumption-threshold-info'),
|
|
87
|
+
__param(0, (0, tsoa_1.Body)())
|
|
88
|
+
], PointsMallService.prototype, "getConsumptionThresholdInfo", null);
|
|
81
89
|
PointsMallService = __decorate([
|
|
82
90
|
(0, tsoa_1.Route)('points-mall'),
|
|
83
91
|
(0, tsoa_1.Tags)('PointsMall')
|
|
@@ -23,6 +23,10 @@ export declare namespace Service {
|
|
|
23
23
|
enabled: number;
|
|
24
24
|
/** 排序权重 */
|
|
25
25
|
sortOrder: number;
|
|
26
|
+
/** 消费周期天数 */
|
|
27
|
+
consumptionPeriodDays: number;
|
|
28
|
+
/** 消费门槛金额(单位:分) */
|
|
29
|
+
consumptionThresholdAmount: number;
|
|
26
30
|
}
|
|
27
31
|
/** 后台管理列表 - 完整12个字段 */
|
|
28
32
|
interface AdminProductItem {
|
|
@@ -42,6 +46,8 @@ export declare namespace Service {
|
|
|
42
46
|
limitPerUser?: number;
|
|
43
47
|
pointsRequired: number;
|
|
44
48
|
couponRequired: number;
|
|
49
|
+
consumptionPeriodDays: number | null;
|
|
50
|
+
consumptionThresholdAmount: number | null;
|
|
45
51
|
productImage: string;
|
|
46
52
|
}
|
|
47
53
|
/** C端积分商城列表 - 精确7个字段 */
|
|
@@ -64,6 +70,8 @@ export declare namespace Service {
|
|
|
64
70
|
exchangeType: ENUM.POINT_MALL.ExchangeType;
|
|
65
71
|
enabled?: number;
|
|
66
72
|
sortOrder?: number;
|
|
73
|
+
consumptionPeriodDays?: number;
|
|
74
|
+
consumptionThresholdAmount?: number;
|
|
67
75
|
}
|
|
68
76
|
export interface CreateProduct extends BaseParams {
|
|
69
77
|
}
|
|
@@ -121,6 +129,13 @@ export declare namespace Service {
|
|
|
121
129
|
/** 是否开启 */
|
|
122
130
|
enabled?: number;
|
|
123
131
|
}
|
|
132
|
+
/** 查询商品消费门槛校验信息 */
|
|
133
|
+
export interface getConsumptionThresholdInfo {
|
|
134
|
+
/** 用户ID */
|
|
135
|
+
userId: string;
|
|
136
|
+
/** 商品ID */
|
|
137
|
+
productId: string;
|
|
138
|
+
}
|
|
124
139
|
export {};
|
|
125
140
|
}
|
|
126
141
|
namespace Response {
|
|
@@ -166,6 +181,17 @@ export declare namespace Service {
|
|
|
166
181
|
/** 可用优惠券数量 */
|
|
167
182
|
count: number;
|
|
168
183
|
}
|
|
184
|
+
/** 查询商品消费门槛校验信息响应 */
|
|
185
|
+
interface getConsumptionThresholdInfo {
|
|
186
|
+
/** 是否需要校验消费门槛 */
|
|
187
|
+
needValidation: boolean;
|
|
188
|
+
/** 消费金额(单位:分,不需要校验返回-1) */
|
|
189
|
+
consumptionAmount: number;
|
|
190
|
+
/** 门槛金额(单位:分,不需要校验返回-1) */
|
|
191
|
+
thresholdAmount: number;
|
|
192
|
+
/** 是否满足消费门槛 */
|
|
193
|
+
isThresholdMet: boolean;
|
|
194
|
+
}
|
|
169
195
|
}
|
|
170
196
|
interface PointsMallController {
|
|
171
197
|
/** 批量创建积分商城商品 */
|
|
@@ -182,5 +208,7 @@ export declare namespace Service {
|
|
|
182
208
|
queryEnabledAndOnlineProducts(request: Service.Request.queryEnabledAndOnlineProducts): Promise<Service.Response.queryEnabledAndOnlineProducts>;
|
|
183
209
|
/** 查询指定商品用户可用优惠券的数量 */
|
|
184
210
|
getAvailableCouponCountForProduct(request: Service.Request.getAvailableCouponCountForProduct): Promise<Service.Response.getAvailableCouponCountForProduct>;
|
|
211
|
+
/** 查询商品消费门槛校验信息 */
|
|
212
|
+
getConsumptionThresholdInfo(request: Service.Request.getConsumptionThresholdInfo): Promise<Service.Response.getConsumptionThresholdInfo>;
|
|
185
213
|
}
|
|
186
214
|
}
|
|
@@ -153,12 +153,6 @@ export declare namespace Service {
|
|
|
153
153
|
originalAmount: number;
|
|
154
154
|
/** 券优惠金额(分) */
|
|
155
155
|
couponDiscountAmount: number;
|
|
156
|
-
/** 会员优惠金额(分) */
|
|
157
|
-
memberDiscount: {
|
|
158
|
-
totalAmount: number;
|
|
159
|
-
itemAmount: number;
|
|
160
|
-
quantity: number;
|
|
161
|
-
};
|
|
162
156
|
/** 优惠后金额(分) */
|
|
163
157
|
discountedAmount: number;
|
|
164
158
|
/** 积分最高抵扣金额(分) */
|
package/package.json
CHANGED
package/utils/http.d.ts
CHANGED
|
@@ -9,3 +9,25 @@ declare module '@fastify/request-context' {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
export declare function callApi<T extends (...args: any[]) => Promise<any>>(url: string, request?: Parameters<T>[0]): Promise<Awaited<ReturnType<T>>>;
|
|
12
|
+
export declare function getConnectionStats(): {
|
|
13
|
+
activeRequests: number;
|
|
14
|
+
totalRequests: number;
|
|
15
|
+
peakActiveRequests: number;
|
|
16
|
+
httpSockets: {
|
|
17
|
+
total: number;
|
|
18
|
+
hosts: number;
|
|
19
|
+
details: NodeJS.ReadOnlyDict<import("net").Socket[]>;
|
|
20
|
+
};
|
|
21
|
+
httpsSockets: {
|
|
22
|
+
total: number;
|
|
23
|
+
hosts: number;
|
|
24
|
+
details: NodeJS.ReadOnlyDict<import("net").Socket[]>;
|
|
25
|
+
};
|
|
26
|
+
config: {
|
|
27
|
+
maxSockets: number;
|
|
28
|
+
maxTotalSockets: number;
|
|
29
|
+
requestTimeout: number;
|
|
30
|
+
note: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export declare function resetPeakStats(): void;
|
package/utils/http.js
CHANGED
|
@@ -4,22 +4,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.callApi = callApi;
|
|
7
|
+
exports.getConnectionStats = getConnectionStats;
|
|
8
|
+
exports.resetPeakStats = resetPeakStats;
|
|
7
9
|
const axios_1 = __importDefault(require("axios"));
|
|
8
10
|
const uuid_1 = require("uuid");
|
|
9
11
|
const axios_retry_1 = __importDefault(require("axios-retry"));
|
|
10
12
|
const request_context_1 = require("@fastify/request-context");
|
|
13
|
+
const safe_stable_stringify_1 = __importDefault(require("safe-stable-stringify"));
|
|
11
14
|
const http_1 = __importDefault(require("http"));
|
|
12
15
|
const https_1 = __importDefault(require("https"));
|
|
13
|
-
//
|
|
16
|
+
// 针对高并发优化配置 - 支持高并发场景
|
|
14
17
|
const HTTP_CONFIG = {
|
|
15
|
-
maxSockets:
|
|
16
|
-
maxFreeSockets: 1000, //
|
|
17
|
-
maxTotalSockets:
|
|
18
|
-
keepAliveMsecs:
|
|
19
|
-
timeout:
|
|
20
|
-
requestTimeout:
|
|
21
|
-
retries:
|
|
22
|
-
retryBaseDelay:
|
|
18
|
+
maxSockets: 2000, // 每个主机的最大socket连接数(支持2000并发)
|
|
19
|
+
maxFreeSockets: 1000, // 空闲socket保留数(50%复用率,提高连接复用效率)
|
|
20
|
+
maxTotalSockets: 3000, // 所有主机总socket数(单后端场景优化)
|
|
21
|
+
keepAliveMsecs: 30000, // 保持连接30秒(平衡资源占用和连接复用)
|
|
22
|
+
timeout: 3000, // socket超时3秒(高并发场景快速失败)
|
|
23
|
+
requestTimeout: 3000, // 请求超时3秒(快速释放资源,避免慢请求占用连接)
|
|
24
|
+
retries: 1, // 重试1次(减少重试,避免高并发时加重后端压力)
|
|
25
|
+
retryBaseDelay: 100, // 基础重试延迟100ms(给服务恢复时间)
|
|
26
|
+
logThreshold: 1000, // 日志采样率(每1000个请求采样1次,降低日志开销)
|
|
27
|
+
monitorInterval: 5000, // 监控日志间隔(毫秒)
|
|
23
28
|
};
|
|
24
29
|
// 配置 HTTP/HTTPS Agent 以支持高并发连接池和 keepAlive
|
|
25
30
|
const httpAgent = new http_1.default.Agent({
|
|
@@ -29,7 +34,7 @@ const httpAgent = new http_1.default.Agent({
|
|
|
29
34
|
maxFreeSockets: HTTP_CONFIG.maxFreeSockets,
|
|
30
35
|
maxTotalSockets: HTTP_CONFIG.maxTotalSockets,
|
|
31
36
|
timeout: HTTP_CONFIG.timeout,
|
|
32
|
-
scheduling: '
|
|
37
|
+
scheduling: 'fifo', // 先进先出调度
|
|
33
38
|
});
|
|
34
39
|
const httpsAgent = new https_1.default.Agent({
|
|
35
40
|
keepAlive: true,
|
|
@@ -38,7 +43,7 @@ const httpsAgent = new https_1.default.Agent({
|
|
|
38
43
|
maxFreeSockets: HTTP_CONFIG.maxFreeSockets,
|
|
39
44
|
maxTotalSockets: HTTP_CONFIG.maxTotalSockets,
|
|
40
45
|
timeout: HTTP_CONFIG.timeout,
|
|
41
|
-
scheduling: '
|
|
46
|
+
scheduling: 'fifo',
|
|
42
47
|
});
|
|
43
48
|
// 配置 axios 默认使用这些 agent
|
|
44
49
|
axios_1.default.defaults.httpAgent = httpAgent;
|
|
@@ -52,16 +57,10 @@ axios_1.default.defaults.maxContentLength = 50 * 1024 * 1024; // 50MB 最大响
|
|
|
52
57
|
retryCondition(error) {
|
|
53
58
|
// 重试临时错误和特定的网络错误
|
|
54
59
|
const status = error.response?.status;
|
|
55
|
-
const isNetworkError = error.code === 'ECONNRESET' ||
|
|
56
|
-
error.code === 'ETIMEDOUT' ||
|
|
57
|
-
error.code === 'ECONNREFUSED' ||
|
|
58
|
-
error.code === 'ENOTFOUND' ||
|
|
59
|
-
error.code === 'ENETUNREACH' ||
|
|
60
|
-
error.code === 'EAI_AGAIN';
|
|
60
|
+
const isNetworkError = error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT';
|
|
61
61
|
const isServerError = status === 502 || status === 503 || status === 504;
|
|
62
62
|
const isRateLimited = status === 429;
|
|
63
|
-
|
|
64
|
-
return isNetworkError || isServerError || isRateLimited || isTimeout;
|
|
63
|
+
return isNetworkError || isServerError || isRateLimited;
|
|
65
64
|
},
|
|
66
65
|
retryDelay: (retryCount, error) => {
|
|
67
66
|
// 指数退避 + 抖动
|
|
@@ -74,35 +73,138 @@ axios_1.default.defaults.maxContentLength = 50 * 1024 * 1024; // 50MB 最大响
|
|
|
74
73
|
}
|
|
75
74
|
return 1000 + Math.random() * 1000; // 1-2秒
|
|
76
75
|
}
|
|
77
|
-
// 指数退避:200ms, 400ms
|
|
76
|
+
// 指数退避:100ms, 200ms, 400ms...
|
|
78
77
|
const exponentialDelay = HTTP_CONFIG.retryBaseDelay * Math.pow(2, retryCount - 1);
|
|
79
|
-
const jitter = Math.random() *
|
|
80
|
-
return Math.min(exponentialDelay + jitter,
|
|
78
|
+
const jitter = Math.random() * 50; // 0-50ms 抖动
|
|
79
|
+
return Math.min(exponentialDelay + jitter, 2000); // 最多延迟2秒
|
|
81
80
|
},
|
|
82
81
|
shouldResetTimeout: true, // 重试时重置超时
|
|
83
82
|
});
|
|
83
|
+
// 连接池状态监控
|
|
84
|
+
let requestCount = 0;
|
|
85
|
+
let activeRequests = 0;
|
|
86
|
+
let peakActiveRequests = 0; // 峰值并发
|
|
84
87
|
async function callApi(url, request) {
|
|
85
|
-
//
|
|
88
|
+
// 不再等待应用层队列,直接发起请求,由底层socket连接池控制并发
|
|
89
|
+
const currentRequestId = ++requestCount;
|
|
90
|
+
activeRequests++;
|
|
91
|
+
// 记录峰值并发
|
|
92
|
+
if (activeRequests > peakActiveRequests) {
|
|
93
|
+
peakActiveRequests = activeRequests;
|
|
94
|
+
}
|
|
95
|
+
// 批量获取请求上下文,减少函数调用开销
|
|
86
96
|
const ctx = request_context_1.requestContext.get('requestId') ? request_context_1.requestContext : null;
|
|
87
97
|
const requestId = ctx?.get('requestId') || ctx?.get('traceMessageId') || (0, uuid_1.v4)();
|
|
88
98
|
const pandoraUserId = ctx?.get('pandoraUserId') || '';
|
|
89
99
|
const beLinkUserId = ctx?.get('beLinkUserId') || '';
|
|
90
100
|
const pandoraRoleId = ctx?.get('pandoraRoleId') || '';
|
|
91
101
|
const realIp = ctx?.get('realIp') || '';
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
// 采样日志决策(提前计算,避免重复判断)
|
|
103
|
+
const shouldLog = currentRequestId % HTTP_CONFIG.logThreshold === 0;
|
|
104
|
+
const startTime = shouldLog ? Date.now() : 0;
|
|
105
|
+
try {
|
|
106
|
+
if (shouldLog) {
|
|
107
|
+
console.log((0, safe_stable_stringify_1.default)({
|
|
108
|
+
message: '发起HTTP请求',
|
|
109
|
+
currentRequestId,
|
|
110
|
+
activeRequests,
|
|
111
|
+
requestId,
|
|
112
|
+
url,
|
|
113
|
+
type: 'http_request_start',
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
const response = await axios_1.default.post(url, request || {}, {
|
|
117
|
+
headers: {
|
|
118
|
+
'x-request-id': requestId,
|
|
119
|
+
'x-belink-pandora-userid': pandoraUserId,
|
|
120
|
+
'x-belink-userid': beLinkUserId,
|
|
121
|
+
'x-belink-pandora-roleid': pandoraRoleId,
|
|
122
|
+
'x-real-ip': realIp,
|
|
123
|
+
Connection: 'keep-alive',
|
|
124
|
+
},
|
|
125
|
+
timeout: HTTP_CONFIG.requestTimeout,
|
|
126
|
+
httpAgent,
|
|
127
|
+
httpsAgent,
|
|
128
|
+
});
|
|
129
|
+
if (shouldLog) {
|
|
130
|
+
const duration = Date.now() - startTime;
|
|
131
|
+
if (duration > 3000) {
|
|
132
|
+
console.log((0, safe_stable_stringify_1.default)({
|
|
133
|
+
message: '请求完成',
|
|
134
|
+
currentRequestId,
|
|
135
|
+
duration: `${duration}ms`,
|
|
136
|
+
activeRequests,
|
|
137
|
+
type: 'http_request_complete',
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const responseData = response.data;
|
|
142
|
+
return responseData.data;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
const axiosError = error;
|
|
146
|
+
const duration = startTime ? Date.now() - startTime : 0;
|
|
147
|
+
// 错误始终记录(但简化输出)
|
|
148
|
+
console.error((0, safe_stable_stringify_1.default)({
|
|
149
|
+
message: '请求失败',
|
|
150
|
+
currentRequestId,
|
|
151
|
+
url,
|
|
152
|
+
duration: `${duration}ms`,
|
|
153
|
+
activeRequests,
|
|
154
|
+
errorMessage: axiosError.message,
|
|
155
|
+
type: 'http_request_failed',
|
|
156
|
+
}));
|
|
157
|
+
if (axiosError.response) {
|
|
158
|
+
const response = axiosError.response;
|
|
159
|
+
const data = response.data;
|
|
160
|
+
console.error((0, safe_stable_stringify_1.default)({
|
|
161
|
+
message: '响应异常',
|
|
162
|
+
status: response.status,
|
|
163
|
+
errorMessage: data?.message || axiosError.message,
|
|
164
|
+
type: 'http_response_error',
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
// 记录请求完成,减少活跃计数
|
|
171
|
+
activeRequests--;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// 导出监控函数
|
|
175
|
+
function getConnectionStats() {
|
|
176
|
+
const stats = {
|
|
177
|
+
// 当前状态
|
|
178
|
+
activeRequests,
|
|
179
|
+
totalRequests: requestCount,
|
|
180
|
+
// 峰值指标
|
|
181
|
+
peakActiveRequests,
|
|
182
|
+
// Socket连接池状态(这是真正的并发控制点)
|
|
183
|
+
httpSockets: {
|
|
184
|
+
total: Object.keys(httpAgent.sockets).reduce((sum, host) => sum + (httpAgent.sockets[host]?.length || 0), 0),
|
|
185
|
+
hosts: Object.keys(httpAgent.sockets).length,
|
|
186
|
+
details: httpAgent.sockets,
|
|
187
|
+
},
|
|
188
|
+
httpsSockets: {
|
|
189
|
+
total: Object.keys(httpsAgent.sockets).reduce((sum, host) => sum + (httpsAgent.sockets[host]?.length || 0), 0),
|
|
190
|
+
hosts: Object.keys(httpsAgent.sockets).length,
|
|
191
|
+
details: httpsAgent.sockets,
|
|
101
192
|
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
193
|
+
// 配置信息
|
|
194
|
+
config: {
|
|
195
|
+
maxSockets: HTTP_CONFIG.maxSockets,
|
|
196
|
+
maxTotalSockets: HTTP_CONFIG.maxTotalSockets,
|
|
197
|
+
requestTimeout: HTTP_CONFIG.requestTimeout,
|
|
198
|
+
note: '已移除应用层并发队列限制,完全依赖socket连接池',
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
return stats;
|
|
202
|
+
}
|
|
203
|
+
// 重置峰值统计(可用于定期重置)
|
|
204
|
+
function resetPeakStats() {
|
|
205
|
+
peakActiveRequests = activeRequests;
|
|
206
|
+
console.log((0, safe_stable_stringify_1.default)({
|
|
207
|
+
message: '峰值统计已重置',
|
|
208
|
+
type: 'stats_reset',
|
|
209
|
+
}));
|
|
108
210
|
}
|