sycommon-python-lib 0.1.8__py3-none-any.whl → 0.1.10__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.

Potentially problematic release.


This version of sycommon-python-lib might be problematic. Click here for more details.

@@ -0,0 +1,30 @@
1
+ from fastapi import FastAPI, APIRouter
2
+ from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
3
+
4
+
5
+ def setup_docs_handler(app: FastAPI):
6
+ docs_router = APIRouter()
7
+
8
+ @docs_router.get("/docs", include_in_schema=False)
9
+ async def custom_swagger_ui_html():
10
+ return get_swagger_ui_html(
11
+ openapi_url=app.openapi_url,
12
+ title=f"{app.title}",
13
+ swagger_favicon_url="https://static.sytechnology.com/img/sylogopng.png",
14
+ swagger_js_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.27.1/swagger-ui-bundle.js",
15
+ swagger_css_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.27.1/swagger-ui.css",
16
+ swagger_ui_parameters={"defaultModelsExpandDepth": -1},
17
+ )
18
+
19
+ @docs_router.get("/redoc", include_in_schema=False)
20
+ async def custom_redoc_html():
21
+ return get_redoc_html(
22
+ openapi_url=app.openapi_url,
23
+ title=f"{app.title}",
24
+ redoc_favicon_url="https://static.sytechnology.com/img/sylogopng.png",
25
+ redoc_js_url="https://cdn.bootcdn.net/ajax/libs/redoc/2.1.5/redoc.standalone.js",
26
+ )
27
+
28
+ app.include_router(docs_router)
29
+
30
+ return app
@@ -1,5 +1,6 @@
1
1
  from sycommon.health.ping import setup_ping_handler
2
2
  from sycommon.middleware.cors import setup_cors_handler
3
+ from sycommon.middleware.docs import setup_docs_handler
3
4
  from sycommon.middleware.exception import setup_exception_handler
4
5
  from sycommon.middleware.monitor_memory import setup_monitor_memory_middleware
5
6
  from sycommon.middleware.mq import setup_mq_middleware
@@ -36,4 +37,7 @@ class Middleware:
36
37
  # 添加mq中间件
37
38
  # app = setup_mq_middleware(app)
38
39
 
40
+ # doc
41
+ # app = setup_docs_handler(app)
42
+
39
43
  return app
@@ -78,13 +78,19 @@ def setup_trace_id_handler(app):
78
78
  response = await call_next(request)
79
79
 
80
80
  content_type = response.headers.get("Content-Type", "")
81
+
82
+ # 处理 SSE 响应 - 关键修复点
81
83
  if "text/event-stream" in content_type:
82
- # 处理 SSE 响应
84
+ # 流式响应不能有Content-Length,移除它
85
+ if "Content-Length" in response.headers:
86
+ del response.headers["Content-Length"]
83
87
  response.headers["x-traceId-header"] = trace_id
84
88
  return response
85
89
 
90
+ # 处理普通响应
86
91
  response_body = b""
87
92
  try:
93
+ # 收集所有响应块
88
94
  async for chunk in response.body_iterator:
89
95
  response_body += chunk
90
96
 
@@ -98,14 +104,36 @@ def setup_trace_id_handler(app):
98
104
  data["traceId"] = trace_id
99
105
  new_body = json.dumps(
100
106
  data, ensure_ascii=False).encode()
107
+
108
+ # 创建新响应,确保Content-Length正确
101
109
  response = Response(
102
110
  content=new_body,
103
111
  status_code=response.status_code,
104
- headers=dict(response.headers)
112
+ headers=dict(response.headers),
113
+ media_type=response.media_type
105
114
  )
115
+ # 显式设置正确的Content-Length
106
116
  response.headers["Content-Length"] = str(len(new_body))
107
117
  except json.JSONDecodeError:
108
- pass
118
+ # 如果不是JSON,恢复原始响应体并更新长度
119
+ response = Response(
120
+ content=response_body,
121
+ status_code=response.status_code,
122
+ headers=dict(response.headers),
123
+ media_type=response.media_type
124
+ )
125
+ response.headers["Content-Length"] = str(
126
+ len(response_body))
127
+ else:
128
+ # 非JSON响应,恢复原始响应体
129
+ response = Response(
130
+ content=response_body,
131
+ status_code=response.status_code,
132
+ headers=dict(response.headers),
133
+ media_type=response.media_type
134
+ )
135
+ response.headers["Content-Length"] = str(
136
+ len(response_body))
109
137
  except StopAsyncIteration:
110
138
  pass
111
139
 
@@ -129,7 +157,7 @@ def setup_trace_id_handler(app):
129
157
  "uploaded_files": files_info if files_info else None
130
158
  }
131
159
  error_message_str = json.dumps(error_message, ensure_ascii=False)
132
- SYLogger.error(error_message_str) # 无需显式传递 trace_id
160
+ SYLogger.error(error_message_str)
133
161
  raise
134
162
  finally:
135
163
  # 清理上下文变量,防止泄漏
@@ -3,14 +3,15 @@ from pydantic import BaseModel, Field
3
3
  from fastapi.responses import JSONResponse
4
4
  from fastapi import status
5
5
 
6
- # 修改泛型约束,支持任意类型(包括基础类型和BaseModel)
6
+ # 支持任意类型的泛型约束
7
7
  T = TypeVar('T')
8
8
 
9
9
 
10
10
  class BaseResponseModel(BaseModel, Generic[T]):
11
- """基础响应模型,支持多种数据类型(包括字符串、字典和Pydantic模型)"""
12
- code: int = Field(default=0, description="业务响应码,成功0,失败非0")
13
- message: str = Field(default="success", description="业务响应信息")
11
+ """基础响应模型,支持自定义状态码"""
12
+ code: int = Field(default=0, description="业务响应码,默认0成功,1失败,支持自定义")
13
+ message: str | None = Field(default=None, description="业务响应信息")
14
+ success: bool = Field(default=True, description="请求是否成功")
14
15
  data: T | None = Field(default=None, description="业务响应数据,支持任意类型")
15
16
  traceId: str | None = Field(default=None, description="请求链路追踪ID")
16
17
 
@@ -22,22 +23,22 @@ class BaseResponseModel(BaseModel, Generic[T]):
22
23
  def build_response_content(
23
24
  data: T | Any = None,
24
25
  code: int = 0,
25
- message: str = "success"
26
+ message: str = None
26
27
  ) -> dict:
27
28
  """
28
- 只构建响应内容的字典部分
29
+ 构建响应内容字典,自动根据code判断success
29
30
 
30
- Args:
31
- data: 响应数据(支持字符串、字典、Pydantic模型等)
32
- code: 业务响应码
33
- message: 响应信息
34
-
35
- Returns:
36
- 响应内容字典,格式为{"code": int, "message": str, "data": Any}
31
+ 规则:
32
+ - code为0时success=True(默认成功)
33
+ - 其他任何code值success=False(包括200等自定义状态码)
37
34
  """
