jettask 0.2.15__py3-none-any.whl → 0.2.17__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 (149) hide show
  1. jettask/__init__.py +14 -35
  2. jettask/{webui/__main__.py → __main__.py} +4 -4
  3. jettask/api/__init__.py +103 -0
  4. jettask/api/v1/__init__.py +29 -0
  5. jettask/api/v1/alerts.py +226 -0
  6. jettask/api/v1/analytics.py +323 -0
  7. jettask/api/v1/namespaces.py +134 -0
  8. jettask/api/v1/overview.py +136 -0
  9. jettask/api/v1/queues.py +530 -0
  10. jettask/api/v1/scheduled.py +420 -0
  11. jettask/api/v1/settings.py +44 -0
  12. jettask/{webui/api.py → api.py} +4 -46
  13. jettask/{webui/backend → backend}/main.py +21 -109
  14. jettask/{webui/backend → backend}/main_unified.py +1 -1
  15. jettask/{webui/backend → backend}/namespace_api_old.py +3 -30
  16. jettask/{webui/backend → backend}/namespace_data_access.py +2 -1
  17. jettask/{webui/backend → backend}/unified_api_router.py +14 -74
  18. jettask/{core/cli.py → cli.py} +106 -26
  19. jettask/config/nacos_config.py +386 -0
  20. jettask/core/app.py +8 -100
  21. jettask/core/db_manager.py +515 -0
  22. jettask/core/event_pool.py +5 -2
  23. jettask/core/unified_manager_base.py +59 -14
  24. jettask/{webui/db_init.py → db_init.py} +1 -1
  25. jettask/executors/asyncio.py +2 -2
  26. jettask/{webui/integrated_gradio_app.py → integrated_gradio_app.py} +1 -1
  27. jettask/{webui/multi_namespace_consumer.py → multi_namespace_consumer.py} +5 -2
  28. jettask/{webui/pg_consumer.py → pg_consumer.py} +137 -69
  29. jettask/{webui/run.py → run.py} +1 -1
  30. jettask/{webui/run_webui.py → run_webui.py} +4 -4
  31. jettask/scheduler/manager.py +6 -0
  32. jettask/scheduler/multi_namespace_scheduler.py +2 -2
  33. jettask/scheduler/unified_manager.py +5 -5
  34. jettask/scheduler/unified_scheduler_manager.py +20 -12
  35. jettask/schemas/__init__.py +166 -0
  36. jettask/schemas/alert.py +99 -0
  37. jettask/schemas/backlog.py +122 -0
  38. jettask/schemas/common.py +139 -0
  39. jettask/schemas/monitoring.py +181 -0
  40. jettask/schemas/namespace.py +168 -0
  41. jettask/schemas/queue.py +83 -0
  42. jettask/schemas/scheduled_task.py +128 -0
  43. jettask/schemas/task.py +70 -0
  44. jettask/services/__init__.py +24 -0
  45. jettask/services/alert_service.py +454 -0
  46. jettask/services/analytics_service.py +46 -0
  47. jettask/services/overview_service.py +978 -0
  48. jettask/services/queue_service.py +711 -0
  49. jettask/services/redis_monitor_service.py +151 -0
  50. jettask/services/scheduled_task_service.py +207 -0
  51. jettask/services/settings_service.py +758 -0
  52. jettask/services/task_service.py +157 -0
  53. jettask/{webui/task_center.py → task_center.py} +30 -8
  54. jettask/{webui/task_center_client.py → task_center_client.py} +1 -1
  55. jettask/{webui/config.py → webui_config.py} +6 -1
  56. jettask/webui_exceptions.py +67 -0
  57. jettask/webui_sql/verify_database.sql +72 -0
  58. {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/METADATA +2 -1
  59. jettask-0.2.17.dist-info/RECORD +150 -0
  60. {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/entry_points.txt +1 -1
  61. jettask/webui/backend/data_api.py +0 -3294
  62. jettask/webui/backend/namespace_api.py +0 -295
  63. jettask/webui/backend/queue_backlog_api.py +0 -727
  64. jettask/webui/backend/redis_monitor_api.py +0 -476
  65. jettask/webui/frontend/index.html +0 -13
  66. jettask/webui/frontend/package.json +0 -30
  67. jettask/webui/frontend/src/App.css +0 -109
  68. jettask/webui/frontend/src/App.jsx +0 -66
  69. jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
  70. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
  71. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
  72. jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
  73. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
  74. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
  75. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
  76. jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
  77. jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
  78. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
  79. jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
  80. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
  81. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
  82. jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
  83. jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
  84. jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
  85. jettask/webui/frontend/src/components/layout/Header.css +0 -106
  86. jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
  87. jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
  88. jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
  89. jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
  90. jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
  91. jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
  92. jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
  93. jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
  94. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
  95. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
  96. jettask/webui/frontend/src/index.css +0 -114
  97. jettask/webui/frontend/src/main.jsx +0 -22
  98. jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
  99. jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
  100. jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
  101. jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
  102. jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
  103. jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
  104. jettask/webui/frontend/src/pages/Queues.jsx +0 -12
  105. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -810
  106. jettask/webui/frontend/src/pages/Settings.jsx +0 -801
  107. jettask/webui/frontend/src/pages/Workers.jsx +0 -12
  108. jettask/webui/frontend/src/services/api.js +0 -159
  109. jettask/webui/frontend/src/services/queueTrend.js +0 -166
  110. jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
  111. jettask/webui/frontend/src/utils/userPreferences.js +0 -154
  112. jettask/webui/frontend/vite.config.js +0 -26
  113. jettask/webui/sql/init_database.sql +0 -640
  114. jettask-0.2.15.dist-info/RECORD +0 -172
  115. /jettask/{webui/backend → backend}/__init__.py +0 -0
  116. /jettask/{webui/backend → backend}/api/__init__.py +0 -0
  117. /jettask/{webui/backend → backend}/api/v1/__init__.py +0 -0
  118. /jettask/{webui/backend → backend}/api/v1/monitoring.py +0 -0
  119. /jettask/{webui/backend → backend}/api/v1/namespaces.py +0 -0
  120. /jettask/{webui/backend → backend}/api/v1/queues.py +0 -0
  121. /jettask/{webui/backend → backend}/api/v1/tasks.py +0 -0
  122. /jettask/{webui/backend → backend}/config.py +0 -0
  123. /jettask/{webui/backend → backend}/core/__init__.py +0 -0
  124. /jettask/{webui/backend → backend}/core/cache.py +0 -0
  125. /jettask/{webui/backend → backend}/core/database.py +0 -0
  126. /jettask/{webui/backend → backend}/core/exceptions.py +0 -0
  127. /jettask/{webui/backend → backend}/data_access.py +0 -0
  128. /jettask/{webui/backend → backend}/dependencies.py +0 -0
  129. /jettask/{webui/backend → backend}/init_meta_db.py +0 -0
  130. /jettask/{webui/backend → backend}/main_v2.py +0 -0
  131. /jettask/{webui/backend → backend}/models/__init__.py +0 -0
  132. /jettask/{webui/backend → backend}/models/requests.py +0 -0
  133. /jettask/{webui/backend → backend}/models/responses.py +0 -0
  134. /jettask/{webui/backend → backend}/queue_stats_v2.py +0 -0
  135. /jettask/{webui/backend → backend}/services/__init__.py +0 -0
  136. /jettask/{webui/backend → backend}/start.py +0 -0
  137. /jettask/{webui/cleanup_deprecated_tables.sql → cleanup_deprecated_tables.sql} +0 -0
  138. /jettask/{webui/gradio_app.py → gradio_app.py} +0 -0
  139. /jettask/{webui/__init__.py → main.py} +0 -0
  140. /jettask/{webui/models.py → models.py} +0 -0
  141. /jettask/{webui/run_monitor.py → run_monitor.py} +0 -0
  142. /jettask/{webui/schema.sql → schema.sql} +0 -0
  143. /jettask/{webui/unified_consumer_manager.py → unified_consumer_manager.py} +0 -0
  144. /jettask/{webui/models → webui_models}/__init__.py +0 -0
  145. /jettask/{webui/models → webui_models}/namespace.py +0 -0
  146. /jettask/{webui/sql → webui_sql}/batch_upsert_functions.sql +0 -0
  147. {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/WHEEL +0 -0
  148. {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/licenses/LICENSE +0 -0
  149. {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,454 @@
1
+ """
2
+ 告警服务层
3
+ 处理告警相关的业务逻辑
4
+ """
5
+ from typing import Optional, List, Dict, Any
6
+ from datetime import datetime, timedelta, timezone
7
+ import logging
8
+
9
+ from jettask.schemas import AlertRuleRequest
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class AlertService:
15
+ """告警服务类"""
16
+
17
+ @staticmethod
18
+ def get_alert_rules(
19
+ page: int = 1,
20
+ page_size: int = 20,
21
+ is_active: Optional[bool] = None
22
+ ) -> Dict[str, Any]:
23
+ """
24
+ 获取告警规则列表
25
+
26
+ Args:
27
+ page: 页码
28
+ page_size: 每页大小
29
+ is_active: 是否激活
30
+
31
+ Returns:
32
+ 告警规则列表
33
+ """
34
+ # 模拟数据
35
+ rules = AlertService._get_mock_rules()
36
+
37
+ # 过滤
38
+ if is_active is not None:
39
+ rules = [r for r in rules if r["is_active"] == is_active]
40
+
41
+ # 分页
42
+ total = len(rules)
43
+ start = (page - 1) * page_size
44
+ end = start + page_size
45
+ paginated_rules = rules[start:end]
46
+
47
+ return {
48
+ "success": True,
49
+ "data": paginated_rules,
50
+ "total": total,
51
+ "page": page,
52
+ "page_size": page_size
53
+ }
54
+
55
+ @staticmethod
56
+ def create_alert_rule(request: AlertRuleRequest) -> Dict[str, Any]:
57
+ """
58
+ 创建告警规则
59
+
60
+ Args:
61
+ request: 告警规则请求
62
+
63
+ Returns:
64
+ 创建的告警规则
65
+ """
66
+ rule = {
67
+ "id": f"rule_{datetime.now().strftime('%Y%m%d%H%M%S')}",
68
+ "name": request.name,
69
+ "rule_type": request.rule_type,
70
+ "target_queues": request.target_queues,
71
+ "condition": request.condition,
72
+ "action_type": request.action_type,
73
+ "action_config": request.action_config,
74
+ "is_active": request.is_active,
75
+ "description": request.description,
76
+ "check_interval": request.check_interval,
77
+ "created_at": datetime.now(timezone.utc).isoformat(),
78
+ "last_triggered": None,
79
+ "updated_at": None
80
+ }
81
+
82
+ # TODO: 实际应该保存到数据库
83
+
84
+ return {
85
+ "success": True,
86
+ "data": rule,
87
+ "message": "告警规则创建成功"
88
+ }
89
+
90
+ @staticmethod
91
+ def update_alert_rule(rule_id: str, request: AlertRuleRequest) -> Dict[str, Any]:
92
+ """
93
+ 更新告警规则
94
+
95
+ Args:
96
+ rule_id: 规则ID
97
+ request: 告警规则请求
98
+
99
+ Returns:
100
+ 更新后的告警规则
101
+ """
102
+ rule = {
103
+ "id": rule_id,
104
+ "name": request.name,
105
+ "rule_type": request.rule_type,
106
+ "target_queues": request.target_queues,
107
+ "condition": request.condition,
108
+ "action_type": request.action_type,
109
+ "action_config": request.action_config,
110
+ "is_active": request.is_active,
111
+ "description": request.description,
112
+ "check_interval": request.check_interval,
113
+ "updated_at": datetime.now(timezone.utc).isoformat()
114
+ }
115
+
116
+ # TODO: 实际应该更新数据库
117
+
118
+ return {
119
+ "success": True,
120
+ "data": rule,
121
+ "message": "告警规则更新成功"
122
+ }
123
+
124
+ @staticmethod
125
+ def delete_alert_rule(rule_id: str) -> Dict[str, Any]:
126
+ """
127
+ 删除告警规则
128
+
129
+ Args:
130
+ rule_id: 规则ID
131
+
132
+ Returns:
133
+ 删除结果
134
+ """
135
+ # TODO: 实际应该从数据库删除
136
+
137
+ return {
138
+ "success": True,
139
+ "message": f"告警规则 {rule_id} 已删除"
140
+ }
141
+
142
+ @staticmethod
143
+ def toggle_alert_rule(rule_id: str) -> Dict[str, Any]:
144
+ """
145
+ 启用/禁用告警规则
146
+
147
+ Args:
148
+ rule_id: 规则ID
149
+
150
+ Returns:
151
+ 切换结果
152
+ """
153
+ # TODO: 实际应该更新数据库中的状态
154
+ # 这里模拟切换逻辑
155
+ current_state = True # 假设当前是激活状态
156
+ new_state = not current_state
157
+
158
+ return {
159
+ "success": True,
160
+ "data": {
161
+ "id": rule_id,
162
+ "is_active": new_state
163
+ },
164
+ "message": f"告警规则已{'启用' if new_state else '禁用'}"
165
+ }
166
+
167
+ @staticmethod
168
+ def get_alert_history(
169
+ rule_id: str,
170
+ page: int = 1,
171
+ page_size: int = 20
172
+ ) -> Dict[str, Any]:
173
+ """
174
+ 获取告警触发历史
175
+
176
+ Args:
177
+ rule_id: 规则ID
178
+ page: 页码
179
+ page_size: 每页大小
180
+
181
+ Returns:
182
+ 告警历史记录
183
+ """
184
+ # 模拟告警历史数据
185
+ history = AlertService._generate_mock_history(rule_id)
186
+
187
+ # 分页
188
+ total = len(history)
189
+ start = (page - 1) * page_size
190
+ end = start + page_size
191
+ paginated_history = history[start:end]
192
+
193
+ return {
194
+ "success": True,
195
+ "data": paginated_history,
196
+ "total": total,
197
+ "page": page,
198
+ "page_size": page_size
199
+ }
200
+
201
+ @staticmethod
202
+ def test_alert_rule(rule_id: str) -> Dict[str, Any]:
203
+ """
204
+ 测试告警规则(发送测试通知)
205
+
206
+ Args:
207
+ rule_id: 规则ID
208
+
209
+ Returns:
210
+ 测试结果
211
+ """
212
+ # TODO: 实际应该触发真实的通知发送
213
+
214
+ return {
215
+ "success": True,
216
+ "message": "测试通知已发送",
217
+ "data": {
218
+ "rule_id": rule_id,
219
+ "test_time": datetime.now(timezone.utc).isoformat(),
220
+ "notification_result": {
221
+ "status": "success",
222
+ "response": {"code": 200, "message": "OK"}
223
+ }
224
+ }
225
+ }
226
+
227
+ @staticmethod
228
+ def get_alert_statistics(namespace: Optional[str] = None) -> Dict[str, Any]:
229
+ """
230
+ 获取告警统计信息
231
+
232
+ Args:
233
+ namespace: 命名空间(可选)
234
+
235
+ Returns:
236
+ 告警统计数据
237
+ """
238
+ # 模拟统计数据
239
+ return {
240
+ "success": True,
241
+ "data": {
242
+ "total_rules": 15,
243
+ "active_rules": 10,
244
+ "inactive_rules": 5,
245
+ "alerts_today": 25,
246
+ "alerts_this_week": 156,
247
+ "alerts_this_month": 892,
248
+ "most_triggered_rule": {
249
+ "id": "rule_001",
250
+ "name": "队列积压告警",
251
+ "trigger_count": 42
252
+ },
253
+ "alert_trend": AlertService._generate_alert_trend(),
254
+ "alert_by_type": {
255
+ "queue_size": 45,
256
+ "error_rate": 30,
257
+ "response_time": 15,
258
+ "custom": 10
259
+ }
260
+ }
261
+ }
262
+
263
+ @staticmethod
264
+ def get_active_alerts(namespace: Optional[str] = None) -> List[Dict[str, Any]]:
265
+ """
266
+ 获取当前活跃的告警
267
+
268
+ Args:
269
+ namespace: 命名空间(可选)
270
+
271
+ Returns:
272
+ 活跃告警列表
273
+ """
274
+ # 模拟活跃告警
275
+ return [
276
+ {
277
+ "id": "alert_active_001",
278
+ "rule_id": "rule_001",
279
+ "rule_name": "队列积压告警",
280
+ "severity": "high",
281
+ "triggered_at": (datetime.now(timezone.utc) - timedelta(hours=2)).isoformat(),
282
+ "trigger_value": 1500,
283
+ "threshold": 1000,
284
+ "queue": "order_queue",
285
+ "status": "active",
286
+ "acknowledged": False
287
+ },
288
+ {
289
+ "id": "alert_active_002",
290
+ "rule_id": "rule_002",
291
+ "rule_name": "高错误率告警",
292
+ "severity": "critical",
293
+ "triggered_at": (datetime.now(timezone.utc) - timedelta(minutes=30)).isoformat(),
294
+ "trigger_value": 0.15,
295
+ "threshold": 0.1,
296
+ "queue": "payment_queue",
297
+ "status": "active",
298
+ "acknowledged": True,
299
+ "acknowledged_by": "admin",
300
+ "acknowledged_at": (datetime.now(timezone.utc) - timedelta(minutes=20)).isoformat()
301
+ }
302
+ ]
303
+
304
+ @staticmethod
305
+ def acknowledge_alert(alert_id: str, user: str = "system") -> Dict[str, Any]:
306
+ """
307
+ 确认告警
308
+
309
+ Args:
310
+ alert_id: 告警ID
311
+ user: 确认用户
312
+
313
+ Returns:
314
+ 确认结果
315
+ """
316
+ # TODO: 实际应该更新数据库
317
+
318
+ return {
319
+ "success": True,
320
+ "data": {
321
+ "alert_id": alert_id,
322
+ "acknowledged": True,
323
+ "acknowledged_by": user,
324
+ "acknowledged_at": datetime.now(timezone.utc).isoformat()
325
+ },
326
+ "message": "告警已确认"
327
+ }
328
+
329
+ @staticmethod
330
+ def resolve_alert(alert_id: str, resolution_note: Optional[str] = None) -> Dict[str, Any]:
331
+ """
332
+ 解决告警
333
+
334
+ Args:
335
+ alert_id: 告警ID
336
+ resolution_note: 解决说明
337
+
338
+ Returns:
339
+ 解决结果
340
+ """
341
+ # TODO: 实际应该更新数据库
342
+
343
+ return {
344
+ "success": True,
345
+ "data": {
346
+ "alert_id": alert_id,
347
+ "status": "resolved",
348
+ "resolved_at": datetime.now(timezone.utc).isoformat(),
349
+ "resolution_note": resolution_note
350
+ },
351
+ "message": "告警已解决"
352
+ }
353
+
354
+ # ============ 私有辅助方法 ============
355
+
356
+ @staticmethod
357
+ def _get_mock_rules() -> List[Dict[str, Any]]:
358
+ """获取模拟的告警规则数据"""
359
+ return [
360
+ {
361
+ "id": "rule_001",
362
+ "name": "队列积压告警",
363
+ "rule_type": "queue_size",
364
+ "target_queues": ["order_queue", "payment_queue"],
365
+ "condition": {"threshold": 1000, "operator": ">"},
366
+ "action_type": "webhook",
367
+ "action_config": {"url": "https://example.com/webhook"},
368
+ "is_active": True,
369
+ "last_triggered": "2025-08-31T14:30:00Z",
370
+ "created_at": "2025-08-01T10:00:00Z",
371
+ "description": "当队列积压超过1000时触发告警"
372
+ },
373
+ {
374
+ "id": "rule_002",
375
+ "name": "高错误率告警",
376
+ "rule_type": "error_rate",
377
+ "target_queues": ["*"],
378
+ "condition": {"threshold": 0.1, "operator": ">", "window": 300},
379
+ "action_type": "email",
380
+ "action_config": {"recipients": ["admin@example.com"]},
381
+ "is_active": True,
382
+ "last_triggered": None,
383
+ "created_at": "2025-08-15T14:00:00Z",
384
+ "description": "5分钟内错误率超过10%时告警"
385
+ },
386
+ {
387
+ "id": "rule_003",
388
+ "name": "响应时间告警",
389
+ "rule_type": "response_time",
390
+ "target_queues": ["api_queue"],
391
+ "condition": {"threshold": 5000, "operator": ">", "percentile": 95},
392
+ "action_type": "webhook",
393
+ "action_config": {"url": "https://slack.com/webhook"},
394
+ "is_active": False,
395
+ "last_triggered": "2025-08-30T09:00:00Z",
396
+ "created_at": "2025-07-01T08:00:00Z",
397
+ "description": "95分位响应时间超过5秒时告警"
398
+ },
399
+ {
400
+ "id": "rule_004",
401
+ "name": "内存使用告警",
402
+ "rule_type": "memory_usage",
403
+ "target_queues": ["*"],
404
+ "condition": {"threshold": 80, "operator": ">"},
405
+ "action_type": "sms",
406
+ "action_config": {"phone": "+1234567890"},
407
+ "is_active": True,
408
+ "last_triggered": "2025-08-29T16:00:00Z",
409
+ "created_at": "2025-06-15T12:00:00Z",
410
+ "description": "内存使用率超过80%时告警"
411
+ }
412
+ ]
413
+
414
+ @staticmethod
415
+ def _generate_mock_history(rule_id: str) -> List[Dict[str, Any]]:
416
+ """生成模拟的告警历史"""
417
+ history = []
418
+ for i in range(1, 21):
419
+ triggered_at = datetime.now(timezone.utc) - timedelta(hours=i*3)
420
+ is_resolved = i % 3 != 0 # 每3个告警有1个未解决
421
+
422
+ alert = {
423
+ "id": f"alert_{rule_id}_{i}",
424
+ "rule_id": rule_id,
425
+ "triggered_at": triggered_at.isoformat(),
426
+ "trigger_value": 1200 + i * 50,
427
+ "threshold": 1000,
428
+ "status": "resolved" if is_resolved else "active",
429
+ "notification_sent": True,
430
+ "notification_response": {"status": "success"}
431
+ }
432
+
433
+ if is_resolved:
434
+ alert["resolved_at"] = (triggered_at + timedelta(hours=1)).isoformat()
435
+ alert["resolution_note"] = f"自动恢复 - 值降至阈值以下"
436
+
437
+ history.append(alert)
438
+
439
+ return history
440
+
441
+ @staticmethod
442
+ def _generate_alert_trend() -> List[Dict[str, Any]]:
443
+ """生成告警趋势数据"""
444
+ trend = []
445
+ now = datetime.now(timezone.utc)
446
+
447
+ for i in range(7):
448
+ date = (now - timedelta(days=6-i)).date()
449
+ trend.append({
450
+ "date": date.isoformat(),
451
+ "count": 10 + i * 3 + (i % 2) * 5 # 模拟波动
452
+ })
453
+
454
+ return trend
@@ -0,0 +1,46 @@
1
+ """
2
+ 分析服务层
3
+ 处理数据分析相关的业务逻辑
4
+ """
5
+ from typing import Optional, List, Dict, Any
6
+ from datetime import datetime
7
+ import logging
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class AnalyticsService:
13
+ """分析服务类"""
14
+
15
+ def __init__(self, namespace_data_access):
16
+ """
17
+ 初始化分析服务
18
+
19
+ Args:
20
+ namespace_data_access: 命名空间数据访问实例
21
+ """
22
+ self.namespace_data_access = namespace_data_access
23
+
24
+ async def get_namespaces(self) -> List[Dict[str, Any]]:
25
+ """
26
+ 获取所有命名空间
27
+
28
+ Returns:
29
+ 命名空间列表
30
+ """
31
+ return await self.namespace_data_access.get_all_namespaces()
32
+
33
+ async def get_queue_stats(
34
+ self,
35
+ namespace: str
36
+ ) -> List[Dict[str, Any]]:
37
+ """
38
+ 获取队列统计信息
39
+
40
+ Args:
41
+ namespace: 命名空间
42
+
43
+ Returns:
44
+ 队列统计数据
45
+ """
46
+ return await self.namespace_data_access.get_queue_stats(namespace)