sycommon-python-lib 0.1.16__py3-none-any.whl → 0.1.56b1__py3-none-any.whl

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 (36) hide show
  1. sycommon/config/Config.py +6 -2
  2. sycommon/config/RerankerConfig.py +1 -0
  3. sycommon/database/async_base_db_service.py +36 -0
  4. sycommon/database/async_database_service.py +96 -0
  5. sycommon/database/database_service.py +6 -1
  6. sycommon/health/metrics.py +13 -0
  7. sycommon/llm/__init__.py +0 -0
  8. sycommon/llm/embedding.py +149 -0
  9. sycommon/llm/get_llm.py +177 -0
  10. sycommon/llm/llm_logger.py +126 -0
  11. sycommon/logging/async_sql_logger.py +65 -0
  12. sycommon/logging/kafka_log.py +36 -14
  13. sycommon/logging/logger_levels.py +23 -0
  14. sycommon/logging/sql_logger.py +53 -0
  15. sycommon/middleware/context.py +2 -0
  16. sycommon/middleware/middleware.py +4 -0
  17. sycommon/middleware/traceid.py +155 -32
  18. sycommon/models/mqlistener_config.py +1 -0
  19. sycommon/rabbitmq/rabbitmq_client.py +377 -821
  20. sycommon/rabbitmq/rabbitmq_pool.py +338 -0
  21. sycommon/rabbitmq/rabbitmq_service.py +411 -229
  22. sycommon/services.py +116 -61
  23. sycommon/synacos/example.py +153 -0
  24. sycommon/synacos/example2.py +129 -0
  25. sycommon/synacos/feign.py +90 -413
  26. sycommon/synacos/feign_client.py +335 -0
  27. sycommon/synacos/nacos_service.py +159 -106
  28. sycommon/synacos/param.py +75 -0
  29. sycommon/tools/merge_headers.py +97 -0
  30. sycommon/tools/snowflake.py +296 -7
  31. {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.56b1.dist-info}/METADATA +19 -13
  32. sycommon_python_lib-0.1.56b1.dist-info/RECORD +68 -0
  33. sycommon_python_lib-0.1.16.dist-info/RECORD +0 -52
  34. {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.56b1.dist-info}/WHEEL +0 -0
  35. {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.56b1.dist-info}/entry_points.txt +0 -0
  36. {sycommon_python_lib-0.1.16.dist-info → sycommon_python_lib-0.1.56b1.dist-info}/top_level.txt +0 -0
sycommon/synacos/feign.py CHANGED
@@ -1,8 +1,9 @@
1
1
  import io
2
2
  import os
3
3
  import time
4
- import inspect
5
- from urllib.parse import urljoin
4
+
5
+ from sycommon.tools.merge_headers import merge_headers
6
+ from sycommon.tools.snowflake import Snowflake
6
7
 
7
8
  import aiohttp
8
9
  from sycommon.logging.kafka_log import SYLogger
@@ -14,397 +15,6 @@ from sycommon.synacos.nacos_service import NacosService
14
15
  方式二: 使用 feign 函数