35
+ # 成功状态仅当code为0时成立,其他任何code都视为失败
36
+ success = code == 0 or code == 200
37
+
38
38
  response = BaseResponseModel(
39
39
  code=code,
40
40
  message=message,
41
+ success=success,
41
42
  data=data
42
43
  )
43
44
 
@@ -45,6 +46,7 @@ def build_response_content(
45
46
  return {
46
47
  "code": response.code,
47
48
  "message": response.message,
49
+ "success": response.success,
48
50
  "data": response.data.model_dump()
49
51
  }
50
52
  else:
@@ -54,10 +56,10 @@ def build_response_content(
54
56
  def create_response(
55
57
  data: T | Any = None,
56
58
  code: int = 0,
57
- message: str = "success",
59
+ message: str = None,
58
60
  status_code: int = status.HTTP_200_OK
59
61
  ) -> JSONResponse:
60
- """创建完整的JSONResponse响应"""
62
+ """创建完整响应,支持自定义业务状态码"""
61
63
  content = build_response_content(data=data, code=code, message=message)
62
64
  return JSONResponse(
63
65
  content=content,
@@ -65,18 +67,18 @@ def create_response(
65
67
  )
66
68
 
67
69
 
68
- def success_response(data: T | Any = None, message: str = "success") -> JSONResponse:
69
- """快捷创建成功响应"""
70
- return create_response(data=data, message=message)
70
+ def success_response(data: T | Any = None, code: int = 0) -> JSONResponse:
71
+ """快捷创建成功响应(code=0, success=True)"""
72
+ return create_response(data=data, code=code)
71
73
 
72
74
 
73
75
  def error_response(
74
- message: str = "error",
76
+ message: str = None,
75
77
  code: int = 1,
76
78
  data: T | Any = None,
77
79
  status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR
78
80
  ) -> JSONResponse:
79
- """快捷创建错误响应"""
81
+ """快捷创建错误响应,支持自定义错误码(如200)"""
80
82
  return create_response(
81
83
  data=data,
82
84
  code=code,
@@ -85,15 +87,15 @@ def error_response(
85
87
  )
86
88
 
87
89
 
88
- def success_content(data: T | Any = None, message: str = "success") -> dict:
89
- """只构建成功响应的内容字典"""
90
- return build_response_content(data=data, message=message)
90
+ def success_content(data: T | Any = None) -> dict:
91
+ """构建成功响应内容字典"""
92
+ return build_response_content(data=data)
91
93
 
92
94
 
93
95
  def error_content(
94
- message: str = "error",
96
+ message: str = None,
95
97
  code: int = 1,
96
98
  data: T | Any = None
97
99
  ) -> dict:
98
- """只构建错误响应的内容字典"""
100
+ """构建错误响应内容字典,支持自定义错误码"""
99
101
  return build_response_content(data=data, code=code, message=message)
@@ -183,8 +183,14 @@ class RabbitMQClient:
183
183
  await asyncio.sleep(1)
184
184
  return False
185
185
 
186
- async def connect(self, force_reconnect: bool = False, max_retries: int = 3) -> None:
187
- """建立连接并检查/创建资源"""
186
+ async def connect(self, force_reconnect: bool = False, declare_queue: bool = True) -> None:
187
+ """建立连接并检查/创建资源,新增declare_queue参数控制是否声明队列"""
188
+ # 增加日志确认参数状态
189
+ logging.debug(
190
+ f"connect() 调用 - force_reconnect={force_reconnect}, "
191
+ f"declare_queue={declare_queue}, create_if_not_exists={self.create_if_not_exists}"
192
+ )
193
+
188
194
  if self.is_connected and not force_reconnect:
189
195
  return
190
196
 
@@ -196,7 +202,8 @@ class RabbitMQClient:
196
202
  f"尝试连接RabbitMQ - 主机: {self.host}:{self.port}, "
197
203
  f"虚拟主机: {self.virtualhost}, "
198
204
  f"队列: {self.queue_name}, "
199
- f"心跳间隔: {self.heartbeat}s"
205
+ f"声明队列: {declare_queue}, "
206
+ f"允许创建: {self.create_if_not_exists}"
200
207
  )
201
208
 
202
209
  # 重置状态
@@ -207,13 +214,13 @@ class RabbitMQClient:
207
214
  retries = 0
208
215
  last_exception = None
209
216
 
210
- while retries < max_retries:
217
+ while retries < 3: # 使用固定重试次数
211
218
  try:
212
219
  # 关闭旧连接
213
220
  if self.connection and not self.connection.is_closed:
214
221
  await self.connection.close()
215
222
 
216
- # 建立新连接 - 配置更短的心跳间隔
223
+ # 建立新连接
217
224
  self.connection = await asyncio.wait_for(
218
225
  aio_pika.connect_robust(
219
226
  host=self.host,
@@ -223,7 +230,7 @@ class RabbitMQClient:
223
230
  virtualhost=self.virtualhost,
224
231
  heartbeat=self.heartbeat,
225
232
  client_properties={
226
- "connection_name": self.app_name or "rabbitmq-client"} # 增加连接标识
233
+ "connection_name": self.app_name or "rabbitmq-client"}
227
234
  ),
228
235
  timeout=self.connection_timeout
229
236
  )
@@ -262,28 +269,29 @@ class RabbitMQClient:
262
269
  )
263
270
  logging.info(f"使用已存在的交换机 '{self.exchange_name}'")
264
271
 
265
- # 2. 处理队列
266
- if self.queue_name:
272
+ # 2. 处理队列 - 只有declare_queue为True时才处理
273
+ if declare_queue and self.queue_name:
267
274
  queue_exists = await self._check_queue_exists()
268
275
 
269
276
  if not queue_exists:
270
- if self.create_if_not_exists:
271
- # 创建队列
272
- self.queue = await asyncio.wait_for(
273
- self.channel.declare_queue(
274
- name=self.queue_name,
275
- durable=self.durable,
276
- auto_delete=self.auto_delete,
277
- exclusive=False,
278
- passive=False
279
- ),
280
- timeout=self.rpc_timeout
281
- )
282
- self._queue_exists = True
283
- logging.info(f"已创建队列 '{self.queue_name}'")
284
- else:
277
+ # 关键检查点:确保有权限创建队列
278
+ if not self.create_if_not_exists:
285
279
  raise Exception(
286
280
  f"队列 '{self.queue_name}' 不存在且不允许自动创建")
281
+
282
+ # 创建队列
283
+ self.queue = await asyncio.wait_for(
284
+ self.channel.declare_queue(
285
+ name=self.queue_name,
286
+ durable=self.durable,
287
+ auto_delete=self.auto_delete,
288
+ exclusive=False,
289
+ passive=False
290
+ ),
291
+ timeout=self.rpc_timeout
292
+ )
293
+ self._queue_exists = True
294
+ logging.info(f"已创建队列 '{self.queue_name}'")
287
295
  else:
288
296
  # 获取已有队列
289
297
  self.queue = await asyncio.wait_for(
@@ -292,12 +300,18 @@ class RabbitMQClient:
292
300
  )
293
301
  logging.info(f"使用已存在的队列 '{self.queue_name}'")
294
302
 
295
- # 3. 绑定队列到交换机
296
- if self.queue and self.exchange:
297
- bound = await self._bind_queue()
298
- if not bound:
299
- raise Exception(
300
- f"队列 '{self.queue_name}' 绑定到交换机 '{self.exchange_name}' 失败")
303
+ # 3. 绑定队列到交换机
304
+ if self.queue and self.exchange:
305
+ bound = await self._bind_queue()
306
+ if not bound:
307
+ raise Exception(
308
+ f"队列 '{self.queue_name}' 绑定到交换机 '{self.exchange_name}' 失败")
309
+ else:
310
+ # 不声明队列时,将队列相关状态设为False
311
+ self.queue = None
312
+ self._queue_exists = False
313
+ self._queue_bound = False
314
+ logging.debug(f"跳过队列 '{self.queue_name}' 的声明和绑定")
301
315
 
302
316
  # 如果之前在消费,重新开始消费
303
317
  if self._is_consuming and self.message_handler:
@@ -308,23 +322,22 @@ class RabbitMQClient:
308
322
  self._start_keepalive_task()
309
323
 
310
324
  self._update_activity_timestamp()
311
- logging.info(f"RabbitMQ客户端连接成功 (队列: {self.queue_name})")
325
+ logging.info(
326
+ f"RabbitMQ客户端连接成功 (队列: {self.queue_name}, 声明队列: {declare_queue})")
312
327
  return
313
328
 
314
329
  except Exception as e:
315
330
  retries += 1
316
331
  last_exception = e
317
332
  logging.warning(
318
- f"连接失败({retries}/{max_retries}): {str(e)},重试中...")
333
+ f"连接失败({retries}/3): {str(e)}, create_if_not_exists={self.create_if_not_exists}, 重试中...")
319
334
 
