@qlover/create-app 0.7.14 → 0.7.15
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/CHANGELOG.md +23 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/README.en.md +131 -0
- package/dist/templates/next-app/README.md +115 -20
- package/dist/templates/next-app/docs/en/api.md +387 -0
- package/dist/templates/next-app/docs/en/component.md +544 -0
- package/dist/templates/next-app/docs/en/database.md +496 -0
- package/dist/templates/next-app/docs/en/development-guide.md +727 -0
- package/dist/templates/next-app/docs/en/env.md +563 -0
- package/dist/templates/next-app/docs/en/i18n.md +287 -0
- package/dist/templates/next-app/docs/en/index.md +166 -0
- package/dist/templates/next-app/docs/en/page.md +457 -0
- package/dist/templates/next-app/docs/en/project-structure.md +177 -0
- package/dist/templates/next-app/docs/en/router.md +427 -0
- package/dist/templates/next-app/docs/en/theme.md +532 -0
- package/dist/templates/next-app/docs/en/validator.md +478 -0
- package/dist/templates/next-app/docs/zh/api.md +387 -0
- package/dist/templates/next-app/docs/zh/component.md +544 -0
- package/dist/templates/next-app/docs/zh/database.md +496 -0
- package/dist/templates/next-app/docs/zh/development-guide.md +727 -0
- package/dist/templates/next-app/docs/zh/env.md +563 -0
- package/dist/templates/next-app/docs/zh/i18n.md +287 -0
- package/dist/templates/next-app/docs/zh/index.md +166 -0
- package/dist/templates/next-app/docs/zh/page.md +457 -0
- package/dist/templates/next-app/docs/zh/project-structure.md +177 -0
- package/dist/templates/next-app/docs/zh/router.md +427 -0
- package/dist/templates/next-app/docs/zh/theme.md +532 -0
- package/dist/templates/next-app/docs/zh/validator.md +476 -0
- package/package.json +1 -1
- package/dist/templates/next-app/docs/env.md +0 -94
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# API 开发指南
|
|
2
|
+
|
|
3
|
+
## 目录
|
|
4
|
+
|
|
5
|
+
1. [API 架构概述](#api-架构概述)
|
|
6
|
+
2. [客户端 API 实现](#客户端-api-实现)
|
|
7
|
+
3. [服务端 API 实现](#服务端-api-实现)
|
|
8
|
+
4. [错误处理和验证](#错误处理和验证)
|
|
9
|
+
5. [最佳实践和示例](#最佳实践和示例)
|
|
10
|
+
|
|
11
|
+
## API 架构概述
|
|
12
|
+
|
|
13
|
+
### 1. 整体架构
|
|
14
|
+
|
|
15
|
+
项目采用分层的 API 架构设计:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
客户端层 服务端层
|
|
19
|
+
┌──────────────┐ ┌──────────────┐
|
|
20
|
+
│ API 接口 │ │ API 路由 │
|
|
21
|
+
├──────────────┤ ├──────────────┤
|
|
22
|
+
│ API 服务类 │ HTTP │ 服务层实现 │
|
|
23
|
+
├──────────────┤ 请求 ├──────────────┤
|
|
24
|
+
│ API Requester│ ◄──────► │ 验证层 │
|
|
25
|
+
├──────────────┤ ├──────────────┤
|
|
26
|
+
│ 插件系统 │ │ 数据层 │
|
|
27
|
+
└──────────────┘ └──────────────┘
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. 核心组件
|
|
31
|
+
|
|
32
|
+
- **接口定义**:`AppApiInterface`, `AppUserApiInterface` 等
|
|
33
|
+
- **请求客户端**:`AppApiRequester`, `AdminApiRequester`
|
|
34
|
+
- **服务实现**:`AppUserApi`, `AdminUserApi` 等
|
|
35
|
+
- **插件系统**:`AppApiPlugin`, `RequestEncryptPlugin` 等
|
|
36
|
+
|
|
37
|
+
### 3. 统一响应格式
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// 成功响应
|
|
41
|
+
interface AppApiSuccessInterface<T = unknown> {
|
|
42
|
+
success: true;
|
|
43
|
+
data?: T;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 错误响应
|
|
47
|
+
interface AppApiErrorInterface {
|
|
48
|
+
success: false;
|
|
49
|
+
id: string;
|
|
50
|
+
message?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 统一响应类型
|
|
54
|
+
type AppApiResult<T = unknown> =
|
|
55
|
+
| AppApiSuccessInterface<T>
|
|
56
|
+
| AppApiErrorInterface;
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 客户端 API 实现
|
|
60
|
+
|
|
61
|
+
### 1. API 请求器
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
@injectable()
|
|
65
|
+
export class AppApiRequester extends RequestTransaction<AppApiConfig> {
|
|
66
|
+
constructor(
|
|
67
|
+
@inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
|
|
68
|
+
) {
|
|
69
|
+
super(
|
|
70
|
+
new RequestAdapterFetch({
|
|
71
|
+
baseURL: '/api',
|
|
72
|
+
responseType: 'json'
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. API 服务实现
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
@injectable()
|
|
83
|
+
export class AppUserApi implements AppUserApiInterface {
|
|
84
|
+
constructor(
|
|
85
|
+
@inject(AppApiRequester)
|
|
86
|
+
protected client: RequestTransaction<AppApiConfig>
|
|
87
|
+
) {}
|
|
88
|
+
|
|
89
|
+
async login(params: UserApiLoginTransaction['data']): Promise<AppApiResult> {
|
|
90
|
+
const response = await this.client.request<UserApiLoginTransaction>({
|
|
91
|
+
url: '/user/login',
|
|
92
|
+
method: 'POST',
|
|
93
|
+
data: params,
|
|
94
|
+
encryptProps: 'password' // 自动加密密码字段
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return response.data;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 3. API 插件系统
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
export class AppUserApiBootstrap implements BootstrapExecutorPlugin {
|
|
106
|
+
onBefore({ parameters: { ioc } }: BootstrapContext): void {
|
|
107
|
+
const appUserApi = ioc.get<AppApiRequester>(AppApiRequester);
|
|
108
|
+
|
|
109
|
+
// 注册插件
|
|
110
|
+
appUserApi.usePlugin(new FetchURLPlugin());
|
|
111
|
+
appUserApi.usePlugin(new RequestEncryptPlugin(ioc.get(StringEncryptor)));
|
|
112
|
+
appUserApi.usePlugin(
|
|
113
|
+
new RequestCommonPlugin({
|
|
114
|
+
requestDataSerializer: this.requestDataSerializer.bind(this)
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
appUserApi.usePlugin(new AppApiPlugin(ioc.get(I.Logger)));
|
|
118
|
+
appUserApi.usePlugin(ioc.get(DialogErrorPlugin));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 服务端 API 实现
|
|
124
|
+
|
|
125
|
+
### 1. API 路由处理
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// app/api/admin/users/route.ts
|
|
129
|
+
export async function GET(req: NextRequest) {
|
|
130
|
+
const server = new BootstrapServer();
|
|
131
|
+
|
|
132
|
+
const result = await server
|
|
133
|
+
.use(new AdminAuthPlugin()) // 使用认证插件
|
|
134
|
+
.execNoError(async ({ parameters: { IOC } }) => {
|
|
135
|
+
// 1. 参数验证
|
|
136
|
+
const searchParams = Object.fromEntries(
|
|
137
|
+
req.nextUrl.searchParams.entries()
|
|
138
|
+
);
|
|
139
|
+
const paginationParams = IOC(PaginationValidator).getThrow(searchParams);
|
|
140
|
+
|
|
141
|
+
// 2. 调用服务
|
|
142
|
+
const apiUserService = IOC(ApiUserService);
|
|
143
|
+
const result = await apiUserService.getUsers({
|
|
144
|
+
page: paginationParams.page,
|
|
145
|
+
pageSize: paginationParams.pageSize
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return result;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// 3. 错误处理
|
|
152
|
+
if (result instanceof ExecutorError) {
|
|
153
|
+
return NextResponse.json(new AppErrorApi(result.id, result.message), {
|
|
154
|
+
status: 400
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 4. 成功响应
|
|
159
|
+
return NextResponse.json(new AppSuccessApi(result));
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 2. 服务层实现
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
@injectable()
|
|
167
|
+
export class ApiUserService {
|
|
168
|
+
constructor(
|
|
169
|
+
@inject(IOCIdentifier.UserRepository)
|
|
170
|
+
private userRepository: UserRepositoryInterface
|
|
171
|
+
) {}
|
|
172
|
+
|
|
173
|
+
async getUsers(params: PaginationInterface): Promise<PaginationResult<User>> {
|
|
174
|
+
return this.userRepository.findUsers(params);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 3. 认证和中间件
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
export class AdminAuthPlugin implements BootstrapExecutorPlugin {
|
|
183
|
+
async onBefore(context: BootstrapContext): Promise<void> {
|
|
184
|
+
const { IOC } = context.parameters;
|
|
185
|
+
const serverAuth = IOC(ServerAuth);
|
|
186
|
+
|
|
187
|
+
// 验证管理员权限
|
|
188
|
+
await serverAuth.verifyAdmin();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## 错误处理和验证
|
|
194
|
+
|
|
195
|
+
### 1. 错误响应
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
export class AppErrorApi implements AppApiErrorInterface {
|
|
199
|
+
success = false as const;
|
|
200
|
+
|
|
201
|
+
constructor(
|
|
202
|
+
public id: string,
|
|
203
|
+
public message?: string
|
|
204
|
+
) {}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export class AppSuccessApi<T = unknown> implements AppApiSuccessInterface<T> {
|
|
208
|
+
success = true as const;
|
|
209
|
+
|
|
210
|
+
constructor(public data?: T) {}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 2. 参数验证
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
@injectable()
|
|
218
|
+
export class PaginationValidator implements ValidatorInterface {
|
|
219
|
+
validate(data: unknown): ValidationResult {
|
|
220
|
+
// 实现验证逻辑
|
|
221
|
+
const errors: ValidationError[] = [];
|
|
222
|
+
|
|
223
|
+
if (!this.isValidPageNumber(data.page)) {
|
|
224
|
+
errors.push({ field: 'page', message: 'Invalid page number' });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
valid: errors.length === 0,
|
|
229
|
+
errors
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
getThrow(data: unknown): PaginationInterface {
|
|
234
|
+
const result = this.validate(data);
|
|
235
|
+
if (!result.valid) {
|
|
236
|
+
throw new ValidationError(result.errors);
|
|
237
|
+
}
|
|
238
|
+
return data as PaginationInterface;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## 最佳实践和示例
|
|
244
|
+
|
|
245
|
+
### 1. API 接口定义
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
// 1. 定义接口
|
|
249
|
+
interface UserApiInterface {
|
|
250
|
+
login(params: LoginParams): Promise<AppApiResult<LoginResult>>;
|
|
251
|
+
logout(): Promise<AppApiResult<void>>;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 2. 实现接口
|
|
255
|
+
@injectable()
|
|
256
|
+
class UserApi implements UserApiInterface {
|
|
257
|
+
constructor(
|
|
258
|
+
@inject(AppApiRequester)
|
|
259
|
+
private client: AppApiRequester
|
|
260
|
+
) {}
|
|
261
|
+
|
|
262
|
+
async login(params: LoginParams): Promise<AppApiResult<LoginResult>> {
|
|
263
|
+
// 实现登录逻辑
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async logout(): Promise<AppApiResult<void>> {
|
|
267
|
+
// 实现登出逻辑
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 2. 错误处理最佳实践
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// 1. 使用统一的错误处理
|
|
276
|
+
try {
|
|
277
|
+
const result = await userApi.login(params);
|
|
278
|
+
if (!result.success) {
|
|
279
|
+
// 处理业务错误
|
|
280
|
+
handleBusinessError(result);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// 处理成功响应
|
|
284
|
+
handleSuccess(result.data);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
// 处理网络错误等
|
|
287
|
+
handleNetworkError(error);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// 2. 使用错误插件
|
|
291
|
+
class DialogErrorPlugin implements ExecutorPlugin {
|
|
292
|
+
onError(error: Error, context: ExecutorContext): void {
|
|
293
|
+
// 显示错误对话框
|
|
294
|
+
this.dialogHandler.showError({
|
|
295
|
+
title: '错误',
|
|
296
|
+
content: error.message
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### 3. API 测试示例
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
describe('UserApi', () => {
|
|
306
|
+
let userApi: UserApi;
|
|
307
|
+
let mockClient: jest.Mocked<AppApiRequester>;
|
|
308
|
+
|
|
309
|
+
beforeEach(() => {
|
|
310
|
+
mockClient = {
|
|
311
|
+
request: jest.fn()
|
|
312
|
+
} as any;
|
|
313
|
+
userApi = new UserApi(mockClient);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should handle login success', async () => {
|
|
317
|
+
const mockResponse = {
|
|
318
|
+
success: true,
|
|
319
|
+
data: { token: 'test-token' }
|
|
320
|
+
};
|
|
321
|
+
mockClient.request.mockResolvedValue({ data: mockResponse });
|
|
322
|
+
|
|
323
|
+
const result = await userApi.login({
|
|
324
|
+
email: 'test@example.com',
|
|
325
|
+
password: 'password'
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
expect(result).toEqual(mockResponse);
|
|
329
|
+
expect(mockClient.request).toHaveBeenCalledWith({
|
|
330
|
+
url: '/user/login',
|
|
331
|
+
method: 'POST',
|
|
332
|
+
data: {
|
|
333
|
+
email: 'test@example.com',
|
|
334
|
+
password: 'password'
|
|
335
|
+
},
|
|
336
|
+
encryptProps: 'password'
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 4. API 文档生成
|
|
343
|
+
|
|
344
|
+
使用 TypeScript 类型和注释来生成 API 文档:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
/**
|
|
348
|
+
* 用户登录 API
|
|
349
|
+
* @param params - 登录参数
|
|
350
|
+
* @param params.email - 用户邮箱
|
|
351
|
+
* @param params.password - 用户密码(将自动加密)
|
|
352
|
+
* @returns 登录结果,包含用户令牌
|
|
353
|
+
* @throws {ValidationError} 当参数验证失败时
|
|
354
|
+
* @throws {AuthError} 当认证失败时
|
|
355
|
+
*/
|
|
356
|
+
async login(params: LoginParams): Promise<AppApiResult<LoginResult>>;
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## 总结
|
|
360
|
+
|
|
361
|
+
项目的 API 设计遵循以下原则:
|
|
362
|
+
|
|
363
|
+
1. **分层架构**:
|
|
364
|
+
- 清晰的接口定义
|
|
365
|
+
- 服务层实现业务逻辑
|
|
366
|
+
- 统一的请求处理
|
|
367
|
+
- 插件化的功能扩展
|
|
368
|
+
|
|
369
|
+
2. **类型安全**:
|
|
370
|
+
- 完整的 TypeScript 类型定义
|
|
371
|
+
- 运行时参数验证
|
|
372
|
+
- 编译时类型检查
|
|
373
|
+
|
|
374
|
+
3. **错误处理**:
|
|
375
|
+
- 统一的错误响应格式
|
|
376
|
+
- 插件化的错误处理
|
|
377
|
+
- 完整的错误追踪
|
|
378
|
+
|
|
379
|
+
4. **可扩展性**:
|
|
380
|
+
- 插件系统支持功能扩展
|
|
381
|
+
- 依赖注入实现松耦合
|
|
382
|
+
- 中间件支持横切关注点
|
|
383
|
+
|
|
384
|
+
5. **安全性**:
|
|
385
|
+
- 自动的参数加密
|
|
386
|
+
- 统一的认证机制
|
|
387
|
+
- 参数验证和清洗
|