create-hest-app 0.1.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 (139) hide show
  1. package/README.md +211 -0
  2. package/dist/index.js +127 -0
  3. package/package.json +50 -0
  4. package/templates/base/.prettierrc +33 -0
  5. package/templates/base/.vscode/extensions.json +79 -0
  6. package/templates/base/.vscode/settings.json +70 -0
  7. package/templates/base/README.md +562 -0
  8. package/templates/base/eslint.config.ts +26 -0
  9. package/templates/base/package.json +62 -0
  10. package/templates/base/src/app.controller.ts +39 -0
  11. package/templates/base/src/app.module.ts +12 -0
  12. package/templates/base/src/app.service.ts +30 -0
  13. package/templates/base/src/common/filters/http-exception.filter.ts +34 -0
  14. package/templates/base/src/common/interceptors/response.interceptor.ts +38 -0
  15. package/templates/base/src/index.ts +35 -0
  16. package/templates/base/src/modules/custom-validation/custom-validation.controller.ts +146 -0
  17. package/templates/base/src/modules/custom-validation/custom-validation.module.ts +10 -0
  18. package/templates/base/src/modules/custom-validation/custom-validation.service.ts +33 -0
  19. package/templates/base/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
  20. package/templates/base/src/modules/users/dto/user.dto.ts +64 -0
  21. package/templates/base/src/modules/users/entities/user.entity.ts +9 -0
  22. package/templates/base/src/modules/users/users.controller.ts +68 -0
  23. package/templates/base/src/modules/users/users.module.ts +10 -0
  24. package/templates/base/src/modules/users/users.service.ts +55 -0
  25. package/templates/base/tsconfig.json +19 -0
  26. package/templates/base_scalar/.prettierrc +32 -0
  27. package/templates/base_scalar/.vscode/extensions.json +79 -0
  28. package/templates/base_scalar/.vscode/settings.json +70 -0
  29. package/templates/base_scalar/README.md +562 -0
  30. package/templates/base_scalar/eslint.config.ts +26 -0
  31. package/templates/base_scalar/package.json +63 -0
  32. package/templates/base_scalar/src/app.controller.ts +196 -0
  33. package/templates/base_scalar/src/app.module.ts +12 -0
  34. package/templates/base_scalar/src/app.service.ts +30 -0
  35. package/templates/base_scalar/src/common/filters/http-exception.filter.ts +34 -0
  36. package/templates/base_scalar/src/common/interceptors/response.interceptor.ts +38 -0
  37. package/templates/base_scalar/src/index.ts +67 -0
  38. package/templates/base_scalar/src/modules/custom-validation/custom-validation.controller.ts +146 -0
  39. package/templates/base_scalar/src/modules/custom-validation/custom-validation.module.ts +10 -0
  40. package/templates/base_scalar/src/modules/custom-validation/custom-validation.service.ts +33 -0
  41. package/templates/base_scalar/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
  42. package/templates/base_scalar/src/modules/users/dto/user.dto.ts +64 -0
  43. package/templates/base_scalar/src/modules/users/entities/user.entity.ts +9 -0
  44. package/templates/base_scalar/src/modules/users/users.controller.ts +68 -0
  45. package/templates/base_scalar/src/modules/users/users.module.ts +10 -0
  46. package/templates/base_scalar/src/modules/users/users.service.ts +55 -0
  47. package/templates/base_scalar/tsconfig.json +19 -0
  48. package/templates/cqrs/.prettierrc +33 -0
  49. package/templates/cqrs/.vscode/extensions.json +79 -0
  50. package/templates/cqrs/.vscode/settings.json +70 -0
  51. package/templates/cqrs/README.md +234 -0
  52. package/templates/cqrs/eslint.config.ts +26 -0
  53. package/templates/cqrs/package.json +62 -0
  54. package/templates/cqrs/src/app.controller.ts +28 -0
  55. package/templates/cqrs/src/app.module.ts +12 -0
  56. package/templates/cqrs/src/app.service.ts +8 -0
  57. package/templates/cqrs/src/common/filters/http-exception.filter.ts +34 -0
  58. package/templates/cqrs/src/common/interceptors/response.interceptor.ts +38 -0
  59. package/templates/cqrs/src/index.ts +38 -0
  60. package/templates/cqrs/src/modules/custom-validation/custom-validation.controller.ts +146 -0
  61. package/templates/cqrs/src/modules/custom-validation/custom-validation.module.ts +10 -0
  62. package/templates/cqrs/src/modules/custom-validation/custom-validation.service.ts +33 -0
  63. package/templates/cqrs/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
  64. package/templates/cqrs/src/modules/users/dto/user.dto.ts +64 -0
  65. package/templates/cqrs/src/modules/users/entities/user.entity.ts +9 -0
  66. package/templates/cqrs/src/modules/users/users.controller.ts +68 -0
  67. package/templates/cqrs/src/modules/users/users.module.ts +10 -0
  68. package/templates/cqrs/src/modules/users/users.service.ts +55 -0
  69. package/templates/cqrs/src/test-error-scenarios.ts +54 -0
  70. package/templates/cqrs/src/users/commands/create-user.command.ts +8 -0
  71. package/templates/cqrs/src/users/commands/index.ts +2 -0
  72. package/templates/cqrs/src/users/commands/update-user.command.ts +11 -0
  73. package/templates/cqrs/src/users/entities/index.ts +1 -0
  74. package/templates/cqrs/src/users/entities/user.entity.ts +22 -0
  75. package/templates/cqrs/src/users/events/index.ts +2 -0
  76. package/templates/cqrs/src/users/events/user-created.event.ts +8 -0
  77. package/templates/cqrs/src/users/events/user-updated.event.ts +8 -0
  78. package/templates/cqrs/src/users/handlers/create-user.handler.ts +26 -0
  79. package/templates/cqrs/src/users/handlers/get-all-users.handler.ts +15 -0
  80. package/templates/cqrs/src/users/handlers/get-user.handler.ts +15 -0
  81. package/templates/cqrs/src/users/handlers/index.ts +6 -0
  82. package/templates/cqrs/src/users/handlers/update-user.handler.ts +33 -0
  83. package/templates/cqrs/src/users/handlers/user-created.handler.ts +15 -0
  84. package/templates/cqrs/src/users/handlers/user-updated.handler.ts +15 -0
  85. package/templates/cqrs/src/users/index.ts +8 -0
  86. package/templates/cqrs/src/users/queries/get-all-users.query.ts +8 -0
  87. package/templates/cqrs/src/users/queries/get-user.query.ts +12 -0
  88. package/templates/cqrs/src/users/queries/index.ts +2 -0
  89. package/templates/cqrs/src/users/repositories/index.ts +1 -0
  90. package/templates/cqrs/src/users/repositories/user.repository.ts +51 -0
  91. package/templates/cqrs/src/users/user.controller.ts +66 -0
  92. package/templates/cqrs/src/users/user.module.ts +30 -0
  93. package/templates/cqrs/tsconfig.json +19 -0
  94. package/templates/cqrs_scalar/.prettierrc +33 -0
  95. package/templates/cqrs_scalar/.vscode/extensions.json +79 -0
  96. package/templates/cqrs_scalar/.vscode/settings.json +70 -0
  97. package/templates/cqrs_scalar/README.md +234 -0
  98. package/templates/cqrs_scalar/eslint.config.ts +26 -0
  99. package/templates/cqrs_scalar/package.json +62 -0
  100. package/templates/cqrs_scalar/src/app.controller.ts +28 -0
  101. package/templates/cqrs_scalar/src/app.module.ts +12 -0
  102. package/templates/cqrs_scalar/src/app.service.ts +8 -0
  103. package/templates/cqrs_scalar/src/common/filters/http-exception.filter.ts +34 -0
  104. package/templates/cqrs_scalar/src/common/interceptors/response.interceptor.ts +38 -0
  105. package/templates/cqrs_scalar/src/index.ts +38 -0
  106. package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.controller.ts +146 -0
  107. package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.module.ts +10 -0
  108. package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.service.ts +33 -0
  109. package/templates/cqrs_scalar/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
  110. package/templates/cqrs_scalar/src/modules/users/dto/user.dto.ts +64 -0
  111. package/templates/cqrs_scalar/src/modules/users/entities/user.entity.ts +9 -0
  112. package/templates/cqrs_scalar/src/modules/users/users.controller.ts +68 -0
  113. package/templates/cqrs_scalar/src/modules/users/users.module.ts +10 -0
  114. package/templates/cqrs_scalar/src/modules/users/users.service.ts +55 -0
  115. package/templates/cqrs_scalar/src/test-error-scenarios.ts +54 -0
  116. package/templates/cqrs_scalar/src/users/commands/create-user.command.ts +8 -0
  117. package/templates/cqrs_scalar/src/users/commands/index.ts +2 -0
  118. package/templates/cqrs_scalar/src/users/commands/update-user.command.ts +11 -0
  119. package/templates/cqrs_scalar/src/users/entities/index.ts +1 -0
  120. package/templates/cqrs_scalar/src/users/entities/user.entity.ts +22 -0
  121. package/templates/cqrs_scalar/src/users/events/index.ts +2 -0
  122. package/templates/cqrs_scalar/src/users/events/user-created.event.ts +8 -0
  123. package/templates/cqrs_scalar/src/users/events/user-updated.event.ts +8 -0
  124. package/templates/cqrs_scalar/src/users/handlers/create-user.handler.ts +26 -0
  125. package/templates/cqrs_scalar/src/users/handlers/get-all-users.handler.ts +15 -0
  126. package/templates/cqrs_scalar/src/users/handlers/get-user.handler.ts +15 -0
  127. package/templates/cqrs_scalar/src/users/handlers/index.ts +6 -0
  128. package/templates/cqrs_scalar/src/users/handlers/update-user.handler.ts +33 -0
  129. package/templates/cqrs_scalar/src/users/handlers/user-created.handler.ts +15 -0
  130. package/templates/cqrs_scalar/src/users/handlers/user-updated.handler.ts +15 -0
  131. package/templates/cqrs_scalar/src/users/index.ts +8 -0
  132. package/templates/cqrs_scalar/src/users/queries/get-all-users.query.ts +8 -0
  133. package/templates/cqrs_scalar/src/users/queries/get-user.query.ts +12 -0
  134. package/templates/cqrs_scalar/src/users/queries/index.ts +2 -0
  135. package/templates/cqrs_scalar/src/users/repositories/index.ts +1 -0
  136. package/templates/cqrs_scalar/src/users/repositories/user.repository.ts +51 -0
  137. package/templates/cqrs_scalar/src/users/user.controller.ts +66 -0
  138. package/templates/cqrs_scalar/src/users/user.module.ts +30 -0
  139. package/templates/cqrs_scalar/tsconfig.json +19 -0