320
- if retries < max_retries:
335
+ if retries < 3:
321
336
  await asyncio.sleep(self.reconnection_delay)
322
337
 
323
- logging.error(f"最终连接失败: {str(last_exception)}", exc_info=True)
338
+ logging.error(f"最终连接失败: {str(last_exception)}")
324
339
  raise Exception(
325
- f"经过{max_retries}次重试后仍无法完成连接和资源初始化。"
326
- f"最后错误: {str(last_exception)}"
327
- )
340
+ f"经过3次重试后仍无法完成连接和资源初始化。最后错误: {str(last_exception)}")
328
341
 
329
342
  def _start_connection_monitor(self):
330
343
  """启动连接监控任务,检测连接/通道关闭"""
@@ -534,16 +547,31 @@ class RabbitMQClient:
534
547
  self.message_handler = handler
535
548
 
536
549
  async def start_consuming(self, timeout: Optional[float] = None) -> str:
537
- """开始消费消息并返回consumer_tag,支持超时控制"""
550
+ """开始消费消息并返回consumer_tag,支持超时控制和队列检查重试"""
538
551
  if self._is_consuming:
539
552
  logging.debug("已经在消费中,返回现有consumer_tag")
540
553
  return self._consumer_tag
541
554
 
542
- if not self.is_connected:
543
- await self.connect()
555
+ # 增加队列检查和连接确保逻辑
556
+ max_attempts = 5
557
+ attempt = 0
558
+ while attempt < max_attempts:
559
+ if not self.is_connected:
560
+ await self.connect()
561
+
562
+ if self.queue:
563
+ break
564
+
565
+ attempt += 1
566
+ logging.warning(f"队列尚未初始化,等待后重试({attempt}/{max_attempts})")
567
+ await asyncio.sleep(1)
544
568
 
545
569
  if not self.queue:
546
- raise Exception("队列未初始化")
570
+ # 最后尝试一次显式连接并声明队列
571
+ logging.warning("最后尝试重新连接并声明队列")
572
+ await self.connect(force_reconnect=True, declare_queue=True)
573
+ if not self.queue:
574
+ raise Exception("队列未初始化,多次尝试后仍无法创建")
547
575
 
548
576
  if not self.message_handler:
549
577
  raise Exception("未设置消息处理函数")
@@ -31,15 +31,22 @@ class RabbitMQService:
31
31
  _initialized_queues: Dict[str, Dict[str, bool]] = {}
32
32
  # 添加异步锁
33
33
  _init_lock: Dict[str, asyncio.Lock] = {}
34
+ _has_listeners: bool = False
35
+ _has_senders: bool = False
34
36
 
35
37
  @classmethod
36
- def init(cls, config: dict) -> Type['RabbitMQService']:
37
- """初始化RabbitMQ服务,保存配置"""
38
+ def init(cls, config: dict, has_listeners: bool = False, has_senders: bool = False) -> Type['RabbitMQService']:
39
+ """初始化RabbitMQ服务,保存配置和发送器/监听器状态"""
38
40
  from sycommon.synacos.nacos_service import NacosService
39
41
  # 获取 common 配置
40
42
  cls.config = NacosService(config).share_configs.get(
41
43
  "mq.yml", {}).get('spring', {}).get('rabbitmq', {})
42
44
  cls.config["APP_NAME"] = config.get("Name", "")
45
+
46
+ # 保存发送器和监听器存在状态
47
+ cls._has_listeners = has_listeners
48
+ cls._has_senders = has_senders
49
+
43
50
  return cls()
44
51
 
45
52
  @classmethod
@@ -56,21 +63,35 @@ class RabbitMQService:
56
63
 
57
64
  @classmethod
58
65
  def create_client(cls, mq_config: dict, queue_name: str, **kwargs):
59
- """创建并返回新的RabbitMQClient实例"""
60
- # 从kwargs中提取是否自动创建队列的参数,默认True
61
- create_if_not_exists = kwargs.get('create_if_not_exists', True)
66
+ """创建并返回新的RabbitMQClient实例,遵循队列创建规则"""
67
+ # 获取当前项目名
68
+ app_name = kwargs.get('app_name', cls.config.get(
69
+ "APP_NAME", "")) if cls.config else kwargs.get('app_name', "")
62
70
 
63
71
  # 确保只在需要时处理一次队列名称
64
72
  processed_queue_name = queue_name
65
73
 
66
- # 如果是监听器且需要自动创建队列,确保队列名拼接app-name(只拼接一次)
67
- if create_if_not_exists and processed_queue_name and 'app_name' in kwargs and kwargs['app_name']:
68
- app_name = kwargs['app_name']
69
- # 只在队列名尚未包含app-name时进行拼接
74
+ # 通过上下文判断是否为发送器
75
+ # 发送器场景:当没有监听器时
76
+ is_sender = not cls._has_listeners
77
+
78
+ # 核心逻辑:根据组件存在状态决定是否允许创建队列
79
+ # 1. 只有发送器:不允许创建队列
80
+ # 2. 只有监听器:允许创建队列
81
+ # 3. 两者都存在:允许创建队列(由监听器负责)
82
+ create_if_not_exists = cls._has_listeners # 只要有监听器就允许创建
83
+
84
+ # 当需要创建队列且是监听器时,拼接项目名
85
+ if create_if_not_exists and not is_sender and processed_queue_name and app_name:
70
86
  if not processed_queue_name.endswith(f".{app_name}"):
