create-dp-koa 1.0.0

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 (235) hide show
  1. package/README.md +50 -0
  2. package/index.mjs +97 -0
  3. package/package.json +33 -0
  4. package/template/.env.development +9 -0
  5. package/template/.env.production +12 -0
  6. package/template/.github/workflows/ci-cd.yml +182 -0
  7. package/template/.trae/documents/controller_development_plan.md +386 -0
  8. package/template/.trae/skills/00-backend-core.skill.md +50 -0
  9. package/template/.trae/skills/01-backend-skill-router.skill.md +55 -0
  10. package/template/.trae/skills/10-backend-api.skill.md +54 -0
  11. package/template/.trae/skills/11-backend-controller-recipes.skill.md +107 -0
  12. package/template/.trae/skills/20-backend-repository.skill.md +25 -0
  13. package/template/.trae/skills/21-backend-service.skill.md +135 -0
  14. package/template/.trae/skills/25-backend-comments-and-doc.skill.md +97 -0
  15. package/template/.trae/skills/30-backend-validation.skill.md +320 -0
  16. package/template/.trae/skills/40-backend-error-logging.skill.md +21 -0
  17. package/template/.trae/skills/50-backend-bootstrap-lifecycle.skill.md +90 -0
  18. package/template/.trae/skills/60-backend-router-registration.skill.md +71 -0
  19. package/template/.trae/skills/70-backend-middleware.skill.md +98 -0
  20. package/template/.trae/skills/80-backend-utils-and-libs.skill.md +90 -0
  21. package/template/.trae/skills/85-backend-plugins.rule.md +64 -0
  22. package/template/.trae/skills/90-backend-testing.skill.md +29 -0
  23. package/template/.trae/skills/README.md +49 -0
  24. package/template/.vscode/launch.json +38 -0
  25. package/template/.vscode/settings.json +1 -0
  26. package/template/Dockerfile +36 -0
  27. package/template/README.md +229 -0
  28. package/template/docker-compose.yml +135 -0
  29. package/template/docs/API_DOCUMENTATION.md +837 -0
  30. package/template/docs/ARCHITECTURE_REFACTOR.md +109 -0
  31. package/template/docs/CACHE_MIGRATION_GUIDE.md +142 -0
  32. package/template/docs/DEPLOYMENT_GUIDE.md +1062 -0
  33. package/template/docs/DEVELOPMENT_GUIDE.md +1097 -0
  34. package/template/docs/DOCUMENTATION_CLEANUP_REPORT.md +166 -0
  35. package/template/docs/DOCUMENTATION_COMPLETION_REPORT.md +223 -0
  36. package/template/docs/DOCUMENTATION_INDEX.md +294 -0
  37. package/template/docs/DOCUMENTATION_STRUCTURE.md +221 -0
  38. package/template/docs/ENTERPRISE_ANNOTATION_SYSTEM_GUIDE.md +2069 -0
  39. package/template/docs/ENTERPRISE_DATABASE_ARCHITECTURE.md +318 -0
  40. package/template/docs/ENTERPRISE_DEPLOYMENT_GUIDE.md +547 -0
  41. package/template/docs/ENTERPRISE_ERROR_HANDLING_GUIDE.md +357 -0
  42. package/template/docs/ENTERPRISE_LOGGING_SYSTEM_GUIDE.md +494 -0
  43. package/template/docs/ENVIRONMENT_CONFIG_EXAMPLE.md +69 -0
  44. package/template/docs/FINAL_IMPLEMENTATION_SUMMARY.md +206 -0
  45. package/template/docs/HEALTH_CHECK_ROUTE_FIX.md +134 -0
  46. package/template/docs/IMPLEMENTATION_CHECKLIST.md +204 -0
  47. package/template/docs/INSTALLATION_GUIDE.md +611 -0
  48. package/template/docs/INTERCEPTOR_TESTING_REPORT.md +226 -0
  49. package/template/docs/INTERCEPTOR_TESTING_SCRIPTS.md +143 -0
  50. package/template/docs/LOGGING_OPTIMIZATION_GUIDE.md +126 -0
  51. package/template/docs/MEMORY_DATABASE_GUIDE.md +212 -0
  52. package/template/docs/NEW_ROUTER_INTEGRATION_GUIDE.md +345 -0
  53. package/template/docs/NEW_ROUTER_INTEGRATION_SUMMARY.md +259 -0
  54. package/template/docs/NEW_ROUTER_USAGE_GUIDE.md +364 -0
  55. package/template/docs/QUICK_START.md +268 -0
  56. package/template/docs/ROUTE_SLASH_COMPATIBILITY_FIX.md +191 -0
  57. package/template/docs/SERVICE_INTERCEPTOR_GUIDE.md +243 -0
  58. package/template/docs/SERVICE_LAYER_INDEX.md +205 -0
  59. package/template/docs/SERVICE_PATTERN_GUIDE.md +270 -0
  60. package/template/docs/SERVICE_RETURN_VALUE_SPECIFICATION.md +466 -0
  61. package/template/docs/SWAGGER_DEBUG_MODE_GUIDE.md +80 -0
  62. package/template/docs/SWAGGER_INTEGRATION_GUIDE.md +416 -0
  63. package/template/docs/TRANSACTION_MANAGER_USAGE.md +360 -0
  64. package/template/docs/TROUBLESHOOTING.md +869 -0
  65. package/template/env.production.example +62 -0
  66. package/template/jest.config.js +34 -0
  67. package/template/package-lock.json +13240 -0
  68. package/template/package.json +119 -0
  69. package/template/patches/typeorm+0.3.25.patch +22 -0
  70. package/template/scripts/sync-template.mjs +84 -0
  71. package/template/scripts/test-annotation-system.sh +48 -0
  72. package/template/scripts/test-core-functionality.sh +28 -0
  73. package/template/src/annotations/decorators/ConfigManagement.ts +9 -0
  74. package/template/src/annotations/decorators/DistributedTracing.ts +9 -0
  75. package/template/src/annotations/decorators/EnterprisePerformance.ts +9 -0
  76. package/template/src/annotations/decorators/PerformanceMonitor.ts +32 -0
  77. package/template/src/annotations/decorators/SecurityAudit.ts +9 -0
  78. package/template/src/annotations/index.ts +50 -0
  79. package/template/src/annotations/processors/ConfigManagementProcessor.ts +369 -0
  80. package/template/src/annotations/processors/DistributedTracingProcessor.ts +288 -0
  81. package/template/src/annotations/processors/EnterprisePerformanceProcessor.ts +189 -0
  82. package/template/src/annotations/processors/PerformanceMonitorProcessor.ts +101 -0
  83. package/template/src/annotations/processors/SecurityAuditProcessor.ts +345 -0
  84. package/template/src/annotations/processors/SwaggerProcessor.ts +612 -0
  85. package/template/src/annotations/processors/index.ts +10 -0
  86. package/template/src/app.ts +123 -0
  87. package/template/src/controllers/base.controller.ts +41 -0
  88. package/template/src/controllers/cacheManagement.controller.ts +131 -0
  89. package/template/src/controllers/captcha.controller.ts +57 -0
  90. package/template/src/controllers/demo/AnnotationDemoController.ts +118 -0
  91. package/template/src/controllers/example/EnterpriseExampleController.ts +297 -0
  92. package/template/src/controllers/example/ExampleController.ts +110 -0
  93. package/template/src/controllers/example/NewAnnotationExampleController.ts +159 -0
  94. package/template/src/controllers/example/SwaggerExampleController.ts +205 -0
  95. package/template/src/controllers/example/TransactionExample.controller.ts +336 -0
  96. package/template/src/controllers/health.controller.ts +235 -0
  97. package/template/src/controllers/home/register.controller.ts +58 -0
  98. package/template/src/controllers/home/ytGoods.controller.ts +92 -0
  99. package/template/src/controllers/home/ytShop.controller.ts +135 -0
  100. package/template/src/controllers/home/ytUser.controller.ts +89 -0
  101. package/template/src/controllers/logManagement.controller.ts +396 -0
  102. package/template/src/controllers/public/emailSend.controller.ts +65 -0
  103. package/template/src/controllers/public/ytUserAuth.controller.ts +174 -0
  104. package/template/src/controllers/testData.controller.ts +253 -0
  105. package/template/src/dto/controller/example/NewAnnotationExampleController.dto.ts +73 -0
  106. package/template/src/dto/controller/home/emailSend.controller.dto.ts +40 -0
  107. package/template/src/dto/controller/home/register.controller.dto.ts +45 -0
  108. package/template/src/dto/controller/home/ytGoods.controller.dto.ts +55 -0
  109. package/template/src/dto/controller/home/ytShop.controller.dto.ts +69 -0
  110. package/template/src/dto/controller/home/ytUser.controller.dto.ts +44 -0
  111. package/template/src/dto/controller/public/ytUserAuth.controller.dto.ts +63 -0
  112. package/template/src/dto/goods.dto.ts +212 -0
  113. package/template/src/dto/service/ytService.dto.ts +13 -0
  114. package/template/src/dto/user.dto.ts +177 -0
  115. package/template/src/entity/base.entity.ts +13 -0
  116. package/template/src/entity/columnTypes.ts +13 -0
  117. package/template/src/entity/goodsImagesUnlockKey.entity.ts +33 -0
  118. package/template/src/entity/goodsUnlocker.entity.ts +34 -0
  119. package/template/src/entity/index.ts +15 -0
  120. package/template/src/entity/shop.entity.ts +52 -0
  121. package/template/src/entity/shopUser.entity.ts +41 -0
  122. package/template/src/entity/ytGoods.entity.ts +94 -0
  123. package/template/src/entity/ytUser.entity.ts +96 -0
  124. package/template/src/examples/InterceptorExampleRunner.ts +284 -0
  125. package/template/src/examples/ServiceInterceptorExample.ts +214 -0
  126. package/template/src/examples/SwaggerProcessorExample.ts +169 -0
  127. package/template/src/examples/TransactionManagerDemo.ts +377 -0
  128. package/template/src/examples/cacheExamples.ts +155 -0
  129. package/template/src/framework/decorator/controller.ts +311 -0
  130. package/template/src/framework/decorator/processor/AnnotationDecorators.ts +100 -0
  131. package/template/src/framework/decorator/processor/AnnotationProcessor.ts +156 -0
  132. package/template/src/framework/decorator/processor/AnnotationProcessorConfig.ts +45 -0
  133. package/template/src/framework/decorator/processor/AnnotationRegistry.ts +117 -0
  134. package/template/src/framework/decorator/processor/AnnotationSystemInitializer.ts +95 -0
  135. package/template/src/framework/decorator/processor/ProcessorManager.ts +76 -0
  136. package/template/src/framework/decorator/processor/processors/CustomProcessors.ts +126 -0
  137. package/template/src/framework/decorator/processor/processors/DefaultProcessors.ts +207 -0
  138. package/template/src/framework/decorator/refactored/DecoratorFactory.ts +99 -0
  139. package/template/src/framework/decorator/refactored/DecoratorMetadataManager.ts +125 -0
  140. package/template/src/framework/decorator/refactored/DecoratorValidator.ts +128 -0
  141. package/template/src/framework/decorator/refactored/TypeSafeDecorators.ts +139 -0
  142. package/template/src/framework/decorator/refactored/index.ts +98 -0
  143. package/template/src/framework/decorator/swagger.ts +150 -0
  144. package/template/src/framework/interceptors/AdvancedServiceCallInterceptor.ts +375 -0
  145. package/template/src/framework/interceptors/ServiceCallInterceptor.ts +348 -0
  146. package/template/src/framework/interceptors/index.ts +19 -0
  147. package/template/src/framework/plugins/registry.ts +63 -0
  148. package/template/src/framework/plugins/types.ts +15 -0
  149. package/template/src/framework/types/ServiceResult.ts +151 -0
  150. package/template/src/framework/types/index.ts +16 -0
  151. package/template/src/framework/utils/CacheManager.ts +430 -0
  152. package/template/src/framework/utils/CacheService.ts +248 -0
  153. package/template/src/framework/utils/DtoValidator.ts +164 -0
  154. package/template/src/framework/utils/MigrationHelper.ts +179 -0
  155. package/template/src/framework/utils/MigrationManager.ts +256 -0
  156. package/template/src/framework/utils/NewRouter.ts +207 -0
  157. package/template/src/framework/utils/TransactionManager.ts +172 -0
  158. package/template/src/framework/utils/bootstrap.ts +445 -0
  159. package/template/src/framework/utils/cache.ts +269 -0
  160. package/template/src/framework/utils/databaseConfig.ts +148 -0
  161. package/template/src/framework/utils/db.ts +39 -0
  162. package/template/src/framework/utils/dbMonitor.ts +106 -0
  163. package/template/src/framework/utils/dynamicSwagger.ts +410 -0
  164. package/template/src/framework/utils/function.ts +61 -0
  165. package/template/src/framework/utils/gracefulShutdown.ts +131 -0
  166. package/template/src/framework/utils/logger.ts +388 -0
  167. package/template/src/framework/utils/metrics.ts +182 -0
  168. package/template/src/framework/utils/router.ts +417 -0
  169. package/template/src/framework/utils/swagger.ts +184 -0
  170. package/template/src/framework/utils/testDb.ts +19 -0
  171. package/template/src/framework/utils/token.ts +23 -0
  172. package/template/src/framework/utils/transform.ts +17 -0
  173. package/template/src/libs/aokEmailSender.ts +42 -0
  174. package/template/src/libs/captcha.ts +37 -0
  175. package/template/src/libs/cos.ts +45 -0
  176. package/template/src/libs/mCache.ts +7 -0
  177. package/template/src/libs/serviceValidate.ts +3 -0
  178. package/template/src/libs/tecentSms.ts +51 -0
  179. package/template/src/middlewares/a.middleware.ts +6 -0
  180. package/template/src/middlewares/error.middleware.ts +14 -0
  181. package/template/src/middlewares/logging.middleware.ts +187 -0
  182. package/template/src/middlewares/static.middleware.ts +79 -0
  183. package/template/src/middlewares/swagger.middleware.ts +70 -0
  184. package/template/src/middlewares/token.middleware.ts +32 -0
  185. package/template/src/migrations/1700000000000-InitialDatabaseStructure.ts +172 -0
  186. package/template/src/migrations/index.ts +6 -0
  187. package/template/src/plugins/weboffice/core/context.ts +47 -0
  188. package/template/src/plugins/weboffice/core/errors.ts +51 -0
  189. package/template/src/plugins/weboffice/core/types.ts +63 -0
  190. package/template/src/plugins/weboffice/core/utils.ts +7 -0
  191. package/template/src/plugins/weboffice/entities/index.ts +3 -0
  192. package/template/src/plugins/weboffice/entities/webofficeFile.entity.ts +28 -0
  193. package/template/src/plugins/weboffice/entities/webofficeFileVersion.entity.ts +29 -0
  194. package/template/src/plugins/weboffice/http/routes.ts +179 -0
  195. package/template/src/plugins/weboffice/index.ts +23 -0
  196. package/template/src/plugins/weboffice/services/webofficeCallback.service.ts +274 -0
  197. package/template/src/repository/UserRepository.ts +122 -0
  198. package/template/src/repository/base/BaseRepository.ts +124 -0
  199. package/template/src/repository/interfaces/IBaseRepository.ts +67 -0
  200. package/template/src/routers/index.ts +49 -0
  201. package/template/src/service/base.service.ts +116 -0
  202. package/template/src/service/paramValidateTest.service.ts +139 -0
  203. package/template/src/service/ytGoods.service.ts +42 -0
  204. package/template/src/service/ytShop.service.ts +90 -0
  205. package/template/src/service/ytUser.service.ts +451 -0
  206. package/template/src/test/swaggerParameterTest.ts +90 -0
  207. package/template/src/utils/testDataInitializer.ts +296 -0
  208. package/template/static/output.json +15203 -0
  209. package/template/test/controllers/controllers.test.ts +173 -0
  210. package/template/test/controllers/example/ExampleController.test.ts +222 -0
  211. package/template/test/controllers/example/NewAnnotationExampleController.test.ts +200 -0
  212. package/template/test/framework/TransactionManagerDemo.test.ts +363 -0
  213. package/template/test/framework/annotation/AnnotationDecorators.test.ts +222 -0
  214. package/template/test/framework/annotation/AnnotationExecutor.test.ts +246 -0
  215. package/template/test/framework/annotation/AnnotationProcessor.test.ts +179 -0
  216. package/template/test/framework/annotation/CustomProcessors.test.ts +313 -0
  217. package/template/test/framework/annotation/DefaultProcessors.test.ts +371 -0
  218. package/template/test/framework/annotation/NewRouter.test.ts +272 -0
  219. package/template/test/framework/annotation/ProcessorManager.test.ts +248 -0
  220. package/template/test/framework/annotation/setup.ts +26 -0
  221. package/template/test/framework/cache.test.ts +101 -0
  222. package/template/test/framework/databaseConfig.test.ts +142 -0
  223. package/template/test/integration/integration.test.ts +153 -0
  224. package/template/test/plugins/weboffice/http.routes.int.test.ts +61 -0
  225. package/template/test/service/business.test.ts +87 -0
  226. package/template/test/service/paramValidateTest.service.test.ts +184 -0
  227. package/template/test/service/ytUser.service.test.ts +566 -0
  228. package/template/test/setup.ts +20 -0
  229. package/template/test/setupAfterEnv.ts +14 -0
  230. package/template/test/utils/testHelpers.ts +220 -0
  231. package/template/test_output.txt +0 -0
  232. package/template/tsconfig.build.json +17 -0
  233. package/template/tsconfig.json +31 -0
  234. package/template/webpack.config.js +71 -0
  235. package/template/yarn.lock +7354 -0
