infomankit 0.3.23__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 (143) hide show
  1. infoman/__init__.py +1 -0
  2. infoman/cli/README.md +378 -0
  3. infoman/cli/__init__.py +7 -0
  4. infoman/cli/commands/__init__.py +3 -0
  5. infoman/cli/commands/init.py +312 -0
  6. infoman/cli/scaffold.py +634 -0
  7. infoman/cli/templates/Makefile.template +132 -0
  8. infoman/cli/templates/app/__init__.py.template +3 -0
  9. infoman/cli/templates/app/app.py.template +4 -0
  10. infoman/cli/templates/app/models_base.py.template +18 -0
  11. infoman/cli/templates/app/models_entity_init.py.template +11 -0
  12. infoman/cli/templates/app/models_schemas_init.py.template +11 -0
  13. infoman/cli/templates/app/repository_init.py.template +11 -0
  14. infoman/cli/templates/app/routers_init.py.template +15 -0
  15. infoman/cli/templates/app/services_init.py.template +11 -0
  16. infoman/cli/templates/app/static_index.html.template +39 -0
  17. infoman/cli/templates/app/static_main.js.template +31 -0
  18. infoman/cli/templates/app/static_style.css.template +111 -0
  19. infoman/cli/templates/app/utils_init.py.template +11 -0
  20. infoman/cli/templates/config/.env.dev.template +43 -0
  21. infoman/cli/templates/config/.env.prod.template +43 -0
  22. infoman/cli/templates/config/README.md.template +28 -0
  23. infoman/cli/templates/docker/.dockerignore.template +60 -0
  24. infoman/cli/templates/docker/Dockerfile.template +47 -0
  25. infoman/cli/templates/docker/README.md.template +240 -0
  26. infoman/cli/templates/docker/docker-compose.yml.template +81 -0
  27. infoman/cli/templates/docker/mysql_custom.cnf.template +42 -0
  28. infoman/cli/templates/docker/mysql_init.sql.template +15 -0
  29. infoman/cli/templates/project/.env.example.template +1 -0
  30. infoman/cli/templates/project/.gitignore.template +60 -0
  31. infoman/cli/templates/project/Makefile.template +38 -0
  32. infoman/cli/templates/project/README.md.template +137 -0
  33. infoman/cli/templates/project/deploy.sh.template +97 -0
  34. infoman/cli/templates/project/main.py.template +10 -0
  35. infoman/cli/templates/project/manage.sh.template +97 -0
  36. infoman/cli/templates/project/pyproject.toml.template +47 -0
  37. infoman/cli/templates/project/service.sh.template +203 -0
  38. infoman/config/__init__.py +25 -0
  39. infoman/config/base.py +67 -0
  40. infoman/config/db_cache.py +237 -0
  41. infoman/config/db_relation.py +181 -0
  42. infoman/config/db_vector.py +39 -0
  43. infoman/config/jwt.py +16 -0
  44. infoman/config/llm.py +16 -0
  45. infoman/config/log.py +627 -0
  46. infoman/config/mq.py +26 -0
  47. infoman/config/settings.py +65 -0
  48. infoman/llm/__init__.py +0 -0
  49. infoman/llm/llm.py +297 -0
  50. infoman/logger/__init__.py +57 -0
  51. infoman/logger/context.py +191 -0
  52. infoman/logger/core.py +358 -0
  53. infoman/logger/filters.py +157 -0
  54. infoman/logger/formatters.py +138 -0
  55. infoman/logger/handlers.py +276 -0
  56. infoman/logger/metrics.py +160 -0
  57. infoman/performance/README.md +583 -0
  58. infoman/performance/__init__.py +19 -0
  59. infoman/performance/cli.py +215 -0
  60. infoman/performance/config.py +166 -0
  61. infoman/performance/reporter.py +519 -0
  62. infoman/performance/runner.py +303 -0
  63. infoman/performance/standards.py +222 -0
  64. infoman/service/__init__.py +8 -0
  65. infoman/service/app.py +67 -0
  66. infoman/service/core/__init__.py +0 -0
  67. infoman/service/core/auth.py +105 -0
  68. infoman/service/core/lifespan.py +132 -0
  69. infoman/service/core/monitor.py +57 -0
  70. infoman/service/core/response.py +37 -0
  71. infoman/service/exception/__init__.py +7 -0
  72. infoman/service/exception/error.py +274 -0
  73. infoman/service/exception/exception.py +25 -0
  74. infoman/service/exception/handler.py +238 -0
  75. infoman/service/infrastructure/__init__.py +8 -0
  76. infoman/service/infrastructure/base.py +212 -0
  77. infoman/service/infrastructure/db_cache/__init__.py +8 -0
  78. infoman/service/infrastructure/db_cache/manager.py +194 -0
  79. infoman/service/infrastructure/db_relation/__init__.py +41 -0
  80. infoman/service/infrastructure/db_relation/manager.py +300 -0
  81. infoman/service/infrastructure/db_relation/manager_pro.py +408 -0
  82. infoman/service/infrastructure/db_relation/mysql.py +52 -0
  83. infoman/service/infrastructure/db_relation/pgsql.py +54 -0
  84. infoman/service/infrastructure/db_relation/sqllite.py +25 -0
  85. infoman/service/infrastructure/db_vector/__init__.py +40 -0
  86. infoman/service/infrastructure/db_vector/manager.py +201 -0
  87. infoman/service/infrastructure/db_vector/qdrant.py +322 -0
  88. infoman/service/infrastructure/mq/__init__.py +15 -0
  89. infoman/service/infrastructure/mq/manager.py +178 -0
  90. infoman/service/infrastructure/mq/nats/__init__.py +0 -0
  91. infoman/service/infrastructure/mq/nats/nats_client.py +57 -0
  92. infoman/service/infrastructure/mq/nats/nats_event_router.py +25 -0
  93. infoman/service/launch.py +284 -0
  94. infoman/service/middleware/__init__.py +7 -0
  95. infoman/service/middleware/base.py +41 -0
  96. infoman/service/middleware/logging.py +51 -0
  97. infoman/service/middleware/rate_limit.py +301 -0
  98. infoman/service/middleware/request_id.py +21 -0
  99. infoman/service/middleware/white_list.py +24 -0
  100. infoman/service/models/__init__.py +8 -0
  101. infoman/service/models/base.py +441 -0
  102. infoman/service/models/type/embed.py +70 -0
  103. infoman/service/routers/__init__.py +18 -0
  104. infoman/service/routers/health_router.py +311 -0
  105. infoman/service/routers/monitor_router.py +44 -0
  106. infoman/service/utils/__init__.py +8 -0
  107. infoman/service/utils/cache/__init__.py +0 -0
  108. infoman/service/utils/cache/cache.py +192 -0
  109. infoman/service/utils/module_loader.py +10 -0
  110. infoman/service/utils/parse.py +10 -0
  111. infoman/service/utils/resolver/__init__.py +8 -0
  112. infoman/service/utils/resolver/base.py +47 -0
  113. infoman/service/utils/resolver/resp.py +102 -0
  114. infoman/service/vector/__init__.py +20 -0
  115. infoman/service/vector/base.py +56 -0
  116. infoman/service/vector/qdrant.py +125 -0
  117. infoman/service/vector/service.py +67 -0
  118. infoman/utils/__init__.py +2 -0
  119. infoman/utils/decorators/__init__.py +8 -0
  120. infoman/utils/decorators/cache.py +137 -0
  121. infoman/utils/decorators/retry.py +99 -0
  122. infoman/utils/decorators/safe_execute.py +99 -0
  123. infoman/utils/decorators/timing.py +99 -0
  124. infoman/utils/encryption/__init__.py +8 -0
  125. infoman/utils/encryption/aes.py +66 -0
  126. infoman/utils/encryption/ecc.py +108 -0
  127. infoman/utils/encryption/rsa.py +112 -0
  128. infoman/utils/file/__init__.py +0 -0
  129. infoman/utils/file/handler.py +22 -0
  130. infoman/utils/hash/__init__.py +0 -0
  131. infoman/utils/hash/hash.py +61 -0
  132. infoman/utils/http/__init__.py +8 -0
  133. infoman/utils/http/client.py +62 -0
  134. infoman/utils/http/info.py +94 -0
  135. infoman/utils/http/result.py +19 -0
  136. infoman/utils/notification/__init__.py +8 -0
  137. infoman/utils/notification/feishu.py +35 -0
  138. infoman/utils/text/__init__.py +8 -0
  139. infoman/utils/text/extractor.py +111 -0
  140. infomankit-0.3.23.dist-info/METADATA +632 -0
  141. infomankit-0.3.23.dist-info/RECORD +143 -0
  142. infomankit-0.3.23.dist-info/WHEEL +4 -0
  143. infomankit-0.3.23.dist-info/entry_points.txt +5 -0