71
87
  processed_queue_name = f"{processed_queue_name}.{app_name}"
72
88
  logging.debug(f"监听器队列名称自动拼接app-name: {processed_queue_name}")
73
89
 
90
+ logging.debug(
91
+ f"队列创建权限 - 监听器存在: {cls._has_listeners}, 发送器存在: {cls._has_senders}, "
92
+ f"是否发送器: {is_sender}, 允许创建: {create_if_not_exists}, 队列: {processed_queue_name}"
93
+ )
94
+
74
95
  return RabbitMQClient(
75
96
  host=mq_config.get('host', ""),
76
97
  port=mq_config.get('port', 0),
@@ -80,7 +101,7 @@ class RabbitMQService:
80
101
  exchange_name=mq_config.get(
81
102
  'exchange_name', "system.topic.exchange"),
82
103
  exchange_type=kwargs.get('exchange_type', "topic"),
83
- queue_name=processed_queue_name, # 使用处理后的队列名
104
+ queue_name=processed_queue_name,
84
105
  routing_key=kwargs.get(
85
106
  'routing_key', f"{processed_queue_name.split('.')[0]}.#" if processed_queue_name else "#"),
86
107
  durable=kwargs.get('durable', True),
@@ -89,7 +110,7 @@ class RabbitMQService:
89
110
  create_if_not_exists=create_if_not_exists,
90
111
  connection_timeout=kwargs.get('connection_timeout', 10),
91
112
  rpc_timeout=kwargs.get('rpc_timeout', 5),
92
- app_name=kwargs.get('app_name', "")
113
+ app_name=app_name
93
114
  )
94
115
 
95
116
  @classmethod
@@ -105,52 +126,80 @@ class RabbitMQService:
105
126
  async with cls._init_lock[client_name]:
106
127
  if client_name in cls.clients:
107
128
  client = cls.clients[client_name]
129
+ # 移除is_sender判断,通过上下文推断
130
+ is_sender = not cls._has_listeners or (
131
+ not kwargs.get('create_if_not_exists', True))
132
+
108
133
  if client.is_connected:
109
- logging.debug(f"客户端 '{client_name}' 已存在且连接有效,直接返回")
134
+ if not is_sender and not client.queue:
135
+ logging.debug(f"客户端 '{client_name}' 存在但队列未初始化,重新连接")
136
+ client.create_if_not_exists = True
137
+ await client.connect(force_reconnect=True, declare_queue=True)
138
+ else:
139
+ logging.debug(f"客户端 '{client_name}' 已存在且连接有效,直接返回")
110
140
  return client
111
141
  else:
112
142
  logging.debug(f"客户端 '{client_name}' 存在但连接已关闭,重新连接")
113
- await client.connect()
143
+ if not is_sender:
144
+ client.create_if_not_exists = True
145
+ await client.connect(declare_queue=not is_sender)
114
146
  return client
115
147
 
116
148
  initial_queue_name = kwargs.pop('queue_name', '')
117
- create_if_not_exists = kwargs.get('create_if_not_exists', True)
149
+ # 移除is_sender参数,通过上下文推断
150
+ is_sender = not cls._has_listeners or (
151
+ not kwargs.get('create_if_not_exists', True))
152
+
153
+ # 发送器特殊处理
154
+ if is_sender:
155
+ kwargs['create_if_not_exists'] = False
118
156
 
119
- # 检查队列是否已初始化
120
- if initial_queue_name in cls._initialized_queues:
121
- logging.debug(f"队列 '{initial_queue_name}' 已初始化过,直接创建客户端")
122
157
  client = RabbitMQService.create_client(
123
158
  mq_config,
124
159
  initial_queue_name,
125
160
  app_name=cls.config.get("APP_NAME", ""),
126
- **kwargs
161
+ **kwargs # 不再传递is_sender参数
127
162
  )
128
- await client.connect()
163
+
164
+ await client.connect(declare_queue=False)
129
165
  cls.clients[client_name] = client
130
166
  return client
131
167
 
132
- # 处理create_if_not_exists参数,避免重复传递
133
- create_if_not_exists = kwargs.pop('create_if_not_exists', True)
168
+ # 监听器逻辑
169
+ kwargs['create_if_not_exists'] = True
134
170
 
135
- # 对于监听器,确保create_if_not_exists为True
136
- # 判断是否为监听器:客户端名称与队列名称相同通常是监听器
137
- is_listener = client_name == initial_queue_name and initial_queue_name
138
- if is_listener:
139
- create_if_not_exists = True
140
- logging.debug(f"监听器 '{client_name}' 将自动创建队列(如果不存在)")
171
+ if initial_queue_name in cls._initialized_queues:
172
+ logging.debug(f"队列 '{initial_queue_name}' 已初始化过,直接创建客户端")
173
+ client = RabbitMQService.create_client(
174
+ mq_config,
175
+ initial_queue_name,
176
+ # 不再传递is_sender参数
177
+ app_name=cls.config.get("APP_NAME", ""), ** kwargs
178
+ )
179
+ await client.connect(declare_queue=True)
180
+ cls.clients[client_name] = client
181
+ return client
141
182
 
142
- # 创建客户端(这里会处理队列名称,添加app-name)
143
183
  client = RabbitMQService.create_client(
144
184
  mq_config,
145
185
  initial_queue_name,
146
186
  app_name=cls.config.get("APP_NAME", ""),
147
- create_if_not_exists=create_if_not_exists,
148
- connection_timeout=mq_config.get('connection_timeout', 15),
149
- rpc_timeout=mq_config.get('rpc_timeout', 5), ** kwargs
187
+ **kwargs # 不再传递is_sender参数
150
188
  )
151
189
 
152
- # 连接时不再处理队列名称,避免重复添加app-name
153
- await client.connect()
190
+ client.create_if_not_exists = True
191
+ logging.debug(
192
+ f"监听器客户端创建 - create_if_not_exists={client.create_if_not_exists}")
193
+
194
+ await client.connect(declare_queue=True)
195
+
196
+ if not client.queue:
197
+ logging.error(f"队列 '{initial_queue_name}' 创建失败,尝试重新创建")
198
+ client.create_if_not_exists = True
199
+ await client.connect(force_reconnect=True, declare_queue=True)
200
+ if not client.queue:
201
+ raise Exception(f"无法创建队列 '{initial_queue_name}'")
202
+
154
203
  final_queue_name = client.queue_name
155
204
 
156
205
  if final_queue_name not in cls._initialized_queues:
@@ -163,8 +212,11 @@ class RabbitMQService:
163
212
  return client
164
213
 
165
214
  @classmethod
