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.
- package/README.md +50 -0
- package/index.mjs +97 -0
- package/package.json +33 -0
- package/template/.env.development +9 -0
- package/template/.env.production +12 -0
- package/template/.github/workflows/ci-cd.yml +182 -0
- package/template/.trae/documents/controller_development_plan.md +386 -0
- package/template/.trae/skills/00-backend-core.skill.md +50 -0
- package/template/.trae/skills/01-backend-skill-router.skill.md +55 -0
- package/template/.trae/skills/10-backend-api.skill.md +54 -0
- package/template/.trae/skills/11-backend-controller-recipes.skill.md +107 -0
- package/template/.trae/skills/20-backend-repository.skill.md +25 -0
- package/template/.trae/skills/21-backend-service.skill.md +135 -0
- package/template/.trae/skills/25-backend-comments-and-doc.skill.md +97 -0
- package/template/.trae/skills/30-backend-validation.skill.md +320 -0
- package/template/.trae/skills/40-backend-error-logging.skill.md +21 -0
- package/template/.trae/skills/50-backend-bootstrap-lifecycle.skill.md +90 -0
- package/template/.trae/skills/60-backend-router-registration.skill.md +71 -0
- package/template/.trae/skills/70-backend-middleware.skill.md +98 -0
- package/template/.trae/skills/80-backend-utils-and-libs.skill.md +90 -0
- package/template/.trae/skills/85-backend-plugins.rule.md +64 -0
- package/template/.trae/skills/90-backend-testing.skill.md +29 -0
- package/template/.trae/skills/README.md +49 -0
- package/template/.vscode/launch.json +38 -0
- package/template/.vscode/settings.json +1 -0
- package/template/Dockerfile +36 -0
- package/template/README.md +229 -0
- package/template/docker-compose.yml +135 -0
- package/template/docs/API_DOCUMENTATION.md +837 -0
- package/template/docs/ARCHITECTURE_REFACTOR.md +109 -0
- package/template/docs/CACHE_MIGRATION_GUIDE.md +142 -0
- package/template/docs/DEPLOYMENT_GUIDE.md +1062 -0
- package/template/docs/DEVELOPMENT_GUIDE.md +1097 -0
- package/template/docs/DOCUMENTATION_CLEANUP_REPORT.md +166 -0
- package/template/docs/DOCUMENTATION_COMPLETION_REPORT.md +223 -0
- package/template/docs/DOCUMENTATION_INDEX.md +294 -0
- package/template/docs/DOCUMENTATION_STRUCTURE.md +221 -0
- package/template/docs/ENTERPRISE_ANNOTATION_SYSTEM_GUIDE.md +2069 -0
- package/template/docs/ENTERPRISE_DATABASE_ARCHITECTURE.md +318 -0
- package/template/docs/ENTERPRISE_DEPLOYMENT_GUIDE.md +547 -0
- package/template/docs/ENTERPRISE_ERROR_HANDLING_GUIDE.md +357 -0
- package/template/docs/ENTERPRISE_LOGGING_SYSTEM_GUIDE.md +494 -0
- package/template/docs/ENVIRONMENT_CONFIG_EXAMPLE.md +69 -0
- package/template/docs/FINAL_IMPLEMENTATION_SUMMARY.md +206 -0
- package/template/docs/HEALTH_CHECK_ROUTE_FIX.md +134 -0
- package/template/docs/IMPLEMENTATION_CHECKLIST.md +204 -0
- package/template/docs/INSTALLATION_GUIDE.md +611 -0
- package/template/docs/INTERCEPTOR_TESTING_REPORT.md +226 -0
- package/template/docs/INTERCEPTOR_TESTING_SCRIPTS.md +143 -0
- package/template/docs/LOGGING_OPTIMIZATION_GUIDE.md +126 -0
- package/template/docs/MEMORY_DATABASE_GUIDE.md +212 -0
- package/template/docs/NEW_ROUTER_INTEGRATION_GUIDE.md +345 -0
- package/template/docs/NEW_ROUTER_INTEGRATION_SUMMARY.md +259 -0
- package/template/docs/NEW_ROUTER_USAGE_GUIDE.md +364 -0
- package/template/docs/QUICK_START.md +268 -0
- package/template/docs/ROUTE_SLASH_COMPATIBILITY_FIX.md +191 -0
- package/template/docs/SERVICE_INTERCEPTOR_GUIDE.md +243 -0
- package/template/docs/SERVICE_LAYER_INDEX.md +205 -0
- package/template/docs/SERVICE_PATTERN_GUIDE.md +270 -0
- package/template/docs/SERVICE_RETURN_VALUE_SPECIFICATION.md +466 -0
- package/template/docs/SWAGGER_DEBUG_MODE_GUIDE.md +80 -0
- package/template/docs/SWAGGER_INTEGRATION_GUIDE.md +416 -0
- package/template/docs/TRANSACTION_MANAGER_USAGE.md +360 -0
- package/template/docs/TROUBLESHOOTING.md +869 -0
- package/template/env.production.example +62 -0
- package/template/jest.config.js +34 -0
- package/template/package-lock.json +13240 -0
- package/template/package.json +119 -0
- package/template/patches/typeorm+0.3.25.patch +22 -0
- package/template/scripts/sync-template.mjs +84 -0
- package/template/scripts/test-annotation-system.sh +48 -0
- package/template/scripts/test-core-functionality.sh +28 -0
- package/template/src/annotations/decorators/ConfigManagement.ts +9 -0
- package/template/src/annotations/decorators/DistributedTracing.ts +9 -0
- package/template/src/annotations/decorators/EnterprisePerformance.ts +9 -0
- package/template/src/annotations/decorators/PerformanceMonitor.ts +32 -0
- package/template/src/annotations/decorators/SecurityAudit.ts +9 -0
- package/template/src/annotations/index.ts +50 -0
- package/template/src/annotations/processors/ConfigManagementProcessor.ts +369 -0
- package/template/src/annotations/processors/DistributedTracingProcessor.ts +288 -0
- package/template/src/annotations/processors/EnterprisePerformanceProcessor.ts +189 -0
- package/template/src/annotations/processors/PerformanceMonitorProcessor.ts +101 -0
- package/template/src/annotations/processors/SecurityAuditProcessor.ts +345 -0
- package/template/src/annotations/processors/SwaggerProcessor.ts +612 -0
- package/template/src/annotations/processors/index.ts +10 -0
- package/template/src/app.ts +123 -0
- package/template/src/controllers/base.controller.ts +41 -0
- package/template/src/controllers/cacheManagement.controller.ts +131 -0
- package/template/src/controllers/captcha.controller.ts +57 -0
- package/template/src/controllers/demo/AnnotationDemoController.ts +118 -0
- package/template/src/controllers/example/EnterpriseExampleController.ts +297 -0
- package/template/src/controllers/example/ExampleController.ts +110 -0
- package/template/src/controllers/example/NewAnnotationExampleController.ts +159 -0
- package/template/src/controllers/example/SwaggerExampleController.ts +205 -0
- package/template/src/controllers/example/TransactionExample.controller.ts +336 -0
- package/template/src/controllers/health.controller.ts +235 -0
- package/template/src/controllers/home/register.controller.ts +58 -0
- package/template/src/controllers/home/ytGoods.controller.ts +92 -0
- package/template/src/controllers/home/ytShop.controller.ts +135 -0
- package/template/src/controllers/home/ytUser.controller.ts +89 -0
- package/template/src/controllers/logManagement.controller.ts +396 -0
- package/template/src/controllers/public/emailSend.controller.ts +65 -0
- package/template/src/controllers/public/ytUserAuth.controller.ts +174 -0
- package/template/src/controllers/testData.controller.ts +253 -0
- package/template/src/dto/controller/example/NewAnnotationExampleController.dto.ts +73 -0
- package/template/src/dto/controller/home/emailSend.controller.dto.ts +40 -0
- package/template/src/dto/controller/home/register.controller.dto.ts +45 -0
- package/template/src/dto/controller/home/ytGoods.controller.dto.ts +55 -0
- package/template/src/dto/controller/home/ytShop.controller.dto.ts +69 -0
- package/template/src/dto/controller/home/ytUser.controller.dto.ts +44 -0
- package/template/src/dto/controller/public/ytUserAuth.controller.dto.ts +63 -0
- package/template/src/dto/goods.dto.ts +212 -0
- package/template/src/dto/service/ytService.dto.ts +13 -0
- package/template/src/dto/user.dto.ts +177 -0
- package/template/src/entity/base.entity.ts +13 -0
- package/template/src/entity/columnTypes.ts +13 -0
- package/template/src/entity/goodsImagesUnlockKey.entity.ts +33 -0
- package/template/src/entity/goodsUnlocker.entity.ts +34 -0
- package/template/src/entity/index.ts +15 -0
- package/template/src/entity/shop.entity.ts +52 -0
- package/template/src/entity/shopUser.entity.ts +41 -0
- package/template/src/entity/ytGoods.entity.ts +94 -0
- package/template/src/entity/ytUser.entity.ts +96 -0
- package/template/src/examples/InterceptorExampleRunner.ts +284 -0
- package/template/src/examples/ServiceInterceptorExample.ts +214 -0
- package/template/src/examples/SwaggerProcessorExample.ts +169 -0
- package/template/src/examples/TransactionManagerDemo.ts +377 -0
- package/template/src/examples/cacheExamples.ts +155 -0
- package/template/src/framework/decorator/controller.ts +311 -0
- package/template/src/framework/decorator/processor/AnnotationDecorators.ts +100 -0
- package/template/src/framework/decorator/processor/AnnotationProcessor.ts +156 -0
- package/template/src/framework/decorator/processor/AnnotationProcessorConfig.ts +45 -0
- package/template/src/framework/decorator/processor/AnnotationRegistry.ts +117 -0
- package/template/src/framework/decorator/processor/AnnotationSystemInitializer.ts +95 -0
- package/template/src/framework/decorator/processor/ProcessorManager.ts +76 -0
- package/template/src/framework/decorator/processor/processors/CustomProcessors.ts +126 -0
- package/template/src/framework/decorator/processor/processors/DefaultProcessors.ts +207 -0
- package/template/src/framework/decorator/refactored/DecoratorFactory.ts +99 -0
- package/template/src/framework/decorator/refactored/DecoratorMetadataManager.ts +125 -0
- package/template/src/framework/decorator/refactored/DecoratorValidator.ts +128 -0
- package/template/src/framework/decorator/refactored/TypeSafeDecorators.ts +139 -0
- package/template/src/framework/decorator/refactored/index.ts +98 -0
- package/template/src/framework/decorator/swagger.ts +150 -0
- package/template/src/framework/interceptors/AdvancedServiceCallInterceptor.ts +375 -0
- package/template/src/framework/interceptors/ServiceCallInterceptor.ts +348 -0
- package/template/src/framework/interceptors/index.ts +19 -0
- package/template/src/framework/plugins/registry.ts +63 -0
- package/template/src/framework/plugins/types.ts +15 -0
- package/template/src/framework/types/ServiceResult.ts +151 -0
- package/template/src/framework/types/index.ts +16 -0
- package/template/src/framework/utils/CacheManager.ts +430 -0
- package/template/src/framework/utils/CacheService.ts +248 -0
- package/template/src/framework/utils/DtoValidator.ts +164 -0
- package/template/src/framework/utils/MigrationHelper.ts +179 -0
- package/template/src/framework/utils/MigrationManager.ts +256 -0
- package/template/src/framework/utils/NewRouter.ts +207 -0
- package/template/src/framework/utils/TransactionManager.ts +172 -0
- package/template/src/framework/utils/bootstrap.ts +445 -0
- package/template/src/framework/utils/cache.ts +269 -0
- package/template/src/framework/utils/databaseConfig.ts +148 -0
- package/template/src/framework/utils/db.ts +39 -0
- package/template/src/framework/utils/dbMonitor.ts +106 -0
- package/template/src/framework/utils/dynamicSwagger.ts +410 -0
- package/template/src/framework/utils/function.ts +61 -0
- package/template/src/framework/utils/gracefulShutdown.ts +131 -0
- package/template/src/framework/utils/logger.ts +388 -0
- package/template/src/framework/utils/metrics.ts +182 -0
- package/template/src/framework/utils/router.ts +417 -0
- package/template/src/framework/utils/swagger.ts +184 -0
- package/template/src/framework/utils/testDb.ts +19 -0
- package/template/src/framework/utils/token.ts +23 -0
- package/template/src/framework/utils/transform.ts +17 -0
- package/template/src/libs/aokEmailSender.ts +42 -0
- package/template/src/libs/captcha.ts +37 -0
- package/template/src/libs/cos.ts +45 -0
- package/template/src/libs/mCache.ts +7 -0
- package/template/src/libs/serviceValidate.ts +3 -0
- package/template/src/libs/tecentSms.ts +51 -0
- package/template/src/middlewares/a.middleware.ts +6 -0
- package/template/src/middlewares/error.middleware.ts +14 -0
- package/template/src/middlewares/logging.middleware.ts +187 -0
- package/template/src/middlewares/static.middleware.ts +79 -0
- package/template/src/middlewares/swagger.middleware.ts +70 -0
- package/template/src/middlewares/token.middleware.ts +32 -0
- package/template/src/migrations/1700000000000-InitialDatabaseStructure.ts +172 -0
- package/template/src/migrations/index.ts +6 -0
- package/template/src/plugins/weboffice/core/context.ts +47 -0
- package/template/src/plugins/weboffice/core/errors.ts +51 -0
- package/template/src/plugins/weboffice/core/types.ts +63 -0
- package/template/src/plugins/weboffice/core/utils.ts +7 -0
- package/template/src/plugins/weboffice/entities/index.ts +3 -0
- package/template/src/plugins/weboffice/entities/webofficeFile.entity.ts +28 -0
- package/template/src/plugins/weboffice/entities/webofficeFileVersion.entity.ts +29 -0
- package/template/src/plugins/weboffice/http/routes.ts +179 -0
- package/template/src/plugins/weboffice/index.ts +23 -0
- package/template/src/plugins/weboffice/services/webofficeCallback.service.ts +274 -0
- package/template/src/repository/UserRepository.ts +122 -0
- package/template/src/repository/base/BaseRepository.ts +124 -0
- package/template/src/repository/interfaces/IBaseRepository.ts +67 -0
- package/template/src/routers/index.ts +49 -0
- package/template/src/service/base.service.ts +116 -0
- package/template/src/service/paramValidateTest.service.ts +139 -0
- package/template/src/service/ytGoods.service.ts +42 -0
- package/template/src/service/ytShop.service.ts +90 -0
- package/template/src/service/ytUser.service.ts +451 -0
- package/template/src/test/swaggerParameterTest.ts +90 -0
- package/template/src/utils/testDataInitializer.ts +296 -0
- package/template/static/output.json +15203 -0
- package/template/test/controllers/controllers.test.ts +173 -0
- package/template/test/controllers/example/ExampleController.test.ts +222 -0
- package/template/test/controllers/example/NewAnnotationExampleController.test.ts +200 -0
- package/template/test/framework/TransactionManagerDemo.test.ts +363 -0
- package/template/test/framework/annotation/AnnotationDecorators.test.ts +222 -0
- package/template/test/framework/annotation/AnnotationExecutor.test.ts +246 -0
- package/template/test/framework/annotation/AnnotationProcessor.test.ts +179 -0
- package/template/test/framework/annotation/CustomProcessors.test.ts +313 -0
- package/template/test/framework/annotation/DefaultProcessors.test.ts +371 -0
- package/template/test/framework/annotation/NewRouter.test.ts +272 -0
- package/template/test/framework/annotation/ProcessorManager.test.ts +248 -0
- package/template/test/framework/annotation/setup.ts +26 -0
- package/template/test/framework/cache.test.ts +101 -0
- package/template/test/framework/databaseConfig.test.ts +142 -0
- package/template/test/integration/integration.test.ts +153 -0
- package/template/test/plugins/weboffice/http.routes.int.test.ts +61 -0
- package/template/test/service/business.test.ts +87 -0
- package/template/test/service/paramValidateTest.service.test.ts +184 -0
- package/template/test/service/ytUser.service.test.ts +566 -0
- package/template/test/setup.ts +20 -0
- package/template/test/setupAfterEnv.ts +14 -0
- package/template/test/utils/testHelpers.ts +220 -0
- package/template/test_output.txt +0 -0
- package/template/tsconfig.build.json +17 -0
- package/template/tsconfig.json +31 -0
- package/template/webpack.config.js +71 -0
- package/template/yarn.lock +7354 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TransactionManager Demo 单元测试
|
|
3
|
+
* 测试各种事务管理器的使用场景
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { TestDatabaseHelper } from '../utils/testHelpers';
|
|
7
|
+
import { transactionManager } from '../../src/framework/utils/TransactionManager';
|
|
8
|
+
import { YtUserEntity } from '../../src/entity/ytUser.entity';
|
|
9
|
+
import { getDataSource } from '../../src/framework/utils/db';
|
|
10
|
+
|
|
11
|
+
describe('TransactionManager Demo 测试', () => {
|
|
12
|
+
let dataSource: any;
|
|
13
|
+
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
await TestDatabaseHelper.initTestDatabase();
|
|
16
|
+
dataSource = getDataSource();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(async () => {
|
|
20
|
+
await TestDatabaseHelper.cleanupTestDatabase();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
await TestDatabaseHelper.resetTestData();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('示例 1: 基本事务', () => {
|
|
28
|
+
test('应该能够成功执行基本事务', async () => {
|
|
29
|
+
const result = await transactionManager.executeInTransaction(async (manager) => {
|
|
30
|
+
const user = new YtUserEntity();
|
|
31
|
+
user.email = 'test@example.com';
|
|
32
|
+
user.telnumber = '13800138000';
|
|
33
|
+
user.password = 'hashed';
|
|
34
|
+
|
|
35
|
+
return await manager.save(user);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(result).toBeDefined();
|
|
39
|
+
expect(result.email).toBe('test@example.com');
|
|
40
|
+
expect(result.telnumber).toBe('13800138000');
|
|
41
|
+
|
|
42
|
+
// 验证数据已保存到数据库
|
|
43
|
+
const savedUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
44
|
+
where: { email: 'test@example.com' }
|
|
45
|
+
});
|
|
46
|
+
expect(savedUser).toBeDefined();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('事务失败时应该自动回滚', async () => {
|
|
50
|
+
await expect(
|
|
51
|
+
transactionManager.executeInTransaction(async (manager) => {
|
|
52
|
+
const user = new YtUserEntity();
|
|
53
|
+
user.email = 'test-rollback@example.com';
|
|
54
|
+
user.telnumber = '13800138001';
|
|
55
|
+
user.password = 'hashed';
|
|
56
|
+
await manager.save(user);
|
|
57
|
+
|
|
58
|
+
// 故意抛出错误
|
|
59
|
+
throw new Error('模拟错误');
|
|
60
|
+
})
|
|
61
|
+
).rejects.toThrow('模拟错误');
|
|
62
|
+
|
|
63
|
+
// 验证数据已被回滚(未保存到数据库)
|
|
64
|
+
const savedUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
65
|
+
where: { email: 'test-rollback@example.com' }
|
|
66
|
+
});
|
|
67
|
+
expect(savedUser).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('示例 2: 嵌套事务', () => {
|
|
72
|
+
test('应该能够处理嵌套事务', async () => {
|
|
73
|
+
const result = await transactionManager.executeInTransaction(async (manager) => {
|
|
74
|
+
// 外层事务:创建主用户
|
|
75
|
+
const mainUser = new YtUserEntity();
|
|
76
|
+
mainUser.email = 'main-user@example.com';
|
|
77
|
+
mainUser.telnumber = '13800138020';
|
|
78
|
+
mainUser.password = 'hashed';
|
|
79
|
+
const savedUser = await manager.save(mainUser);
|
|
80
|
+
|
|
81
|
+
// 嵌套事务:创建相关用户
|
|
82
|
+
await transactionManager.executeNestedTransaction(async (nestedManager) => {
|
|
83
|
+
const relatedUser = new YtUserEntity();
|
|
84
|
+
relatedUser.email = 'related-user@example.com';
|
|
85
|
+
relatedUser.telnumber = '13800138021';
|
|
86
|
+
relatedUser.password = 'hashed';
|
|
87
|
+
await nestedManager.save(relatedUser);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return savedUser;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(result).toBeDefined();
|
|
94
|
+
expect(result.email).toBe('main-user@example.com');
|
|
95
|
+
|
|
96
|
+
// 验证所有数据都已保存
|
|
97
|
+
const mainUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
98
|
+
where: { email: 'main-user@example.com' }
|
|
99
|
+
});
|
|
100
|
+
const relatedUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
101
|
+
where: { email: 'related-user@example.com' }
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(mainUser).toBeDefined();
|
|
105
|
+
expect(relatedUser).toBeDefined();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('嵌套事务失败应该回滚外层事务', async () => {
|
|
109
|
+
await expect(
|
|
110
|
+
transactionManager.executeInTransaction(async (manager) => {
|
|
111
|
+
// 外层事务
|
|
112
|
+
const mainUser = new YtUserEntity();
|
|
113
|
+
mainUser.email = 'main-fail@example.com';
|
|
114
|
+
mainUser.telnumber = '13800138022';
|
|
115
|
+
mainUser.password = 'hashed';
|
|
116
|
+
await manager.save(mainUser);
|
|
117
|
+
|
|
118
|
+
// 嵌套事务:故意失败
|
|
119
|
+
await transactionManager.executeNestedTransaction(async (nestedManager) => {
|
|
120
|
+
throw new Error('嵌套事务失败');
|
|
121
|
+
});
|
|
122
|
+
})
|
|
123
|
+
).rejects.toThrow('嵌套事务失败');
|
|
124
|
+
|
|
125
|
+
// 验证外层事务也被回滚
|
|
126
|
+
const savedUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
127
|
+
where: { email: 'main-fail@example.com' }
|
|
128
|
+
});
|
|
129
|
+
expect(savedUser).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('示例 3: 批量操作事务', () => {
|
|
134
|
+
test('应该能够批量创建用户', async () => {
|
|
135
|
+
const operations = [
|
|
136
|
+
async (manager: any) => {
|
|
137
|
+
const user = new YtUserEntity();
|
|
138
|
+
user.email = 'batch-1@example.com';
|
|
139
|
+
user.telnumber = '13800138010';
|
|
140
|
+
user.password = 'hashed';
|
|
141
|
+
return await manager.save(user);
|
|
142
|
+
},
|
|
143
|
+
async (manager: any) => {
|
|
144
|
+
const user = new YtUserEntity();
|
|
145
|
+
user.email = 'batch-2@example.com';
|
|
146
|
+
user.telnumber = '13800138011';
|
|
147
|
+
user.password = 'hashed';
|
|
148
|
+
return await manager.save(user);
|
|
149
|
+
},
|
|
150
|
+
async (manager: any) => {
|
|
151
|
+
const user = new YtUserEntity();
|
|
152
|
+
user.email = 'batch-3@example.com';
|
|
153
|
+
user.telnumber = '13800138012';
|
|
154
|
+
user.password = 'hashed';
|
|
155
|
+
return await manager.save(user);
|
|
156
|
+
}
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const results = await transactionManager.executeBatchTransaction(operations);
|
|
160
|
+
|
|
161
|
+
expect(results).toHaveLength(3);
|
|
162
|
+
expect(results[0].email).toBe('batch-1@example.com');
|
|
163
|
+
expect(results[1].email).toBe('batch-2@example.com');
|
|
164
|
+
expect(results[2].email).toBe('batch-3@example.com');
|
|
165
|
+
|
|
166
|
+
// 验证所有数据都已保存
|
|
167
|
+
const savedUsers = await dataSource.manager.find(YtUserEntity, {
|
|
168
|
+
where: [
|
|
169
|
+
{ email: 'batch-1@example.com' },
|
|
170
|
+
{ email: 'batch-2@example.com' },
|
|
171
|
+
{ email: 'batch-3@example.com' }
|
|
172
|
+
] as any
|
|
173
|
+
});
|
|
174
|
+
expect(savedUsers).toHaveLength(3);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('批量操作中任一失败应该全部回滚', async () => {
|
|
178
|
+
const operations = [
|
|
179
|
+
async (manager: any) => {
|
|
180
|
+
const user = new YtUserEntity();
|
|
181
|
+
user.email = 'batch-ok@example.com';
|
|
182
|
+
user.telnumber = '13800138013';
|
|
183
|
+
user.password = 'hashed';
|
|
184
|
+
return await manager.save(user);
|
|
185
|
+
},
|
|
186
|
+
async (manager: any) => {
|
|
187
|
+
// 故意抛出错误
|
|
188
|
+
throw new Error('批量操作失败');
|
|
189
|
+
}
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
await expect(
|
|
193
|
+
transactionManager.executeBatchTransaction(operations)
|
|
194
|
+
).rejects.toThrow('批量操作失败');
|
|
195
|
+
|
|
196
|
+
// 验证没有数据被保存(全部回滚)
|
|
197
|
+
const savedUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
198
|
+
where: { email: 'batch-ok@example.com' }
|
|
199
|
+
});
|
|
200
|
+
expect(savedUser).toBeNull();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('示例 4: 复杂业务场景', () => {
|
|
205
|
+
test('应该能够处理复杂的多步骤业务场景', async () => {
|
|
206
|
+
const result = await transactionManager.executeInTransaction(async (manager) => {
|
|
207
|
+
// 步骤 1: 创建主用户
|
|
208
|
+
const mainUser = new YtUserEntity();
|
|
209
|
+
mainUser.email = 'complex-1@example.com';
|
|
210
|
+
mainUser.telnumber = '13800138040';
|
|
211
|
+
mainUser.password = 'hashed';
|
|
212
|
+
const savedMainUser = await manager.save(mainUser);
|
|
213
|
+
|
|
214
|
+
// 步骤 2: 创建第二个用户
|
|
215
|
+
const secondUser = new YtUserEntity();
|
|
216
|
+
secondUser.email = 'complex-2@example.com';
|
|
217
|
+
secondUser.telnumber = '13800138041';
|
|
218
|
+
secondUser.password = 'hashed';
|
|
219
|
+
const savedSecondUser = await manager.save(secondUser);
|
|
220
|
+
|
|
221
|
+
// 步骤 3: 更新主用户
|
|
222
|
+
savedMainUser.email = 'complex-1-updated@example.com';
|
|
223
|
+
const updatedMainUser = await manager.save(savedMainUser);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
mainUser: updatedMainUser,
|
|
227
|
+
secondUser: savedSecondUser
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
expect(result).toBeDefined();
|
|
232
|
+
expect(result.mainUser.email).toBe('complex-1-updated@example.com');
|
|
233
|
+
expect(result.secondUser.email).toBe('complex-2@example.com');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('复杂场景失败时应该回滚所有步骤', async () => {
|
|
237
|
+
await expect(
|
|
238
|
+
transactionManager.executeInTransaction(async (manager) => {
|
|
239
|
+
// 步骤 1
|
|
240
|
+
const user1 = new YtUserEntity();
|
|
241
|
+
user1.email = 'fail-step1@example.com';
|
|
242
|
+
user1.telnumber = '13800138050';
|
|
243
|
+
user1.password = 'hashed';
|
|
244
|
+
await manager.save(user1);
|
|
245
|
+
|
|
246
|
+
// 步骤 2
|
|
247
|
+
const user2 = new YtUserEntity();
|
|
248
|
+
user2.email = 'fail-step2@example.com';
|
|
249
|
+
user2.telnumber = '13800138051';
|
|
250
|
+
user2.password = 'hashed';
|
|
251
|
+
await manager.save(user2);
|
|
252
|
+
|
|
253
|
+
// 步骤 3: 失败
|
|
254
|
+
throw new Error('第三步失败');
|
|
255
|
+
})
|
|
256
|
+
).rejects.toThrow('第三步失败');
|
|
257
|
+
|
|
258
|
+
// 验证所有步骤都被回滚
|
|
259
|
+
const user1 = await dataSource.manager.findOne(YtUserEntity, {
|
|
260
|
+
where: { email: 'fail-step1@example.com' }
|
|
261
|
+
});
|
|
262
|
+
const user2 = await dataSource.manager.findOne(YtUserEntity, {
|
|
263
|
+
where: { email: 'fail-step2@example.com' }
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(user1).toBeNull();
|
|
267
|
+
expect(user2).toBeNull();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('示例 5: 条件事务', () => {
|
|
272
|
+
test('应该能够根据条件控制事务提交', async () => {
|
|
273
|
+
const result = await transactionManager.executeInTransaction(async (manager) => {
|
|
274
|
+
const user = new YtUserEntity();
|
|
275
|
+
user.email = 'conditional@example.com';
|
|
276
|
+
user.telnumber = '13800138060';
|
|
277
|
+
user.password = 'hashed';
|
|
278
|
+
|
|
279
|
+
return await manager.save(user);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
expect(result).toBeDefined();
|
|
283
|
+
|
|
284
|
+
// 验证数据已保存
|
|
285
|
+
const savedUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
286
|
+
where: { email: 'conditional@example.com' }
|
|
287
|
+
});
|
|
288
|
+
expect(savedUser).toBeDefined();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('条件不满足时应该回滚事务', async () => {
|
|
292
|
+
await expect(
|
|
293
|
+
transactionManager.executeInTransaction(async (manager) => {
|
|
294
|
+
const user = new YtUserEntity();
|
|
295
|
+
user.email = 'conditional-fail@example.com';
|
|
296
|
+
user.telnumber = '13800138061';
|
|
297
|
+
user.password = 'hashed';
|
|
298
|
+
await manager.save(user);
|
|
299
|
+
|
|
300
|
+
// 模拟条件判断
|
|
301
|
+
if (true) { // 条件不满足
|
|
302
|
+
throw new Error('不符合提交条件');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return user;
|
|
306
|
+
})
|
|
307
|
+
).rejects.toThrow('不符合提交条件');
|
|
308
|
+
|
|
309
|
+
// 验证数据已被回滚
|
|
310
|
+
const savedUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
311
|
+
where: { email: 'conditional-fail@example.com' }
|
|
312
|
+
});
|
|
313
|
+
expect(savedUser).toBeNull();
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe('性能测试', () => {
|
|
318
|
+
test('批量操作性能', async () => {
|
|
319
|
+
const operationCount = 5; // 减少数量以加快测试
|
|
320
|
+
const operations = Array.from({ length: operationCount }, (_, i) => {
|
|
321
|
+
return async (manager: any) => {
|
|
322
|
+
const user = new YtUserEntity();
|
|
323
|
+
user.email = `perf-${i}@example.com`;
|
|
324
|
+
user.telnumber = `13800138${String(i).padStart(3, '0')}`;
|
|
325
|
+
user.password = 'hashed';
|
|
326
|
+
return await manager.save(user);
|
|
327
|
+
};
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const startTime = Date.now();
|
|
331
|
+
const results = await transactionManager.executeBatchTransaction(operations);
|
|
332
|
+
const endTime = Date.now();
|
|
333
|
+
|
|
334
|
+
expect(results).toHaveLength(operationCount);
|
|
335
|
+
expect(endTime - startTime).toBeLessThan(3000); // 应该在3秒内完成
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('错误处理测试', () => {
|
|
340
|
+
test('应该能够正确处理回滚', async () => {
|
|
341
|
+
try {
|
|
342
|
+
await transactionManager.executeInTransaction(async (manager) => {
|
|
343
|
+
const user = new YtUserEntity();
|
|
344
|
+
user.email = 'rollback-test@example.com';
|
|
345
|
+
user.telnumber = '13800138030';
|
|
346
|
+
user.password = 'hashed';
|
|
347
|
+
await manager.save(user);
|
|
348
|
+
|
|
349
|
+
// 模拟业务规则错误
|
|
350
|
+
throw new Error('业务规则:测试用户不允许');
|
|
351
|
+
});
|
|
352
|
+
} catch (error) {
|
|
353
|
+
expect(error).toBeDefined();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 验证数据已被回滚
|
|
357
|
+
const savedUser = await dataSource.manager.findOne(YtUserEntity, {
|
|
358
|
+
where: { email: 'rollback-test@example.com' }
|
|
359
|
+
});
|
|
360
|
+
expect(savedUser).toBeNull();
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
});
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { AnnotationRegistry, createAnnotationDecorator, createParameterDecorator } from '../../../src/framework/decorator/processor/AnnotationDecorators';
|
|
2
|
+
|
|
3
|
+
describe('注解装饰器测试', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
// 清空所有注解数据
|
|
6
|
+
jest.clearAllMocks();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
describe('AnnotationRegistry', () => {
|
|
10
|
+
test('应该能够注册注解数据', () => {
|
|
11
|
+
const target = {};
|
|
12
|
+
const methodName = 'testMethod';
|
|
13
|
+
const processorName = 'TestProcessor';
|
|
14
|
+
const data = { test: 'value' };
|
|
15
|
+
|
|
16
|
+
AnnotationRegistry.register(target, methodName, processorName, data);
|
|
17
|
+
|
|
18
|
+
expect(target.$_Annotations).toBeDefined();
|
|
19
|
+
expect(target.$_Annotations.has(methodName)).toBe(true);
|
|
20
|
+
expect(target.$_Annotations.get(methodName).get(processorName)).toEqual(data);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('应该能够获取注解数据', () => {
|
|
24
|
+
const target = {};
|
|
25
|
+
const methodName = 'testMethod';
|
|
26
|
+
const processorName = 'TestProcessor';
|
|
27
|
+
const data = { test: 'value' };
|
|
28
|
+
|
|
29
|
+
AnnotationRegistry.register(target, methodName, processorName, data);
|
|
30
|
+
|
|
31
|
+
const retrievedData = AnnotationRegistry.get(target, methodName, processorName);
|
|
32
|
+
expect(retrievedData).toEqual(data);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('获取不存在的注解数据应该返回null', () => {
|
|
36
|
+
const target = {};
|
|
37
|
+
const retrievedData = AnnotationRegistry.get(target, 'nonExistentMethod', 'NonExistentProcessor');
|
|
38
|
+
expect(retrievedData).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('应该能够获取方法的所有注解', () => {
|
|
42
|
+
const target = {};
|
|
43
|
+
const methodName = 'testMethod';
|
|
44
|
+
|
|
45
|
+
AnnotationRegistry.register(target, methodName, 'Processor1', { data1: 'value1' });
|
|
46
|
+
AnnotationRegistry.register(target, methodName, 'Processor2', { data2: 'value2' });
|
|
47
|
+
|
|
48
|
+
const allAnnotations = AnnotationRegistry.getAll(target, methodName);
|
|
49
|
+
expect(allAnnotations.size).toBe(2);
|
|
50
|
+
expect(allAnnotations.get('Processor1')).toEqual({ data1: 'value1' });
|
|
51
|
+
expect(allAnnotations.get('Processor2')).toEqual({ data2: 'value2' });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('获取不存在方法的注解应该返回空Map', () => {
|
|
55
|
+
const target = {};
|
|
56
|
+
const allAnnotations = AnnotationRegistry.getAll(target, 'nonExistentMethod');
|
|
57
|
+
expect(allAnnotations.size).toBe(0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('应该支持多次注册同一注解', () => {
|
|
61
|
+
const target = {};
|
|
62
|
+
const methodName = 'testMethod';
|
|
63
|
+
const processorName = 'TestProcessor';
|
|
64
|
+
|
|
65
|
+
AnnotationRegistry.register(target, methodName, processorName, { data1: 'value1' });
|
|
66
|
+
AnnotationRegistry.register(target, methodName, processorName, { data2: 'value2' });
|
|
67
|
+
|
|
68
|
+
const retrievedData = AnnotationRegistry.get(target, methodName, processorName);
|
|
69
|
+
expect(retrievedData).toEqual({ data2: 'value2' }); // 应该是最新的数据
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('createAnnotationDecorator', () => {
|
|
74
|
+
test('应该创建注解装饰器', () => {
|
|
75
|
+
const TestAnnotation = createAnnotationDecorator('TestProcessor');
|
|
76
|
+
expect(typeof TestAnnotation).toBe('function');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('创建的装饰器应该注册注解数据', () => {
|
|
80
|
+
const TestAnnotation = createAnnotationDecorator('TestProcessor');
|
|
81
|
+
const target = {};
|
|
82
|
+
const methodName = 'testMethod';
|
|
83
|
+
const descriptor = { value: jest.fn() };
|
|
84
|
+
const annotationData = { test: 'value' };
|
|
85
|
+
|
|
86
|
+
const result = TestAnnotation(annotationData)(target, methodName, descriptor);
|
|
87
|
+
|
|
88
|
+
expect(result).toBe(descriptor);
|
|
89
|
+
expect(AnnotationRegistry.get(target, methodName, 'TestProcessor')).toEqual(annotationData);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('装饰器没有数据时应该注册undefined', () => {
|
|
93
|
+
const TestAnnotation = createAnnotationDecorator('TestProcessor');
|
|
94
|
+
const target = {};
|
|
95
|
+
const methodName = 'testMethod';
|
|
96
|
+
const descriptor = { value: jest.fn() };
|
|
97
|
+
|
|
98
|
+
TestAnnotation()(target, methodName, descriptor);
|
|
99
|
+
|
|
100
|
+
expect(AnnotationRegistry.get(target, methodName, 'TestProcessor')).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('应该支持链式调用', () => {
|
|
104
|
+
const TestAnnotation = createAnnotationDecorator('TestProcessor');
|
|
105
|
+
const target = {};
|
|
106
|
+
const methodName = 'testMethod';
|
|
107
|
+
const descriptor = { value: jest.fn() };
|
|
108
|
+
|
|
109
|
+
const result = TestAnnotation({ data: 'value' })(target, methodName, descriptor);
|
|
110
|
+
|
|
111
|
+
expect(result).toBe(descriptor);
|
|
112
|
+
expect(AnnotationRegistry.get(target, methodName, 'TestProcessor')).toEqual({ data: 'value' });
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('createParameterDecorator', () => {
|
|
117
|
+
test('应该创建参数装饰器', () => {
|
|
118
|
+
const TestParamAnnotation = createParameterDecorator('TestProcessor');
|
|
119
|
+
expect(typeof TestParamAnnotation).toBe('function');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('创建的参数装饰器应该注册注解数据', () => {
|
|
123
|
+
const TestParamAnnotation = createParameterDecorator('TestProcessor');
|
|
124
|
+
const target = {};
|
|
125
|
+
const methodName = 'testMethod';
|
|
126
|
+
const parameterIndex = 0;
|
|
127
|
+
const annotationData = { test: 'value' };
|
|
128
|
+
|
|
129
|
+
TestParamAnnotation(annotationData)(target, methodName, parameterIndex);
|
|
130
|
+
|
|
131
|
+
const registeredData = AnnotationRegistry.get(target, methodName, 'TestProcessor');
|
|
132
|
+
expect(registeredData).toEqual({
|
|
133
|
+
...annotationData,
|
|
134
|
+
parameterIndex: parameterIndex
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('参数装饰器应该记录参数索引', () => {
|
|
139
|
+
const TestParamAnnotation = createParameterDecorator('TestProcessor');
|
|
140
|
+
const target = {};
|
|
141
|
+
const methodName = 'testMethod';
|
|
142
|
+
const parameterIndex = 2;
|
|
143
|
+
|
|
144
|
+
TestParamAnnotation({ data: 'value' })(target, methodName, parameterIndex);
|
|
145
|
+
|
|
146
|
+
const registeredData = AnnotationRegistry.get(target, methodName, 'TestProcessor');
|
|
147
|
+
expect(registeredData.parameterIndex).toBe(parameterIndex);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('应该支持多个参数装饰器', () => {
|
|
151
|
+
const TestParamAnnotation1 = createParameterDecorator('Processor1');
|
|
152
|
+
const TestParamAnnotation2 = createParameterDecorator('Processor2');
|
|
153
|
+
const target = {};
|
|
154
|
+
const methodName = 'testMethod';
|
|
155
|
+
|
|
156
|
+
TestParamAnnotation1({ data1: 'value1' })(target, methodName, 0);
|
|
157
|
+
TestParamAnnotation2({ data2: 'value2' })(target, methodName, 1);
|
|
158
|
+
|
|
159
|
+
expect(AnnotationRegistry.get(target, methodName, 'Processor1')).toEqual({
|
|
160
|
+
data1: 'value1',
|
|
161
|
+
parameterIndex: 0
|
|
162
|
+
});
|
|
163
|
+
expect(AnnotationRegistry.get(target, methodName, 'Processor2')).toEqual({
|
|
164
|
+
data2: 'value2',
|
|
165
|
+
parameterIndex: 1
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('装饰器集成测试', () => {
|
|
171
|
+
test('应该支持多个注解装饰器', () => {
|
|
172
|
+
const LoggingAnnotation = createAnnotationDecorator('Logging');
|
|
173
|
+
const PermissionAnnotation = createAnnotationDecorator('Permission');
|
|
174
|
+
const target = {};
|
|
175
|
+
const methodName = 'testMethod';
|
|
176
|
+
const descriptor = { value: jest.fn() };
|
|
177
|
+
|
|
178
|
+
LoggingAnnotation({ level: 'info' })(target, methodName, descriptor);
|
|
179
|
+
PermissionAnnotation({ requiredPermission: 'read:data' })(target, methodName, descriptor);
|
|
180
|
+
|
|
181
|
+
expect(AnnotationRegistry.get(target, methodName, 'Logging')).toEqual({ level: 'info' });
|
|
182
|
+
expect(AnnotationRegistry.get(target, methodName, 'Permission')).toEqual({ requiredPermission: 'read:data' });
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('应该支持注解装饰器和参数装饰器混合使用', () => {
|
|
186
|
+
const LoggingAnnotation = createAnnotationDecorator('Logging');
|
|
187
|
+
const ValidatedParamAnnotation = createParameterDecorator('ParameterValidation');
|
|
188
|
+
const target = {};
|
|
189
|
+
const methodName = 'testMethod';
|
|
190
|
+
const descriptor = { value: jest.fn() };
|
|
191
|
+
|
|
192
|
+
LoggingAnnotation({ level: 'info' })(target, methodName, descriptor);
|
|
193
|
+
ValidatedParamAnnotation({ validate: 'TestClass' })(target, methodName, 0);
|
|
194
|
+
|
|
195
|
+
expect(AnnotationRegistry.get(target, methodName, 'Logging')).toEqual({ level: 'info' });
|
|
196
|
+
expect(AnnotationRegistry.get(target, methodName, 'ParameterValidation')).toEqual({
|
|
197
|
+
validate: 'TestClass',
|
|
198
|
+
parameterIndex: 0
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('应该支持同一方法的多个参数装饰器', () => {
|
|
203
|
+
const ValidatedParamAnnotation = createParameterDecorator('ParameterValidation');
|
|
204
|
+
const LoggedParamAnnotation = createParameterDecorator('Logging');
|
|
205
|
+
const target = {};
|
|
206
|
+
const methodName = 'testMethod';
|
|
207
|
+
|
|
208
|
+
ValidatedParamAnnotation({ validate: 'TestClass' })(target, methodName, 0);
|
|
209
|
+
LoggedParamAnnotation({ logLevel: 'debug' })(target, methodName, 1);
|
|
210
|
+
|
|
211
|
+
const allAnnotations = AnnotationRegistry.getAll(target, methodName);
|
|
212
|
+
expect(allAnnotations.size).toBe(2);
|
|
213
|
+
|
|
214
|
+
// 由于参数装饰器会覆盖同一处理器的数据,这里只检查最后一个
|
|
215
|
+
const lastAnnotation = AnnotationRegistry.get(target, methodName, 'Logging');
|
|
216
|
+
expect(lastAnnotation).toEqual({
|
|
217
|
+
logLevel: 'debug',
|
|
218
|
+
parameterIndex: 1
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|