sycommon-python-lib 0.1.28__py3-none-any.whl → 0.1.29__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.
- sycommon/logging/kafka_log.py +16 -6
- sycommon/rabbitmq/rabbitmq_client.py +4 -0
- sycommon/synacos/feign.py +72 -22
- sycommon/synacos/nacos_service.py +14 -9
- {sycommon_python_lib-0.1.28.dist-info → sycommon_python_lib-0.1.29.dist-info}/METADATA +9 -9
- {sycommon_python_lib-0.1.28.dist-info → sycommon_python_lib-0.1.29.dist-info}/RECORD +9 -9
- {sycommon_python_lib-0.1.28.dist-info → sycommon_python_lib-0.1.29.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.28.dist-info → sycommon_python_lib-0.1.29.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.28.dist-info → sycommon_python_lib-0.1.29.dist-info}/top_level.txt +0 -0
sycommon/logging/kafka_log.py
CHANGED
|
@@ -470,12 +470,22 @@ class SYLogger:
|
|
|
470
470
|
thread_info = SYLogger._get_execution_context()
|
|
471
471
|
|
|
472
472
|
# 构建日志结构,添加线程/协程信息到threadName字段
|
|
473
|
-
request_log = {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
473
|
+
request_log = {}
|
|
474
|
+
if level == "ERROR":
|
|
475
|
+
request_log = {
|
|
476
|
+
"trace_id": str(trace_id) if trace_id else Snowflake.next_id(),
|
|
477
|
+
"message": msg_str,
|
|
478
|
+
"traceback": traceback.format_exc(),
|
|
479
|
+
"level": level,
|
|
480
|
+
"threadName": thread_info
|
|
481
|
+
}
|
|
482
|
+
else:
|
|
483
|
+
request_log = {
|
|
484
|
+
"trace_id": str(trace_id) if trace_id else Snowflake.next_id(),
|
|
485
|
+
"message": msg_str,
|
|
486
|
+
"level": level,
|
|
487
|
+
"threadName": thread_info
|
|
488
|
+
}
|
|
479
489
|
|
|
480
490
|
# 选择日志级别
|
|
481
491
|
_log = ''
|
|
@@ -608,6 +608,10 @@ class RabbitMQClient:
|
|
|
608
608
|
if not self.message_handler or not self._is_consuming:
|
|
609
609
|
logger.warning("未设置消息处理器或已停止消费")
|
|
610
610
|
# await message.ack()
|
|
611
|
+
try:
|
|
612
|
+
await message.reject(requeue=True)
|
|
613
|
+
except Exception as e:
|
|
614
|
+
logger.error(f"拒绝消息失败: {e}")
|
|
611
615
|
return
|
|
612
616
|
|
|
613
617
|
message_id = message.message_id or str(id(message))
|
sycommon/synacos/feign.py
CHANGED
|
@@ -94,20 +94,21 @@ from sycommon.synacos.nacos_service import NacosService
|
|
|
94
94
|
# pass
|
|
95
95
|
#
|
|
96
96
|
# # ------------------------------
|
|
97
|
-
# # 场景6:
|
|
97
|
+
# # 场景6: 多文件上传 + 表单字段混合
|
|
98
98
|
# # 请求示例: POST /products/{product_id}/images (multipart/form-data)
|
|
99
|
+
# # 支持同时上传多个文件,共用字段名 "image_file"
|
|
99
100
|
# # ------------------------------
|
|
100
|
-
# @feign_upload(field_name="image_file") #
|
|
101
|
+
# @feign_upload(field_name="image_file") # 指定所有文件的表单字段名
|
|
101
102
|
# @feign_request("POST", "/products/{product_id}/images")
|
|
102
103
|
# async def upload_product_image(
|
|
103
104
|
# self,
|
|
104
|
-
# product_id: int, # Path
|
|
105
|
-
#
|
|
106
|
-
# image_type: str, #
|
|
105
|
+
# product_id: int, # Path参数(URL路径中的占位符)
|
|
106
|
+
# file_paths: str | list[str], # 本地文件路径(单个路径字符串或多个路径列表)
|
|
107
|
+
# image_type: str, # 表单字段(图片类型,如"main"、"detail")
|
|
107
108
|
# is_primary: bool = False, # 表单字段(是否主图)
|
|
108
|
-
# remark: Optional[str] = None #
|
|
109
|
+
# remark: Optional[str] = None # 可选表单字段(备注信息)
|
|
109
110
|
# ) -> Dict[str, Any]:
|
|
110
|
-
# """
|
|
111
|
+
# """上传商品图片(支持多文件 + 表单字段混合)"""
|
|
111
112
|
# pass
|
|
112
113
|
#
|
|
113
114
|
# # ------------------------------
|
|
@@ -203,17 +204,18 @@ from sycommon.synacos.nacos_service import NacosService
|
|
|
203
204
|
# print(f"场景5 - 批量更新: 成功{batch_result.get('success_count')}个")
|
|
204
205
|
#
|
|
205
206
|
# # ------------------------------
|
|
206
|
-
# # 调用场景6:
|
|
207
|
+
# # 调用场景6: 多文件上传 + 表单字段
|
|
208
|
+
# # 支持两种调用方式:单文件上传 / 多文件上传
|
|
207
209
|
# # ------------------------------
|
|
208
210
|
# if product_id:
|
|
209
|
-
#
|
|
211
|
+
# single_upload_result = await ProductServiceClient().upload_product_image(
|
|
210
212
|
# product_id=product_id,
|
|
211
|
-
#
|
|
213
|
+
# file_paths="/tmp/product_main.jpg", # 单个文件路径或多个路径[""]
|
|
212
214
|
# image_type="main",
|
|
213
215
|
# is_primary=True,
|
|
214
|
-
# remark="
|
|
216
|
+
# remark="商品主图(单文件)"
|
|
215
217
|
# )
|
|
216
|
-
# print(f"场景6 -
|
|
218
|
+
# print(f"场景6 - 单文件上传: 主图URL={single_upload_result.get('image_urls')[0]}")
|
|
217
219
|
#
|
|
218
220
|
# # ------------------------------
|
|
219
221
|
# # 调用场景7: 多Path参数 + DELETE
|
|
@@ -392,16 +394,47 @@ def feign_request(method: str, path: str, headers: dict = None):
|
|
|
392
394
|
|
|
393
395
|
|
|
394
396
|
def feign_upload(field_name: str = "file"):
|
|
395
|
-
"""
|
|
397
|
+
"""处理多文件上传的装饰器(支持同字段名多文件 + 表单字段混合)"""
|
|
396
398
|
def decorator(func):
|
|
397
399
|
async def wrapper(*args, **kwargs):
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
400
|
+
# 获取文件路径列表(支持单个文件路径字符串或多个文件路径列表)
|
|
401
|
+
file_paths = kwargs.get('file_paths')
|
|
402
|
+
if not file_paths:
|
|
403
|
+
raise ValueError("缺少文件路径参数: file_paths(可为单个路径字符串或列表)")
|
|
404
|
+
|
|
405
|
+
# 统一转为列表格式(兼容单个文件的情况)
|
|
406
|
+
if isinstance(file_paths, str):
|
|
407
|
+
file_paths = [file_paths]
|
|
408
|
+
if not isinstance(file_paths, list):
|
|
409
|
+
raise ValueError("file_paths 必须是字符串或列表")
|
|
410
|
+
|
|
411
|
+
# 验证所有文件是否存在
|
|
412
|
+
for path in file_paths:
|
|
413
|
+
if not os.path.exists(path):
|
|
414
|
+
raise FileNotFoundError(f"文件不存在: {path}")
|
|
415
|
+
|
|
416
|
+
# 构建 multipart/form-data 表单数据
|
|
417
|
+
form_data = aiohttp.FormData()
|
|
418
|
+
|
|
419
|
+
# 添加所有文件(共用同一个 field_name)
|
|
420
|
+
for file_path in file_paths:
|
|
421
|
+
with open(file_path, 'rb') as f:
|
|
422
|
+
form_data.add_field(
|
|
423
|
+
field_name, # 所有文件使用相同的表单字段名
|
|
424
|
+
f.read(),
|
|
425
|
+
filename=os.path.basename(file_path) # 保留原文件名
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# 添加其他表单字段(从 kwargs 中提取非文件参数)
|
|
429
|
+
form_fields = {k: v for k, v in kwargs.items() if k !=
|
|
430
|
+
'file_paths'}
|
|
431
|
+
for key, value in form_fields.items():
|
|
432
|
+
if value is not None:
|
|
433
|
+
form_data.add_field(key, str(value)) # 表单字段转为字符串
|
|
434
|
+
|
|
435
|
+
# 将构建好的表单数据传入原函数
|
|
436
|
+
kwargs['form_data'] = form_data
|
|
437
|
+
return await func(*args, **kwargs)
|
|
405
438
|
return wrapper
|
|
406
439
|
return decorator
|
|
407
440
|
|
|
@@ -457,8 +490,25 @@ async def feign(service_name, api_path, method='GET', params=None, headers=None,
|
|
|
457
490
|
for key, value in form_data.items():
|
|
458
491
|
data.add_field(key, value)
|
|
459
492
|
if files:
|
|
460
|
-
|
|
461
|
-
|
|
493
|
+
# 兼容处理:同时支持字典(单文件)和列表(多文件)
|
|
494
|
+
if isinstance(files, dict):
|
|
495
|
+
# 处理原有字典格式(单文件)
|
|
496
|
+
# 字典格式:{field_name: (filename, content)}
|
|
497
|
+
for field_name, (filename, content) in files.items():
|
|
498
|
+
data.add_field(field_name, content,
|
|
499
|
+
filename=filename)
|
|
500
|
+
elif isinstance(files, list):
|
|
501
|
+
# 处理新列表格式(多文件)
|
|
502
|
+
# 列表格式:[(field_name, filename, content), ...]
|
|
503
|
+
for item in files:
|
|
504
|
+
if len(item) != 3:
|
|
505
|
+
raise ValueError(
|
|
506
|
+
f"列表元素格式错误,需为 (field_name, filename, content),实际为 {item}")
|
|
507
|
+
field_name, filename, content = item
|
|
508
|
+
data.add_field(field_name, content,
|
|
509
|
+
filename=filename)
|
|
510
|
+
else:
|
|
511
|
+
raise TypeError(f"files 参数必须是字典或列表,实际为 {type(files)}")
|
|
462
512
|
if file_path:
|
|
463
513
|
filename = os.path.basename(file_path)
|
|
464
514
|
with open(file_path, 'rb') as f:
|
|
@@ -87,12 +87,18 @@ class NacosService(metaclass=SingletonMeta):
|
|
|
87
87
|
# 心跳相关
|
|
88
88
|
self._last_heartbeat_time = 0
|
|
89
89
|
self._heartbeat_fail_count = 0
|
|
90
|
-
self._heartbeat_lock = threading.Lock()
|
|
90
|
+
self._heartbeat_lock = threading.Lock()
|
|
91
91
|
self._heartbeat_thread = None
|
|
92
92
|
|
|
93
93
|
self.max_heartbeat_timeout = self.nacos_config.get(
|
|
94
|
-
'maxHeartbeatTimeout', 30)
|
|
95
|
-
self._last_successful_heartbeat = time.time()
|
|
94
|
+
'maxHeartbeatTimeout', 30)
|
|
95
|
+
self._last_successful_heartbeat = time.time()
|
|
96
|
+
# 连接监控检查间隔(新增配置,默认30秒,避免硬编码)
|
|
97
|
+
self.connection_check_interval = self.nacos_config.get(
|
|
98
|
+
'connectionCheckInterval', 30)
|
|
99
|
+
# 配置监视线程检查间隔(默认30秒)
|
|
100
|
+
self.config_watch_interval = self.nacos_config.get(
|
|
101
|
+
'configWatchInterval', 30)
|
|
96
102
|
|
|
97
103
|
# 启动配置监视线程
|
|
98
104
|
self._watch_thread = threading.Thread(
|
|
@@ -586,10 +592,9 @@ class NacosService(metaclass=SingletonMeta):
|
|
|
586
592
|
|
|
587
593
|
def monitor_connection(self):
|
|
588
594
|
"""优化的连接监控线程,缩短检查间隔"""
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
check_counter = 0 # 检查计数器
|
|
595
|
+
check_interval = self.connection_check_interval
|
|
596
|
+
thread_start_time = time.time()
|
|
597
|
+
check_counter = 0
|
|
593
598
|
|
|
594
599
|
while not self._shutdown_event.is_set():
|
|
595
600
|
try:
|
|
@@ -739,7 +744,7 @@ class NacosService(metaclass=SingletonMeta):
|
|
|
739
744
|
|
|
740
745
|
def _watch_configs(self):
|
|
741
746
|
"""配置监听线程"""
|
|
742
|
-
check_interval =
|
|
747
|
+
check_interval = self.config_watch_interval
|
|
743
748
|
|
|
744
749
|
while not self._shutdown_event.is_set():
|
|
745
750
|
try:
|
|
@@ -750,7 +755,7 @@ class NacosService(metaclass=SingletonMeta):
|
|
|
750
755
|
self._config_cache[data_id] = new_config
|
|
751
756
|
except Exception as e:
|
|
752
757
|
SYLogger.error(f"nacos:配置监视线程异常: {str(e)}")
|
|
753
|
-
self._shutdown_event.wait(check_interval)
|
|
758
|
+
self._shutdown_event.wait(check_interval)
|
|
754
759
|
|
|
755
760
|
def discover_services(self, service_name: str, group: str = "DEFAULT_GROUP", version: str = None) -> List[Dict]:
|
|
756
761
|
"""发现服务实例列表 (与Java格式兼容)"""
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sycommon-python-lib
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.29
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
Requires-Dist: aio-pika>=9.5.7
|
|
8
|
-
Requires-Dist: aiohttp>=3.
|
|
8
|
+
Requires-Dist: aiohttp>=3.13.1
|
|
9
9
|
Requires-Dist: decorator>=5.2.1
|
|
10
|
-
Requires-Dist: fastapi>=0.
|
|
10
|
+
Requires-Dist: fastapi>=0.120.0
|
|
11
11
|
Requires-Dist: kafka-python>=2.2.15
|
|
12
12
|
Requires-Dist: loguru>=0.7.3
|
|
13
|
-
Requires-Dist: mysql-connector-python>=9.
|
|
13
|
+
Requires-Dist: mysql-connector-python>=9.5.0
|
|
14
14
|
Requires-Dist: nacos-sdk-python>=2.0.9
|
|
15
|
-
Requires-Dist: pydantic>=2.
|
|
16
|
-
Requires-Dist: python-dotenv>=1.
|
|
15
|
+
Requires-Dist: pydantic>=2.12.3
|
|
16
|
+
Requires-Dist: python-dotenv>=1.2.1
|
|
17
17
|
Requires-Dist: pyyaml>=6.0.3
|
|
18
|
-
Requires-Dist: sqlalchemy>=2.0.
|
|
19
|
-
Requires-Dist: starlette>=0.
|
|
18
|
+
Requires-Dist: sqlalchemy>=2.0.44
|
|
19
|
+
Requires-Dist: starlette>=0.48.0
|
|
20
20
|
Requires-Dist: uuid>=1.30
|
|
21
|
-
Requires-Dist: uvicorn>=0.
|
|
21
|
+
Requires-Dist: uvicorn>=0.38.0
|
|
22
22
|
|
|
23
23
|
# sycommon-python-lib
|
|
24
24
|
|
|
@@ -15,7 +15,7 @@ sycommon/health/health_check.py,sha256=EhfbhspRpQiKJaxdtE-PzpKQO_ucaFKtQxIm16F5M
|
|
|
15
15
|
sycommon/health/metrics.py,sha256=fHqO73JuhoZkNPR-xIlxieXiTCvttq-kG-tvxag1s1s,268
|
|
16
16
|
sycommon/health/ping.py,sha256=FTlnIKk5y1mPfS1ZGOeT5IM_2udF5aqVLubEtuBp18M,250
|
|
17
17
|
sycommon/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
sycommon/logging/kafka_log.py,sha256=
|
|
18
|
+
sycommon/logging/kafka_log.py,sha256=qHq4sU42aGrTm9DWYKxGDp1pe9ENfYSbzI4iPDPNvJQ,21128
|
|
19
19
|
sycommon/logging/logger_wrapper.py,sha256=TiHsrIIHiQMzXgXK12-0KIpU9GhwQJOoHslakzmq2zc,357
|
|
20
20
|
sycommon/logging/sql_logger.py,sha256=aEU3OGnI_51Tjyuuf4FpUi9KPTceFRuKAOyQbPzGhzM,2021
|
|
21
21
|
sycommon/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -35,21 +35,21 @@ sycommon/models/mqlistener_config.py,sha256=PPwhAVJ2AWvVAvNox_1t0fuBKTyRH3Ui9cuu
|
|
|
35
35
|
sycommon/models/mqmsg_model.py,sha256=cxn0M5b0utQK6crMYmL-1waeGYHvK3AlGaRy23clqTE,277
|
|
36
36
|
sycommon/models/mqsend_config.py,sha256=NQX9dc8PpuquMG36GCVhJe8omAW1KVXXqr6lSRU6D7I,268
|
|
37
37
|
sycommon/models/sso_user.py,sha256=i1WAN6k5sPcPApQEdtjpWDy7VrzWLpOrOQewGLGoGIw,2702
|
|
38
|
-
sycommon/rabbitmq/rabbitmq_client.py,sha256=
|
|
38
|
+
sycommon/rabbitmq/rabbitmq_client.py,sha256=kiQBMwLJW1sx9llxHUMXHHKXY5SJdafIHyVrEOu6OO4,27259
|
|
39
39
|
sycommon/rabbitmq/rabbitmq_pool.py,sha256=_NMOO4CZy-I_anMqpzfYinz-8373_rg5FM9eqzdjGyU,3598
|
|
40
40
|
sycommon/rabbitmq/rabbitmq_service.py,sha256=NWoMtRhvLjEsIX3sMgaSkrguE3gT5wLYj0u7laWZh8c,29997
|
|
41
41
|
sycommon/sse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
42
|
sycommon/sse/event.py,sha256=k_rBJy23R7crtzQeetT0Q73D8o5-5p-eESGSs_BPOj0,2797
|
|
43
43
|
sycommon/sse/sse.py,sha256=__CfWEcYxOxQ-HpLor4LTZ5hLWqw9-2X7CngqbVHsfw,10128
|
|
44
44
|
sycommon/synacos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
-
sycommon/synacos/feign.py,sha256=
|
|
46
|
-
sycommon/synacos/nacos_service.py,sha256=
|
|
45
|
+
sycommon/synacos/feign.py,sha256=g2Mt3Iyxrk8doQsuIRNYMoHi-TP-k6FO5SDMWAbyqTo,24208
|
|
46
|
+
sycommon/synacos/nacos_service.py,sha256=SO1s83Y8A5jyQNFhk7ZZ_BrGQyGZ8TXBKtzRYxI-uDQ,34661
|
|
47
47
|
sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
48
|
sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
|
|
49
49
|
sycommon/tools/snowflake.py,sha256=DdEj3T5r5OEvikp3puxqmmmz6BrggxomoSlnsRFb5dM,1174
|
|
50
50
|
sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
|
|
51
|
-
sycommon_python_lib-0.1.
|
|
52
|
-
sycommon_python_lib-0.1.
|
|
53
|
-
sycommon_python_lib-0.1.
|
|
54
|
-
sycommon_python_lib-0.1.
|
|
55
|
-
sycommon_python_lib-0.1.
|
|
51
|
+
sycommon_python_lib-0.1.29.dist-info/METADATA,sha256=iun0vsoi2Xdi4hRDNxwAldSoOhVsM8mEdqNIoQ0Vwg8,7037
|
|
52
|
+
sycommon_python_lib-0.1.29.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
53
|
+
sycommon_python_lib-0.1.29.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
|
|
54
|
+
sycommon_python_lib-0.1.29.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
|
|
55
|
+
sycommon_python_lib-0.1.29.dist-info/RECORD,,
|
|
File without changes
|
{sycommon_python_lib-0.1.28.dist-info → sycommon_python_lib-0.1.29.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|