166
- async def setup_senders(cls, senders: List[RabbitMQSendConfig]):
167
- """设置MQ发送客户端 - 保持不自动创建队列的行为"""
215
+ async def setup_senders(cls, senders: List[RabbitMQSendConfig], has_listeners: bool = False):
216
+ """设置MQ发送客户端"""
217
+ cls._has_listeners = has_listeners
218
+ cls._has_senders = True # 明确标记存在发送器
219
+
168
220
  async def setup_sender_tasks():
169
221
  for idx, sender_config in enumerate(senders):
170
222
  try:
@@ -173,20 +225,23 @@ class RabbitMQService:
173
225
 
174
226
  normalized_name = sender_config.queue_name
175
227
  app_name = cls.config.get("APP_NAME", "")
228
+
176
229
  if app_name and normalized_name.endswith(f".{app_name}"):
177
230
  normalized_name = normalized_name[:-
178
231
  len(f".{app_name}")]
232
+ logging.debug(
233
+ f"发送器队列名称移除app-name后缀: {normalized_name}")
179
234
 
180
235
  if normalized_name in cls.sender_client_names:
181
- logging.debug(f"发送客户端 '{normalized_name}' 已存在,跳过重复配置")
236
+ logging.debug(f"发送客户端 '{normalized_name}' 已存在,跳过")
182
237
  continue
183
238
 
184
239
  if normalized_name in cls.clients:
185
240
  client = cls.clients[normalized_name]
186
241
  if not client.is_connected:
187
- await client.connect()
242
+ await client.connect(declare_queue=False)
188
243
  else:
189
- # 发送端明确设置不自动创建队列
244
+ # 移除is_sender参数传递
190
245
  client = await cls.setup_rabbitmq(
191
246
  cls.config,
192
247
  client_name=normalized_name,
@@ -195,12 +250,12 @@ class RabbitMQService:
195
250
  auto_delete=sender_config.auto_delete,
196
251
  auto_parse_json=sender_config.auto_parse_json,
197
252
  queue_name=sender_config.queue_name,
198
- create_if_not_exists=False # 发送端不自动创建队列
253
+ create_if_not_exists=False # 仅通过此参数控制
199
254
  )
200
255
 
201
256
  if normalized_name not in cls.clients:
202
257
  cls.clients[normalized_name] = client
203
- logging.info(f"客户端 '{normalized_name}' 已添加到客户端列表")
258
+ logging.info(f"发送客户端 '{normalized_name}' 已添加")
204
259
 
205
260
  if normalized_name not in cls.sender_client_names:
206
261
  cls.sender_client_names.append(normalized_name)
@@ -217,8 +272,11 @@ class RabbitMQService:
217
272
  raise
218
273
 
219
274
  @classmethod
220
- async def setup_listeners(cls, listeners: List[RabbitMQListenerConfig]):
275
+ async def setup_listeners(cls, listeners: List[RabbitMQListenerConfig], has_senders: bool = False):
221
276
  """设置MQ监听器 - 确保自动创建队列"""
277
+ # 存在监听器,设置标志
278
+ cls._has_listeners = True
279
+
222
280
  for listener_config in listeners:
223
281
  # 将监听器配置转换为字典并添加到监听器
224
282
  # 强制设置create_if_not_exists为True
@@ -337,19 +395,17 @@ class RabbitMQService:
337
395
 
338
396
  @classmethod
339
397
  def get_sender(cls, client_name: Optional[str] = None) -> Optional[RabbitMQClient]:
340
- """获取发送客户端(仅返回已注册的客户端,移除模糊匹配)"""
398
+ """获取发送客户端(仅返回已注册的客户端)"""
341
399
  if not client_name:
342
400
  logging.warning("发送器名称不能为空")
343
401
  return None
344
402
 
345
- # 仅精确匹配已注册的客户端,不做模糊匹配
403
+ # 仅精确匹配已注册的客户端
346
404
  if client_name in cls.clients:
347
405
  return cls.clients[client_name]
348
406
 
349
- # 不允许通过拼接app-name反向查找(避免绕过注册)
350
407
  app_name = cls.config.get("APP_NAME", "") if cls.config else ""
351
408
  if app_name and not client_name.endswith(f".{app_name}"):
352
- # 不尝试拼接app-name查找,确保只有注册的名称可被找到
353
409
  return None
354
410
 
355
411
  logging.debug(f"发送器 '{client_name}' 不在已注册客户端列表中")
@@ -359,7 +415,7 @@ class RabbitMQService:
359
415
  async def send_message(
360
416
  cls,
361
417
  data: Union[BaseModel, str, Dict[str, Any], None],
362
- queue_name: Optional[str] = None, ** kwargs
418
+ queue_name: Optional[str] = None, **kwargs
363
419
  ) -> None:
364
420
  """发送消息到RabbitMQ"""
365
421
  sender = cls.get_sender(queue_name)
@@ -472,5 +528,6 @@ class RabbitMQService:
472
528
  cls.clients.clear()
473
529
  cls.sender_client_names.clear()
474
530
  cls._init_lock.clear()
531
+ cls._has_listeners = False # 重置标志
475
532
 
476
533
  logging.info("RabbitMQ服务已完全关闭")
sycommon/services.py CHANGED
@@ -1,15 +1,14 @@
1
- from typing import Any, Callable, Dict, List, Tuple, Union, Optional, Awaitable
1
+ from typing import Any, Callable, Dict, List, Tuple, Union, Optional, AsyncGenerator
2
2
  import asyncio
3
3
  import logging
4
4
  from contextlib import asynccontextmanager
5
- from dotenv import load_dotenv
6
- from fastapi import FastAPI
5
+ from fastapi import FastAPI, applications
7
6
  from pydantic import BaseModel
8
- import yaml
9
7
  from sycommon.config.Config import SingletonMeta
10
8
  from sycommon.models.mqlistener_config import RabbitMQListenerConfig
11
9
  from sycommon.models.mqsend_config import RabbitMQSendConfig
12
10
  from sycommon.rabbitmq.rabbitmq_service import RabbitMQService
11
+ from sycommon.tools.docs import custom_redoc_html, custom_swagger_ui_html
13
12
 
14
13
 
15
14
  class Services(metaclass=SingletonMeta):
@@ -18,187 +17,179 @@ class Services(metaclass=SingletonMeta):
18
17
  _initialized: bool = False
19
18
  _registered_senders: List[str] = []
20
19
  _mq_tasks: List[asyncio.Task] = []
21
- _pending_setup: Optional[Callable[..., Awaitable[None]]] = None
22
20
  _instance: Optional['Services'] = None
21
+ _app: Optional[FastAPI] = None
23
22
 
24
- def __init__(self, config):
23
+ def __init__(self, config: dict, app: FastAPI):
25
24
  if not Services._config:
26
25
  Services._config = config
27
26
  Services._instance = self
27
+ Services._app = app
28
+ self._init_event_loop()
28
29
 
