create-dp-koa 1.1.1 → 1.1.3
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.
- package/package.json +1 -1
- package/template/scripts/sync-template.mjs +21 -0
- package/template/src/app.ts +1 -2
- package/template/src/{framework/plugins → plugins}/registry.ts +2 -2
- package/template/src/plugins/weboffice/http/routes.ts +2 -2
- package/template/src/plugins/weboffice/index.ts +3 -3
- package/template/src/plugins/weboffice/services/webofficeCallback.service.ts +3 -4
- package/template/src/utils/testDataInitializer.ts +1 -1
- package/template/tsconfig.json +6 -0
- package/template/src/annotations/decorators/ConfigManagement.ts +0 -9
- package/template/src/annotations/decorators/DistributedTracing.ts +0 -9
- package/template/src/annotations/decorators/EnterprisePerformance.ts +0 -9
- package/template/src/annotations/decorators/PerformanceMonitor.ts +0 -32
- package/template/src/annotations/decorators/SecurityAudit.ts +0 -9
- package/template/src/annotations/index.ts +0 -50
- package/template/src/annotations/processors/ConfigManagementProcessor.ts +0 -369
- package/template/src/annotations/processors/DistributedTracingProcessor.ts +0 -288
- package/template/src/annotations/processors/EnterprisePerformanceProcessor.ts +0 -189
- package/template/src/annotations/processors/PerformanceMonitorProcessor.ts +0 -101
- package/template/src/annotations/processors/SecurityAuditProcessor.ts +0 -345
- package/template/src/annotations/processors/SwaggerProcessor.ts +0 -612
- package/template/src/annotations/processors/index.ts +0 -10
- package/template/src/examples/InterceptorExampleRunner.ts +0 -284
- package/template/src/examples/ServiceInterceptorExample.ts +0 -214
- package/template/src/examples/cacheExamples.ts +0 -155
- package/template/src/framework/decorator/controller.ts +0 -311
- package/template/src/framework/decorator/processor/AnnotationDecorators.ts +0 -100
- package/template/src/framework/decorator/processor/AnnotationProcessor.ts +0 -160
- package/template/src/framework/decorator/processor/AnnotationProcessorConfig.ts +0 -45
- package/template/src/framework/decorator/processor/AnnotationRegistry.ts +0 -117
- package/template/src/framework/decorator/processor/AnnotationSystemInitializer.ts +0 -95
- package/template/src/framework/decorator/processor/ProcessorManager.ts +0 -76
- package/template/src/framework/decorator/processor/processors/CustomProcessors.ts +0 -126
- package/template/src/framework/decorator/processor/processors/DefaultProcessors.ts +0 -207
- package/template/src/framework/decorator/refactored/DecoratorFactory.ts +0 -99
- package/template/src/framework/decorator/refactored/DecoratorMetadataManager.ts +0 -125
- package/template/src/framework/decorator/refactored/DecoratorValidator.ts +0 -128
- package/template/src/framework/decorator/refactored/TypeSafeDecorators.ts +0 -139
- package/template/src/framework/decorator/refactored/index.ts +0 -98
- package/template/src/framework/decorator/swagger.ts +0 -150
- package/template/src/framework/interceptors/AdvancedServiceCallInterceptor.ts +0 -375
- package/template/src/framework/interceptors/ServiceCallInterceptor.ts +0 -348
- package/template/src/framework/interceptors/index.ts +0 -19
- package/template/src/framework/plugins/types.ts +0 -15
- package/template/src/framework/types/ServiceResult.ts +0 -151
- package/template/src/framework/types/index.ts +0 -16
- package/template/src/framework/utils/CacheManager.ts +0 -430
- package/template/src/framework/utils/CacheService.ts +0 -248
- package/template/src/framework/utils/DtoValidator.ts +0 -164
- package/template/src/framework/utils/MigrationHelper.ts +0 -179
- package/template/src/framework/utils/MigrationManager.ts +0 -256
- package/template/src/framework/utils/NewRouter.ts +0 -207
- package/template/src/framework/utils/TransactionManager.ts +0 -172
- package/template/src/framework/utils/bootstrap.ts +0 -445
- package/template/src/framework/utils/cache.ts +0 -269
- package/template/src/framework/utils/databaseConfig.ts +0 -148
- package/template/src/framework/utils/db.ts +0 -39
- package/template/src/framework/utils/dbMonitor.ts +0 -106
- package/template/src/framework/utils/function.ts +0 -61
- package/template/src/framework/utils/gracefulShutdown.ts +0 -131
- package/template/src/framework/utils/logger.ts +0 -388
- package/template/src/framework/utils/metrics.ts +0 -182
- package/template/src/framework/utils/router.ts +0 -417
- package/template/src/framework/utils/swagger.ts +0 -184
- package/template/src/framework/utils/testDb.ts +0 -19
- package/template/src/framework/utils/token.ts +0 -23
- package/template/src/framework/utils/transform.ts +0 -17
- package/template/src/libs/aokEmailSender.ts +0 -42
- package/template/src/libs/captcha.ts +0 -37
- package/template/src/libs/cos.ts +0 -45
- package/template/src/libs/mCache.ts +0 -7
- package/template/src/libs/serviceValidate.ts +0 -3
- package/template/src/libs/tecentSms.ts +0 -51
- package/template/src/middlewares/a.middleware.ts +0 -6
- package/template/src/middlewares/error.middleware.ts +0 -14
- package/template/src/middlewares/logging.middleware.ts +0 -187
- package/template/src/middlewares/static.middleware.ts +0 -79
- package/template/src/middlewares/swagger.middleware.ts +0 -70
- package/template/src/middlewares/token.middleware.ts +0 -32
- package/template/src/migrations/1700000000000-InitialDatabaseStructure.ts +0 -172
- package/template/src/migrations/index.ts +0 -6
- package/template/src/repository/base/BaseRepository.ts +0 -124
- package/template/src/repository/interfaces/IBaseRepository.ts +0 -67
- package/template/src/service/base.service.ts +0 -116
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { ValidationError, validate } from 'class-validator';
|
|
2
|
-
import { plainToClass } from 'class-transformer';
|
|
3
|
-
import { logger } from '@src/framework/utils/logger';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 验证结果接口
|
|
7
|
-
*/
|
|
8
|
-
export interface ValidationResult {
|
|
9
|
-
isValid: boolean;
|
|
10
|
-
errors: string[];
|
|
11
|
-
data?: any;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* DTO验证器
|
|
16
|
-
* 提供企业级数据验证功能
|
|
17
|
-
*/
|
|
18
|
-
export class DtoValidator {
|
|
19
|
-
/**
|
|
20
|
-
* 验证DTO对象
|
|
21
|
-
* @param dtoClass DTO类
|
|
22
|
-
* @param data 要验证的数据
|
|
23
|
-
* @param skipMissingProperties 是否跳过缺失属性
|
|
24
|
-
*/
|
|
25
|
-
static async validate<T>(
|
|
26
|
-
dtoClass: new () => T,
|
|
27
|
-
data: any,
|
|
28
|
-
skipMissingProperties: boolean = false
|
|
29
|
-
): Promise<ValidationResult> {
|
|
30
|
-
try {
|
|
31
|
-
// 将普通对象转换为DTO类实例
|
|
32
|
-
const dto = plainToClass(dtoClass, data);
|
|
33
|
-
|
|
34
|
-
// 执行验证
|
|
35
|
-
const errors = await validate(dto as any, {
|
|
36
|
-
skipMissingProperties,
|
|
37
|
-
whitelist: true, // 只允许白名单中的属性
|
|
38
|
-
forbidNonWhitelisted: true, // 禁止非白名单属性
|
|
39
|
-
transform: true, // 自动转换类型
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (errors.length === 0) {
|
|
43
|
-
return {
|
|
44
|
-
isValid: true,
|
|
45
|
-
errors: [],
|
|
46
|
-
data: dto
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 提取错误信息
|
|
51
|
-
const errorMessages = this.extractErrorMessages(errors);
|
|
52
|
-
|
|
53
|
-
logger.warn('DTO验证失败:', {
|
|
54
|
-
dtoClass: dtoClass.name,
|
|
55
|
-
errors: errorMessages,
|
|
56
|
-
data: data
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
isValid: false,
|
|
61
|
-
errors: errorMessages,
|
|
62
|
-
data: null
|
|
63
|
-
};
|
|
64
|
-
} catch (error) {
|
|
65
|
-
logger.error('DTO验证过程中发生错误:', error as Error);
|
|
66
|
-
return {
|
|
67
|
-
isValid: false,
|
|
68
|
-
errors: ['验证过程中发生内部错误'],
|
|
69
|
-
data: null
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 提取验证错误信息
|
|
76
|
-
* @param errors 验证错误数组
|
|
77
|
-
*/
|
|
78
|
-
private static extractErrorMessages(errors: ValidationError[]): string[] {
|
|
79
|
-
const messages: string[] = [];
|
|
80
|
-
|
|
81
|
-
for (const error of errors) {
|
|
82
|
-
if (error.constraints) {
|
|
83
|
-
// 添加约束错误信息
|
|
84
|
-
messages.push(...Object.values(error.constraints));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (error.children && error.children.length > 0) {
|
|
88
|
-
// 递归处理嵌套错误
|
|
89
|
-
messages.push(...this.extractErrorMessages(error.children));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return messages;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* 验证并转换DTO
|
|
98
|
-
* @param dtoClass DTO类
|
|
99
|
-
* @param data 要验证的数据
|
|
100
|
-
*/
|
|
101
|
-
static async validateAndTransform<T>(
|
|
102
|
-
dtoClass: new () => T,
|
|
103
|
-
data: any
|
|
104
|
-
): Promise<ValidationResult> {
|
|
105
|
-
return await this.validate(dtoClass, data, false);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 验证部分DTO(允许缺失属性)
|
|
110
|
-
* @param dtoClass DTO类
|
|
111
|
-
* @param data 要验证的数据
|
|
112
|
-
*/
|
|
113
|
-
static async validatePartial<T>(
|
|
114
|
-
dtoClass: new () => T,
|
|
115
|
-
data: any
|
|
116
|
-
): Promise<ValidationResult> {
|
|
117
|
-
return await this.validate(dtoClass, data, true);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 批量验证DTO
|
|
122
|
-
* @param validations 验证配置数组
|
|
123
|
-
*/
|
|
124
|
-
static async validateBatch(
|
|
125
|
-
validations: Array<{
|
|
126
|
-
dtoClass: new () => any;
|
|
127
|
-
data: any;
|
|
128
|
-
skipMissingProperties?: boolean;
|
|
129
|
-
}>
|
|
130
|
-
): Promise<ValidationResult[]> {
|
|
131
|
-
const results: ValidationResult[] = [];
|
|
132
|
-
|
|
133
|
-
for (const validation of validations) {
|
|
134
|
-
const result = await this.validate(
|
|
135
|
-
validation.dtoClass,
|
|
136
|
-
validation.data,
|
|
137
|
-
validation.skipMissingProperties || false
|
|
138
|
-
);
|
|
139
|
-
results.push(result);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return results;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* 检查验证结果是否全部通过
|
|
147
|
-
* @param results 验证结果数组
|
|
148
|
-
*/
|
|
149
|
-
static allValid(results: ValidationResult[]): boolean {
|
|
150
|
-
return results.every(result => result.isValid);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* 获取所有验证错误
|
|
155
|
-
* @param results 验证结果数组
|
|
156
|
-
*/
|
|
157
|
-
static getAllErrors(results: ValidationResult[]): string[] {
|
|
158
|
-
const allErrors: string[] = [];
|
|
159
|
-
for (const result of results) {
|
|
160
|
-
allErrors.push(...result.errors);
|
|
161
|
-
}
|
|
162
|
-
return allErrors;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { ProcessorManager } from '@src/framework/decorator/processor/ProcessorManager';
|
|
2
|
-
import { logger } from '@src/framework/utils/logger';
|
|
3
|
-
import { getRuntimeEnvironmentLabel } from '@src/framework/utils/function';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 迁移工具类
|
|
7
|
-
* 用于帮助从旧注解系统迁移到新注解处理器系统
|
|
8
|
-
*/
|
|
9
|
-
export class MigrationHelper {
|
|
10
|
-
private static initialized = false;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* 检查控制器是否使用了新注解系统
|
|
14
|
-
*/
|
|
15
|
-
static isUsingNewAnnotationSystem(controller: any): boolean {
|
|
16
|
-
return !!(controller.$_Annotations);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 检查控制器是否使用了旧注解系统
|
|
21
|
-
*/
|
|
22
|
-
static isUsingLegacyAnnotationSystem(controller: any): boolean {
|
|
23
|
-
return !!(controller.$_MethdosParamInfo ||
|
|
24
|
-
controller.$_ResponseValidator ||
|
|
25
|
-
controller.$_ControllerCache ||
|
|
26
|
-
controller.$_ResponseCode ||
|
|
27
|
-
controller.$_ResponseHeader);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 检查控制器是否使用了混合注解系统
|
|
32
|
-
*/
|
|
33
|
-
static isUsingMixedAnnotationSystem(controller: any): boolean {
|
|
34
|
-
return this.isUsingNewAnnotationSystem(controller) &&
|
|
35
|
-
this.isUsingLegacyAnnotationSystem(controller);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 初始化新注解系统
|
|
40
|
-
*/
|
|
41
|
-
static initializeNewSystem(): void {
|
|
42
|
-
if (!this.initialized) {
|
|
43
|
-
ProcessorManager.initialize();
|
|
44
|
-
this.initialized = true;
|
|
45
|
-
logger.info('新注解处理器系统已初始化');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 获取系统状态信息
|
|
51
|
-
*/
|
|
52
|
-
static getSystemStatus(): {
|
|
53
|
-
newSystemEnabled: boolean;
|
|
54
|
-
newSystemInitialized: boolean;
|
|
55
|
-
environment: string;
|
|
56
|
-
} {
|
|
57
|
-
return {
|
|
58
|
-
newSystemEnabled: process.env.USE_NEW_ANNOTATION_SYSTEM === '1',
|
|
59
|
-
newSystemInitialized: this.initialized,
|
|
60
|
-
environment: getRuntimeEnvironmentLabel()
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* 检查控制器兼容性
|
|
66
|
-
*/
|
|
67
|
-
static checkControllerCompatibility(controller: any): {
|
|
68
|
-
compatible: boolean;
|
|
69
|
-
issues: string[];
|
|
70
|
-
recommendations: string[];
|
|
71
|
-
} {
|
|
72
|
-
const issues: string[] = [];
|
|
73
|
-
const recommendations: string[] = [];
|
|
74
|
-
|
|
75
|
-
if (this.isUsingMixedAnnotationSystem(controller)) {
|
|
76
|
-
issues.push('控制器同时使用了新旧注解系统');
|
|
77
|
-
recommendations.push('建议完全迁移到新注解系统,移除旧注解');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (this.isUsingLegacyAnnotationSystem(controller) && !this.isUsingNewAnnotationSystem(controller)) {
|
|
81
|
-
recommendations.push('建议迁移到新注解系统以获得更好的功能和性能');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
compatible: issues.length === 0,
|
|
86
|
-
issues,
|
|
87
|
-
recommendations
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* 生成迁移报告
|
|
93
|
-
*/
|
|
94
|
-
static generateMigrationReport(controllers: any[]): {
|
|
95
|
-
total: number;
|
|
96
|
-
legacy: number;
|
|
97
|
-
new: number;
|
|
98
|
-
mixed: number;
|
|
99
|
-
compatible: number;
|
|
100
|
-
incompatible: number;
|
|
101
|
-
recommendations: string[];
|
|
102
|
-
} {
|
|
103
|
-
let legacy = 0;
|
|
104
|
-
let newSystem = 0;
|
|
105
|
-
let mixed = 0;
|
|
106
|
-
let compatible = 0;
|
|
107
|
-
let incompatible = 0;
|
|
108
|
-
const allRecommendations: string[] = [];
|
|
109
|
-
|
|
110
|
-
controllers.forEach(controller => {
|
|
111
|
-
const compatibility = this.checkControllerCompatibility(controller);
|
|
112
|
-
|
|
113
|
-
if (this.isUsingLegacyAnnotationSystem(controller) && !this.isUsingNewAnnotationSystem(controller)) {
|
|
114
|
-
legacy++;
|
|
115
|
-
} else if (this.isUsingNewAnnotationSystem(controller) && !this.isUsingLegacyAnnotationSystem(controller)) {
|
|
116
|
-
newSystem++;
|
|
117
|
-
} else if (this.isUsingMixedAnnotationSystem(controller)) {
|
|
118
|
-
mixed++;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (compatibility.compatible) {
|
|
122
|
-
compatible++;
|
|
123
|
-
} else {
|
|
124
|
-
incompatible++;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
allRecommendations.push(...compatibility.recommendations);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
total: controllers.length,
|
|
132
|
-
legacy,
|
|
133
|
-
new: newSystem,
|
|
134
|
-
mixed,
|
|
135
|
-
compatible,
|
|
136
|
-
incompatible,
|
|
137
|
-
recommendations: [...new Set(allRecommendations)] // 去重
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* 启用新注解系统
|
|
143
|
-
*/
|
|
144
|
-
static enableNewSystem(): void {
|
|
145
|
-
process.env.USE_NEW_ANNOTATION_SYSTEM = '1';
|
|
146
|
-
this.initializeNewSystem();
|
|
147
|
-
logger.info('新注解系统已启用');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* 禁用新注解系统
|
|
152
|
-
*/
|
|
153
|
-
static disableNewSystem(): void {
|
|
154
|
-
process.env.USE_NEW_ANNOTATION_SYSTEM = '0';
|
|
155
|
-
logger.info('新注解系统已禁用,使用旧系统');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 切换注解系统
|
|
160
|
-
*/
|
|
161
|
-
static toggleSystem(): boolean {
|
|
162
|
-
const currentState = process.env.USE_NEW_ANNOTATION_SYSTEM === '1';
|
|
163
|
-
if (currentState) {
|
|
164
|
-
this.disableNewSystem();
|
|
165
|
-
return false;
|
|
166
|
-
} else {
|
|
167
|
-
this.enableNewSystem();
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* 获取当前系统状态
|
|
174
|
-
*/
|
|
175
|
-
static getCurrentSystem(): 'legacy' | 'new' | 'mixed' {
|
|
176
|
-
const newSystemEnabled = process.env.USE_NEW_ANNOTATION_SYSTEM === '1';
|
|
177
|
-
return newSystemEnabled ? 'new' : 'legacy';
|
|
178
|
-
}
|
|
179
|
-
}
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import { DataSource, DataSourceOptions } from 'typeorm';
|
|
2
|
-
import { logger } from '@src/framework/utils/logger';
|
|
3
|
-
import { isDebug } from '@src/framework/utils/function';
|
|
4
|
-
import dbEntities from '@src/entity';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 数据库迁移管理器
|
|
8
|
-
* 提供企业级数据库迁移功能
|
|
9
|
-
*/
|
|
10
|
-
export class MigrationManager {
|
|
11
|
-
private static instance: MigrationManager;
|
|
12
|
-
private dataSource: DataSource | null = null;
|
|
13
|
-
|
|
14
|
-
private constructor() {}
|
|
15
|
-
|
|
16
|
-
static getInstance(): MigrationManager {
|
|
17
|
-
if (!MigrationManager.instance) {
|
|
18
|
-
MigrationManager.instance = new MigrationManager();
|
|
19
|
-
}
|
|
20
|
-
return MigrationManager.instance;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 设置数据源
|
|
25
|
-
*/
|
|
26
|
-
setDataSource(dataSource: DataSource): void {
|
|
27
|
-
this.dataSource = dataSource;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 获取生产环境数据库配置
|
|
32
|
-
* 关闭synchronize,启用迁移
|
|
33
|
-
*/
|
|
34
|
-
getProductionConfig(): DataSourceOptions {
|
|
35
|
-
return {
|
|
36
|
-
type: "mysql",
|
|
37
|
-
host: process.env.db_host || '127.0.0.1',
|
|
38
|
-
port: parseInt(process.env.db_port || "3306"),
|
|
39
|
-
username: process.env.db_username || 'root',
|
|
40
|
-
password: process.env.db_password || '',
|
|
41
|
-
database: process.env.db_database || 'production_db',
|
|
42
|
-
entities: dbEntities,
|
|
43
|
-
synchronize: false, // 生产环境关闭自动同步
|
|
44
|
-
logging: false,
|
|
45
|
-
migrations: ['src/migrations/*.ts'],
|
|
46
|
-
migrationsRun: false, // 手动控制迁移执行
|
|
47
|
-
migrationsTableName: 'migrations',
|
|
48
|
-
// 连接池配置
|
|
49
|
-
extra: {
|
|
50
|
-
connectionLimit: 20,
|
|
51
|
-
acquireTimeout: 60000,
|
|
52
|
-
timeout: 60000,
|
|
53
|
-
reconnect: true,
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 获取开发环境数据库配置
|
|
60
|
-
* 启用synchronize用于快速开发
|
|
61
|
-
*/
|
|
62
|
-
getDevelopmentConfig(): DataSourceOptions {
|
|
63
|
-
return {
|
|
64
|
-
type: "mysql",
|
|
65
|
-
host: process.env.db_host || '127.0.0.1',
|
|
66
|
-
port: parseInt(process.env.db_port || "3306"),
|
|
67
|
-
username: process.env.db_username || 'test',
|
|
68
|
-
password: process.env.db_password || 'test',
|
|
69
|
-
database: process.env.db_database || 'test',
|
|
70
|
-
entities: dbEntities,
|
|
71
|
-
synchronize: true, // 开发环境启用自动同步
|
|
72
|
-
logging: true,
|
|
73
|
-
migrations: ['src/migrations/*.ts'],
|
|
74
|
-
migrationsRun: false,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 获取测试环境数据库配置
|
|
80
|
-
*/
|
|
81
|
-
getTestConfig(): DataSourceOptions {
|
|
82
|
-
return {
|
|
83
|
-
type: "mysql",
|
|
84
|
-
host: process.env.db_host || '127.0.0.1',
|
|
85
|
-
port: parseInt(process.env.db_port || "3306"),
|
|
86
|
-
username: process.env.db_username || 'test',
|
|
87
|
-
password: process.env.db_password || 'test',
|
|
88
|
-
database: process.env.db_database || 'test',
|
|
89
|
-
entities: dbEntities,
|
|
90
|
-
synchronize: true, // 测试环境启用自动同步
|
|
91
|
-
logging: false,
|
|
92
|
-
dropSchema: true, // 每次测试重新创建表结构
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* 根据环境获取数据库配置
|
|
98
|
-
*/
|
|
99
|
-
getConfigByEnvironment(): DataSourceOptions {
|
|
100
|
-
// Jest 等测试仍用 NODE_ENV=test,避免与「非 debug=生产」冲突
|
|
101
|
-
if (process.env.NODE_ENV === 'test') {
|
|
102
|
-
return this.getTestConfig();
|
|
103
|
-
}
|
|
104
|
-
return !isDebug() ? this.getProductionConfig() : this.getDevelopmentConfig();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* 生成迁移文件
|
|
109
|
-
* @param name 迁移名称
|
|
110
|
-
*/
|
|
111
|
-
async generateMigration(name: string): Promise<void> {
|
|
112
|
-
if (!this.dataSource) {
|
|
113
|
-
throw new Error('数据源未初始化');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
const migrations = await this.dataSource.runMigrations();
|
|
118
|
-
logger.info(`生成迁移文件: ${name}`);
|
|
119
|
-
} catch (error) {
|
|
120
|
-
logger.error('生成迁移文件失败:', error as Error);
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* 执行待执行的迁移
|
|
127
|
-
*/
|
|
128
|
-
async runMigrations(): Promise<void> {
|
|
129
|
-
if (!this.dataSource) {
|
|
130
|
-
throw new Error('数据源未初始化');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
const migrations = await this.dataSource.runMigrations();
|
|
135
|
-
if (migrations.length > 0) {
|
|
136
|
-
logger.info(`执行了 ${migrations.length} 个迁移:`, migrations.map(m => m.name));
|
|
137
|
-
} else {
|
|
138
|
-
logger.info('没有待执行的迁移');
|
|
139
|
-
}
|
|
140
|
-
} catch (error) {
|
|
141
|
-
logger.error('执行迁移失败:', error as Error);
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* 回滚最后一个迁移
|
|
148
|
-
*/
|
|
149
|
-
async undoLastMigration(): Promise<void> {
|
|
150
|
-
if (!this.dataSource) {
|
|
151
|
-
throw new Error('数据源未初始化');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
await this.dataSource.undoLastMigration();
|
|
156
|
-
logger.info('回滚最后一个迁移成功');
|
|
157
|
-
} catch (error) {
|
|
158
|
-
logger.error('回滚迁移失败:', error as Error);
|
|
159
|
-
throw error;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* 获取迁移状态
|
|
165
|
-
*/
|
|
166
|
-
async getMigrationStatus(): Promise<{
|
|
167
|
-
executed: any[];
|
|
168
|
-
pending: any[];
|
|
169
|
-
}> {
|
|
170
|
-
if (!this.dataSource) {
|
|
171
|
-
throw new Error('数据源未初始化');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
const executedMigrations = await this.dataSource.query(
|
|
176
|
-
'SELECT * FROM migrations ORDER BY timestamp'
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
const pendingMigrations = await this.dataSource.showMigrations();
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
executed: executedMigrations,
|
|
183
|
-
pending: Array.isArray(pendingMigrations) ? pendingMigrations : []
|
|
184
|
-
};
|
|
185
|
-
} catch (error) {
|
|
186
|
-
logger.error('获取迁移状态失败:', error as Error);
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* 检查数据库连接
|
|
193
|
-
*/
|
|
194
|
-
async checkConnection(): Promise<boolean> {
|
|
195
|
-
if (!this.dataSource) {
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
await this.dataSource.query('SELECT 1');
|
|
201
|
-
return true;
|
|
202
|
-
} catch (error) {
|
|
203
|
-
logger.error('数据库连接检查失败:', error as Error);
|
|
204
|
-
return false;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* 创建迁移表(如果不存在)
|
|
210
|
-
*/
|
|
211
|
-
async createMigrationsTable(): Promise<void> {
|
|
212
|
-
if (!this.dataSource) {
|
|
213
|
-
throw new Error('数据源未初始化');
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
try {
|
|
217
|
-
await this.dataSource.query(`
|
|
218
|
-
CREATE TABLE IF NOT EXISTS migrations (
|
|
219
|
-
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
220
|
-
timestamp BIGINT NOT NULL,
|
|
221
|
-
name VARCHAR(255) NOT NULL,
|
|
222
|
-
UNIQUE KEY unique_migration (timestamp, name)
|
|
223
|
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
224
|
-
`);
|
|
225
|
-
logger.info('迁移表创建成功');
|
|
226
|
-
} catch (error) {
|
|
227
|
-
logger.error('创建迁移表失败:', error as Error);
|
|
228
|
-
throw error;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* 备份数据库(生产环境)
|
|
234
|
-
*/
|
|
235
|
-
async backupDatabase(): Promise<string> {
|
|
236
|
-
if (!this.dataSource) {
|
|
237
|
-
throw new Error('数据源未初始化');
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
241
|
-
const backupFileName = `backup_${timestamp}.sql`;
|
|
242
|
-
|
|
243
|
-
try {
|
|
244
|
-
// 这里应该调用实际的备份工具,如mysqldump
|
|
245
|
-
logger.info(`数据库备份开始: ${backupFileName}`);
|
|
246
|
-
// 实际实现需要根据具体环境配置
|
|
247
|
-
return backupFileName;
|
|
248
|
-
} catch (error) {
|
|
249
|
-
logger.error('数据库备份失败:', error as Error);
|
|
250
|
-
throw error;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// 导出单例实例
|
|
256
|
-
export const migrationManager = MigrationManager.getInstance();
|