@@ -0,0 +1,10 @@
1
+ import { Module } from '@hestjs/core';
2
+ import { UsersController } from './users.controller';
3
+ import { UsersService } from './users.service';
4
+
5
+ @Module({
6
+ controllers: [UsersController],
7
+ providers: [UsersService],
8
+ exports: [UsersService],
9
+ })
10
+ export class UsersModule {}
@@ -0,0 +1,55 @@
1
+ import { Injectable } from '@hestjs/core';
2
+ import type { User } from './entities/user.entity';
3
+ import type { CreateUserDto, UpdateUserDto } from './dto/user.dto';
4
+
5
+ /**
6
+ * 用户服务
7
+ */
8
+ @Injectable()
9
+ export class UsersService {
10
+ private users: User[] = [
11
+ { id: 1, name: 'John Doe', email: 'john@example.com', age: 30 },
12
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', age: 25 },
13
+ ];
14
+
15
+ findAll(): User[] {
16
+ return this.users;
17
+ }
18
+
19
+ findOne(id: number): User | undefined {
20
+ return this.users.find(user => user.id === id);
21
+ }
22
+
23
+ create(createUserDto: CreateUserDto): Omit<User, 'password'> {
24
+ const newUser: User = {
25
+ id: this.users.length + 1,
26
+ ...createUserDto,
27
+ };
28
+ this.users.push(newUser);
29
+
30
+ // 不返回密码
31
+ const { password: _password, ...userWithoutPassword } = newUser;
32
+ return userWithoutPassword;
33
+ }
34
+
35
+ update(id: number, updateUserDto: UpdateUserDto): User {
36
+ const userIndex = this.users.findIndex(user => user.id === id);
37
+ if (userIndex === -1) {
38
+ throw new Error('User not found');
39
+ }
40
+
41
+ this.users[userIndex] = { ...this.users[userIndex], ...updateUserDto };
42
+ return this.users[userIndex];
43
+ }
44
+
45
+ remove(id: number): User {
46
+ const userIndex = this.users.findIndex(user => user.id === id);
47
+ if (userIndex === -1) {
48
+ throw new Error('User not found');
49
+ }
50
+
51
+ const deletedUser = this.users[userIndex];
52
+ this.users.splice(userIndex, 1);
53
+ return deletedUser;
54
+ }
55
+ }
@@ -0,0 +1,54 @@
1
+ import { logger } from '@hestjs/logger';
2
+
3
+ // 模拟应用启动时的错误处理
4
+ async function testErrorScenarios() {
5
+ console.log('=== Testing Real-world Error Scenarios ===\n');
6
+
7
+ // 场景 1: 数据库连接错误
8
+ try {
9
+ throw new Error('Database connection failed');
10
+ } catch (error) {
11
+ logger.error(
12
+ '❌ Failed to connect to database:',
13
+ error instanceof Error ? error : new Error(String(error)),
14
+ );
15
+ }
16
+
17
+ // 场景 2: API 调用失败
18
+ try {
19
+ const apiError = new Error('API request timeout');
20
+ (apiError as any).status = 504;
21
+ (apiError as any).endpoint = '/api/users';
22
+ throw apiError;
23
+ } catch (error) {
24
+ logger.error(
25
+ '❌ API call failed:',
26
+ error instanceof Error ? error : new Error(String(error)),
27
+ {
28
+ requestId: 'req-12345',
29
+ userId: 'user-67890',
30
+ },
31
+ );
32
+ }
33
+
34
+ // 场景 3: 验证错误
35
+ try {
36
+ const validationError = new Error('Validation failed');
37
+ (validationError as any).fields = ['email', 'password'];
38
+ (validationError as any).code = 'VALIDATION_ERROR';
39
+ throw validationError;
40
+ } catch (error) {
41
+ logger.error(
42
+ '❌ Validation failed:',
43
+ error instanceof Error ? error : new Error(String(error)),
44
+ );
45
+ }
46
+
47
+ // 场景 4: 使用简化的语法
48
+ const simpleError = new Error('Something went wrong');
49
+ logger.error('❌ Simple error logging:', simpleError);
50
+
51
+ console.log('\n=== Test Complete ===');
52
+ }
53
+
54
+ testErrorScenarios();
@@ -0,0 +1,8 @@
1
+ import { Command } from '@hestjs/cqrs';
2
+ import { CreateUserData, User } from '../entities';
3
+
4
+ export class CreateUserCommand extends Command<User> {
5
+ constructor(public readonly userData: CreateUserData) {
6
+ super();
7
+ }
8
+ }
@@ -0,0 +1,2 @@
1
+ export * from './create-user.command';
2
+ export * from './update-user.command';
@@ -0,0 +1,11 @@
1
+ import { Command } from '@hestjs/cqrs';
2
+ import { UpdateUserData, User } from '../entities';
3
+
4
+ export class UpdateUserCommand extends Command<User> {
5
+ constructor(
6
+ public readonly userId: string,
7
+ public readonly userData: UpdateUserData,
8
+ ) {
9
+ super();
10
+ }
11
+ }
@@ -0,0 +1 @@
1
+ export * from './user.entity';
@@ -0,0 +1,22 @@
1
+ import { IQueryResult } from '@hestjs/cqrs';
2
+
3
+ export interface User extends IQueryResult {
4
+ id: string;
5
+ name: string;
6
+ email: string;
7
+ age?: number;
8
+ createdAt: Date;
9
+ updatedAt: Date;
10
+ }
11
+
12
+ export interface CreateUserData {
13
+ name: string;
14
+ email: string;
15
+ age?: number;
16
+ }
17
+
18
+ export interface UpdateUserData {
19
+ name?: string;
20
+ email?: string;
21
+ age?: number;
22
+ }
@@ -0,0 +1,2 @@
1
+ export * from './user-created.event';
2
+ export * from './user-updated.event';
@@ -0,0 +1,8 @@
1
+ import { Event } from '@hestjs/cqrs';
2
+ import { User } from '../entities';
3
+
4
+ export class UserCreatedEvent extends Event {
5
+ constructor(public readonly user: User) {
6
+ super();
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import { Event } from '@hestjs/cqrs';
2
+ import { User } from '../entities';
3
+
4
+ export class UserUpdatedEvent extends Event {
5
+ constructor(public readonly user: User) {
6
+ super();
7
+ }
8
+ }
@@ -0,0 +1,26 @@
1
+ import { Injectable } from '@hestjs/core';
2
+ import { CommandHandler, EventBus, ICommandHandler } from '@hestjs/cqrs';
3
+ import { CreateUserCommand } from '../commands';
4
+ import { User } from '../entities';
5
+ import { UserCreatedEvent } from '../events';
6
+ import { UserRepository } from '../repositories';
7
+
8
+ @Injectable()
9
+ @CommandHandler(CreateUserCommand)
10
+ export class CreateUserHandler
11
+ implements ICommandHandler<CreateUserCommand, User>
12
+ {
13
+ constructor(
14
+ private readonly userRepository: UserRepository,
15
+ private readonly eventBus: EventBus,
16
+ ) {}
17
+
18
+ async execute(command: CreateUserCommand): Promise<User> {
19
+ const user = await this.userRepository.create(command.userData);
20
+
21
+ // 发布用户创建事件
22
+ await this.eventBus.publish(new UserCreatedEvent(user));
23
+
24
+ return user;
25
+ }
26
+ }
@@ -0,0 +1,15 @@
1
+ import { Injectable } from '@hestjs/core';
2
+ import { QueryHandler, IQueryHandler } from '@hestjs/cqrs';
3
+ import { GetAllUsersQuery, GetAllUsersResult } from '../queries';
4
+ import { UserRepository } from '../repositories';
5
+
6
+ @Injectable()
7
+ @QueryHandler(GetAllUsersQuery)
8
+ export class GetAllUsersHandler implements IQueryHandler<GetAllUsersQuery, GetAllUsersResult> {
9
+ constructor(private readonly userRepository: UserRepository) {}
10
+
11
+ async execute(_query: GetAllUsersQuery): Promise<GetAllUsersResult> {
12
+ const users = await this.userRepository.findAll();
13
+ return { users };
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ import { Injectable } from '@hestjs/core';
2
+ import { QueryHandler, IQueryHandler } from '@hestjs/cqrs';
3
+ import { GetUserQuery, GetUserResult } from '../queries';
4
+ import { UserRepository } from '../repositories';
5
+
6
+ @Injectable()
7
+ @QueryHandler(GetUserQuery)
8
+ export class GetUserHandler implements IQueryHandler<GetUserQuery, GetUserResult> {
9
+ constructor(private readonly userRepository: UserRepository) {}
10
+
11
+ async execute(query: GetUserQuery): Promise<GetUserResult> {
12
+ const user = await this.userRepository.findById(query.userId);
13
+ return { user };
14
+ }
15
+ }
@@ -0,0 +1,6 @@
1
+ export * from './create-user.handler';
2
+ export * from './update-user.handler';
3
+ export * from './get-user.handler';
4
+ export * from './get-all-users.handler';
5
+ export * from './user-created.handler';
6
+ export * from './user-updated.handler';
@@ -0,0 +1,33 @@
1
+ import { Injectable } from '@hestjs/core';
2
+ import { CommandHandler, EventBus, ICommandHandler } from '@hestjs/cqrs';
3
+ import { UpdateUserCommand } from '../commands';
4
+ import { User } from '../entities';
5
+ import { UserUpdatedEvent } from '../events';
6
+ import { UserRepository } from '../repositories';
7
+
8
+ @Injectable()
9
+ @CommandHandler(UpdateUserCommand)
10
+ export class UpdateUserHandler
11
+ implements ICommandHandler<UpdateUserCommand, User>
12
+ {
13
+ constructor(
14
+ private readonly userRepository: UserRepository,
15
+ private readonly eventBus: EventBus,
16
+ ) {}
17
+
18
+ async execute(command: UpdateUserCommand): Promise<User> {
19
+ const user = await this.userRepository.update(
20
+ command.userId,
21
+ command.userData,
22
+ );
23
+
24
+ if (!user) {
25
+ throw new Error(`User with id ${command.userId} not found`);
26
+ }
27
+
28
+ // 发布用户更新事件
29
+ await this.eventBus.publish(new UserUpdatedEvent(user));
30
+
31
+ return user;
32
+ }
33
+ }
@@ -0,0 +1,15 @@
1
+ import { Injectable } from '@hestjs/core';
2
+ import { EventsHandler, IEventHandler } from '@hestjs/cqrs';
3
+ import { logger } from '@hestjs/logger';
4
+ import { UserCreatedEvent } from '../events';
5
+
6
+ @Injectable()
7
+ @EventsHandler(UserCreatedEvent)
8
+ export class UserCreatedHandler implements IEventHandler<UserCreatedEvent> {
9
+ async handle(event: UserCreatedEvent): Promise<void> {
10
+ logger.info(`User created: ${event.user.name} (${event.user.email})`);
11
+
12
+ // 这里可以执行其他业务逻辑,比如发送欢迎邮件等
13
+ // await this.emailService.sendWelcomeEmail(event.user);
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ import { Injectable } from '@hestjs/core';
2
+ import { EventsHandler, IEventHandler } from '@hestjs/cqrs';
3
+ import { logger } from '@hestjs/logger';
4
+ import { UserUpdatedEvent } from '../events';
5
+
6
+ @Injectable()
7
+ @EventsHandler(UserUpdatedEvent)
8
+ export class UserUpdatedHandler implements IEventHandler<UserUpdatedEvent> {
9
+ async handle(event: UserUpdatedEvent): Promise<void> {
10
+ logger.info(`User updated: ${event.user.name} (${event.user.email})`);
11
+
12
+ // 这里可以执行其他业务逻辑,比如发送通知等
13
+ // await this.notificationService.sendUpdateNotification(event.user);
14
+ }
15
+ }
@@ -0,0 +1,8 @@
1
+ export * from './entities';
2
+ export * from './commands';
3
+ export * from './queries';
4
+ export * from './events';
5
+ export * from './handlers';
6
+ export * from './repositories';
7
+ export * from './user.controller';
8
+ export * from './user.module';
@@ -0,0 +1,8 @@
1
+ import { Query, IQueryResult } from '@hestjs/cqrs';
2
+ import { User } from '../entities';
3
+
4
+ export interface GetAllUsersResult extends IQueryResult {
5
+ users: User[];
6
+ }
7
+
8
+ export class GetAllUsersQuery extends Query<GetAllUsersResult> {}
@@ -0,0 +1,12 @@
1
+ import { Query, IQueryResult } from '@hestjs/cqrs';
2
+ import { User } from '../entities';
3
+
4
+ export interface GetUserResult extends IQueryResult {
5
+ user: User | null;
6
+ }
7
+
8
+ export class GetUserQuery extends Query<GetUserResult> {
9
+ constructor(public readonly userId: string) {
10
+ super();
11
+ }
12
+ }
@@ -0,0 +1,2 @@
1
+ export * from './get-user.query';
2
+ export * from './get-all-users.query';
@@ -0,0 +1 @@
1
+ export * from './user.repository';
@@ -0,0 +1,51 @@
1
+ import { Injectable } from '@hestjs/core';
2
+ import { User, CreateUserData, UpdateUserData } from '../entities';
3
+
4
+ @Injectable()
5
+ export class UserRepository {
6
+ private users: Map<string, User> = new Map();
7
+ private nextId = 1;
8
+
9
+ async create(userData: CreateUserData): Promise<User> {
10
+ const id = this.nextId.toString();
11
+ this.nextId++;
12
+
13
+ const user: User = {
14
+ id,
15
+ ...userData,
16
+ createdAt: new Date(),
17
+ updatedAt: new Date(),
18
+ };
19
+
20
+ this.users.set(id, user);
21
+ return user;
22
+ }
23
+
24
+ async findById(id: string): Promise<User | null> {
25
+ return this.users.get(id) || null;
26
+ }
27
+
28
+ async findAll(): Promise<User[]> {
29
+ return Array.from(this.users.values());
30
+ }
31
+
32
+ async update(id: string, userData: UpdateUserData): Promise<User | null> {
33
+ const user = this.users.get(id);
34
+ if (!user) {
35
+ return null;
36
+ }
37
+
38
+ const updatedUser: User = {
39
+ ...user,
40
+ ...userData,
41
+ updatedAt: new Date(),
42
+ };
43
+
44
+ this.users.set(id, updatedUser);
45
+ return updatedUser;
46
+ }
47
+
48
+ async delete(id: string): Promise<boolean> {
49
+ return this.users.delete(id);
50
+ }
51
+ }
@@ -0,0 +1,66 @@
1
+ import { Controller, Get, Post, Put, Context } from '@hestjs/core';
2
+ import type { HestContext } from '@hestjs/core';
3
+ import { CommandBus, QueryBus } from '@hestjs/cqrs';
4
+ import { CreateUserCommand, UpdateUserCommand } from './commands';
5
+ import { CreateUserData, UpdateUserData } from './entities';
6
+ import { GetAllUsersQuery, GetUserQuery } from './queries';
7
+
8
+ @Controller('/users')
9
+ export class UserController {
10
+ constructor(
11
+ private readonly commandBus: CommandBus,
12
+ private readonly queryBus: QueryBus,
13
+ ) {}
14
+
15
+ @Post('/')
16
+ async createUser(@Context() c: HestContext) {
17
+ const userData = await c.req.json<CreateUserData>();
18
+
19
+ // 基本验证(可以添加更复杂的验证逻辑)
20
+ if (!userData.name || !userData.email) {
21
+ return c.json(
22
+ { success: false, error: 'Name and email are required' },
23
+ 400,
24
+ );
25
+ }
26
+
27
+ const user = await this.commandBus.execute(new CreateUserCommand(userData));
28
+ return c.json({ success: true, data: user }, 201);
29
+ }
30
+
31
+ @Get('/')
32
+ async getAllUsers(@Context() c: HestContext) {
33
+ const result = await this.queryBus.execute(new GetAllUsersQuery());
34
+ return c.json({ success: true, data: result.users });
35
+ }
36
+
37
+ @Get('/:id')
38
+ async getUserById(@Context() c: HestContext) {
39
+ const id = c.req.param('id');
40
+ const result = await this.queryBus.execute(new GetUserQuery(id));
41
+
42
+ if (!result.user) {
43
+ return c.json({ success: false, error: 'User not found' }, 404);
44
+ }
45
+
46
+ return c.json({ success: true, data: result.user });
47
+ }
48
+
49
+ @Put('/:id')
50
+ async updateUser(@Context() c: HestContext) {
51
+ const id = c.req.param('id');
52
+ const userData = await c.req.json<UpdateUserData>();
53
+
54
+ try {
55
+ const user = await this.commandBus.execute(
56
+ new UpdateUserCommand(id, userData),
57
+ );
58
+ return c.json({ success: true, data: user });
59
+ } catch (error) {
60
+ if (error instanceof Error && error.message.includes('not found')) {
61
+ return c.json({ success: false, error: 'User not found' }, 404);
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,30 @@
1
+ import { Module } from '@hestjs/core';
2
+ import { CqrsModule } from '@hestjs/cqrs';
3
+ import { UserController } from './user.controller';
4
+ import { UserRepository } from './repositories';
5
+ import {
6
+ CreateUserHandler,
7
+ UpdateUserHandler,
8
+ GetUserHandler,
9
+ GetAllUsersHandler,
10
+ UserCreatedHandler,
11
+ UserUpdatedHandler,
12
+ } from './handlers';
13
+
14
+ @Module({
15
+ imports: [CqrsModule],
16
+ controllers: [UserController],
17
+ providers: [
18
+ UserRepository,
19
+ // Command Handlers
20
+ CreateUserHandler,
21
+ UpdateUserHandler,
22
+ // Query Handlers
23
+ GetUserHandler,
24
+ GetAllUsersHandler,
25
+ // Event Handlers
26
+ UserCreatedHandler,
27
+ UserUpdatedHandler,
28
+ ],
29
+ })
30
+ export class UserModule {}
@@ -0,0 +1,19 @@
1
+ {
2
+ "extends": "@hestjs/typescript-config/base.json",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "jsx": "react-jsx",
6
+ "jsxImportSource": "hono/jsx",
7
+ "experimentalDecorators": true,
8
+ "emitDecoratorMetadata": true,
9
+ "target": "ES2022",
10
+ "module": "ESNext",
11
+ "moduleResolution": "bundler",
12
+ "outDir": "./dist",
13
+ "declaration": true,
14
+ "sourceMap": true,
15
+ "paths": {
16
+ "@/*": ["./src/*"]
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "trailingComma": "all",
5
+ "quoteProps": "as-needed",
6
+ "tabWidth": 2,
7
+ "printWidth": 80,
8
+ "bracketSpacing": true,
9
+ "arrowParens": "avoid",
10
+ "endOfLine": "lf",
11
+ "useTabs": false,
12
+ "overrides": [
13
+ {
14
+ "files": "*.json",
15
+ "options": {
16
+ "singleQuote": false
17
+ }
18
+ },
19
+ {
20
+ "files": "*.md",
21
+ "options": {
22
+ "proseWrap": "preserve"
23
+ }
24
+ },
25
+ {
26
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
27
+ "options": {
28
+ "singleQuote": true,
29
+ "semi": true
30
+ }
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ "recommendations": [
3
+ // Cline
4
+ "saoudrizwan.claude-dev",
5
+
6
+ // Code Runner
7
+ "formulahendry.code-runner",
8
+
9
+ // Color Highlight
10
+ "naumovs.color-highlight",
11
+
12
+ // Draw Folder Structure
13
+ "jmkrivocapich.drawfolderstructure",
14
+
15
+ // ESLint
16
+ "dbaeumer.vscode-eslint",
17
+
18
+ // Git Graph
19
+ "mhutchie.git-graph",
20
+
21
+ // GitHub Copilot
22
+ "GitHub.copilot",
23
+
24
+ // GitHub Copilot Chat
25
+ "GitHub.copilot-chat",
26
+
27
+ // Github Actions
28
+ "github.vscode-github-actions",
29
+
30
+ // i18n Ally
31
+ "lokalise.i18n-ally",
32
+
33
+ // Iconify IntelliSense
34
+ "antfu.iconify",
35
+
36
+ // Indenticator
37
+ "SirTori.indenticator",
38
+
39
+ // Jest
40
+ "Orta.vscode-jest",
41
+
42
+ // Lingma - Alibaba
43
+ "Alibaba-Cloud.tongyi-lingma",
44
+
45
+ // Monorepo Workspace
46
+ "folke.vscode-monorepo-workspace",
47
+
48
+ // Prettier
49
+ "esbenp.prettier-vscode",
50
+
51
+ // Pretty TypeScript Errors
52
+ "YoavBls.pretty-ts-errors",
53
+
54
+ // Prisma
55
+ "Prisma.prisma",
56
+
57
+ // ,Todo Tree
58
+ "Gruntfuggly.todo-tree",
59
+
60
+ // Git Tags
61
+ "howardzuo.vscode-git-tags",
62
+
63
+ // JSON Comments
64
+ "wxzhang.json-comments",
65
+
66
+ // Markdown 相关
67
+ "kortina.vscode-markdown-notes",
68
+ "bierner.markdown-mermaid",
69
+ "DavidAnson.vscode-markdownlint",
70
+ "yzhang.markdown-all-in-one",
71
+ "zaaack.markdown-editor",
72
+
73
+ // 半透明背景
74
+ "illixion.vscode-vibrancy-continued",
75
+
76
+ // tailwindcss
77
+ "bradlc.vscode-tailwindcss"
78
+ ]
79
+ }