29
- @classmethod
30
- def get_lifespan(cls, config):
31
- """返回 FastAPI 的 lifespan 管理器"""
32
- cls._config = config
33
-
34
- @asynccontextmanager
35
- async def lifespan(app):
36
- # 应用启动时初始化
37
- cls._loop = asyncio.get_running_loop()
38
- cls._initialized = True
39
- logging.info("Services initialized with FastAPI event loop")
40
-
41
- # 执行之前缓存的MQ设置
42
- if cls._pending_setup:
43
- try:
44
- await cls._pending_setup()
45
- except Exception as e:
46
- logging.error(f"执行MQ初始化失败: {str(e)}", exc_info=True)
47
- finally:
48
- cls._pending_setup = None
49
-
30
+ def _init_event_loop(self):
31
+ """初始化事件循环,确保全局只有一个循环实例"""
32
+ if not Services._loop:
50
33
  try:
51
- yield
52
- finally:
53
- # 应用关闭时清理
54
- await cls.shutdown()
55
- logging.info("Services shutdown completed")
56
-
57
- return lifespan
34
+ Services._loop = asyncio.get_running_loop()
35
+ except RuntimeError:
36
+ Services._loop = asyncio.new_event_loop()
37
+ asyncio.set_event_loop(Services._loop)
58
38
 
59
39
  @classmethod
60
- def plugins(cls,
61
- middleware: Optional[Tuple[Callable, FastAPI]] = None,
62
- nacos_service: Optional[Callable] = None,
63
- logging_service: Optional[Callable] = None,
64
- database_service: Optional[Union[Tuple[Callable, str],
65
- List[Tuple[Callable, str]]]] = None,
66
- rabbitmq_listeners: Optional[List[RabbitMQListenerConfig]] = None,
67
- rabbitmq_senders: Optional[List[RabbitMQSendConfig]] = None
68
- ) -> None:
69
- """
70
- 类方法:注册各种服务插件
71
- 确保单例实例存在后调用实例方法的register_plugins
72
- """
73
- # 确保实例已创建
74
- if not cls._instance:
75
- if not cls._config:
76
- raise ValueError("Services尚未初始化,请先提供配置")
77
- cls._instance = Services(cls._config)
78
-
79
- cls._instance.register_plugins(
80
- middleware=middleware,
81
- nacos_service=nacos_service,
82
- logging_service=logging_service,
83
- database_service=database_service,
84
- rabbitmq_listeners=rabbitmq_listeners,
85
- rabbitmq_senders=rabbitmq_senders
86
- )
87
-
88
- def register_plugins(
89
- self,
90
- middleware: Optional[Tuple[Callable, FastAPI]] = None,
91
- nacos_service: Optional[Callable] = None,
92
- logging_service: Optional[Callable] = None,
93
- database_service: Optional[Union[Tuple[Callable,
94
- str], List[Tuple[Callable, str]]]] = None,
40
+ def plugins(
41
+ cls,
42
+ app: FastAPI,
43
+ config: dict,
44
+ middleware: Optional[Callable[[FastAPI, dict], None]] = None,
45
+ nacos_service: Optional[Callable[[dict], None]] = None,
46
+ logging_service: Optional[Callable[[dict], None]] = None,
47
+ database_service: Optional[Union[
48
+ Tuple[Callable[[dict, str], None], str],
49
+ List[Tuple[Callable[[dict, str], None], str]]
50
+ ]] = None,
95
51
  rabbitmq_listeners: Optional[List[RabbitMQListenerConfig]] = None,
96
52
  rabbitmq_senders: Optional[List[RabbitMQSendConfig]] = None
97
- ) -> None:
98
- """实例方法:实际执行各种服务插件的注册逻辑"""
99
- # 注册非异步服务
53
+ ) -> FastAPI:
54
+ # 保存应用实例和配置
55
+ cls._app = app
56
+ cls._config = config
57
+ # 设置文档
58
+ applications.get_swagger_ui_html = custom_swagger_ui_html
59
+ applications.get_redoc_html = custom_redoc_html
60
+
61
+ # 立即配置非异步服务(在应用启动前)
100
62
  if middleware:
101
- self._setup_middleware(middleware)
63
+ middleware(app, config)
64
+
102
65
  if nacos_service:
103
- nacos_service(self._config)
66
+ nacos_service(config)
67
+
104
68
  if logging_service:
105
- logging_service(self._config)
69
+ logging_service(config)
70
+
106
71
  if database_service:
107
- self._setup_database(database_service)
72
+ cls._setup_database_static(database_service, config)
73
+
74
+ # 创建生命周期管理器
75
+ @asynccontextmanager
76
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
77
+ # 启动阶段 - 执行初始化
78
+ instance = cls(config, app)
79
+
80
+ # 检查是否存在监听器
81
+ has_listeners = bool(
82
+ rabbitmq_listeners and len(rabbitmq_listeners) > 0)
83
+ # 检查是否存在发送器
84
+ has_senders = bool(
85
+ rabbitmq_senders and len(rabbitmq_senders) > 0)
86
+
87
+ # 执行MQ异步初始化,传递是否有监听器的标志
88
+ try:
89
+ await instance._setup_mq_async(
90
+ rabbitmq_listeners=rabbitmq_listeners,
91
+ rabbitmq_senders=rabbitmq_senders,
92
+ has_listeners=has_listeners,
93
+ has_senders=has_senders
94
+ )
95
+ cls._initialized = True
96
+ logging.info("所有服务初始化完成")
97
+ except Exception as e:
98
+ logging.error(f"服务初始化失败: {str(e)}", exc_info=True)
99
+ raise
100
+
101
+ # 将实例挂载到app上
102
+ app.state.services = instance
108
103
 
109
- RabbitMQService.init(self._config)
104
+ yield # 应用运行阶段
110
105
 
111
- # MQ设置异步函数
112
- async def setup_mq_components():
113
- if rabbitmq_senders:
114
- self._setup_senders(rabbitmq_senders)
115
- if rabbitmq_listeners:
116
- self._setup_listeners(rabbitmq_listeners)
106
+ # 关闭阶段 - 清理资源
107
+ await cls.shutdown()
108
+ logging.info("所有服务已关闭")
117
109
 
118
- # 存储到_pending_setup,等待lifespan异步执行
119
- Services._pending_setup = setup_mq_components
110
+ # 设置生命周期
111
+ app.router.lifespan_context = lifespan
120
112
 
121
- def _setup_database(self, database_service):
113
+ return app
114
+
115
+ @staticmethod
116
+ def _setup_database_static(database_service, config):
117
+ """静态方法:设置数据库服务"""
122
118
  if isinstance(database_service, tuple):
123
119
  db_setup, db_name = database_service
124
- db_setup(self._config, db_name)
120
+ db_setup(config, db_name)
125
121
  elif isinstance(database_service, list):
126
122
  for db_setup, db_name in database_service:
127
- db_setup(self._config, db_name)
123
+ db_setup(config, db_name)
128
124
 
