@nest-omni/core 4.1.3-19 → 4.1.3-20
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/cache/cache.module.d.ts +0 -6
- package/cache/cache.module.js +7 -7
- package/cache/cache.service.js +12 -0
- package/cache/dependencies/db.dependency.d.ts +0 -13
- package/cache/dependencies/db.dependency.js +0 -16
- package/cache/dependencies/tag.dependency.d.ts +39 -4
- package/cache/dependencies/tag.dependency.js +109 -11
- package/cache/interfaces/cache-options.interface.d.ts +8 -0
- package/cache/providers/memory-cache.provider.d.ts +20 -0
- package/cache/providers/memory-cache.provider.js +40 -0
- package/http-client/config/http-client.config.d.ts +5 -0
- package/http-client/config/http-client.config.js +24 -13
- package/http-client/decorators/http-client.decorators.d.ts +1 -25
- package/http-client/decorators/http-client.decorators.js +97 -90
- package/http-client/entities/http-log.entity.d.ts +0 -20
- package/http-client/entities/http-log.entity.js +0 -12
- package/http-client/examples/advanced-usage.example.d.ts +4 -5
- package/http-client/examples/advanced-usage.example.js +4 -56
- package/http-client/http-client.module.d.ts +35 -2
- package/http-client/http-client.module.js +80 -75
- package/http-client/index.d.ts +1 -1
- package/http-client/interfaces/api-client-config.interface.d.ts +1 -91
- package/http-client/interfaces/http-client-config.interface.d.ts +53 -62
- package/http-client/services/api-client-registry.service.d.ts +5 -23
- package/http-client/services/api-client-registry.service.js +41 -284
- package/http-client/services/circuit-breaker.service.d.ts +69 -2
- package/http-client/services/circuit-breaker.service.js +185 -7
- package/http-client/services/http-client.service.d.ts +58 -23
- package/http-client/services/http-client.service.js +294 -150
- package/http-client/services/http-log-query.service.js +0 -13
- package/http-client/services/index.d.ts +0 -1
- package/http-client/services/index.js +0 -1
- package/http-client/services/logging.service.d.ts +79 -10
- package/http-client/services/logging.service.js +246 -51
- package/http-client/utils/call-stack-extractor.util.d.ts +26 -0
- package/http-client/utils/call-stack-extractor.util.js +35 -0
- package/http-client/utils/security-validator.util.d.ts +118 -0
- package/http-client/utils/security-validator.util.js +352 -0
- package/package.json +1 -1
- package/redis-lock/lock-heartbeat.service.d.ts +2 -0
- package/redis-lock/lock-heartbeat.service.js +12 -2
- package/redis-lock/redis-lock.service.d.ts +4 -0
- package/redis-lock/redis-lock.service.js +61 -8
- package/http-client/services/cache.service.d.ts +0 -76
- package/http-client/services/cache.service.js +0 -333
|
@@ -22,26 +22,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
22
22
|
exports.HttpClientService = void 0;
|
|
23
23
|
const common_1 = require("@nestjs/common");
|
|
24
24
|
const axios_retry_1 = require("axios-retry");
|
|
25
|
-
const
|
|
25
|
+
const api_client_config_interface_1 = require("../interfaces/api-client-config.interface");
|
|
26
26
|
const circuit_breaker_service_1 = require("./circuit-breaker.service");
|
|
27
27
|
const logging_service_1 = require("./logging.service");
|
|
28
|
-
const cache_service_1 = require("./cache.service");
|
|
29
28
|
const decorators_1 = require("../decorators");
|
|
30
29
|
const context_extractor_util_1 = require("../utils/context-extractor.util");
|
|
31
30
|
const curl_generator_util_1 = require("../utils/curl-generator.util");
|
|
31
|
+
const call_stack_extractor_util_1 = require("../utils/call-stack-extractor.util");
|
|
32
32
|
const retry_recorder_util_1 = require("../utils/retry-recorder.util");
|
|
33
33
|
const request_id_util_1 = require("../utils/request-id.util");
|
|
34
34
|
const proxy_environment_util_1 = require("../utils/proxy-environment.util");
|
|
35
35
|
const redis_lock_service_1 = require("../../redis-lock/redis-lock.service");
|
|
36
|
+
const security_validator_util_1 = require("../utils/security-validator.util");
|
|
36
37
|
/**
|
|
37
38
|
* HTTP客户端服务
|
|
38
39
|
* 基于Spring RestTemplate的设计理念,集成axios-retry库
|
|
40
|
+
* 支持两种创建模式:
|
|
41
|
+
* 1. 直接创建: 用于简单的HTTP请求场景
|
|
42
|
+
* 2. API客户端模式: 用于需要认证、统计等高级功能的API客户端
|
|
39
43
|
*/
|
|
40
44
|
let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
41
|
-
constructor(circuitBreakerService, loggingService,
|
|
45
|
+
constructor(circuitBreakerService, loggingService, redisLockService, config = {}, clientName, authConfig) {
|
|
42
46
|
this.circuitBreakerService = circuitBreakerService;
|
|
43
47
|
this.loggingService = loggingService;
|
|
44
|
-
this.cacheService = cacheService;
|
|
45
48
|
this.redisLockService = redisLockService;
|
|
46
49
|
this.logger = new common_1.Logger(HttpClientService_1.name);
|
|
47
50
|
this.requestStats = {
|
|
@@ -49,55 +52,84 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
49
52
|
successfulRequests: 0,
|
|
50
53
|
failedRequests: 0,
|
|
51
54
|
totalResponseTime: 0,
|
|
55
|
+
averageResponseTime: 0,
|
|
52
56
|
requestsByMethod: {},
|
|
53
57
|
requestsByStatus: {},
|
|
54
58
|
};
|
|
55
59
|
this.defaultConfig = this.mergeWithDefaults(config);
|
|
60
|
+
this.clientName = clientName;
|
|
61
|
+
this.authConfig = authConfig;
|
|
56
62
|
this.axiosInstance = this.createAxiosInstance();
|
|
57
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* 静态工厂方法:创建API客户端模式的 HttpClientService
|
|
66
|
+
* @param dependencies 依赖服务
|
|
67
|
+
* @param config API客户端配置
|
|
68
|
+
* @returns HttpClientService 实例
|
|
69
|
+
*/
|
|
70
|
+
static createApiClient(dependencies, config) {
|
|
71
|
+
return new HttpClientService_1(dependencies.circuitBreakerService, dependencies.loggingService, dependencies.redisLockService, Object.assign({ baseURL: config.baseURL, timeout: config.timeout || 30000 }, config.httpConfig), config.name, config.auth);
|
|
72
|
+
}
|
|
58
73
|
/**
|
|
59
74
|
* 执行HTTP请求
|
|
60
75
|
*/
|
|
61
|
-
request(config, decoratorContext) {
|
|
76
|
+
request(config, decoratorContext, clientName) {
|
|
62
77
|
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
78
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
79
|
+
// Use the instance's clientName as fallback if not provided
|
|
80
|
+
const effectiveClientName = clientName || this.clientName;
|
|
81
|
+
// If no decorator context provided, try to get it from CallStackExtractor
|
|
82
|
+
// This allows decorators on service methods to pass context to HTTP client
|
|
83
|
+
const effectiveDecoratorContext = decoratorContext || call_stack_extractor_util_1.CallStackExtractor.getDecoratorContext();
|
|
84
|
+
// ========== 安全验证开始 ==========
|
|
85
|
+
// 构造完整URL进行验证
|
|
86
|
+
const fullURL = config.url || '';
|
|
87
|
+
const baseURL = config.baseURL || this.defaultConfig.baseURL || '';
|
|
88
|
+
// 执行URL安全验证
|
|
89
|
+
const sanitizeResult = security_validator_util_1.SecurityValidator.sanitizeURL(fullURL, {
|
|
90
|
+
urlConfig: (_a = this.defaultConfig.security) === null || _a === void 0 ? void 0 : _a.urlValidation,
|
|
91
|
+
ssrfConfig: (_b = this.defaultConfig.security) === null || _b === void 0 ? void 0 : _b.ssrfProtection,
|
|
92
|
+
});
|
|
93
|
+
if (!sanitizeResult.valid) {
|
|
94
|
+
const error = new Error(`URL validation failed: ${sanitizeResult.error}`);
|
|
95
|
+
this.logger.error(`URL validation failed for ${fullURL}: ${sanitizeResult.error}`);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
// 如果URL被修改,更新配置
|
|
99
|
+
if (sanitizeResult.url !== fullURL) {
|
|
100
|
+
config.url = sanitizeResult.url;
|
|
101
|
+
this.logger.debug(`URL sanitized from "${fullURL}" to "${sanitizeResult.url}"`);
|
|
102
|
+
}
|
|
103
|
+
// ========== 安全验证结束 ==========
|
|
104
|
+
// Capture the calling context information
|
|
105
|
+
const callingContext = this.captureCallingContext();
|
|
64
106
|
const startTime = Date.now();
|
|
65
107
|
const retryRecorder = retry_recorder_util_1.RetryRecorder.create();
|
|
66
108
|
let requestId;
|
|
67
109
|
let circuitBreakerState;
|
|
68
|
-
let cacheHit = false;
|
|
69
110
|
try {
|
|
111
|
+
// 应用认证配置
|
|
112
|
+
let authConfig = yield this.applyAuthToConfig(config);
|
|
70
113
|
// 应用装饰器配置
|
|
71
|
-
const enhancedConfig = this.applyDecoratorConfig(
|
|
114
|
+
const enhancedConfig = this.applyDecoratorConfig(authConfig, decoratorContext);
|
|
115
|
+
// 将 retryRecorder 存储到 config metadata 中,供 onRetry 回调使用
|
|
116
|
+
enhancedConfig.metadata = enhancedConfig.metadata || {};
|
|
117
|
+
enhancedConfig.metadata.retryRecorder = retryRecorder;
|
|
72
118
|
// 获取装饰器配置
|
|
73
|
-
const decoratorConfigs =
|
|
74
|
-
? decorators_1.HttpDecoratorUtils.getAllDecoratorConfigs(
|
|
119
|
+
const decoratorConfigs = effectiveDecoratorContext
|
|
120
|
+
? decorators_1.HttpDecoratorUtils.getAllDecoratorConfigs(effectiveDecoratorContext.target, effectiveDecoratorContext.propertyKey)
|
|
75
121
|
: {};
|
|
76
122
|
// 日志记录开始
|
|
77
|
-
const
|
|
123
|
+
const decoratorLogging = decoratorConfigs.logging || {};
|
|
124
|
+
this.logger.debug(`Logging config merge: decoratorLogging=${JSON.stringify(decoratorLogging)}, defaultLogging=${JSON.stringify(this.defaultConfig.logging)}`);
|
|
125
|
+
const loggingOptions = Object.assign(Object.assign(Object.assign({}, this.defaultConfig.logging), decoratorLogging), { databaseLogging: (_c = this.defaultConfig.logging) === null || _c === void 0 ? void 0 : _c.databaseLogging });
|
|
126
|
+
this.logger.debug(`Merged loggingOptions: databaseLog=${loggingOptions.databaseLog}, hasDatabaseLogging=${!!loggingOptions.databaseLogging}`);
|
|
78
127
|
if (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.enabled) {
|
|
79
128
|
requestId = this.loggingService.logRequestStart(enhancedConfig, loggingOptions);
|
|
80
129
|
// 将requestId保存到config中
|
|
81
130
|
enhancedConfig.metadata = enhancedConfig.metadata || {};
|
|
82
131
|
enhancedConfig.metadata.requestId = requestId;
|
|
83
132
|
}
|
|
84
|
-
// 缓存检查
|
|
85
|
-
const cacheConfig = decoratorConfigs.cache || this.defaultConfig.cache;
|
|
86
|
-
if ((cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.enabled) &&
|
|
87
|
-
this.shouldCacheRequest(enhancedConfig, cacheConfig)) {
|
|
88
|
-
const cachedResponse = yield this.cacheService.get(enhancedConfig, cacheConfig);
|
|
89
|
-
if (cachedResponse) {
|
|
90
|
-
cacheHit = true;
|
|
91
|
-
if (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.enabled) {
|
|
92
|
-
const databaseLogging = (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.databaseLog) ||
|
|
93
|
-
((_b = (_a = this.defaultConfig.logging) === null || _a === void 0 ? void 0 : _a.databaseLogging) === null || _b === void 0 ? void 0 : _b.enabled);
|
|
94
|
-
this.loggingService.logRequestSuccess(cachedResponse, startTime, requestId, loggingOptions, databaseLogging, undefined, // no retry records for cache hit
|
|
95
|
-
cacheHit, undefined, // circuit breaker state
|
|
96
|
-
decoratorContext);
|
|
97
|
-
}
|
|
98
|
-
return cachedResponse.data;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
133
|
// 应用超时配置
|
|
102
134
|
const timeout = decoratorConfigs.timeout || this.defaultConfig.timeout;
|
|
103
135
|
if (timeout) {
|
|
@@ -111,46 +143,38 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
111
143
|
enhancedConfig.proxy = resolvedProxy;
|
|
112
144
|
}
|
|
113
145
|
}
|
|
114
|
-
// 配置axios-retry的onRetry回调以记录重试
|
|
115
|
-
if ((_c = this.defaultConfig.retry) === null || _c === void 0 ? void 0 : _c.enabled) {
|
|
116
|
-
const originalOnRetry = this.defaultConfig.retry.onRetry;
|
|
117
|
-
this.defaultConfig.retry.onRetry = (retryCount, error, requestConfig) => {
|
|
118
|
-
// 记录重试
|
|
119
|
-
const retryRecord = retry_recorder_util_1.RetryRecorder.recordRetry(retryCount, error, requestConfig, this.calculateRetryDelay(retryCount, this.defaultConfig.retry));
|
|
120
|
-
retryRecorder.addRecord(retryRecord);
|
|
121
|
-
// 调用原始回调
|
|
122
|
-
if (originalOnRetry) {
|
|
123
|
-
originalOnRetry(retryCount, error, requestConfig);
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
146
|
// 执行请求(带重试和熔断器)
|
|
128
|
-
const response = yield this.executeWithFeatures(enhancedConfig, decoratorConfigs, requestId, startTime, retryRecorder
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if ((cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.enabled) &&
|
|
133
|
-
this.shouldCacheRequest(enhancedConfig, cacheConfig)) {
|
|
134
|
-
yield this.cacheService.set(enhancedConfig, response, cacheConfig);
|
|
135
|
-
}
|
|
147
|
+
const { response, circuitBreakerState: state } = yield this.executeWithFeatures(enhancedConfig, decoratorConfigs, requestId, startTime, retryRecorder);
|
|
148
|
+
// 更新 circuitBreakerState 变量
|
|
149
|
+
circuitBreakerState = state;
|
|
150
|
+
this.logger.debug(`Request completed, circuitBreakerState: ${circuitBreakerState || 'undefined'}`);
|
|
136
151
|
// 成功日志
|
|
137
152
|
if (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.enabled) {
|
|
138
153
|
const databaseLogging = (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.databaseLogging) ||
|
|
139
154
|
((_e = (_d = this.defaultConfig.logging) === null || _d === void 0 ? void 0 : _d.databaseLogging) === null || _e === void 0 ? void 0 : _e.enabled);
|
|
140
|
-
this.
|
|
155
|
+
this.logger.debug(`Logging request success with circuitBreakerState: ${circuitBreakerState || 'undefined'}`);
|
|
156
|
+
this.loggingService.logRequestSuccess(response, startTime, requestId, loggingOptions, databaseLogging, retryRecorder.getRecords(), circuitBreakerState, decoratorContext, effectiveClientName, callingContext);
|
|
141
157
|
}
|
|
158
|
+
// 更新统计信息
|
|
159
|
+
this.updateRequestStats(true, Date.now() - startTime, response.config.method, response.status);
|
|
142
160
|
return response.data;
|
|
143
161
|
}
|
|
144
162
|
catch (error) {
|
|
145
163
|
// 错误日志
|
|
146
|
-
const
|
|
147
|
-
? decorators_1.HttpDecoratorUtils.getLoggingOptions(
|
|
148
|
-
:
|
|
164
|
+
const decoratorLogging = effectiveDecoratorContext
|
|
165
|
+
? decorators_1.HttpDecoratorUtils.getLoggingOptions(effectiveDecoratorContext.target, effectiveDecoratorContext.propertyKey)
|
|
166
|
+
: {};
|
|
167
|
+
const loggingOptions = Object.assign(Object.assign(Object.assign({}, this.defaultConfig.logging), decoratorLogging), { databaseLogging: (_f = this.defaultConfig.logging) === null || _f === void 0 ? void 0 : _f.databaseLogging });
|
|
149
168
|
if (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.enabled) {
|
|
150
169
|
const databaseLogging = (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.databaseLog) ||
|
|
151
|
-
((
|
|
152
|
-
|
|
170
|
+
((_h = (_g = this.defaultConfig.logging) === null || _g === void 0 ? void 0 : _g.databaseLogging) === null || _h === void 0 ? void 0 : _h.enabled);
|
|
171
|
+
const records = retryRecorder.getRecords();
|
|
172
|
+
this.loggingService.logRequestError(error, startTime, requestId, (records === null || records === void 0 ? void 0 : records.length) ? Math.max(...records.map((r) => r.attempt)) + 1 : 1, loggingOptions, databaseLogging, records, circuitBreakerState, effectiveDecoratorContext, effectiveClientName, callingContext);
|
|
153
173
|
}
|
|
174
|
+
// 更新统计信息
|
|
175
|
+
const errorMethod = (_j = error.config) === null || _j === void 0 ? void 0 : _j.method;
|
|
176
|
+
const errorStatus = (_k = error.response) === null || _k === void 0 ? void 0 : _k.status;
|
|
177
|
+
this.updateRequestStats(false, Date.now() - startTime, errorMethod, errorStatus);
|
|
154
178
|
throw error;
|
|
155
179
|
}
|
|
156
180
|
});
|
|
@@ -171,14 +195,21 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
171
195
|
* 获取请求统计信息
|
|
172
196
|
*/
|
|
173
197
|
getStats() {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
:
|
|
177
|
-
|
|
198
|
+
return {
|
|
199
|
+
totalRequests: this.requestStats.totalRequests,
|
|
200
|
+
successfulRequests: this.requestStats.successfulRequests,
|
|
201
|
+
failedRequests: this.requestStats.failedRequests,
|
|
202
|
+
totalResponseTime: this.requestStats.totalResponseTime,
|
|
203
|
+
averageResponseTime: this.requestStats.averageResponseTime,
|
|
204
|
+
successRate: this.requestStats.totalRequests > 0
|
|
178
205
|
? (this.requestStats.successfulRequests /
|
|
179
206
|
this.requestStats.totalRequests) *
|
|
180
207
|
100
|
|
181
|
-
: 0,
|
|
208
|
+
: 0,
|
|
209
|
+
requestsByMethod: this.requestStats.requestsByMethod,
|
|
210
|
+
requestsByStatus: this.requestStats.requestsByStatus,
|
|
211
|
+
circuitBreakerStats: this.circuitBreakerService.getAllStates(),
|
|
212
|
+
};
|
|
182
213
|
}
|
|
183
214
|
/**
|
|
184
215
|
* 重置统计信息
|
|
@@ -189,34 +220,69 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
189
220
|
successfulRequests: 0,
|
|
190
221
|
failedRequests: 0,
|
|
191
222
|
totalResponseTime: 0,
|
|
223
|
+
averageResponseTime: 0,
|
|
192
224
|
requestsByMethod: {},
|
|
193
225
|
requestsByStatus: {},
|
|
194
226
|
};
|
|
195
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* 获取客户端名称
|
|
230
|
+
*/
|
|
231
|
+
getName() {
|
|
232
|
+
return this.clientName;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Capture calling context information
|
|
236
|
+
* @returns Calling context with service class and method name
|
|
237
|
+
*/
|
|
238
|
+
captureCallingContext() {
|
|
239
|
+
try {
|
|
240
|
+
// Use CallStackExtractor to get the calling context
|
|
241
|
+
const stackInfo = call_stack_extractor_util_1.CallStackExtractor.getCallInfo();
|
|
242
|
+
// Skip internal HTTP client calls
|
|
243
|
+
if (stackInfo.serviceClass && stackInfo.methodName) {
|
|
244
|
+
// Check if it's an internal call
|
|
245
|
+
const isInternal = call_stack_extractor_util_1.CallStackExtractor['isInternalCall'](stackInfo.serviceClass);
|
|
246
|
+
if (!isInternal) {
|
|
247
|
+
return {
|
|
248
|
+
serviceClass: stackInfo.serviceClass,
|
|
249
|
+
methodName: stackInfo.methodName,
|
|
250
|
+
operationName: stackInfo.operationName,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// If no useful context found, return empty
|
|
255
|
+
return {};
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
this.logger.warn('Failed to capture calling context:', error.message);
|
|
259
|
+
return {};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
196
262
|
// 便捷方法
|
|
197
|
-
get(url, config) {
|
|
263
|
+
get(url, config, clientName) {
|
|
198
264
|
return __awaiter(this, void 0, void 0, function* () {
|
|
199
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'GET', url }));
|
|
265
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'GET', url }), undefined, clientName);
|
|
200
266
|
});
|
|
201
267
|
}
|
|
202
|
-
post(url, data, config) {
|
|
268
|
+
post(url, data, config, clientName) {
|
|
203
269
|
return __awaiter(this, void 0, void 0, function* () {
|
|
204
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'POST', url, data }));
|
|
270
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'POST', url, data }), undefined, clientName);
|
|
205
271
|
});
|
|
206
272
|
}
|
|
207
|
-
put(url, data, config) {
|
|
273
|
+
put(url, data, config, clientName) {
|
|
208
274
|
return __awaiter(this, void 0, void 0, function* () {
|
|
209
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'PUT', url, data }));
|
|
275
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'PUT', url, data }), undefined, clientName);
|
|
210
276
|
});
|
|
211
277
|
}
|
|
212
|
-
patch(url, data, config) {
|
|
278
|
+
patch(url, data, config, clientName) {
|
|
213
279
|
return __awaiter(this, void 0, void 0, function* () {
|
|
214
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'PATCH', url, data }));
|
|
280
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'PATCH', url, data }), undefined, clientName);
|
|
215
281
|
});
|
|
216
282
|
}
|
|
217
|
-
delete(url, config) {
|
|
283
|
+
delete(url, config, clientName) {
|
|
218
284
|
return __awaiter(this, void 0, void 0, function* () {
|
|
219
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'DELETE', url }));
|
|
285
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'DELETE', url }), undefined, clientName);
|
|
220
286
|
});
|
|
221
287
|
}
|
|
222
288
|
/**
|
|
@@ -228,11 +294,12 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
228
294
|
* @param options 等待锁选项
|
|
229
295
|
* @returns 响应数据
|
|
230
296
|
*/
|
|
231
|
-
authRequest(config, tokenProvider, options) {
|
|
297
|
+
authRequest(config, tokenProvider, options, clientName) {
|
|
232
298
|
return __awaiter(this, void 0, void 0, function* () {
|
|
299
|
+
const effectiveClientName = clientName || this.clientName;
|
|
233
300
|
if (!this.redisLockService) {
|
|
234
301
|
this.logger.warn('RedisLockService not available, proceeding without lock');
|
|
235
|
-
return this.request(config);
|
|
302
|
+
return this.request(config, undefined, effectiveClientName);
|
|
236
303
|
}
|
|
237
304
|
const { lockKey = `auth-request:${(0, request_id_util_1.generateRequestId)()}`, lockTimeout = 30000, waitTimeout = 60000, enableRetry = true, } = options || {};
|
|
238
305
|
try {
|
|
@@ -243,7 +310,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
243
310
|
// 添加认证头
|
|
244
311
|
const authConfig = Object.assign(Object.assign({}, config), { headers: Object.assign(Object.assign({}, config.headers), { Authorization: `Bearer ${token}` }) });
|
|
245
312
|
// 执行请求
|
|
246
|
-
return this.request(authConfig);
|
|
313
|
+
return this.request(authConfig, undefined, effectiveClientName);
|
|
247
314
|
}), {
|
|
248
315
|
ttl: lockTimeout,
|
|
249
316
|
retryCount: enableRetry ? 3 : 0,
|
|
@@ -262,7 +329,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
262
329
|
this.logger.warn('Retrying auth request without lock');
|
|
263
330
|
const token = yield tokenProvider();
|
|
264
331
|
const authConfig = Object.assign(Object.assign({}, config), { headers: Object.assign(Object.assign({}, config.headers), { Authorization: `Bearer ${token}` }) });
|
|
265
|
-
return this.request(authConfig);
|
|
332
|
+
return this.request(authConfig, undefined, effectiveClientName);
|
|
266
333
|
}
|
|
267
334
|
throw error;
|
|
268
335
|
}
|
|
@@ -277,8 +344,9 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
277
344
|
* @param options 等待锁选项
|
|
278
345
|
* @returns 响应数组
|
|
279
346
|
*/
|
|
280
|
-
authBatchRequest(requests, tokenProvider, options) {
|
|
347
|
+
authBatchRequest(requests, tokenProvider, options, clientName) {
|
|
281
348
|
return __awaiter(this, void 0, void 0, function* () {
|
|
349
|
+
const effectiveClientName = clientName || this.clientName;
|
|
282
350
|
if (!this.redisLockService) {
|
|
283
351
|
this.logger.warn('RedisLockService not available, proceeding batch auth requests without lock');
|
|
284
352
|
// 无锁批量执行
|
|
@@ -286,7 +354,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
286
354
|
const promises = requests.map((request) => __awaiter(this, void 0, void 0, function* () {
|
|
287
355
|
try {
|
|
288
356
|
const authConfig = Object.assign(Object.assign({}, request.config), { headers: Object.assign(Object.assign({}, request.config.headers), { Authorization: `Bearer ${token}` }) });
|
|
289
|
-
return yield this.request(authConfig);
|
|
357
|
+
return yield this.request(authConfig, undefined, effectiveClientName);
|
|
290
358
|
}
|
|
291
359
|
catch (error) {
|
|
292
360
|
this.logger.error(`Batch auth request failed for key: ${request.key}`, error);
|
|
@@ -308,7 +376,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
308
376
|
const executeRequest = (requestConfig, key, index) => __awaiter(this, void 0, void 0, function* () {
|
|
309
377
|
try {
|
|
310
378
|
const authConfig = Object.assign(Object.assign({}, requestConfig), { headers: Object.assign(Object.assign({}, requestConfig.headers), { Authorization: `Bearer ${token}` }) });
|
|
311
|
-
const result = yield this.request(authConfig);
|
|
379
|
+
const result = yield this.request(authConfig, undefined, effectiveClientName);
|
|
312
380
|
results[index] = result;
|
|
313
381
|
return result;
|
|
314
382
|
}
|
|
@@ -388,11 +456,27 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
388
456
|
return status >= 500 || status === 429; // 5xx错误或429限流
|
|
389
457
|
}),
|
|
390
458
|
shouldResetTimeout: this.defaultConfig.retry.shouldResetTimeout !== false,
|
|
391
|
-
onRetry:
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
459
|
+
onRetry: (retryCount, error, requestConfig) => {
|
|
460
|
+
var _a, _b, _c, _d;
|
|
461
|
+
// 从 request config metadata 中获取 retryRecorder
|
|
462
|
+
const retryRecorder = (_a = requestConfig.metadata) === null || _a === void 0 ? void 0 : _a.retryRecorder;
|
|
463
|
+
if (retryRecorder) {
|
|
464
|
+
this.logger.debug(`Recording retry attempt ${retryCount} for ${(_b = requestConfig.method) === null || _b === void 0 ? void 0 : _b.toUpperCase()} ${requestConfig.url}`);
|
|
465
|
+
const retryRecord = retry_recorder_util_1.RetryRecorder.recordRetry(retryCount, error, requestConfig, this.calculateRetryDelay(retryCount, this.defaultConfig.retry));
|
|
466
|
+
retryRecorder.addRecord(retryRecord);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
this.logger.warn(`RetryRecorder not found in request config metadata for ${(_c = requestConfig.method) === null || _c === void 0 ? void 0 : _c.toUpperCase()} ${requestConfig.url}. Retry records will not be saved.`);
|
|
470
|
+
}
|
|
471
|
+
// 调用用户自定义的 onRetry 回调
|
|
472
|
+
if (this.defaultConfig.retry.onRetry) {
|
|
473
|
+
this.defaultConfig.retry.onRetry(retryCount, error, requestConfig);
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
// 默认日志
|
|
477
|
+
this.logger.warn(`Retrying request (attempt ${retryCount}): ${(_d = requestConfig.method) === null || _d === void 0 ? void 0 : _d.toUpperCase()} ${requestConfig.url}`);
|
|
478
|
+
}
|
|
479
|
+
},
|
|
396
480
|
});
|
|
397
481
|
}
|
|
398
482
|
// 设置请求拦截器
|
|
@@ -405,7 +489,8 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
405
489
|
if (context.userId) {
|
|
406
490
|
config.headers['X-User-ID'] = context.userId;
|
|
407
491
|
}
|
|
408
|
-
// 存储requestId到config中供后续使用
|
|
492
|
+
// 存储requestId到config metadata中供后续使用
|
|
493
|
+
// 注意:不要覆盖已存在的 metadata,保留 retryRecorder 等其他数据
|
|
409
494
|
config.metadata = config.metadata || {};
|
|
410
495
|
config.metadata.requestId = requestId;
|
|
411
496
|
return config;
|
|
@@ -414,17 +499,19 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
414
499
|
return Promise.reject(error);
|
|
415
500
|
});
|
|
416
501
|
// 设置响应拦截器
|
|
502
|
+
// 注意: 统计信息已移至 request() 方法中统一管理,避免双重计数
|
|
417
503
|
instance.interceptors.response.use((response) => {
|
|
418
|
-
|
|
504
|
+
// 不再在这里更新统计,由 request() 方法统一管理
|
|
419
505
|
return response;
|
|
420
506
|
}, (error) => {
|
|
421
|
-
|
|
507
|
+
// 不再在这里更新统计,由 request() 方法统一管理
|
|
422
508
|
return Promise.reject(error);
|
|
423
509
|
});
|
|
424
510
|
return instance;
|
|
425
511
|
}
|
|
426
512
|
/**
|
|
427
513
|
* 执行带特性的请求(重试、熔断器等)
|
|
514
|
+
* 返回响应和熔断器状态
|
|
428
515
|
*/
|
|
429
516
|
executeWithFeatures(config, decoratorConfigs, requestId, startTime, retryRecorder, onCircuitBreakerStateChange) {
|
|
430
517
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -433,10 +520,17 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
433
520
|
const circuitBreakerConfig = decoratorConfigs.circuitBreaker || this.defaultConfig.circuitBreaker;
|
|
434
521
|
if (circuitBreakerConfig === null || circuitBreakerConfig === void 0 ? void 0 : circuitBreakerConfig.enabled) {
|
|
435
522
|
const circuitBreakerKey = this.generateCircuitBreakerKey(config);
|
|
436
|
-
|
|
523
|
+
this.logger.debug(`Circuit breaker key: ${circuitBreakerKey}`);
|
|
524
|
+
// 执行请求
|
|
525
|
+
const result = yield this.circuitBreakerService.executeWithCircuitBreaker(circuitBreakerKey, requestFn, circuitBreakerConfig, onCircuitBreakerStateChange);
|
|
526
|
+
// 请求完成后,主动获取当前熔断器状态
|
|
527
|
+
const currentState = this.circuitBreakerService.getCircuitBreakerState(circuitBreakerKey);
|
|
528
|
+
this.logger.debug(`After request, circuit breaker state: ${currentState || 'undefined'}`);
|
|
529
|
+
return { response: result, circuitBreakerState: currentState };
|
|
437
530
|
}
|
|
438
531
|
else {
|
|
439
|
-
|
|
532
|
+
const result = yield requestFn();
|
|
533
|
+
return { response: result, circuitBreakerState: undefined };
|
|
440
534
|
}
|
|
441
535
|
});
|
|
442
536
|
}
|
|
@@ -459,60 +553,27 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
459
553
|
const httpClientConfig = decorators_1.HttpDecoratorUtils.getHttpClientOptions(decoratorContext.target);
|
|
460
554
|
return Object.assign(Object.assign({}, config), httpClientConfig);
|
|
461
555
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
*/
|
|
465
|
-
shouldCacheRequest(config, cacheConfig) {
|
|
466
|
-
var _a;
|
|
467
|
-
const method = (_a = config.method) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
468
|
-
return cacheConfig.cacheableMethods.includes(method || 'get');
|
|
469
|
-
}
|
|
556
|
+
// Note: applyDecoratorConfig is not currently being called in executeWithFeatures
|
|
557
|
+
// The decorator config is already being applied via effectiveDecoratorContext
|
|
470
558
|
/**
|
|
471
559
|
* 生成熔断器键
|
|
472
560
|
*/
|
|
473
561
|
generateCircuitBreakerKey(config) {
|
|
474
562
|
var _a;
|
|
475
563
|
const method = ((_a = config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN';
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
* 更新请求统计
|
|
481
|
-
*/
|
|
482
|
-
updateStats(response) {
|
|
483
|
-
var _a, _b;
|
|
484
|
-
this.requestStats.totalRequests++;
|
|
485
|
-
this.requestStats.successfulRequests++;
|
|
486
|
-
const method = ((_a = response.config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN';
|
|
487
|
-
const status = response.status;
|
|
488
|
-
this.requestStats.requestsByMethod[method] =
|
|
489
|
-
(this.requestStats.requestsByMethod[method] || 0) + 1;
|
|
490
|
-
this.requestStats.requestsByStatus[status] =
|
|
491
|
-
(this.requestStats.requestsByStatus[status] || 0) + 1;
|
|
492
|
-
if ((_b = response.config.metadata) === null || _b === void 0 ? void 0 : _b.startTime) {
|
|
493
|
-
const responseTime = Date.now() -
|
|
494
|
-
response.config.metadata
|
|
495
|
-
.startTime;
|
|
496
|
-
this.requestStats.totalResponseTime += responseTime;
|
|
564
|
+
let url;
|
|
565
|
+
try {
|
|
566
|
+
// Try to construct URL with proper base
|
|
567
|
+
url = new URL(config.url || '', this.defaultConfig.baseURL || 'http://localhost');
|
|
497
568
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this.requestStats.totalRequests++;
|
|
505
|
-
this.requestStats.failedRequests++;
|
|
506
|
-
if (error.config) {
|
|
507
|
-
const method = ((_a = error.config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN';
|
|
508
|
-
this.requestStats.requestsByMethod[method] =
|
|
509
|
-
(this.requestStats.requestsByMethod[method] || 0) + 1;
|
|
510
|
-
if (error.response) {
|
|
511
|
-
const status = error.response.status;
|
|
512
|
-
this.requestStats.requestsByStatus[status] =
|
|
513
|
-
(this.requestStats.requestsByStatus[status] || 0) + 1;
|
|
514
|
-
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
// If URL construction fails, fall back to simple string format
|
|
571
|
+
this.logger.warn(`Failed to construct URL for circuit breaker key: ${config.url}, using fallback format`);
|
|
572
|
+
const baseURL = this.defaultConfig.baseURL || 'http://localhost';
|
|
573
|
+
const urlPath = config.url || '/';
|
|
574
|
+
return `${method}:${baseURL}${urlPath}`;
|
|
515
575
|
}
|
|
576
|
+
return `${method}:${url.host}${url.pathname}`;
|
|
516
577
|
}
|
|
517
578
|
/**
|
|
518
579
|
* 合并默认配置
|
|
@@ -541,15 +602,6 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
541
602
|
minimumThroughputThreshold: 10,
|
|
542
603
|
countHalfOpenCalls: true,
|
|
543
604
|
},
|
|
544
|
-
cache: {
|
|
545
|
-
enabled: true,
|
|
546
|
-
defaultTtl: 300000, // 5分钟
|
|
547
|
-
cacheableMethods: ['get'],
|
|
548
|
-
cacheableStatusCodes: [200, 201, 202, 204, 301, 302, 304],
|
|
549
|
-
options: {
|
|
550
|
-
layers: [cache_1.CacheLayer.MEMORY, cache_1.CacheLayer.REDIS],
|
|
551
|
-
},
|
|
552
|
-
},
|
|
553
605
|
logging: {
|
|
554
606
|
enabled: true,
|
|
555
607
|
logRequests: true,
|
|
@@ -561,9 +613,8 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
561
613
|
sanitizeHeaders: ['authorization', 'apikey', 'password', 'token'],
|
|
562
614
|
logLevel: 'info',
|
|
563
615
|
databaseLogging: {
|
|
564
|
-
enabled:
|
|
616
|
+
enabled: true,
|
|
565
617
|
dataSource: 'default',
|
|
566
|
-
tableName: 'http_logs',
|
|
567
618
|
},
|
|
568
619
|
},
|
|
569
620
|
};
|
|
@@ -594,6 +645,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
594
645
|
* @returns 解析后的代理配置,如果不应使用代理则返回 false 或 undefined
|
|
595
646
|
*/
|
|
596
647
|
resolveProxyConfig(proxyConfig, targetUrl) {
|
|
648
|
+
var _a, _b, _c, _d;
|
|
597
649
|
if (!(proxyConfig === null || proxyConfig === void 0 ? void 0 : proxyConfig.enabled)) {
|
|
598
650
|
return undefined;
|
|
599
651
|
}
|
|
@@ -619,31 +671,123 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
619
671
|
this.logger.debug('Proxy bypassed based on environment configuration');
|
|
620
672
|
return false;
|
|
621
673
|
}
|
|
622
|
-
|
|
674
|
+
const result = {
|
|
623
675
|
host: envProxy.host,
|
|
624
676
|
port: envProxy.port,
|
|
625
677
|
protocol: envProxy.protocol,
|
|
626
|
-
auth: envProxy.auth,
|
|
627
678
|
};
|
|
679
|
+
// 只有当 auth 存在且包含有效值时才添加
|
|
680
|
+
if (((_a = envProxy.auth) === null || _a === void 0 ? void 0 : _a.username) && ((_b = envProxy.auth) === null || _b === void 0 ? void 0 : _b.password)) {
|
|
681
|
+
result.auth = {
|
|
682
|
+
username: envProxy.auth.username,
|
|
683
|
+
password: envProxy.auth.password,
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
return result;
|
|
628
687
|
}
|
|
629
688
|
// 使用手动配置
|
|
630
689
|
if (proxyConfig.host && proxyConfig.port) {
|
|
631
|
-
|
|
690
|
+
const result = {
|
|
632
691
|
host: proxyConfig.host,
|
|
633
692
|
port: proxyConfig.port,
|
|
634
693
|
protocol: proxyConfig.protocol,
|
|
635
|
-
auth: proxyConfig.auth,
|
|
636
694
|
};
|
|
695
|
+
// 只有当 auth 存在且包含有效值时才添加
|
|
696
|
+
if (((_c = proxyConfig.auth) === null || _c === void 0 ? void 0 : _c.username) && ((_d = proxyConfig.auth) === null || _d === void 0 ? void 0 : _d.password)) {
|
|
697
|
+
result.auth = {
|
|
698
|
+
username: proxyConfig.auth.username,
|
|
699
|
+
password: proxyConfig.auth.password,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
return result;
|
|
637
703
|
}
|
|
638
704
|
this.logger.warn('Proxy is enabled but neither fromEnvironment nor manual configuration is provided');
|
|
639
705
|
return undefined;
|
|
640
706
|
}
|
|
707
|
+
/**
|
|
708
|
+
* 应用认证配置到请求配置
|
|
709
|
+
*/
|
|
710
|
+
applyAuthToConfig(config) {
|
|
711
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
712
|
+
if (!this.authConfig) {
|
|
713
|
+
return config;
|
|
714
|
+
}
|
|
715
|
+
// Create a new config object to avoid type issues
|
|
716
|
+
const authConfig = Object.assign({}, config);
|
|
717
|
+
switch (this.authConfig.type) {
|
|
718
|
+
case api_client_config_interface_1.AuthType.NONE:
|
|
719
|
+
break;
|
|
720
|
+
case api_client_config_interface_1.AuthType.API_KEY:
|
|
721
|
+
const apiKeyConfig = this.authConfig.config;
|
|
722
|
+
if (apiKeyConfig.location === 'header') {
|
|
723
|
+
const headerName = apiKeyConfig.name || 'X-API-Key';
|
|
724
|
+
const value = apiKeyConfig.prefix
|
|
725
|
+
? `${apiKeyConfig.prefix} ${apiKeyConfig.key}`
|
|
726
|
+
: apiKeyConfig.key;
|
|
727
|
+
authConfig.headers = Object.assign(Object.assign({}, authConfig.headers), { [headerName]: value });
|
|
728
|
+
}
|
|
729
|
+
else if (apiKeyConfig.location === 'query') {
|
|
730
|
+
const paramName = apiKeyConfig.name || 'api_key';
|
|
731
|
+
authConfig.params = Object.assign(Object.assign({}, authConfig.params), { [paramName]: apiKeyConfig.key });
|
|
732
|
+
}
|
|
733
|
+
break;
|
|
734
|
+
case api_client_config_interface_1.AuthType.BEARER_TOKEN:
|
|
735
|
+
const bearerConfig = this.authConfig.config;
|
|
736
|
+
const scheme = bearerConfig.scheme || 'Bearer';
|
|
737
|
+
authConfig.headers = Object.assign(Object.assign({}, authConfig.headers), { Authorization: `${scheme} ${bearerConfig.token}` });
|
|
738
|
+
break;
|
|
739
|
+
case api_client_config_interface_1.AuthType.BASIC_AUTH:
|
|
740
|
+
const basicConfig = this.authConfig.config;
|
|
741
|
+
const credentials = Buffer.from(`${basicConfig.username}:${basicConfig.password}`).toString('base64');
|
|
742
|
+
authConfig.headers = Object.assign(Object.assign({}, authConfig.headers), { Authorization: `Basic ${credentials}` });
|
|
743
|
+
break;
|
|
744
|
+
case api_client_config_interface_1.AuthType.OAUTH2:
|
|
745
|
+
const oauthConfig = this.authConfig.config;
|
|
746
|
+
// For now, using the access token directly if available
|
|
747
|
+
if (oauthConfig.accessToken) {
|
|
748
|
+
authConfig.headers = Object.assign(Object.assign({}, authConfig.headers), { Authorization: `Bearer ${oauthConfig.accessToken}` });
|
|
749
|
+
}
|
|
750
|
+
break;
|
|
751
|
+
case api_client_config_interface_1.AuthType.CUSTOM:
|
|
752
|
+
// Handle CUSTOM authentication which requires async processing
|
|
753
|
+
const customConfig = this.authConfig.config;
|
|
754
|
+
return yield customConfig.authenticator(authConfig);
|
|
755
|
+
}
|
|
756
|
+
return authConfig;
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* 更新请求统计信息
|
|
761
|
+
* 统一管理所有统计,避免在拦截器中重复统计
|
|
762
|
+
*/
|
|
763
|
+
updateRequestStats(success, responseTime, method, status) {
|
|
764
|
+
this.requestStats.totalRequests++;
|
|
765
|
+
if (success) {
|
|
766
|
+
this.requestStats.successfulRequests++;
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
this.requestStats.failedRequests++;
|
|
770
|
+
}
|
|
771
|
+
this.requestStats.totalResponseTime += responseTime;
|
|
772
|
+
this.requestStats.averageResponseTime =
|
|
773
|
+
this.requestStats.totalResponseTime / this.requestStats.totalRequests;
|
|
774
|
+
// 统计方法分布
|
|
775
|
+
if (method) {
|
|
776
|
+
const normalizedMethod = method.toUpperCase();
|
|
777
|
+
this.requestStats.requestsByMethod[normalizedMethod] =
|
|
778
|
+
(this.requestStats.requestsByMethod[normalizedMethod] || 0) + 1;
|
|
779
|
+
}
|
|
780
|
+
// 统计状态码分布
|
|
781
|
+
if (status) {
|
|
782
|
+
this.requestStats.requestsByStatus[status] =
|
|
783
|
+
(this.requestStats.requestsByStatus[status] || 0) + 1;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
641
786
|
};
|
|
642
787
|
exports.HttpClientService = HttpClientService;
|
|
643
788
|
exports.HttpClientService = HttpClientService = HttpClientService_1 = __decorate([
|
|
644
789
|
(0, common_1.Injectable)(),
|
|
645
790
|
__metadata("design:paramtypes", [circuit_breaker_service_1.HttpCircuitBreakerService,
|
|
646
791
|
logging_service_1.HttpLoggingService,
|
|
647
|
-
|
|
648
|
-
redis_lock_service_1.RedisLockService, Object])
|
|
792
|
+
redis_lock_service_1.RedisLockService, Object, String, Object])
|
|
649
793
|
], HttpClientService);
|