15
16
  """
16
17
 
17
- # 示例Feign客户端接口
18
- # 1. 定义完整的Feign客户端接口
19
- # @feign_client(service_name="product-service", path_prefix="/api/v2")
20
- # class ProductServiceClient:
21
- # """商品服务Feign客户端示例,涵盖所有参数类型"""
22
- #
23
- # # ------------------------------
24
- # # 场景1: Path参数 + Query参数
25
- # # 请求示例: GET /products/{product_id}/reviews?status=APPROVED&page=1&size=10
26
- # # ------------------------------
27
- # @feign_request("GET", "/products/{product_id}/reviews")
28
- # async def get_product_reviews(
29
- # self,
30
- # product_id: int, # Path参数 (URL路径中的占位符)
31
- # status: Optional[str] = None, # Query参数 (URL查询字符串)
32
- # page: int = 1, # Query参数 (默认值)
33
- # size: int = 10 # Query参数 (默认值)
34
- # ) -> Dict[str, Any]:
35
- # """获取商品评价列表"""
36
- # pass
37
- #
38
- # # ------------------------------
39
- # # 场景2: 仅Query参数
40
- # # 请求示例: GET /products?category=electronics&min_price=100&max_price=500&sort=price_asc
41
- # # ------------------------------
42
- # @feign_request("GET", "/products")
43
- # async def search_products(
44
- # self,
45
- # category: str, # 必选Query参数
46
- # min_price: Optional[float] = None, # 可选Query参数
47
- # max_price: Optional[float] = None, # 可选Query参数
48
- # sort: str = "created_desc" # 带默认值的Query参数
49
- # ) -> Dict[str, Any]:
50
- # """搜索商品(仅查询参数)"""
51
- # pass
52
- #
53
- # # ------------------------------
54
- # # 场景3: JSON Body参数 (POST)
55
- # # 请求示例: POST /products (请求体为JSON)
56
- # # ------------------------------
57
- # @feign_request("POST", "/products", headers={"s-y-version": "2.1"})
58
- # async def create_product(
59
- # self,
60
- # product_data: Dict[str, Any] # JSON请求体
61
- # ) -> Dict[str, Any]:
62
- # """创建商品(JSON请求体)"""
63
- # pass
64
- #
65
- # # ------------------------------
66
- # # 场景4: Path参数 + JSON Body (PUT)
67
- # # 请求示例: PUT /products/{product_id} (请求体为JSON)
68
- # # ------------------------------
69
- # @feign_request("PUT", "/products/{product_id}")
70
- # async def update_product(
71
- # self,
72
- # product_id: int, # Path参数
73
- # update_data: Dict[str, Any] # JSON请求体
74
- # ) -> Dict[str, Any]:
75
- # """更新商品信息"""
76
- # pass
77
- #
78
- # # ------------------------------
79
- # # 场景5: FormData表单提交 (x-www-form-urlencoded)
80
- # # 请求示例: POST /products/batch-status (表单字段)
81
- # # ------------------------------
82
- # @feign_request(
83
- # "POST",
84
- # "/products/batch-status",
85
- # headers={"Content-Type": "application/x-www-form-urlencoded"}
86
- # )
87
- # async def batch_update_status(
88
- # self,
89
- # product_ids: str, # 表单字段 (多个ID用逗号分隔)
90
- # status: str, # 表单字段 (目标状态)
91
- # operator: str # 表单字段 (操作人)
92
- # ) -> Dict[str, Any]:
93
- # """批量更新商品状态(表单提交)"""
94
- # pass
95
- #
96
- # # ------------------------------
97
- # # 场景6: 文件上传 + 表单字段混合
98
- # # 请求示例: POST /products/{product_id}/images (multipart/form-data)
99
- # # ------------------------------
100
- # @feign_upload(field_name="image_file") # 指定文件表单字段名
101
- # @feign_request("POST", "/products/{product_id}/images")
102
- # async def upload_product_image(
103
- # self,
104
- # product_id: int, # Path参数
105
- # file_path: str, # 本地文件路径(会被转为文件上传)
106
- # image_type: str, # 表单字段(图片类型)
107
- # is_primary: bool = False, # 表单字段(是否主图)
108
- # remark: Optional[str] = None # 可选表单字段
109
- # ) -> Dict[str, Any]:
110
- # """上传商品图片(文件+表单字段)"""
111
- # pass
112
- #
113
- # # ------------------------------
114
- # # 场景7: 多Path参数 + DELETE请求
115
- # # 请求示例: DELETE /products/{product_id}/skus/{sku_id}
116
- # # ------------------------------
117
- # @feign_request("DELETE", "/products/{product_id}/skus/{sku_id}")
118
- # async def delete_product_sku(
119
- # self,
120
- # product_id: int, # Path参数1
121
- # sku_id: int # Path参数2
122
- # ) -> Dict[str, Any]:
123
- # """删除商品SKU(多路径参数)"""
124
- # pass
125
- #
126
- # # ------------------------------
127
- # # 场景8: 复杂JSON Body + Query参数
128
- # # 请求示例: POST /products/filter?include_out_of_stock=false
129
- # # ------------------------------
130
- # @feign_request("POST", "/products/filter")
131
- # async def advanced_filter(
132
- # self,
133
- # filter_condition: Dict[str, Any], # JSON请求体(复杂筛选条件)
134
- # include_out_of_stock: bool = False, # Query参数
135
- # page: int = 1, # Query参数
136
- # size: int = 20 # Query参数
137
- # ) -> Dict[str, Any]:
138
- # """高级筛选商品(JSON体+查询参数)"""
139
- # pass
140
- #
141
- #
142
- # # 2. 完整调用示例
143
- # async def feign_complete_demo():
144
- # # ------------------------------
145
- # # 调用场景1: Path参数 + Query参数
146
- # # ------------------------------
147
- # reviews = await ProductServiceClient().get_product_reviews(
148
- # product_id=10086, # Path参数
149
- # status="APPROVED", # Query参数
150
- # page=1,
151
- # size=20
152
- # )
153
- # print(f"场景1 - 商品评价: {reviews.get('total', 0)}条评价")
154
- #
155
- # # ------------------------------
156
- # # 调用场景2: 仅Query参数
157
- # # ------------------------------
158
- # electronics = await ProductServiceClient().search_products(
159
- # category="electronics", # 必选Query
160
- # min_price=1000,
161
- # max_price=5000,
162
- # sort="price_asc"
163
- # )
164
- # print(f"场景2 - 搜索结果: {len(electronics.get('items', []))}个商品")
165
- #
166
- # # ------------------------------
167
- # # 调用场景3: JSON Body参数 (POST)
168
- # # ------------------------------
169
- # new_product = await ProductServiceClient().create_product({
170
- # "name": "无线蓝牙耳机",
171
- # "category": "electronics",
172
- # "price": 299.99,
173
- # "stock": 500,
174
- # "attributes": {
175
- # "brand": "Feign",
176
- # "battery_life": "24h"
177
- # }
178
- # })
179
- # print(f"场景3 - 新建商品: ID={new_product.get('id')}")
180
- # product_id = new_product.get('id') # 用于后续示例
181
- #
182
- # # ------------------------------
183
- # # 调用场景4: Path参数 + JSON Body (PUT)
184
- # # ------------------------------
185
- # if product_id:
186
- # updated = await ProductServiceClient().update_product(
187
- # product_id=product_id,
188
- # update_data={
189
- # "price": 279.99, # 降价
190
- # "stock": 600
191
- # }
192
- # )
193
- # print(f"场景4 - 商品更新: 状态={updated.get('success')}")
194
- #
195
- # # ------------------------------
196
- # # 调用场景5: FormData表单提交
197
- # # ------------------------------
198
- # batch_result = await ProductServiceClient().batch_update_status(
199
- # product_ids="1001,1002,1003", # 多个ID用逗号分隔
200
- # status="ON_SALE",
201
- # operator="system"
202
- # )
203
- # print(f"场景5 - 批量更新: 成功{batch_result.get('success_count')}个")
204
- #
205
- # # ------------------------------
206
- # # 调用场景6: 文件上传 + 表单字段
207
- # # ------------------------------
208
- # if product_id:
209
- # upload_result = await ProductServiceClient().upload_product_image(
210
- # product_id=product_id,
211
- # file_path="/tmp/product_main.jpg", # 本地图片路径
212
- # image_type="main",
213
- # is_primary=True,
214
- # remark="商品主图"
215
- # )
216
- # print(f"场景6 - 图片上传: URL={upload_result.get('image_url')}")
217
- #
218
- # # ------------------------------
219
- # # 调用场景7: 多Path参数 + DELETE
220
- # # ------------------------------
221
- # delete_result = await ProductServiceClient().delete_product_sku(
222
- # product_id=10086,
223
- # sku_id=5001
224
- # )
225
- # print(f"场景7 - 删除SKU: {delete_result.get('message')}")
226
- #
227
- # # ------------------------------
228
- # # 调用场景8: 复杂JSON Body + Query参数
229
- # # ------------------------------
230
- # filtered = await ProductServiceClient().advanced_filter(
231
- # filter_condition={ # 复杂JSON条件
232
- # "categories": ["electronics", "home"],
233
- # "price_range": {"min": 500, "max": 3000},
234
- # "tags": ["new", "promotion"]
235
- # },
236
- # include_out_of_stock=False, # Query参数
237
- # page=1,
238
- # size=10
239
- # )
240
- # print(f"场景8 - 高级筛选: {filtered.get('total')}个匹配商品")
241
-
242
-
243
- def feign_client(service_name: str, path_prefix: str = "", default_timeout: float | None = None):
244
- """Feign客户端装饰器,每次请求后自动关闭会话"""
245
- def decorator(cls):
246
- class FeignWrapper:
247
- def __init__(self):
248
- self.service_name = service_name
249
- self.path_prefix = path_prefix
250
- self.default_timeout = default_timeout
251
- self.nacos_manager = None # 延迟初始化Nacos
252
- self.session = None # 延迟初始化aiohttp会话
253
-
254
- def __getattr__(self, name):
255
- """动态获取方法并包装为Feign调用,请求后自动关闭会话"""
256
- func = getattr(cls, name)
257
-
258
- async def wrapper(*args, **kwargs):
259
- # 确保会话初始化
260
- if self.session is None:
261
- self.session = aiohttp.ClientSession()
262
- if self.nacos_manager is None:
263
- self.nacos_manager = NacosService(None)
264
-
265
- try:
266
- # 1. 解析参数
267
- sig = inspect.signature(func)
268
- param_names = list(sig.parameters.keys())
269
- try:
270
- if param_names and param_names[0] == 'self':
271
- bound_args = sig.bind(self, *args, **kwargs)
272
- else:
273
- bound_args = sig.bind(*args, **kwargs)
274
- bound_args.apply_defaults()
275
- params = dict(bound_args.arguments)
276
- params.pop('self', None)
277
- SYLogger.debug(f"解析参数: {params}")
278
- except TypeError as e:
279
- SYLogger.error(f"参数绑定失败: {str(e)}")
280
- raise
281
-
282
- # 2. 构建请求
283
- request_meta = getattr(func, "_feign_meta", {})
284
- method = request_meta.get("method", "GET")
285
- path = request_meta.get("path", "")
286
- headers = request_meta.get("headers", {}).copy()
287
- timeout = kwargs.pop('timeout', self.default_timeout)
288
-
289
- full_path = f"{self.path_prefix}{path}"
290
- for param_name, param_value in params.items():
291
- if param_value is not None:
292
- full_path = full_path.replace(
293
- f"{{{param_name}}}", str(param_value))
294
-
295
- is_json_request = method.upper() in ["POST", "PUT", "PATCH"] and \
296
- not request_meta.get("is_upload", False)
297
- if is_json_request and "Content-Type" not in headers:
298
- headers["Content-Type"] = "application/json"
299
-
300
- # 3. 服务发现
301
- version = headers.get('s-y-version')
302
- instances = self.nacos_manager.get_service_instances(
303
- self.service_name, target_version=version)
304
- if not instances:
305
- raise RuntimeError(
306
- f"未找到服务 {self.service_name} 的健康实例")
307
-
308
- instance = instances[int(time.time()) % len(instances)]
309
- base_url = f"http://{instance['ip']}:{instance['port']}"
310
- url = urljoin(base_url, full_path)
311
- SYLogger.info(f"请求: {method} {url}")
312
-
313
- # 4. 准备请求参数
314
- query_params = {k: v for k, v in params.items()
315
- if f"{{{k}}}" not in path and v is not None}
316
- request_data = None
317
- files = None
318
-
319
- if request_meta.get("is_upload", False):
320
- files = aiohttp.FormData()
321
- file_path = params.get('file_path')
322
- if file_path and os.path.exists(file_path):
323
- file_field = request_meta.get(
324
- "upload_field", "file")
325
- with open(file_path, 'rb') as f:
326
- files.add_field(
327
- file_field,
328
- f.read(),
329
- filename=os.path.basename(file_path)
330
- )
331
- for key, value in params.items():
332
- if key != 'file_path' and value is not None:
333
- files.add_field(key, str(value))
334
- headers.pop('Content-Type', None)
335
- elif is_json_request:
336
- body_params = [k for k in params if k not in query_params
337
- and f"{{{k}}}" not in path]
338
- if body_params:
339
- request_data = params[body_params[0]] if len(body_params) == 1 else \
340
- {k: params[k] for k in body_params}
341
-
342
- # 5. 发送请求并获取响应
343
- async with self.session.request(
344
- method=method,
345
- url=url,
346
- headers=headers,
347
- params=query_params,
348
- json=request_data if not files else None,
349
- data=files,
350
- timeout=timeout
351
- ) as response:
352
- return await self._handle_response(response)
353
-
354
- finally:
355
- # 请求完成后自动关闭会话
356
- if self.session:
357
- await self.session.close()
358
- self.session = None # 重置会话,下次调用重新创建
359
- SYLogger.info(
360
- f"自动关闭aiohttp会话: {self.service_name}")
361
-
362
- return wrapper
363
-
364
- async def _handle_response(self, response):
365
- """处理响应结果(保持不变)"""
366
- status = response.status
367
- if 200 <= status < 300:
368
- content_type = response.headers.get('Content-Type', '')
369
- if 'application/json' in content_type:
370
- return await response.json()
371
- else:
372
- return await response.read()
373
- else:
374
- error_msg = await response.text()
375
- SYLogger.error(f"响应错误: {status} - {error_msg}")
376
- raise RuntimeError(f"HTTP {status}: {error_msg}")
377
-
378
- return FeignWrapper
379
- return decorator
380
-
381
-
382
- def feign_request(method: str, path: str, headers: dict = None):
383
- """定义请求元数据的装饰器"""
384
- def decorator(func):
385
- func._feign_meta = {
386
- "method": method.upper(),
387
- "path": path,
388
- "headers": headers.copy() if headers else {}
389
- }
390
- return func
391
- return decorator
392
-
393
-
394
- def feign_upload(field_name: str = "file"):
395
- """处理文件上传的装饰器"""
396
- def decorator(func):
397
- async def wrapper(*args, **kwargs):
398
- file_path = kwargs.get('file_path')
399
- if not file_path or not os.path.exists(file_path):
400
- raise ValueError(f"文件路径不存在: {file_path}")
401
- with open(file_path, 'rb') as f:
402
- files = {field_name: (os.path.basename(file_path), f.read())}
403
- kwargs['files'] = files
404
- return await func(*args, **kwargs)
405
- return wrapper
406
- return decorator
407
-
408
18
 
409
19
  async def feign(service_name, api_path, method='GET', params=None, headers=None, file_path=None,
410
20
  path_params=None, body=None, files=None, form_data=None, timeout=None):
@@ -415,12 +25,18 @@ async def feign(service_name, api_path, method='GET', params=None, headers=None,
415
25
  try:
416
26
  # 初始化headers,确保是可修改的字典
417
27
  headers = headers.copy() if headers else {}
28
+ headers = merge_headers(SYLogger.get_headers(), headers)
29
+ if "x-traceId-header" not in headers:
30
+ headers["x-traceId-header"] = SYLogger.get_trace_id() or Snowflake.id
418
31
 
419
32
  # 处理JSON请求的Content-Type
420
33
  is_json_request = method.upper() in ["POST", "PUT", "PATCH"] and not (
421
34
  files or form_data or file_path)
422
- if is_json_request and "Content-Type" not in headers:
423
- headers["Content-Type"] = "application/json"
35
+ if is_json_request:
36
+ # 将headers的key全部转为小写,统一判断
37
+ headers_lower = {k.lower(): v for k, v in headers.items()}
38
+ if "content-type" not in headers_lower:
39
+ headers["Content-Type"] = "application/json"
424
40
 
425
41
  nacos_service = NacosService(None)
426
42
  version = headers.get('s-y-version')
@@ -436,7 +52,7 @@ async def feign(service_name, api_path, method='GET', params=None, headers=None,
436
52
  instance = instances[int(time.time()) % len(instances)]
437
53
 
438
54
  SYLogger.info(f"nacos:开始调用服务: {service_name}")
439
- SYLogger.info(f"nacos:请求头: {headers}")
55
+ # SYLogger.info(f"nacos:请求头: {headers}")
440
56
 
441
57
  ip = instance.get('ip')
442
58
  port = instance.get('port')
@@ -457,8 +73,25 @@ async def feign(service_name, api_path, method='GET', params=None, headers=None,
457
73
  for key, value in form_data.items():
458
74
  data.add_field(key, value)
459
75
  if files:
460
- for field_name, (filename, content) in files.items():
461
- data.add_field(field_name, content, filename=filename)
76
+ # 兼容处理:同时支持字典(单文件)和列表(多文件)
77
+ if isinstance(files, dict):
78
+ # 处理原有字典格式(单文件)
79
+ # 字典格式:{field_name: (filename, content)}
80
+ for field_name, (filename, content) in files.items():
81
+ data.add_field(field_name, content,
82
+ filename=filename)
83
+ elif isinstance(files, list):
84
+ # 处理新列表格式(多文件)
85
+ # 列表格式:[(field_name, filename, content), ...]
86
+ for item in files:
87
+ if len(item) != 3:
88
+ raise ValueError(
89
+ f"列表元素格式错误,需为 (field_name, filename, content),实际为 {item}")
90
+ field_name, filename, content = item
91
+ data.add_field(field_name, content,
92
+ filename=filename)
93
+ else:
94
+ raise TypeError(f"files 参数必须是字典或列表,实际为 {type(files)}")
462
95
  if file_path:
463
96
  filename = os.path.basename(file_path)
464
97
  with open(file_path, 'rb') as f:
@@ -473,7 +106,7 @@ async def feign(service_name, api_path, method='GET', params=None, headers=None,
473
106
  data=data,
474
107
  timeout=timeout
475
108
  ) as response:
476
- return await _handle_feign_response(response)
109
+ return await _handle_feign_response(response, service_name, api_path)
477
110
  else:
478
111
  # 普通JSON请求
479
112
  async with session.request(
@@ -484,30 +117,74 @@ async def feign(service_name, api_path, method='GET', params=None, headers=None,
484
117
  json=body,
485
118
  timeout=timeout
486
119
  ) as response:
487
- return await _handle_feign_response(response)
120
+ return await _handle_feign_response(response, service_name, api_path)
488
121
  except aiohttp.ClientError as e:
489
122
  SYLogger.error(
490
- f"nacos:请求服务接口时出错ClientError path: {api_path} error:{e}")
123
+ f"nacos:请求服务接口时出错ClientError server: {service_name} path: {api_path} error:{e}")
491
124
  return None
492
125
  except Exception as e:
493
126
  import traceback
494
127
  SYLogger.error(
495
- f"nacos:请求服务接口时出错 path: {api_path} error:{traceback.format_exc()}")
128
+ f"nacos:请求服务接口时出错 server: {service_name} path: {api_path} error:{traceback.format_exc()}")
496
129
  return None
497
130
  finally:
498
131
  await session.close()
499
132
 
500
133
 
501
- async def _handle_feign_response(response):
502
- """处理Feign请求的响应"""
503
- if response.status == 200:
504
- content_type = response.headers.get('Content-Type')
505
- if 'application/json' in content_type:
506
- return await response.json()
134
+ async def _handle_feign_response(response, service_name: str, api_path: str):
135
+ """
136
+ 处理Feign请求的响应,统一返回格式
137
+ 调整逻辑:先判断状态码,再处理内容
138
+ - 200状态:优先识别JSON/文本,其他均按文件流(二进制)处理
139
+ - 非200状态:统一返回错误字典
140
+ """
141
+ try:
142
+ status_code = response.status
143
+ content_type = response.headers.get('Content-Type', '')
144
+ content_type = content_type.lower() if content_type else ''
145
+
146
+ response_body = None
147
+
148
+ if status_code == 200:
149
+ if content_type and 'application/json' in content_type:
150
+ response_body = await response.json()
151
+ elif content_type and 'text/' in content_type:
152
+ # 文本类型(text/plain、text/html等):按文本读取
153
+ try:
154
+ response_body = await response.text(encoding='utf-8')
155
+ except UnicodeDecodeError:
156
+ # 兼容中文编码(gbk)
157
+ response_body = await response.text(encoding='gbk')
158
+ else:
159
+ # 其他类型(PDF、图片、octet-stream等):按文件流(二进制)读取
160
+ binary_data = await response.read()
161
+ SYLogger.info(
162
+ f"按文件流处理响应,类型:{content_type},大小:{len(binary_data)/1024:.2f}KB")
163
+ return io.BytesIO(binary_data) # 返回BytesIO,支持read()
164
+ return response_body
507
165
  else:
508
- content = await response.read()
509
- return io.BytesIO(content)
510
- else:
511
- error_msg = await response.text()
512
- SYLogger.error(f"nacos:请求失败,状态码: {response.status},响应内容: {error_msg}")
166
+ # 非200状态:统一读取响应体(兼容文本/二进制错误信息)
167
+ try:
168
+ if content_type and 'application/json' in content_type:
169
+ response_body = await response.json()
170
+ else:
171
+ response_body = await response.text(encoding='utf-8', errors='ignore')
172
+ except Exception:
173
+ binary_data = await response.read()
174
+ response_body = f"非200状态,响应无法解码:{binary_data[:100].hex()} server: {service_name} path: {api_path}"
175
+
176
+ error_msg = f"请求失败,状态码: {status_code},响应内容: {str(response_body)[:500]} server: {service_name} path: {api_path}"
177
+ SYLogger.error(error_msg)
178
+ return {
179
+ "success": False,
180
+ "code": status_code,
181
+ "message": error_msg,
182
+ "data": response_body
183
+ }
184
+
185
+ except Exception as e:
186
+ import traceback
187
+ error_detail = f"处理响应异常: {str(e)}\n{traceback.format_exc()}"
188
+ SYLogger.error(
189
+ f"nacos:处理响应时出错: {error_detail} server: {service_name} path: {api_path}")
513
190
  return None