129
- def _setup_middleware(self, middleware):
130
- if isinstance(middleware, tuple):
131
- middleware_setup, app = middleware
132
- middleware_setup(app, self._config)
133
-
134
- def _setup_senders(self, rabbitmq_senders):
125
+ async def _setup_mq_async(
126
+ self,
127
+ rabbitmq_listeners: Optional[List[RabbitMQListenerConfig]] = None,
128
+ rabbitmq_senders: Optional[List[RabbitMQSendConfig]] = None,
129
+ has_listeners: bool = False,
130
+ has_senders: bool = False,
131
+ ):
132
+ """异步设置MQ相关服务,保存发送器和监听器存在状态"""
133
+ # 保存状态到类变量
134
+ Services._has_listeners = has_listeners
135
+ Services._has_senders = has_senders
136
+
137
+ # 初始化RabbitMQ服务,传递状态
138
+ RabbitMQService.init(self._config, has_listeners, has_senders)
139
+
140
+ # 设置发送器,传递是否有监听器的标志
141
+ if rabbitmq_senders:
142
+ await self._setup_senders_async(rabbitmq_senders, has_listeners)
143
+
144
+ # 设置监听器,传递是否有发送器的标志
145
+ if rabbitmq_listeners:
146
+ await self._setup_listeners_async(rabbitmq_listeners, has_senders)
147
+
148
+ async def _setup_senders_async(self, rabbitmq_senders, has_listeners: bool):
135
149
  Services._registered_senders = [
136
150
  sender.queue_name for sender in rabbitmq_senders]
137
- if self._loop: # 确保loop存在
138
- task = self._loop.create_task(
139
- self._setup_and_wait(
140
- RabbitMQService.setup_senders, rabbitmq_senders)
141
- )
142
- self._mq_tasks.append(task)
151
+
152
+ # 将是否有监听器的信息传递给RabbitMQService
153
+ await RabbitMQService.setup_senders(rabbitmq_senders, has_listeners)
143
154
  logging.info(f"已注册的RabbitMQ发送器: {Services._registered_senders}")
144
155
 
145
- def _setup_listeners(self, rabbitmq_listeners):
146
- if self._loop: # 确保loop存在
147
- task = self._loop.create_task(
148
- self._setup_and_wait(
149
- RabbitMQService.setup_listeners, rabbitmq_listeners)
150
- )
151
- self._mq_tasks.append(task)
152
-
153
- async def _setup_and_wait(self, setup_func, *args, **kwargs):
154
- try:
155
- await setup_func(*args, **kwargs)
156
- except Exception as e:
157
- logging.error(
158
- f"Error in {setup_func.__name__}: {str(e)}", exc_info=True)
156
+ async def _setup_listeners_async(self, rabbitmq_listeners, has_senders: bool):
157
+ await RabbitMQService.setup_listeners(rabbitmq_listeners, has_senders)
159
158
 
160
159
  @classmethod
161
160
  async def send_message(
162
161
  cls,
163
162
  queue_name: str,
164
163
  data: Union[str, Dict[str, Any], BaseModel, None],
165
- max_retries: int = 3, # 最大重试次数
166
- retry_delay: float = 1.0, # 重试间隔(秒)
167
- **kwargs
164
+ max_retries: int = 3,
165
+ retry_delay: float = 1.0, ** kwargs
168
166
  ) -> None:
169
- """发送消息,添加重试机制处理发送器不存在的情况"""
167
+ """发送消息,添加重试机制"""
170
168
  if not cls._initialized or not cls._loop:
171
169
  logging.error("Services not properly initialized!")
172
170
  raise ValueError("服务未正确初始化")
173
171
 
174
- # 重试逻辑
175
172
  for attempt in range(max_retries):
176
173
  try:
177
- # 检查发送器是否已注册
178
174
  if queue_name not in cls._registered_senders:
179
- # 可能是初始化尚未完成,尝试刷新注册列表
180
175
  cls._registered_senders = RabbitMQService.sender_client_names
181
176
  if queue_name not in cls._registered_senders:
182
177
  raise ValueError(f"发送器 {queue_name} 未注册")
183
178
 
184
- # 获取发送器
185
179
  sender = RabbitMQService.get_sender(queue_name)
186
180
  if not sender:
187
181
  raise ValueError(f"发送器 '{queue_name}' 不存在")
188
182
 
189
- # 发送消息
190
- await RabbitMQService.send_message(data, queue_name, ** kwargs)
183
+ await RabbitMQService.send_message(data, queue_name, **kwargs)
191
184
  logging.info(f"消息发送成功(尝试 {attempt+1}/{max_retries})")
192
- return # 成功发送,退出函数
185
+ return
193
186
 
194
187
  except Exception as e:
195
- # 最后一次尝试失败则抛出异常
196
188
  if attempt == max_retries - 1:
197
189
  logging.error(
198
190
  f"消息发送失败(已尝试 {max_retries} 次): {str(e)}", exc_info=True)
199
191
  raise
200
192
 