@@ -0,0 +1,303 @@
1
+ """
2
+ 性能测试运行器
3
+
4
+ 执行性能测试并收集结果
5
+ """
6
+
7
+ import asyncio
8
+ import time
9
+ import statistics
10
+ from typing import List, Dict, Any
11
+ from dataclasses import dataclass, field
12
+ from datetime import datetime
13
+ import httpx
14
+ from loguru import logger
15
+
16
+ from .config import TestConfig, APITestCase
17
+ from .standards import PerformanceStandards, StandardLevel
18
+
19
+
20
+ @dataclass
21
+ class TestResult:
22
+ """单次测试结果"""
23
+ test_case_name: str
24
+ url: str
25
+ method: str
26
+ status_code: int
27
+ response_time: float # 毫秒
28
+ success: bool
29
+ error_message: str = ""
30
+ timestamp: float = field(default_factory=time.time)
31
+
32
+
33
+ @dataclass
34
+ class AggregatedResult:
35
+ """聚合测试结果"""
36
+ test_case_name: str
37
+ url: str
38
+ method: str
39
+ interface_type: str
40
+
41
+ # 请求统计
42
+ total_requests: int = 0
43
+ successful_requests: int = 0
44
+ failed_requests: int = 0
45
+ success_rate: float = 0.0
46
+
47
+ # 响应时间统计 (毫秒)
48
+ min_response_time: float = 0.0
49
+ max_response_time: float = 0.0
50
+ avg_response_time: float = 0.0
51
+ median_response_time: float = 0.0
52
+ p50_response_time: float = 0.0
53
+ p95_response_time: float = 0.0
54
+ p99_response_time: float = 0.0
55
+
56
+ # 吞吐量 (requests/second)
57
+ throughput: float = 0.0
58
+
59
+ # 性能评级
60
+ response_time_level: StandardLevel = StandardLevel.ACCEPTABLE
61
+ throughput_level: StandardLevel = StandardLevel.ACCEPTABLE
62
+ success_rate_level: StandardLevel = StandardLevel.ACCEPTABLE
63
+ overall_level: StandardLevel = StandardLevel.ACCEPTABLE
64
+
65
+ # 错误信息
66
+ error_messages: List[str] = field(default_factory=list)
67
+
68
+
69
+ class PerformanceTestRunner:
70
+ """性能测试运行器"""
71
+
72
+ def __init__(self, config: TestConfig):
73
+ self.config = config
74
+ self.results: Dict[str, List[TestResult]] = {}
75
+ self.start_time: float = 0
76
+ self.end_time: float = 0
77
+
78
+ async def run(self) -> Dict[str, AggregatedResult]:
79
+ """
80
+ 运行性能测试
81
+
82
+ Returns:
83
+ 聚合结果字典 {test_case_name: AggregatedResult}
84
+ """
85
+ logger.info(f"🚀 开始性能测试: {self.config.project_name}")
86
+ logger.info(f" 并发用户: {self.config.concurrent_users}")
87
+ logger.info(f" 持续时间: {self.config.duration}秒")
88
+ logger.info(f" 测试用例: {len(self.config.get_enabled_test_cases())}个")
89
+
90
+ self.start_time = time.time()
91
+
92
+ # 创建并发任务
93
+ tasks = []
94
+ for i in range(self.config.concurrent_users):
95
+ task = asyncio.create_task(self._user_task(i))
96
+ tasks.append(task)
97
+ # 控制启动速率
98
+ await asyncio.sleep(1 / self.config.spawn_rate)
99
+
100
+ # 等待所有任务完成
101
+ await asyncio.gather(*tasks, return_exceptions=True)
102
+
103
+ self.end_time = time.time()
104
+
105
+ logger.success(f"✅ 性能测试完成,耗时: {self.end_time - self.start_time:.2f}秒")
106
+
107
+ # 聚合结果
108
+ aggregated = self._aggregate_results()
109
+
110
+ return aggregated
111
+
112
+ async def _user_task(self, user_id: int):
113
+ """单个用户的测试任务"""
114
+ test_cases = self.config.get_enabled_test_cases()
115
+ if not test_cases:
116
+ logger.warning("没有启用的测试用例")
117
+ return
118
+
119
+ end_time = self.start_time + self.config.duration
120
+
121
+ async with httpx.AsyncClient(timeout=30.0) as client:
122
+ while time.time() < end_time:
123
+ # 依次执行所有测试用例
124
+ for test_case in test_cases:
125
+ if time.time() >= end_time:
126
+ break
127
+
128
+ result = await self._execute_test_case(client, test_case)
129
+
130
+ # 保存结果
131
+ if test_case.name not in self.results:
132
+ self.results[test_case.name] = []
133
+ self.results[test_case.name].append(result)
134
+
135
+ # 思考时间
136
+ think_time = (
137
+ self.config.think_time_min +
138
+ (self.config.think_time_max - self.config.think_time_min) * 0.5
139
+ )
140
+ await asyncio.sleep(think_time)
141
+
142
+ async def _execute_test_case(
143
+ self,
144
+ client: httpx.AsyncClient,
145
+ test_case: APITestCase
146
+ ) -> TestResult:
147
+ """执行单个测试用例"""
148
+ url = self._build_url(test_case.url)
149
+
150
+ # 构建请求头
151
+ headers = {**self.config.global_headers, **test_case.headers}
152
+
153
+ # 添加认证
154
+ if self.config.auth_type == "bearer" and self.config.auth_token:
155
+ headers["Authorization"] = f"Bearer {self.config.auth_token}"
156
+
157
+ start_time = time.time()
158
+ success = False
159
+ status_code = 0
160
+ error_message = ""
161
+
162
+ try:
163
+ response = await client.request(
164
+ method=test_case.method,
165
+ url=url,
166
+ headers=headers,
167
+ params=test_case.params,
168
+ json=test_case.json,
169
+ data=test_case.data,
170
+ timeout=test_case.timeout,
171
+ )
172
+
173
+ status_code = response.status_code
174
+ success = 200 <= status_code < 300
175
+
176
+ if not success:
177
+ error_message = f"HTTP {status_code}: {response.text[:200]}"
178
+
179
+ except httpx.TimeoutException:
180
+ error_message = "请求超时"
181
+ except httpx.ConnectError:
182
+ error_message = "连接失败"
183
+ except Exception as e:
184
+ error_message = str(e)
185
+
186
+ end_time = time.time()
187
+ response_time = (end_time - start_time) * 1000 # 转换为毫秒
188
+
189
+ return TestResult(
190
+ test_case_name=test_case.name,
191
+ url=url,
192
+ method=test_case.method,
193
+ status_code=status_code,
194
+ response_time=response_time,
195
+ success=success,
196
+ error_message=error_message,
197
+ )
198
+
199
+ def _build_url(self, path: str) -> str:
200
+ """构建完整 URL"""
201
+ if path.startswith("http://") or path.startswith("https://"):
202
+ return path
203
+
204
+ base_url = self.config.base_url.rstrip("/")
205
+ path = path.lstrip("/")
206
+ return f"{base_url}/{path}"
207
+
208
+ def _aggregate_results(self) -> Dict[str, AggregatedResult]:
209
+ """聚合测试结果"""
210
+ aggregated = {}
211
+ test_duration = self.end_time - self.start_time
212
+
213
+ for test_case_name, results in self.results.items():
214
+ if not results:
215
+ continue
216
+
217
+ # 找到对应的测试用例配置
218
+ test_case = next(
219
+ (tc for tc in self.config.test_cases if tc.name == test_case_name),
220
+ None
221
+ )
222
+ interface_type = test_case.interface_type if test_case else "normal"
223
+
224
+ # 基本统计
225
+ total = len(results)
226
+ successful = sum(1 for r in results if r.success)
227
+ failed = total - successful
228
+ success_rate = (successful / total * 100) if total > 0 else 0
229
+
230
+ # 响应时间统计
231
+ response_times = [r.response_time for r in results]
232
+ response_times.sort()
233
+
234
+ min_rt = min(response_times) if response_times else 0
235
+ max_rt = max(response_times) if response_times else 0
236
+ avg_rt = statistics.mean(response_times) if response_times else 0
237
+ median_rt = statistics.median(response_times) if response_times else 0
238
+
239
+ # 百分位
240
+ p50 = self._percentile(response_times, 0.50)
241
+ p95 = self._percentile(response_times, 0.95)
242
+ p99 = self._percentile(response_times, 0.99)
243
+
244
+ # 吞吐量
245
+ throughput = total / test_duration if test_duration > 0 else 0
246
+
247
+ # 性能评级
248
+ rt_level = PerformanceStandards.evaluate_response_time(
249
+ avg_rt, interface_type
250
+ )
251
+ tp_level = PerformanceStandards.evaluate_throughput(
252
+ throughput, interface_type
253
+ )
254
+ sr_level = PerformanceStandards.evaluate_success_rate(success_rate)
255
+
256
+ # 综合评级 (取最差的)
257
+ overall_level = max(
258
+ [rt_level, tp_level, sr_level],
259
+ key=lambda x: list(StandardLevel).index(x)
260
+ )
261
+
262
+ # 错误信息
263
+ error_messages = [
264
+ r.error_message
265
+ for r in results
266
+ if not r.success and r.error_message
267
+ ]
268
+ unique_errors = list(set(error_messages))[:10] # 最多10条
269
+
270
+ aggregated[test_case_name] = AggregatedResult(
271
+ test_case_name=test_case_name,
272
+ url=results[0].url,
273
+ method=results[0].method,
274
+ interface_type=interface_type,
275
+ total_requests=total,
276
+ successful_requests=successful,
277
+ failed_requests=failed,
278
+ success_rate=success_rate,
279
+ min_response_time=min_rt,
280
+ max_response_time=max_rt,
281
+ avg_response_time=avg_rt,
282
+ median_response_time=median_rt,
283
+ p50_response_time=p50,
284
+ p95_response_time=p95,
285
+ p99_response_time=p99,
286
+ throughput=throughput,
287
+ response_time_level=rt_level,
288
+ throughput_level=tp_level,
289
+ success_rate_level=sr_level,
290
+ overall_level=overall_level,
291
+ error_messages=unique_errors,
292
+ )
293
+
294
+ return aggregated
295
+
296
+ @staticmethod
297
+ def _percentile(data: List[float], percentile: float) -> float:
298
+ """计算百分位数"""
299
+ if not data:
300
+ return 0
301
+ sorted_data = sorted(data)
302
+ index = int(len(sorted_data) * percentile)
303
+ return sorted_data[min(index, len(sorted_data) - 1)]
@@ -0,0 +1,222 @@
1
+ """
2
+ 性能测试标准定义
3
+
4
+ 定义不同级别的性能标准,用于评估接口性能
5
+ """
6
+
7
+ from enum import Enum
8
+ from typing import Dict, Optional
9
+ from dataclasses import dataclass
10
+
11
+
12
+ class StandardLevel(str, Enum):
13
+ """标准级别"""
14
+ EXCELLENT = "excellent" # 优秀
15
+ GOOD = "good" # 良好
16
+ ACCEPTABLE = "acceptable" # 可接受
17
+ POOR = "poor" # 较差
18
+ CRITICAL = "critical" # 严重
19
+
20
+
21
+ @dataclass
22
+ class PerformanceThreshold:
23
+ """性能阈值"""
24
+ excellent: float # 优秀阈值
25
+ good: float # 良好阈值
26
+ acceptable: float # 可接受阈值
27
+ poor: float # 较差阈值
28
+ # 超过 poor 即为 critical
29
+
30
+
31
+ class PerformanceStandards:
32
+ """
33
+ 性能测试标准
34
+
35
+ 定义了不同类型接口的性能标准
36
+ """
37
+
38
+ # 标准定义 (单位: 毫秒)
39
+ STANDARDS = {
40
+ # 快速接口 (如健康检查、静态资源)
41
+ "fast": PerformanceThreshold(
42
+ excellent=10, # < 10ms
43
+ good=30, # < 30ms
44
+ acceptable=50, # < 50ms
45
+ poor=100, # < 100ms
46
+ ),
47
+
48
+ # 一般接口 (如简单查询、列表)
49
+ "normal": PerformanceThreshold(
50
+ excellent=50, # < 50ms
51
+ good=100, # < 100ms
52
+ acceptable=200, # < 200ms
53
+ poor=500, # < 500ms
54
+ ),
55
+
56
+ # 复杂接口 (如复杂查询、聚合)
57
+ "complex": PerformanceThreshold(
58
+ excellent=100, # < 100ms
59
+ good=200, # < 200ms
60
+ acceptable=500, # < 500ms
61
+ poor=1000, # < 1s
62
+ ),
63
+
64
+ # 重型接口 (如文件处理、批量操作)
65
+ "heavy": PerformanceThreshold(
66
+ excellent=200, # < 200ms
67
+ good=500, # < 500ms
68
+ acceptable=1000, # < 1s
69
+ poor=3000, # < 3s
70
+ ),
71
+ }
72
+
73
+ # 吞吐量标准 (requests/second)
74
+ THROUGHPUT_STANDARDS = {
75
+ "fast": {
76
+ "excellent": 1000,
77
+ "good": 500,
78
+ "acceptable": 200,
79
+ "poor": 100,
80
+ },
81
+ "normal": {
82
+ "excellent": 500,
83
+ "good": 200,
84
+ "acceptable": 100,
85
+ "poor": 50,
86
+ },
87
+ "complex": {
88
+ "excellent": 200,
89
+ "good": 100,
90
+ "acceptable": 50,
91
+ "poor": 20,
92
+ },
93
+ "heavy": {
94
+ "excellent": 100,
95
+ "good": 50,
96
+ "acceptable": 20,
97
+ "poor": 10,
98
+ },
99
+ }
100
+
101
+ # 并发用户标准
102
+ CONCURRENCY_STANDARDS = {
103
+ "low": 10, # 低并发
104
+ "medium": 50, # 中并发
105
+ "high": 100, # 高并发
106
+ "extreme": 500, # 极限并发
107
+ }
108
+
109
+ # 成功率标准
110
+ SUCCESS_RATE_STANDARDS = {
111
+ "excellent": 99.9, # 99.9%
112
+ "good": 99.0, # 99%
113
+ "acceptable": 95.0, # 95%
114
+ "poor": 90.0, # 90%
115
+ }
116
+
117
+ @classmethod
118
+ def evaluate_response_time(
119
+ cls,
120
+ response_time: float,
121
+ interface_type: str = "normal"
122
+ ) -> StandardLevel:
123
+ """
124
+ 评估响应时间
125
+
126
+ Args:
127
+ response_time: 响应时间 (毫秒)
128
+ interface_type: 接口类型 (fast/normal/complex/heavy)
129
+
130
+ Returns:
131
+ 标准级别
132
+ """
133
+ threshold = cls.STANDARDS.get(interface_type, cls.STANDARDS["normal"])
134
+
135
+ if response_time <= threshold.excellent:
136
+ return StandardLevel.EXCELLENT
137
+ elif response_time <= threshold.good:
138
+ return StandardLevel.GOOD
139
+ elif response_time <= threshold.acceptable:
140
+ return StandardLevel.ACCEPTABLE
141
+ elif response_time <= threshold.poor:
142
+ return StandardLevel.POOR
143
+ else:
144
+ return StandardLevel.CRITICAL
145
+
146
+ @classmethod
147
+ def evaluate_throughput(
148
+ cls,
149
+ throughput: float,
150
+ interface_type: str = "normal"
151
+ ) -> StandardLevel:
152
+ """评估吞吐量"""
153
+ standards = cls.THROUGHPUT_STANDARDS.get(
154
+ interface_type,
155
+ cls.THROUGHPUT_STANDARDS["normal"]
156
+ )
157
+
158
+ if throughput >= standards["excellent"]:
159
+ return StandardLevel.EXCELLENT
160
+ elif throughput >= standards["good"]:
161
+ return StandardLevel.GOOD
162
+ elif throughput >= standards["acceptable"]:
163
+ return StandardLevel.ACCEPTABLE
164
+ elif throughput >= standards["poor"]:
165
+ return StandardLevel.POOR
166
+ else:
167
+ return StandardLevel.CRITICAL
168
+
169
+ @classmethod
170
+ def evaluate_success_rate(cls, success_rate: float) -> StandardLevel:
171
+ """评估成功率"""
172
+ if success_rate >= cls.SUCCESS_RATE_STANDARDS["excellent"]:
173
+ return StandardLevel.EXCELLENT
174
+ elif success_rate >= cls.SUCCESS_RATE_STANDARDS["good"]:
175
+ return StandardLevel.GOOD
176
+ elif success_rate >= cls.SUCCESS_RATE_STANDARDS["acceptable"]:
177
+ return StandardLevel.ACCEPTABLE
178
+ elif success_rate >= cls.SUCCESS_RATE_STANDARDS["poor"]:
179
+ return StandardLevel.POOR
180
+ else:
181
+ return StandardLevel.CRITICAL
182
+
183
+ @classmethod
184
+ def get_threshold(cls, interface_type: str = "normal") -> PerformanceThreshold:
185
+ """获取性能阈值"""
186
+ return cls.STANDARDS.get(interface_type, cls.STANDARDS["normal"])
187
+
188
+ @classmethod
189
+ def get_level_color(cls, level: StandardLevel) -> str:
190
+ """获取级别对应的颜色"""
191
+ colors = {
192
+ StandardLevel.EXCELLENT: "#10b981", # 绿色
193
+ StandardLevel.GOOD: "#3b82f6", # 蓝色
194
+ StandardLevel.ACCEPTABLE: "#f59e0b", # 橙色
195
+ StandardLevel.POOR: "#ef4444", # 红色
196
+ StandardLevel.CRITICAL: "#991b1b", # 深红色
197
+ }
198
+ return colors.get(level, "#6b7280")
199
+
200
+ @classmethod
201
+ def get_level_label(cls, level: StandardLevel) -> str:
202
+ """获取级别标签 (中文)"""
203
+ labels = {
204
+ StandardLevel.EXCELLENT: "优秀",
205
+ StandardLevel.GOOD: "良好",
206
+ StandardLevel.ACCEPTABLE: "可接受",
207
+ StandardLevel.POOR: "较差",
208
+ StandardLevel.CRITICAL: "严重",
209
+ }
210
+ return labels.get(level, "未知")
211
+
212
+ @classmethod
213
+ def get_recommendation(cls, level: StandardLevel) -> str:
214
+ """获取优化建议"""
215
+ recommendations = {
216
+ StandardLevel.EXCELLENT: "性能优异,继续保持",
217
+ StandardLevel.GOOD: "性能良好,可考虑进一步优化",
218
+ StandardLevel.ACCEPTABLE: "性能可接受,建议优化以提升用户体验",
219
+ StandardLevel.POOR: "性能较差,建议优先优化此接口",
220
+ StandardLevel.CRITICAL: "性能严重不足,需要立即优化",
221
+ }
222
+ return recommendations.get(level, "")
@@ -0,0 +1,8 @@
1
+ # !/usr/bin/env python
2
+ # -*-coding:utf-8 -*-
3
+
4
+ """
5
+ # Time :2025/6/21 19:59
6
+ # Author :Maxwell
7
+ # Description:
8
+ """
infoman/service/app.py ADDED
@@ -0,0 +1,67 @@
1
+ # !/usr/bin/env python
2
+ # -*-coding:utf-8 -*-
3
+
4
+ from infoman.config.settings import settings
5
+ from infoman.logger import setup_logger
6
+ from fastapi import FastAPI
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.middleware.gzip import GZipMiddleware
9
+ from infoman.service.middleware.logging import LoggingMiddleware
10
+ from infoman.service.middleware.request_id import RequestIDMiddleware
11
+ from infoman.service.routers import api_router
12
+ from infoman.config.settings import settings as config
13
+ from infoman.service.core.response import ProRJSONResponse
14
+ from infoman.service.core.monitor import instrumentator
15
+ from infoman.service.core.lifespan import lifespan
16
+ from infoman.service.exception.handler import register_exception_handlers
17
+
18
+ # ============ 日志配置 ============
19
+ setup_logger()
20
+
21
+ # ============ 创建 FastAPI 应用实例 ============
22
+ application = FastAPI(
23
+ title=config.APP_NAME,
24
+ docs_url=config.DOCS_URL,
25
+ redoc_url=config.APP_REDOC_URL,
26
+ description=config.APP_DESCRIPTION,
27
+ default_response_class=ProRJSONResponse,
28
+ lifespan=lifespan
29
+ )
30
+
31
+ # ============ 异常处理器 ============
32
+ register_exception_handlers(application)
33
+
34
+ # ============ 中间件(添加顺序和执行顺序相反-洋葱模型) ============
35
+ application.add_middleware(GZipMiddleware, minimum_size=1000)
36
+ application.add_middleware(
37
+ CORSMiddleware,
38
+ allow_origins=config.ALLOW_ORIGINS,
39
+ allow_credentials=config.ALLOW_CREDENTIALS,
40
+ allow_methods=config.ALLOW_METHODS,
41
+ allow_headers=config.ALLOW_HEADERS,
42
+ max_age=config.MAX_AGE,
43
+ )
44
+ application.add_middleware(RequestIDMiddleware)
45
+ application.add_middleware(LoggingMiddleware)
46
+
47
+ # ============ 内置路由(可选,用户可以选择不使用) ============
48
+ if config.USE_DEFAULT_ROUTER:
49
+ application.include_router(api_router)
50
+
51
+ if config.USE_TEMPLATES:
52
+ from fastapi.templating import Jinja2Templates
53
+ application.state.templates = Jinja2Templates(directory=config.TEMPLATE_DIR)
54
+
55
+ if config.USE_STATIC:
56
+ from fastapi.staticfiles import StaticFiles
57
+ application.mount(
58
+ path=config.STATIC_URL,
59
+ app=StaticFiles(directory=config.STATIC_DIR),
60
+ name=config.STATIC_NAME
61
+ )
62
+
63
+ # ============ Prometheus 监控 ============
64
+ if config.USE_PROMETHEUS_ROUTER:
65
+ instrumentator.instrument(application).expose(application, endpoint="/metrics")
66
+
67
+
File without changes
@@ -0,0 +1,105 @@
1
+ # !/usr/bin/env python
2
+ # -*-coding:utf-8 -*-
3
+
4
+ """
5
+ # Time :2025/6/21 23:21
6
+ # Author :Maxwell
7
+ # Description:
8
+ """
9
+ import jwt
10
+ from datetime import timedelta, datetime
11
+ from enum import Enum
12
+ from typing import Optional, Dict, Any, TypeVar
13
+
14
+ from fastapi import Request, Depends
15
+ from fastapi.security.oauth2 import OAuth2PasswordBearer
16
+ from jwt import PyJWTError
17
+ from pydantic import ValidationError, BaseModel
18
+
19
+ from infoman.config import settings as config
20
+ from infoman.service.exception import exception
21
+ from infoman.service.exception import error
22
+
23
+ T = TypeVar("T")
24
+
25
+
26
+ class SecurityConstants:
27
+ REDIS_USER_KEY_PREFIX = "U_API_AUTH_KEY_"
28
+ REDIS_OPEN_API_KEY_PREFIX = "O_API_AUTH_KEY_"
29
+ REDIS_CACHE_EXPIRE = 3 * 3600
30
+
31
+
32
+ class TokenPayload(BaseModel):
33
+ exp: Optional[datetime] = None
34
+ user_id: Optional[int] = None
35
+
36
+
37
+ class TokenType(str, Enum):
38
+ ACCESS = "access"
39
+ REFRESH = "refresh"
40
+ OPEN_API = "open_api"
41
+
42
+
43
+ class JWTHandler:
44
+
45
+ @staticmethod
46
+ def create_token(
47
+ data: Dict[str, Any],
48
+ expires_delta: Optional[timedelta] = None,
49
+ token_type: TokenType = TokenType.ACCESS,
50
+ ) -> str:
51
+ payload = data.copy()
52
+ if expires_delta:
53
+ expire = datetime.utcnow() + expires_delta
54
+ elif token_type == TokenType.ACCESS:
55
+ expire = datetime.utcnow() + timedelta(
56
+ minutes=config.JWT_ACCESS_TOKEN_EXPIRE_MINUTES
57
+ )
58
+ else:
59
+ expire = datetime.utcnow() + timedelta(days=365)
60
+ payload.update({"exp": expire, "token_type": token_type})
61
+ return jwt.encode(
62
+ payload, config.JWT_SECRET_KEY, algorithm=config.JWT_ALGORITHM
63
+ )
64
+
65
+ @staticmethod
66
+ def decode_token(token: str) -> Dict[str, Any]:
67
+ try:
68
+ payload = jwt.decode(
69
+ token, config.JWT_SECRET_KEY, algorithms=[config.JWT_ALGORITHM]
70
+ )
71
+ return payload
72
+ except jwt.ExpiredSignatureError:
73
+ raise exception.AppException(error.SecurityError.TOKEN_EXPIRED)
74
+ except (jwt.InvalidTokenError, PyJWTError, ValidationError):
75
+ raise exception.AppException(error.SecurityError.INVALID_CREDENTIALS)
76
+
77
+ @staticmethod
78
+ def verify_token(token: str) -> TokenPayload:
79
+ try:
80
+ payload = JWTHandler.decode_token(token)
81
+ token_data = TokenPayload(**payload)
82
+ if token_data.exp and token_data.exp < datetime.utcnow():
83
+ raise exception.AppException(error.SecurityError.TOKEN_EXPIRED)
84
+ return token_data
85
+ except ValidationError:
86
+ raise exception.AppException(error.SecurityError.INVALID_TOKEN)
87
+
88
+
89
+ class TokenExtractor:
90
+
91
+ @staticmethod
92
+ def get_user_id(token: str) -> Optional[int]:
93
+ try:
94
+ payload = JWTHandler.decode_token(token)
95
+ return payload.get("user_id")
96
+ except Exception:
97
+ return None
98
+
99
+ @staticmethod
100
+ def get_open_user_id(token: str) -> Optional[int]:
101
+ try:
102
+ payload = JWTHandler.decode_token(token)
103
+ return payload.get("open_user_id")
104
+ except Exception:
105
+ return None