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.
- package/README.md +211 -0
- package/dist/index.js +127 -0
- package/package.json +50 -0
- package/templates/base/.prettierrc +33 -0
- package/templates/base/.vscode/extensions.json +79 -0
- package/templates/base/.vscode/settings.json +70 -0
- package/templates/base/README.md +562 -0
- package/templates/base/eslint.config.ts +26 -0
- package/templates/base/package.json +62 -0
- package/templates/base/src/app.controller.ts +39 -0
- package/templates/base/src/app.module.ts +12 -0
- package/templates/base/src/app.service.ts +30 -0
- package/templates/base/src/common/filters/http-exception.filter.ts +34 -0
- package/templates/base/src/common/interceptors/response.interceptor.ts +38 -0
- package/templates/base/src/index.ts +35 -0
- package/templates/base/src/modules/custom-validation/custom-validation.controller.ts +146 -0
- package/templates/base/src/modules/custom-validation/custom-validation.module.ts +10 -0
- package/templates/base/src/modules/custom-validation/custom-validation.service.ts +33 -0
- package/templates/base/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
- package/templates/base/src/modules/users/dto/user.dto.ts +64 -0
- package/templates/base/src/modules/users/entities/user.entity.ts +9 -0
- package/templates/base/src/modules/users/users.controller.ts +68 -0
- package/templates/base/src/modules/users/users.module.ts +10 -0
- package/templates/base/src/modules/users/users.service.ts +55 -0
- package/templates/base/tsconfig.json +19 -0
- package/templates/base_scalar/.prettierrc +32 -0
- package/templates/base_scalar/.vscode/extensions.json +79 -0
- package/templates/base_scalar/.vscode/settings.json +70 -0
- package/templates/base_scalar/README.md +562 -0
- package/templates/base_scalar/eslint.config.ts +26 -0
- package/templates/base_scalar/package.json +63 -0
- package/templates/base_scalar/src/app.controller.ts +196 -0
- package/templates/base_scalar/src/app.module.ts +12 -0
- package/templates/base_scalar/src/app.service.ts +30 -0
- package/templates/base_scalar/src/common/filters/http-exception.filter.ts +34 -0
- package/templates/base_scalar/src/common/interceptors/response.interceptor.ts +38 -0
- package/templates/base_scalar/src/index.ts +67 -0
- package/templates/base_scalar/src/modules/custom-validation/custom-validation.controller.ts +146 -0
- package/templates/base_scalar/src/modules/custom-validation/custom-validation.module.ts +10 -0
- package/templates/base_scalar/src/modules/custom-validation/custom-validation.service.ts +33 -0
- package/templates/base_scalar/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
- package/templates/base_scalar/src/modules/users/dto/user.dto.ts +64 -0
- package/templates/base_scalar/src/modules/users/entities/user.entity.ts +9 -0
- package/templates/base_scalar/src/modules/users/users.controller.ts +68 -0
- package/templates/base_scalar/src/modules/users/users.module.ts +10 -0
- package/templates/base_scalar/src/modules/users/users.service.ts +55 -0
- package/templates/base_scalar/tsconfig.json +19 -0
- package/templates/cqrs/.prettierrc +33 -0
- package/templates/cqrs/.vscode/extensions.json +79 -0
- package/templates/cqrs/.vscode/settings.json +70 -0
- package/templates/cqrs/README.md +234 -0
- package/templates/cqrs/eslint.config.ts +26 -0
- package/templates/cqrs/package.json +62 -0
- package/templates/cqrs/src/app.controller.ts +28 -0
- package/templates/cqrs/src/app.module.ts +12 -0
- package/templates/cqrs/src/app.service.ts +8 -0
- package/templates/cqrs/src/common/filters/http-exception.filter.ts +34 -0
- package/templates/cqrs/src/common/interceptors/response.interceptor.ts +38 -0
- package/templates/cqrs/src/index.ts +38 -0
- package/templates/cqrs/src/modules/custom-validation/custom-validation.controller.ts +146 -0
- package/templates/cqrs/src/modules/custom-validation/custom-validation.module.ts +10 -0
- package/templates/cqrs/src/modules/custom-validation/custom-validation.service.ts +33 -0
- package/templates/cqrs/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
- package/templates/cqrs/src/modules/users/dto/user.dto.ts +64 -0
- package/templates/cqrs/src/modules/users/entities/user.entity.ts +9 -0
- package/templates/cqrs/src/modules/users/users.controller.ts +68 -0
- package/templates/cqrs/src/modules/users/users.module.ts +10 -0
- package/templates/cqrs/src/modules/users/users.service.ts +55 -0
- package/templates/cqrs/src/test-error-scenarios.ts +54 -0
- package/templates/cqrs/src/users/commands/create-user.command.ts +8 -0
- package/templates/cqrs/src/users/commands/index.ts +2 -0
- package/templates/cqrs/src/users/commands/update-user.command.ts +11 -0
- package/templates/cqrs/src/users/entities/index.ts +1 -0
- package/templates/cqrs/src/users/entities/user.entity.ts +22 -0
- package/templates/cqrs/src/users/events/index.ts +2 -0
- package/templates/cqrs/src/users/events/user-created.event.ts +8 -0
- package/templates/cqrs/src/users/events/user-updated.event.ts +8 -0
- package/templates/cqrs/src/users/handlers/create-user.handler.ts +26 -0
- package/templates/cqrs/src/users/handlers/get-all-users.handler.ts +15 -0
- package/templates/cqrs/src/users/handlers/get-user.handler.ts +15 -0
- package/templates/cqrs/src/users/handlers/index.ts +6 -0
- package/templates/cqrs/src/users/handlers/update-user.handler.ts +33 -0
- package/templates/cqrs/src/users/handlers/user-created.handler.ts +15 -0
- package/templates/cqrs/src/users/handlers/user-updated.handler.ts +15 -0
- package/templates/cqrs/src/users/index.ts +8 -0
- package/templates/cqrs/src/users/queries/get-all-users.query.ts +8 -0
- package/templates/cqrs/src/users/queries/get-user.query.ts +12 -0
- package/templates/cqrs/src/users/queries/index.ts +2 -0
- package/templates/cqrs/src/users/repositories/index.ts +1 -0
- package/templates/cqrs/src/users/repositories/user.repository.ts +51 -0
- package/templates/cqrs/src/users/user.controller.ts +66 -0
- package/templates/cqrs/src/users/user.module.ts +30 -0
- package/templates/cqrs/tsconfig.json +19 -0
- package/templates/cqrs_scalar/.prettierrc +33 -0
- package/templates/cqrs_scalar/.vscode/extensions.json +79 -0
- package/templates/cqrs_scalar/.vscode/settings.json +70 -0
- package/templates/cqrs_scalar/README.md +234 -0
- package/templates/cqrs_scalar/eslint.config.ts +26 -0
- package/templates/cqrs_scalar/package.json +62 -0
- package/templates/cqrs_scalar/src/app.controller.ts +28 -0
- package/templates/cqrs_scalar/src/app.module.ts +12 -0
- package/templates/cqrs_scalar/src/app.service.ts +8 -0
- package/templates/cqrs_scalar/src/common/filters/http-exception.filter.ts +34 -0
- package/templates/cqrs_scalar/src/common/interceptors/response.interceptor.ts +38 -0
- package/templates/cqrs_scalar/src/index.ts +38 -0
- package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.controller.ts +146 -0
- package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.module.ts +10 -0
- package/templates/cqrs_scalar/src/modules/custom-validation/custom-validation.service.ts +33 -0
- package/templates/cqrs_scalar/src/modules/custom-validation/dto/custom-validation.dto.ts +132 -0
- package/templates/cqrs_scalar/src/modules/users/dto/user.dto.ts +64 -0
- package/templates/cqrs_scalar/src/modules/users/entities/user.entity.ts +9 -0
- package/templates/cqrs_scalar/src/modules/users/users.controller.ts +68 -0
- package/templates/cqrs_scalar/src/modules/users/users.module.ts +10 -0
- package/templates/cqrs_scalar/src/modules/users/users.service.ts +55 -0
- package/templates/cqrs_scalar/src/test-error-scenarios.ts +54 -0
- package/templates/cqrs_scalar/src/users/commands/create-user.command.ts +8 -0
- package/templates/cqrs_scalar/src/users/commands/index.ts +2 -0
- package/templates/cqrs_scalar/src/users/commands/update-user.command.ts +11 -0
- package/templates/cqrs_scalar/src/users/entities/index.ts +1 -0
- package/templates/cqrs_scalar/src/users/entities/user.entity.ts +22 -0
- package/templates/cqrs_scalar/src/users/events/index.ts +2 -0
- package/templates/cqrs_scalar/src/users/events/user-created.event.ts +8 -0
- package/templates/cqrs_scalar/src/users/events/user-updated.event.ts +8 -0
- package/templates/cqrs_scalar/src/users/handlers/create-user.handler.ts +26 -0
- package/templates/cqrs_scalar/src/users/handlers/get-all-users.handler.ts +15 -0
- package/templates/cqrs_scalar/src/users/handlers/get-user.handler.ts +15 -0
- package/templates/cqrs_scalar/src/users/handlers/index.ts +6 -0
- package/templates/cqrs_scalar/src/users/handlers/update-user.handler.ts +33 -0
- package/templates/cqrs_scalar/src/users/handlers/user-created.handler.ts +15 -0
- package/templates/cqrs_scalar/src/users/handlers/user-updated.handler.ts +15 -0
- package/templates/cqrs_scalar/src/users/index.ts +8 -0
- package/templates/cqrs_scalar/src/users/queries/get-all-users.query.ts +8 -0
- package/templates/cqrs_scalar/src/users/queries/get-user.query.ts +12 -0
- package/templates/cqrs_scalar/src/users/queries/index.ts +2 -0
- package/templates/cqrs_scalar/src/users/repositories/index.ts +1 -0
- package/templates/cqrs_scalar/src/users/repositories/user.repository.ts +51 -0
- package/templates/cqrs_scalar/src/users/user.controller.ts +66 -0
- package/templates/cqrs_scalar/src/users/user.module.ts +30 -0
- package/templates/cqrs_scalar/tsconfig.json +19 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
import {
|
2
|
+
IsEmail,
|
3
|
+
IsNumber,
|
4
|
+
IsOptional,
|
5
|
+
IsString,
|
6
|
+
Length,
|
7
|
+
Max,
|
8
|
+
Min,
|
9
|
+
} from '@hestjs/validation';
|
10
|
+
|
11
|
+
/**
|
12
|
+
* 创建用户 DTO
|
13
|
+
*/
|
14
|
+
export class CreateUserDto {
|
15
|
+
@IsString({
|
16
|
+
minLength: 2,
|
17
|
+
maxLength: 50,
|
18
|
+
message: '用户名长度必须在2-50字符之间',
|
19
|
+
})
|
20
|
+
name!: string;
|
21
|
+
|
22
|
+
@IsEmail({ message: '请输入有效的邮箱地址' })
|
23
|
+
email!: string;
|
24
|
+
|
25
|
+
@IsNumber({ message: '年龄必须是数字' })
|
26
|
+
@Min(0, { message: '年龄不能小于0' })
|
27
|
+
@Max(120, { message: '年龄不能大于120' })
|
28
|
+
age!: number;
|
29
|
+
|
30
|
+
@IsString({ message: '密码必须是字符串' })
|
31
|
+
@Length(8, 100, { message: '密码长度必须在8-100字符之间' })
|
32
|
+
password!: string;
|
33
|
+
|
34
|
+
@IsOptional()
|
35
|
+
@IsString({ message: '个人简介必须是字符串' })
|
36
|
+
bio?: string;
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* 更新用户 DTO
|
41
|
+
*/
|
42
|
+
export class UpdateUserDto {
|
43
|
+
@IsOptional()
|
44
|
+
@IsString({
|
45
|
+
minLength: 2,
|
46
|
+
maxLength: 50,
|
47
|
+
message: '用户名长度必须在2-50字符之间',
|
48
|
+
})
|
49
|
+
name?: string;
|
50
|
+
|
51
|
+
@IsOptional()
|
52
|
+
@IsEmail({ message: '请输入有效的邮箱地址' })
|
53
|
+
email?: string;
|
54
|
+
|
55
|
+
@IsOptional()
|
56
|
+
@IsNumber({ message: '年龄必须是数字' })
|
57
|
+
@Min(0, { message: '年龄不能小于0' })
|
58
|
+
@Max(120, { message: '年龄不能大于120' })
|
59
|
+
age?: number;
|
60
|
+
|
61
|
+
@IsOptional()
|
62
|
+
@IsString({ message: '个人简介必须是字符串' })
|
63
|
+
bio?: string;
|
64
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import type { HestContext } from '@hestjs/core';
|
2
|
+
import { Context, Controller, Get, Post } from '@hestjs/core';
|
3
|
+
import { Body } from '@hestjs/validation';
|
4
|
+
import { CreateUserDto, UpdateUserDto } from './dto/user.dto';
|
5
|
+
import { UsersService } from './users.service';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* 用户控制器 - 展示验证功能
|
9
|
+
*/
|
10
|
+
@Controller('/users')
|
11
|
+
export class UsersController {
|
12
|
+
constructor(private readonly usersService: UsersService) {}
|
13
|
+
|
14
|
+
@Get('/')
|
15
|
+
async getAllUsers() {
|
16
|
+
return {
|
17
|
+
success: true,
|
18
|
+
data: this.usersService.findAll(),
|
19
|
+
message: 'Users retrieved successfully',
|
20
|
+
};
|
21
|
+
}
|
22
|
+
|
23
|
+
@Get('/:id')
|
24
|
+
async getUser(@Context() c: HestContext) {
|
25
|
+
const id = parseInt(c.req.param('id'));
|
26
|
+
const user = this.usersService.findOne(id);
|
27
|
+
|
28
|
+
if (!user) {
|
29
|
+
return c.json(
|
30
|
+
{
|
31
|
+
success: false,
|
32
|
+
message: 'User not found',
|
33
|
+
},
|
34
|
+
404,
|
35
|
+
);
|
36
|
+
}
|
37
|
+
|
38
|
+
return {
|
39
|
+
success: true,
|
40
|
+
data: user,
|
41
|
+
message: 'User retrieved successfully',
|
42
|
+
};
|
43
|
+
}
|
44
|
+
|
45
|
+
@Post('/')
|
46
|
+
async createUser(@Body(CreateUserDto) createUserDto: CreateUserDto) {
|
47
|
+
const newUser = this.usersService.create(createUserDto);
|
48
|
+
return {
|
49
|
+
success: true,
|
50
|
+
data: newUser,
|
51
|
+
message: 'User created successfully',
|
52
|
+
};
|
53
|
+
}
|
54
|
+
|
55
|
+
@Post('/:id')
|
56
|
+
async updateUser(
|
57
|
+
@Context() c: HestContext,
|
58
|
+
@Body(UpdateUserDto) updateUserDto: UpdateUserDto,
|
59
|
+
) {
|
60
|
+
const id = parseInt(c.req.param('id'));
|
61
|
+
const updatedUser = this.usersService.update(id, updateUserDto);
|
62
|
+
return {
|
63
|
+
success: true,
|
64
|
+
data: updatedUser,
|
65
|
+
message: 'User updated successfully',
|
66
|
+
};
|
67
|
+
}
|
68
|
+
}
|
@@ -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,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
|
+
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
{
|
2
|
+
"prettier.enable": true,
|
3
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
4
|
+
"editor.formatOnSave": true,
|
5
|
+
"[typescript]": {
|
6
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
7
|
+
},
|
8
|
+
"[javascript]": {
|
9
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
10
|
+
},
|
11
|
+
|
12
|
+
// i18n-ally
|
13
|
+
"i18n-ally.enabledParsers": ["json"],
|
14
|
+
"i18n-ally.displayLanguage": "zh-CN",
|
15
|
+
"i18n-ally.localesPaths": ["i18n", "public/messages", "script/i18n"],
|
16
|
+
"i18n-ally.keystyle": "nested",
|
17
|
+
|
18
|
+
// 文件嵌套
|
19
|
+
"explorer.fileNesting.enabled": true,
|
20
|
+
"explorer.fileNesting.expand": true,
|
21
|
+
"explorer.fileNesting.patterns": {
|
22
|
+
"*.ts": "${basename}.md, ${basename}.d.ts, ${basename}.test.ts, ${basename}.spec.ts, ${basename}.stories.ts, ${basename}.stories.tsx",
|
23
|
+
"*.tsx": "${basename}.md, ${basename}.d.ts, ${basename}.test.tsx, ${basename}.spec.tsx, ${basename}.stories.ts, ${basename}.stories.tsx",
|
24
|
+
"*.js": "${basename}.md, ${basename}.d.ts, ${basename}.test.js, ${basename}.spec.js, ${basename}.stories.js, ${basename}.stories.jsx",
|
25
|
+
"*.jsx": "${basename}.md, ${basename}.d.ts, ${basename}.test.jsx, ${basename}.spec.jsx, ${basename}.stories.js, ${basename}.stories.jsx",
|
26
|
+
"*.env": ".env.local, .env.development, .env.production, .env.test",
|
27
|
+
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, .gitignore, .prettierrc, eslint.config.mjs, i18n.ts, components.json, postcss.config.mjs, tsconfig.json, eslint.config.ts, .prettierignore, bun.lock, .dockerignore",
|
28
|
+
"eslint.config.mjs": "eslint.config.cjs, eslint.config.jsx, eslint.config.ts, eslint.config.tsx",
|
29
|
+
"tsconfig.json": "tsconfig.cjs.json, tsconfig.jsx.json, tsconfig.ts.json, tsconfig.tsx.json",
|
30
|
+
"tailwind.config.mjs": "tailwind.config.cjs, tailwind.config.jsx, tailwind.config.ts, tailwind.config.tsx",
|
31
|
+
"next.config.ts": "next.config.cjs, next.config.jsx, next.config.tsx, next-env.d.ts"
|
32
|
+
},
|
33
|
+
|
34
|
+
// ESLint 配置
|
35
|
+
"eslint.enable": true,
|
36
|
+
"eslint.validate": [
|
37
|
+
"javascript",
|
38
|
+
"javascriptreact",
|
39
|
+
"typescript",
|
40
|
+
"typescriptreact"
|
41
|
+
],
|
42
|
+
"editor.codeActionsOnSave": {
|
43
|
+
"source.fixAll.eslint": "explicit",
|
44
|
+
"source.organizeImports": "explicit"
|
45
|
+
},
|
46
|
+
"eslint.alwaysShowStatus": true,
|
47
|
+
|
48
|
+
// TypeScript 配置
|
49
|
+
"typescript.preferences.importModuleSpecifier": "relative",
|
50
|
+
"typescript.suggest.autoImports": true,
|
51
|
+
|
52
|
+
// 额外的文件排除
|
53
|
+
"files.exclude": {
|
54
|
+
"**/.git": true,
|
55
|
+
"**/.svn": true,
|
56
|
+
"**/.hg": true,
|
57
|
+
"**/CVS": true,
|
58
|
+
"**/.DS_Store": true,
|
59
|
+
"**/Thumbs.db": true
|
60
|
+
// "**/node_modules": true,
|
61
|
+
// "**/.next": true
|
62
|
+
},
|
63
|
+
"search.exclude": {
|
64
|
+
"**/node_modules": true,
|
65
|
+
"**/bower_components": true,
|
66
|
+
"**/*.code-search": true,
|
67
|
+
"**/.next": true,
|
68
|
+
"**/dist": true
|
69
|
+
}
|
70
|
+
}
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# @hestjs/cqrs
|
2
|
+
|
3
|
+
HestJS CQRS - Command Query Responsibility Segregation module for HestJS
|
4
|
+
|
5
|
+
## 安装
|
6
|
+
|
7
|
+
```bash
|
8
|
+
npm install @hestjs/cqrs
|
9
|
+
```
|
10
|
+
|
11
|
+
## 基本用法
|
12
|
+
|
13
|
+
### 1. 初始化CQRS模块
|
14
|
+
|
15
|
+
```typescript
|
16
|
+
import { CqrsModule } from "@hestjs/cqrs";
|
17
|
+
|
18
|
+
// 基本初始化
|
19
|
+
CqrsModule.forRoot();
|
20
|
+
|
21
|
+
// 或者使用选项
|
22
|
+
CqrsModule.forRoot({
|
23
|
+
// 自定义配置
|
24
|
+
});
|
25
|
+
```
|
26
|
+
|
27
|
+
### 2. 创建Command
|
28
|
+
|
29
|
+
```typescript
|
30
|
+
import { Command } from "@hestjs/cqrs";
|
31
|
+
|
32
|
+
export class CreateUserCommand extends Command<string> {
|
33
|
+
constructor(
|
34
|
+
public readonly name: string,
|
35
|
+
public readonly email: string
|
36
|
+
) {
|
37
|
+
super();
|
38
|
+
}
|
39
|
+
}
|
40
|
+
```
|
41
|
+
|
42
|
+
### 3. 创建Command Handler
|
43
|
+
|
44
|
+
```typescript
|
45
|
+
import { CommandHandler, ICommandHandler } from "@hestjs/cqrs";
|
46
|
+
import { CreateUserCommand } from "./create-user.command";
|
47
|
+
|
48
|
+
@CommandHandler(CreateUserCommand)
|
49
|
+
export class CreateUserHandler
|
50
|
+
implements ICommandHandler<CreateUserCommand, string>
|
51
|
+
{
|
52
|
+
async execute(command: CreateUserCommand): Promise<string> {
|
53
|
+
// 处理命令逻辑
|
54
|
+
console.log(`Creating user: ${command.name} (${command.email})`);
|
55
|
+
return "user-id-123";
|
56
|
+
}
|
57
|
+
}
|
58
|
+
```
|
59
|
+
|
60
|
+
### 4. 创建Query
|
61
|
+
|
62
|
+
```typescript
|
63
|
+
import { Query, IQueryResult } from "@hestjs/cqrs";
|
64
|
+
|
65
|
+
export class GetUserQuery extends Query<GetUserResult> {
|
66
|
+
constructor(public readonly userId: string) {
|
67
|
+
super();
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
export class GetUserResult implements IQueryResult {
|
72
|
+
constructor(
|
73
|
+
public readonly id: string,
|
74
|
+
public readonly name: string,
|
75
|
+
public readonly email: string
|
76
|
+
) {}
|
77
|
+
}
|
78
|
+
```
|
79
|
+
|
80
|
+
### 5. 创建Query Handler
|
81
|
+
|
82
|
+
```typescript
|
83
|
+
import { QueryHandler, IQueryHandler } from "@hestjs/cqrs";
|
84
|
+
import { GetUserQuery, GetUserResult } from "./get-user.query";
|
85
|
+
|
86
|
+
@QueryHandler(GetUserQuery)
|
87
|
+
export class GetUserHandler
|
88
|
+
implements IQueryHandler<GetUserQuery, GetUserResult>
|
89
|
+
{
|
90
|
+
async execute(query: GetUserQuery): Promise<GetUserResult> {
|
91
|
+
// 查询逻辑
|
92
|
+
return new GetUserResult(query.userId, "John Doe", "john@example.com");
|
93
|
+
}
|
94
|
+
}
|
95
|
+
```
|
96
|
+
|
97
|
+
### 6. 创建Event
|
98
|
+
|
99
|
+
```typescript
|
100
|
+
import { Event } from "@hestjs/cqrs";
|
101
|
+
|
102
|
+
export class UserCreatedEvent extends Event {
|
103
|
+
constructor(
|
104
|
+
public readonly userId: string,
|
105
|
+
public readonly name: string,
|
106
|
+
public readonly email: string
|
107
|
+
) {
|
108
|
+
super(userId);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
```
|
112
|
+
|
113
|
+
### 7. 创建Event Handler
|
114
|
+
|
115
|
+
```typescript
|
116
|
+
import { EventsHandler, IEventHandler } from "@hestjs/cqrs";
|
117
|
+
import { UserCreatedEvent } from "./user-created.event";
|
118
|
+
|
119
|
+
@EventsHandler(UserCreatedEvent)
|
120
|
+
export class UserCreatedHandler implements IEventHandler<UserCreatedEvent> {
|
121
|
+
async handle(event: UserCreatedEvent): Promise<void> {
|
122
|
+
// 处理事件逻辑
|
123
|
+
console.log(`User created: ${event.name} (${event.email})`);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
```
|
127
|
+
|
128
|
+
### 8. 在控制器中使用
|
129
|
+
|
130
|
+
```typescript
|
131
|
+
import { Controller, Post } from "@hestjs/core";
|
132
|
+
import { CommandBus, QueryBus, EventBus } from "@hestjs/cqrs";
|
133
|
+
import { CreateUserCommand } from "./commands/create-user.command";
|
134
|
+
import { GetUserQuery } from "./queries/get-user.query";
|
135
|
+
import { UserCreatedEvent } from "./events/user-created.event";
|
136
|
+
|
137
|
+
@Controller("/users")
|
138
|
+
export class UserController {
|
139
|
+
constructor(
|
140
|
+
private readonly commandBus: CommandBus,
|
141
|
+
private readonly queryBus: QueryBus,
|
142
|
+
private readonly eventBus: EventBus
|
143
|
+
) {}
|
144
|
+
|
145
|
+
@Post()
|
146
|
+
async createUser(data: { name: string; email: string }) {
|
147
|
+
const command = new CreateUserCommand(data.name, data.email);
|
148
|
+
const userId = await this.commandBus.execute(command);
|
149
|
+
|
150
|
+
// 发布事件
|
151
|
+
const event = new UserCreatedEvent(userId, data.name, data.email);
|
152
|
+
await this.eventBus.publish(event);
|
153
|
+
|
154
|
+
return { userId };
|
155
|
+
}
|
156
|
+
|
157
|
+
@Get("/:id")
|
158
|
+
async getUser(id: string) {
|
159
|
+
const query = new GetUserQuery(id);
|
160
|
+
return await this.queryBus.execute(query);
|
161
|
+
}
|
162
|
+
}
|
163
|
+
```
|
164
|
+
|
165
|
+
### 9. 注册处理器
|
166
|
+
|
167
|
+
```typescript
|
168
|
+
import { CqrsModule } from "@hestjs/cqrs";
|
169
|
+
import { CreateUserHandler } from "./handlers/create-user.handler";
|
170
|
+
import { GetUserHandler } from "./handlers/get-user.handler";
|
171
|
+
import { UserCreatedHandler } from "./handlers/user-created.handler";
|
172
|
+
|
173
|
+
// 初始化CQRS模块
|
174
|
+
CqrsModule.forRoot();
|
175
|
+
|
176
|
+
// 获取CQRS模块实例
|
177
|
+
const cqrsModule = CqrsModule.getInstance();
|
178
|
+
|
179
|
+
// 注册处理器
|
180
|
+
cqrsModule.registerHandler(CreateUserHandler);
|
181
|
+
cqrsModule.registerHandler(GetUserHandler);
|
182
|
+
cqrsModule.registerHandler(UserCreatedHandler);
|
183
|
+
|
184
|
+
// 启动应用时调用
|
185
|
+
await cqrsModule.onApplicationBootstrap();
|
186
|
+
```
|
187
|
+
|
188
|
+
### 10. Saga (长期运行的流程)
|
189
|
+
|
190
|
+
```typescript
|
191
|
+
import { Saga, ICommand } from "@hestjs/cqrs";
|
192
|
+
import { UserCreatedEvent } from "./events/user-created.event";
|
193
|
+
import { SendWelcomeEmailCommand } from "./commands/send-welcome-email.command";
|
194
|
+
|
195
|
+
@Saga()
|
196
|
+
export class UserSaga {
|
197
|
+
// 当用户创建事件发生时,发送欢迎邮件
|
198
|
+
async onUserCreatedEvent(event: UserCreatedEvent): Promise<ICommand[]> {
|
199
|
+
return [new SendWelcomeEmailCommand(event.userId, event.email)];
|
200
|
+
}
|
201
|
+
}
|
202
|
+
```
|
203
|
+
|
204
|
+
## API 参考
|
205
|
+
|
206
|
+
### 装饰器
|
207
|
+
|
208
|
+
- `@CommandHandler(command)` - 标记命令处理器
|
209
|
+
- `@QueryHandler(query)` - 标记查询处理器
|
210
|
+
- `@EventsHandler(...events)` - 标记事件处理器
|
211
|
+
- `@Saga()` - 标记Saga
|
212
|
+
|
213
|
+
### 基类
|
214
|
+
|
215
|
+
- `Command<T>` - 命令基类,T为返回类型
|
216
|
+
- `Query<T>` - 查询基类,T为结果类型
|
217
|
+
- `Event` - 事件基类
|
218
|
+
|
219
|
+
### 总线
|
220
|
+
|
221
|
+
- `CommandBus` - 命令总线
|
222
|
+
- `QueryBus` - 查询总线
|
223
|
+
- `EventBus` - 事件总线
|
224
|
+
|
225
|
+
### 接口
|
226
|
+
|
227
|
+
- `ICommandHandler<TCommand, TResult>` - 命令处理器接口
|
228
|
+
- `IQueryHandler<TQuery, TResult>` - 查询处理器接口
|
229
|
+
- `IEventHandler<TEvent>` - 事件处理器接口
|
230
|
+
- `ISaga<TEvent>` - Saga接口
|
231
|
+
|
232
|
+
## 许可证
|
233
|
+
|
234
|
+
MIT
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import wfConfig from '@hestjs/eslint-config';
|
2
|
+
|
3
|
+
export default [
|
4
|
+
...wfConfig,
|
5
|
+
{
|
6
|
+
files: ['**/*.ts', '**/*.tsx'],
|
7
|
+
rules: {
|
8
|
+
'no-console': 'error', // Demo 应用允许使用 console 进行调试
|
9
|
+
},
|
10
|
+
},
|
11
|
+
{
|
12
|
+
// 忽略构建产物和临时文件
|
13
|
+
ignores: [
|
14
|
+
'node_modules/**',
|
15
|
+
'dist/**',
|
16
|
+
'build/**',
|
17
|
+
'*.d.ts',
|
18
|
+
'.bun/**',
|
19
|
+
'bun.lockb',
|
20
|
+
// shadcn 目录
|
21
|
+
'src/components/ui/**',
|
22
|
+
// scripts 目录
|
23
|
+
'scripts/**',
|
24
|
+
],
|
25
|
+
},
|
26
|
+
];
|