201
- # 非最后一次尝试,记录警告并等待重试
202
193
  logging.warning(
203
194
  f"消息发送失败(尝试 {attempt+1}/{max_retries}): {str(e)},"
204
195
  f"{retry_delay}秒后重试..."
@@ -207,6 +198,8 @@ class Services(metaclass=SingletonMeta):
207
198
 
208
199
  @staticmethod
209
200
  async def shutdown():
201
+ """关闭所有服务"""
202
+ # 取消所有MQ任务
210
203
  for task in Services._mq_tasks:
211
204
  if not task.done():
212
205
  task.cancel()
@@ -214,4 +207,6 @@ class Services(metaclass=SingletonMeta):
214
207
  await task
215
208
  except asyncio.CancelledError:
216
209
  pass
210
+
211
+ # 关闭RabbitMQ服务
217
212
  await RabbitMQService.shutdown()
sycommon/tools/docs.py ADDED
@@ -0,0 +1,42 @@
1
+ from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html, swagger_ui_default_parameters
2
+
3
+
4
+ def custom_swagger_ui_html(*args, **kwargs):
5
+ custom_params = {
6
+ "dom_id": "#swagger-ui",
7
+ "layout": "BaseLayout",
8
+ "deepLinking": True,
9
+ "showExtensions": True,
10
+ "showCommonExtensionsExtensions": True,
11
+ "defaultModelsExpandDepth": -1,
12
+ "persistAuthorization": True,
13
+ "displayRequestDuration": True
14
+ }
15
+
16
+ # 初始化合并参数为默认参数的副本
17
+ merged_params = swagger_ui_default_parameters.copy()
18
+
19
+ # 安全地合并kwargs中的参数(处理可能为None的情况)
20
+ if "swagger_ui_parameters" in kwargs and kwargs["swagger_ui_parameters"] is not None:
21
+ merged_params.update(kwargs["swagger_ui_parameters"])
22
+
23
+ # 最后应用自定义参数
24
+ merged_params.update(custom_params)
25
+ kwargs["swagger_ui_parameters"] = merged_params
26
+
27
+ return get_swagger_ui_html(
28
+ *args, ** kwargs,
29
+ swagger_favicon_url="https://static.sytechnology.com/img/sylogopng.png",
30
+ swagger_js_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.27.1/swagger-ui-bundle.js",
31
+ swagger_css_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/5.27.1/swagger-ui.css",
32
+ )
33
+
34
+
35
+ def custom_redoc_html(*args, **kwargs):
36
+ return get_redoc_html(
37
+ *args,
38
+ **kwargs,
39
+ redoc_favicon_url='https://static.sytechnology.com/img/sylogopng.png',
40
+ # redoc_js_url="https://cdn.jsdelivr.net/npm/@stardustai/redoc@2.0.0-rc.66/bundles/redoc.browser.lib.min.js",
41
+ with_google_fonts=False,
42
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,5 +1,5 @@
1
1
  sycommon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- sycommon/services.py,sha256=Ow3BwiscQeX0k9fcr5t9ykpw6Oy-vcKkLR_UlFTb4WQ,8537
2
+ sycommon/services.py,sha256=_XgQF72-NisNV2bQJcHPWSIDd_RG-g8RmGN_QxQ8noE,8104
3
3
  sycommon/config/Config.py,sha256=9yO5b8WfvEDvkyrGrlwrLFasgh_-MjcEvGF20Gz5Xo4,3041
4
4
  sycommon/config/DatabaseConfig.py,sha256=ILiUuYT9_xJZE2W-RYuC3JCt_YLKc1sbH13-MHIOPhg,804
5
5
  sycommon/config/EmbeddingConfig.py,sha256=gPKwiDYbeu1GpdIZXMmgqM7JqBIzCXi0yYuGRLZooMI,362
@@ -18,28 +18,30 @@ sycommon/logging/logger_wrapper.py,sha256=TiHsrIIHiQMzXgXK12-0KIpU9GhwQJOoHslakz
18
18
  sycommon/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  sycommon/middleware/context.py,sha256=_5ghpv4u_yAvjkgM-XDx8OnO-YI1XtntHrXsHJHZkwo,88
20
20
  sycommon/middleware/cors.py,sha256=bGwDPI5Bfr0yH0hM-K7wK8mur5YqTy_XVZqlsdY_WdU,301
21
+ sycommon/middleware/docs.py,sha256=bVdDBIHXGVBv562MQLSroa1DgHoObxR9gFzv71PIejg,1187
21
22
  sycommon/middleware/exception.py,sha256=Bk8IchNND1wg6tUX9hf4xWeEJhvA-E_zE9ysBpRZrqE,3010
22
- sycommon/middleware/middleware.py,sha256=z6osiDyKDxNKqErI29RVgijcjjl8e2wg-T9WN4xdbys,1220
23
+ sycommon/middleware/middleware.py,sha256=KP7oM6rBowzF89wxBAUWArlC8GklrroP--gm4DtyAuo,1331
23
24
  sycommon/middleware/monitor_memory.py,sha256=pYRK-wRuDd6enSg9Pf8tQxPdYQS6S0AyjyXeKFRLKEs,628
24
25
  sycommon/middleware/mq.py,sha256=4wBqiT5wJGcrfjk2GSr0_U3TStBxoNpHTzcRxVlMEHE,183
25
26
  sycommon/middleware/timeout.py,sha256=fImlAPLm4Oa8N9goXtT_0os1GZPCi9F92OgXU81DgDU,656
26
- sycommon/middleware/traceid.py,sha256=24OAvsprq_LGT1zc5Jacd_lBIt8-eU6akm-gkgLTIEo,5469
27
+ sycommon/middleware/traceid.py,sha256=oGTJ2jtdea_3NgaAwXLpUug5dGUYRQeM4r1n2icuvC8,6839
27
28
  sycommon/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- sycommon/models/base_http.py,sha256=bbVvNnqi22DZavYKsYJN0Z1yJzfF9vzUzG_u2lFpNlc,2886
29
+ sycommon/models/base_http.py,sha256=hcebQSPlGtx0GrLNK02VTDpviCBILaMrWijmlKEQu3I,3013
29
30
  sycommon/models/log.py,sha256=rZpj6VkDRxK3B6H7XSeWdYZshU8F0Sks8bq1p6pPlDw,500
30
31
  sycommon/models/mqlistener_config.py,sha256=PPwhAVJ2AWvVAvNox_1t0fuBKTyRH3Ui9cuuU-q7Byo,1590
31
32
  sycommon/models/mqmsg_model.py,sha256=cxn0M5b0utQK6crMYmL-1waeGYHvK3AlGaRy23clqTE,277
32
33
  sycommon/models/mqsend_config.py,sha256=NQX9dc8PpuquMG36GCVhJe8omAW1KVXXqr6lSRU6D7I,268
33
34
  sycommon/models/sso_user.py,sha256=i1WAN6k5sPcPApQEdtjpWDy7VrzWLpOrOQewGLGoGIw,2702
34
- sycommon/rabbitmq/rabbitmq_client.py,sha256=8QeshhkWliJ6ed2-0LmFUWQ9ZLXdDBwNsZrvRiDWMlc,29390
35
- sycommon/rabbitmq/rabbitmq_service.py,sha256=7Ol1jQ7glV5mrGLU6hORv-UTjJH_SeyDk3JgSIS45uA,20194
35
+ sycommon/rabbitmq/rabbitmq_client.py,sha256=QLsn_jqWtBihmBXgrFRZx9S5AhtnvWIuolkfOHno0Wc,30782
36
+ sycommon/rabbitmq/rabbitmq_service.py,sha256=Cl3zWa1PVv3kqpYWKtkP-SomXUSgVNDqzkhE8PuvCR0,22274
36
37
  sycommon/synacos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
38
  sycommon/synacos/feign.py,sha256=ghbI2_6g1C5P0EcU8y2DeDZ9E7Mv0T1iHwptLX1U6NA,12075
38
39
  sycommon/synacos/nacos_service.py,sha256=8l2qaVg5GQSTybGrcdmg0B5jRNAzbOPM3mjQ6T04A2I,30816
39
40
  sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
+ sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
40
42
  sycommon/tools/snowflake.py,sha256=rc-VUjBMMpdAvbnHroVwfVt1xzApJbTCthUy9mglAuw,237
41
43
  sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
42
- sycommon_python_lib-0.1.8.dist-info/METADATA,sha256=p4qDr-4b_hhuTHMi7JMrEyj3vzuITuMY0_IGrNgKSsw,7004
43
- sycommon_python_lib-0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- sycommon_python_lib-0.1.8.dist-info/top_level.txt,sha256=qb-vRf3lrmIaCLrGZxsv6k8Q8l3_lGuAeFtwjh0wYCQ,9
45
- sycommon_python_lib-0.1.8.dist-info/RECORD,,
44
+ sycommon_python_lib-0.1.10.dist-info/METADATA,sha256=UteBh6HBuWlcC-6wGj2ro9zGDM0f1O5HM39o6yi2jMM,7005
45
+ sycommon_python_lib-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
+ sycommon_python_lib-0.1.10.dist-info/top_level.txt,sha256=qb-vRf3lrmIaCLrGZxsv6k8Q8l3_lGuAeFtwjh0wYCQ,9
47
+ sycommon_python_lib-0.1.10.dist-info/RECORD,,