@@ -0,0 +1,369 @@
1
+ /**
2
+ * 企业级配置管理注解处理器
3
+ * 提供动态配置、环境变量管理和配置验证
4
+ */
5
+
6
+ import { Context } from 'koa';
7
+ import { AnnotationProcessor } from '@src/framework/decorator/processor/AnnotationProcessor';
8
+ import { logger } from '@src/framework/utils/logger';
9
+ import { getRuntimeEnvironmentLabel } from '@src/framework/utils/function';
10
+
11
+ export interface ConfigManagementConfig {
12
+ configKey?: string;
13
+ defaultValue?: any;
14
+ required?: boolean;
15
+ validationRules?: ValidationRule[];
16
+ refreshInterval?: number;
17
+ environment?: 'development' | 'staging' | 'production';
18
+ fallbackConfig?: Record<string, any>;
19
+ }
20
+
21
+ export interface ValidationRule {
22
+ type: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'email' | 'url';
23
+ min?: number;
24
+ max?: number;
25
+ pattern?: RegExp;
26
+ custom?: (value: any) => boolean;
27
+ message?: string;
28
+ }
29
+
30
+ export interface ConfigValue {
31
+ key: string;
32
+ value: any;
33
+ source: 'environment' | 'database' | 'file' | 'default';
34
+ lastUpdated: Date;
35
+ environment: string;
36
+ validated: boolean;
37
+ }
38
+
39
+ export class ConfigManagementProcessor implements AnnotationProcessor {
40
+ readonly name = 'ConfigManagement';
41
+ readonly priority = 4;
42
+
43
+ private configCache: Map<string, ConfigValue> = new Map();
44
+ private validationRules: Map<string, ValidationRule[]> = new Map();
45
+ private refreshTimers: Map<string, NodeJS.Timeout> = new Map();
46
+ private configRefreshInterval: NodeJS.Timeout | null = null; // 存储定时器引用
47
+
48
+ constructor() {
49
+ this.loadEnvironmentConfigs();
50
+ this.startConfigRefresh();
51
+ }
52
+
53
+ async process(
54
+ ctx: Context,
55
+ controller: any,
56
+ methodName: string,
57
+ annotationData: ConfigManagementConfig,
58
+ callParams: any[]
59
+ ): Promise<boolean> {
60
+ const config = annotationData || {};
61
+
62
+ if (!config.configKey) return true;
63
+
64
+ try {
65
+ // 获取配置值
66
+ const configValue = await this.getConfigValue(config.configKey, config);
67
+
68
+ // 验证配置
69
+ if (config.validationRules) {
70
+ const isValid = this.validateConfig(config.configKey, configValue.value, config.validationRules);
71
+ if (!isValid) {
72
+ logger.error(`[${this.name}] 配置验证失败: ${config.configKey}`);
73
+ ctx.status = 500;
74
+ ctx.body = { error: 'Configuration validation failed' };
75
+ return false;
76
+ }
77
+ }
78
+
79
+ // 将配置注入到上下文
80
+ ctx.state.config = ctx.state.config || {};
81
+ ctx.state.config[config.configKey] = configValue.value;
82
+
83
+ logger.debug(`[${this.name}] 配置加载成功: ${config.configKey}`, {
84
+ value: this.sanitizeValue(configValue.value),
85
+ source: configValue.source
86
+ });
87
+
88
+ } catch (error) {
89
+ logger.error(`[${this.name}] 配置加载失败: ${config.configKey}`, error as Error);
90
+
91
+ if (config.required) {
92
+ ctx.status = 500;
93
+ ctx.body = { error: 'Required configuration missing' };
94
+ return false;
95
+ }
96
+ }
97
+
98
+ return true;
99
+ }
100
+
101
+ private async getConfigValue(key: string, config: ConfigManagementConfig): Promise<ConfigValue> {
102
+ // 检查缓存
103
+ const cached = this.configCache.get(key);
104
+ if (cached && this.isCacheValid(cached)) {
105
+ return cached;
106
+ }
107
+
108
+ let value: any;
109
+ let source: ConfigValue['source'] = 'default';
110
+
111
+ // 1. 尝试从环境变量获取
112
+ const envValue = process.env[key.toUpperCase()];
113
+ if (envValue !== undefined) {
114
+ value = this.parseValue(envValue);
115
+ source = 'environment';
116
+ }
117
+ // 2. 尝试从数据库获取
118
+ else if (await this.hasDatabaseConfig(key)) {
119
+ value = await this.getDatabaseConfig(key);
120
+ source = 'database';
121
+ }
122
+ // 3. 尝试从配置文件获取
123
+ else if (await this.hasFileConfig(key)) {
124
+ value = await this.getFileConfig(key);
125
+ source = 'file';
126
+ }
127
+ // 4. 使用默认值
128
+ else {
129
+ value = config.defaultValue;
130
+ source = 'default';
131
+ }
132
+
133
+ const configValue: ConfigValue = {
134
+ key,
135
+ value,
136
+ source,
137
+ lastUpdated: new Date(),
138
+ environment: config.environment || getRuntimeEnvironmentLabel(),
139
+ validated: false
140
+ };
141
+
142
+ // 缓存配置
143
+ this.configCache.set(key, configValue);
144
+
145
+ return configValue;
146
+ }
147
+
148
+ private parseValue(value: string): any {
149
+ // 尝试解析 JSON
150
+ try {
151
+ return JSON.parse(value);
152
+ } catch {
153
+ // 尝试解析布尔值
154
+ if (value.toLowerCase() === 'true') return true;
155
+ if (value.toLowerCase() === 'false') return false;
156
+
157
+ // 尝试解析数字
158
+ const num = Number(value);
159
+ if (!isNaN(num)) return num;
160
+
161
+ // 返回字符串
162
+ return value;
163
+ }
164
+ }
165
+
166
+ private validateConfig(key: string, value: any, rules: ValidationRule[]): boolean {
167
+ for (const rule of rules) {
168
+ if (!this.validateRule(value, rule)) {
169
+ logger.warn(`[${this.name}] 配置验证失败: ${key}`, {
170
+ value: this.sanitizeValue(value),
171
+ rule: rule.type,
172
+ message: rule.message
173
+ });
174
+ return false;
175
+ }
176
+ }
177
+ return true;
178
+ }
179
+
180
+ private validateRule(value: any, rule: ValidationRule): boolean {
181
+ switch (rule.type) {
182
+ case 'string':
183
+ if (typeof value !== 'string') return false;
184
+ if (rule.min && value.length < rule.min) return false;
185
+ if (rule.max && value.length > rule.max) return false;
186
+ if (rule.pattern && !rule.pattern.test(value)) return false;
187
+ break;
188
+
189
+ case 'number':
190
+ if (typeof value !== 'number') return false;
191
+ if (rule.min && value < rule.min) return false;
192
+ if (rule.max && value > rule.max) return false;
193
+ break;
194
+
195
+ case 'boolean':
196
+ if (typeof value !== 'boolean') return false;
197
+ break;
198
+
199
+ case 'array':
200
+ if (!Array.isArray(value)) return false;
201
+ if (rule.min && value.length < rule.min) return false;
202
+ if (rule.max && value.length > rule.max) return false;
203
+ break;
204
+
205
+ case 'object':
206
+ if (typeof value !== 'object' || value === null) return false;
207
+ break;
208
+
209
+ case 'email':
210
+ if (typeof value !== 'string' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) return false;
211
+ break;
212
+
213
+ case 'url':
214
+ if (typeof value !== 'string' || !/^https?:\/\/.+/.test(value)) return false;
215
+ break;
216
+ }
217
+
218
+ // 自定义验证
219
+ if (rule.custom && !rule.custom(value)) return false;
220
+
221
+ return true;
222
+ }
223
+
224
+ private async hasDatabaseConfig(key: string): Promise<boolean> {
225
+ // 这里应该实现数据库配置检查
226
+ return false;
227
+ }
228
+
229
+ private async getDatabaseConfig(key: string): Promise<any> {
230
+ // 这里应该实现数据库配置获取
231
+ return null;
232
+ }
233
+
234
+ private async hasFileConfig(key: string): Promise<boolean> {
235
+ // 这里应该实现文件配置检查
236
+ return false;
237
+ }
238
+
239
+ private async getFileConfig(key: string): Promise<any> {
240
+ // 这里应该实现文件配置获取
241
+ return null;
242
+ }
243
+
244
+ private isCacheValid(configValue: ConfigValue): boolean {
245
+ const maxAge = 5 * 60 * 1000; // 5分钟
246
+ return Date.now() - configValue.lastUpdated.getTime() < maxAge;
247
+ }
248
+
249
+ private sanitizeValue(value: any): any {
250
+ if (typeof value === 'string' && value.length > 100) {
251
+ return value.substring(0, 100) + '...';
252
+ }
253
+ return value;
254
+ }
255
+
256
+ private loadEnvironmentConfigs(): void {
257
+ // 加载环境变量配置
258
+ Object.keys(process.env).forEach(key => {
259
+ if (key.startsWith('CONFIG_')) {
260
+ const configKey = key.substring(7).toLowerCase();
261
+ const value = this.parseValue(process.env[key]!);
262
+
263
+ this.configCache.set(configKey, {
264
+ key: configKey,
265
+ value,
266
+ source: 'environment',
267
+ lastUpdated: new Date(),
268
+ environment: getRuntimeEnvironmentLabel(),
269
+ validated: false
270
+ });
271
+ }
272
+ });
273
+ }
274
+
275
+ private startConfigRefresh(): void {
276
+ // 如果已有定时器,先清除
277
+ if (this.configRefreshInterval) {
278
+ clearInterval(this.configRefreshInterval);
279
+ }
280
+
281
+ // 定期刷新配置
282
+ this.configRefreshInterval = setInterval(() => {
283
+ this.refreshConfigs();
284
+ }, 60000); // 每分钟刷新一次
285
+ }
286
+
287
+ /**
288
+ * 停止配置刷新定时器
289
+ * 防止内存泄露
290
+ */
291
+ stopConfigRefresh(): void {
292
+ if (this.configRefreshInterval) {
293
+ clearInterval(this.configRefreshInterval);
294
+ this.configRefreshInterval = null;
295
+ logger.info(`[${this.name}] 配置刷新定时器已停止`);
296
+ }
297
+ }
298
+
299
+ /**
300
+ * 清理所有定时器
301
+ */
302
+ cleanup(): void {
303
+ // 清理主刷新定时器
304
+ this.stopConfigRefresh();
305
+
306
+ // 清理所有独立的刷新定时器
307
+ this.refreshTimers.forEach((timer) => {
308
+ clearInterval(timer);
309
+ });
310
+ this.refreshTimers.clear();
311
+
312
+ // 清理缓存
313
+ this.configCache.clear();
314
+ this.validationRules.clear();
315
+
316
+ logger.info(`[${this.name}] 所有资源已清理`);
317
+ }
318
+
319
+ private async refreshConfigs(): Promise<void> {
320
+ const keys = Array.from(this.configCache.keys());
321
+
322
+ for (const key of keys) {
323
+ try {
324
+ const cached = this.configCache.get(key)!;
325
+ if (cached.source === 'environment') {
326
+ // 环境变量配置不需要刷新
327
+ continue;
328
+ }
329
+
330
+ // 重新加载配置
331
+ const newValue = await this.getConfigValue(key, {});
332
+ if (JSON.stringify(newValue.value) !== JSON.stringify(cached.value)) {
333
+ logger.info(`[${this.name}] 配置已更新: ${key}`);
334
+ }
335
+ } catch (error) {
336
+ logger.error(`[${this.name}] 配置刷新失败: ${key}`, error as Error);
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * 获取所有配置
343
+ */
344
+ getAllConfigs(): ConfigValue[] {
345
+ return Array.from(this.configCache.values());
346
+ }
347
+
348
+ /**
349
+ * 获取指定配置
350
+ */
351
+ getConfig(key: string): ConfigValue | undefined {
352
+ return this.configCache.get(key);
353
+ }
354
+
355
+ /**
356
+ * 设置配置验证规则
357
+ */
358
+ setValidationRules(key: string, rules: ValidationRule[]): void {
359
+ this.validationRules.set(key, rules);
360
+ }
361
+
362
+ /**
363
+ * 清理配置缓存
364
+ */
365
+ clearCache(): void {
366
+ this.configCache.clear();
367
+ logger.info(`[${this.name}] 配置缓存已清理`);
368
+ }
369
+ }
@@ -0,0 +1,288 @@
1
+ /**
2
+ * 企业级分布式追踪注解处理器
3
+ * 提供跨服务的请求追踪和性能分析
4
+ */
5
+
6
+ import { Context } from 'koa';
7
+ import { AnnotationProcessor } from '@src/framework/decorator/processor/AnnotationProcessor';
8
+ import { logger } from '@src/framework/utils/logger';
9
+
10
+ export interface TracingConfig {
11
+ enableTracing?: boolean;
12
+ traceIdHeader?: string;
13
+ spanIdHeader?: string;
14
+ parentSpanIdHeader?: string;
15
+ samplingRate?: number;
16
+ exportToJaeger?: boolean;
17
+ exportToZipkin?: boolean;
18
+ customTags?: Record<string, string>;
19
+ }
20
+
21
+ export interface TraceSpan {
22
+ traceId: string;
23
+ spanId: string;
24
+ parentSpanId?: string;
25
+ operationName: string;
26
+ startTime: number;
27
+ endTime?: number;
28
+ duration?: number;
29
+ tags: Record<string, any>;
30
+ logs: TraceLog[];
31
+ status: 'started' | 'completed' | 'error';
32
+ serviceName: string;
33
+ componentName: string;
34
+ }
35
+
36
+ export interface TraceLog {
37
+ timestamp: number;
38
+ level: 'info' | 'warn' | 'error';
39
+ message: string;
40
+ fields?: Record<string, any>;
41
+ }
42
+
43
+ export class DistributedTracingProcessor implements AnnotationProcessor {
44
+ readonly name = 'DistributedTracing';
45
+ readonly priority = 3;
46
+
47
+ private activeSpans: Map<string, TraceSpan> = new Map();
48
+ private completedSpans: TraceSpan[] = [];
49
+ private samplingRate: number = 1.0;
50
+
51
+ constructor() {
52
+ // 从环境变量读取采样率
53
+ this.samplingRate = parseFloat(process.env.TRACING_SAMPLING_RATE || '1.0');
54
+ }
55
+
56
+ async process(
57
+ ctx: Context,
58
+ controller: any,
59
+ methodName: string,
60
+ annotationData: TracingConfig,
61
+ callParams: any[]
62
+ ): Promise<boolean> {
63
+ const config = annotationData || {};
64
+
65
+ if (!config.enableTracing) return true;
66
+
67
+ // 决定是否采样
68
+ if (!this.shouldSample()) {
69
+ return true;
70
+ }
71
+
72
+ // 获取或创建追踪上下文
73
+ const traceContext = this.getOrCreateTraceContext(ctx, config);
74
+
75
+ // 创建新的 Span
76
+ const span = this.createSpan(traceContext, controller, methodName, config);
77
+
78
+ // 存储到上下文
79
+ ctx.state.traceSpan = span;
80
+ ctx.state.traceContext = traceContext;
81
+
82
+ // 添加追踪头到响应
83
+ ctx.set('X-Trace-Id', span.traceId);
84
+ ctx.set('X-Span-Id', span.spanId);
85
+
86
+ // 记录开始日志
87
+ this.addSpanLog(span, 'info', 'Span started', {
88
+ controller: controller.constructor.name,
89
+ method: methodName,
90
+ traceId: span.traceId,
91
+ spanId: span.spanId
92
+ });
93
+
94
+ logger.info(`[${this.name}] 开始追踪: ${span.operationName}`, {
95
+ traceId: span.traceId,
96
+ spanId: span.spanId,
97
+ parentSpanId: span.parentSpanId
98
+ });
99
+
100
+ return true;
101
+ }
102
+
103
+ async postProcess(
104
+ ctx: Context,
105
+ controller: any,
106
+ methodName: string,
107
+ response: any
108
+ ): Promise<void> {
109
+ const span = ctx.state.traceSpan;
110
+ if (!span) return;
111
+
112
+ // 完成 Span
113
+ span.endTime = Date.now();
114
+ span.duration = span.endTime - span.startTime;
115
+ span.status = 'completed';
116
+
117
+ // 添加响应信息到标签
118
+ span.tags['http.status_code'] = ctx.status;
119
+ span.tags['response.size'] = JSON.stringify(response).length;
120
+ span.tags['response.type'] = typeof response;
121
+
122
+ // 记录完成日志
123
+ this.addSpanLog(span, 'info', 'Span completed', {
124
+ duration: span.duration,
125
+ statusCode: ctx.status,
126
+ responseSize: span.tags['response.size']
127
+ });
128
+
129
+ // 移动到已完成列表
130
+ this.activeSpans.delete(span.spanId);
131
+ this.completedSpans.push(span);
132
+
133
+ // 导出到外部系统
134
+ await this.exportSpan(span);
135
+
136
+ logger.info(`[${this.name}] 完成追踪: ${span.operationName}`, {
137
+ traceId: span.traceId,
138
+ spanId: span.spanId,
139
+ duration: `${span.duration}ms`
140
+ });
141
+ }
142
+
143
+ private shouldSample(): boolean {
144
+ return Math.random() < this.samplingRate;
145
+ }
146
+
147
+ private getOrCreateTraceContext(ctx: Context, config: TracingConfig): any {
148
+ const traceIdHeader = config.traceIdHeader || 'X-Trace-Id';
149
+ const spanIdHeader = config.spanIdHeader || 'X-Span-Id';
150
+ const parentSpanIdHeader = config.parentSpanIdHeader || 'X-Parent-Span-Id';
151
+
152
+ // 尝试从请求头获取现有的追踪上下文
153
+ const traceId = ctx.get(traceIdHeader) || this.generateTraceId();
154
+ const parentSpanId = ctx.get(parentSpanIdHeader);
155
+
156
+ return {
157
+ traceId,
158
+ parentSpanId,
159
+ headers: {
160
+ traceIdHeader,
161
+ spanIdHeader,
162
+ parentSpanIdHeader
163
+ }
164
+ };
165
+ }
166
+
167
+ private createSpan(traceContext: any, controller: any, methodName: string, config: TracingConfig): TraceSpan {
168
+ const spanId = this.generateSpanId();
169
+ const operationName = `${controller.constructor.name}.${methodName}`;
170
+
171
+ const span: TraceSpan = {
172
+ traceId: traceContext.traceId,
173
+ spanId,
174
+ parentSpanId: traceContext.parentSpanId,
175
+ operationName,
176
+ startTime: Date.now(),
177
+ tags: {
178
+ 'component': 'controller',
179
+ 'controller.name': controller.constructor.name,
180
+ 'method.name': methodName,
181
+ 'service.name': process.env.SERVICE_NAME || 'koa-framework',
182
+ 'http.method': 'GET', // 这里应该从 ctx 获取
183
+ 'http.url': '', // 这里应该从 ctx 获取
184
+ ...config.customTags
185
+ },
186
+ logs: [],
187
+ status: 'started',
188
+ serviceName: process.env.SERVICE_NAME || 'koa-framework',
189
+ componentName: 'controller'
190
+ };
191
+
192
+ this.activeSpans.set(spanId, span);
193
+ return span;
194
+ }
195
+
196
+ private addSpanLog(span: TraceSpan, level: TraceLog['level'], message: string, fields?: Record<string, any>): void {
197
+ span.logs.push({
198
+ timestamp: Date.now(),
199
+ level,
200
+ message,
201
+ fields
202
+ });
203
+ }
204
+
205
+ private async exportSpan(span: TraceSpan): Promise<void> {
206
+ try {
207
+ // 导出到 Jaeger
208
+ if (process.env.JAEGER_ENDPOINT) {
209
+ await this.exportToJaeger(span);
210
+ }
211
+
212
+ // 导出到 Zipkin
213
+ if (process.env.ZIPKIN_ENDPOINT) {
214
+ await this.exportToZipkin(span);
215
+ }
216
+
217
+ // 导出到自定义系统
218
+ if (process.env.CUSTOM_TRACING_ENDPOINT) {
219
+ await this.exportToCustom(span);
220
+ }
221
+ } catch (error) {
222
+ logger.error(`[${this.name}] 导出追踪数据失败:`, error as Error);
223
+ }
224
+ }
225
+
226
+ private async exportToJaeger(span: TraceSpan): Promise<void> {
227
+ // 这里应该实现 Jaeger 导出逻辑
228
+ logger.debug(`[${this.name}] 导出到 Jaeger: ${span.traceId}`);
229
+ }
230
+
231
+ private async exportToZipkin(span: TraceSpan): Promise<void> {
232
+ // 这里应该实现 Zipkin 导出逻辑
233
+ logger.debug(`[${this.name}] 导出到 Zipkin: ${span.traceId}`);
234
+ }
235
+
236
+ private async exportToCustom(span: TraceSpan): Promise<void> {
237
+ // 这里应该实现自定义系统导出逻辑
238
+ logger.debug(`[${this.name}] 导出到自定义系统: ${span.traceId}`);
239
+ }
240
+
241
+ private generateTraceId(): string {
242
+ return Math.random().toString(36).substring(2, 15) +
243
+ Math.random().toString(36).substring(2, 15);
244
+ }
245
+
246
+ private generateSpanId(): string {
247
+ return Math.random().toString(36).substring(2, 15);
248
+ }
249
+
250
+ /**
251
+ * 获取追踪统计信息
252
+ */
253
+ getTracingStats(): any {
254
+ const activeCount = this.activeSpans.size;
255
+ const completedCount = this.completedSpans.length;
256
+
257
+ const avgDuration = completedCount > 0
258
+ ? this.completedSpans.reduce((sum, span) => sum + (span.duration || 0), 0) / completedCount
259
+ : 0;
260
+
261
+ return {
262
+ activeSpans: activeCount,
263
+ completedSpans: completedCount,
264
+ averageDuration: avgDuration,
265
+ samplingRate: this.samplingRate
266
+ };
267
+ }
268
+
269
+ /**
270
+ * 获取指定追踪的所有 Span
271
+ */
272
+ getTraceSpans(traceId: string): TraceSpan[] {
273
+ return this.completedSpans.filter(span => span.traceId === traceId);
274
+ }
275
+
276
+ /**
277
+ * 清理过期的 Span
278
+ */
279
+ cleanupExpiredSpans(maxAge: number = 3600000): void { // 1小时
280
+ const cutoffTime = Date.now() - maxAge;
281
+
282
+ this.completedSpans = this.completedSpans.filter(span =>
283
+ span.startTime > cutoffTime
284
+ );
285
+
286
+ logger.info(`[${this.name}] 清理过期 Span 完成,保留 ${this.completedSpans.length} 个 Span`);
287
+ }
288
+ }