503 lines
16 KiB
Plaintext
503 lines
16 KiB
Plaintext
# ElysiaAPI开发流程
|
||
|
||
## 0. 概览
|
||
|
||
这是一个基于 **Bun + Elysia** 的现代化后端API项目,采用TypeScript开发,集成了MySQL、Redis、JWT认证、Swagger文档等功能。
|
||
|
||
- **运行时**:Bun
|
||
- **框架**:Elysia
|
||
- **语言**:TypeScript
|
||
- **数据库**:MySQL + Drizzle ORM
|
||
- **缓存**:Redis
|
||
- **认证**:JWT
|
||
- **测试**:Vitest
|
||
- **文档**:Swagger
|
||
- **日志**:Winston
|
||
- **代码规范**:ESLint + Prettier
|
||
|
||
使用的技术和库
|
||
|
||
- **数据库**:`"mysql2": "^3.14.1"`, `"drizzle-orm": "^0.44.2"`
|
||
- **token验证**:`"jsonwebtoken": "^9.0.2"`
|
||
- **密码加密**:`"bcrypt": "^6.0.0"`
|
||
|
||
## 1. 相关目录结构和描述
|
||
|
||
### 1.0 整体目录结构
|
||
|
||
```
|
||
project/
|
||
├── 📋 配置文件(config/)
|
||
├── 📁 源代码 (src/)
|
||
├───└───config
|
||
├───├───eneities
|
||
├───├───modules
|
||
│ ├───├───auth
|
||
│ ├───├───captcha
|
||
│ ├───├───health
|
||
│ ├───├───test
|
||
│ ├───└───user
|
||
├───├───plugins
|
||
│ ├───├───drizzle
|
||
│ ├───├───email
|
||
│ ├───├───errorHandle
|
||
│ ├───├───jwt
|
||
│ ├───├───logger
|
||
│ ├───├───redis
|
||
│ ├───└───swagger
|
||
├───└───tests
|
||
│ └───demo
|
||
├───type
|
||
├───utils
|
||
├── 📁 文档 (docs/)
|
||
├── 📁 需求文档 (prd/)
|
||
├── 📁 任务管理 (tasks/)
|
||
├── 📁 AI对话记录 (aiChat/)
|
||
├── 📁 数据库迁移 (drizzle/)
|
||
└── 📁 静态资源 (public/)
|
||
| 文件 | 说明 |
|
||
|------|------|
|
||
| `package.json` | 项目依赖和脚本配置 |
|
||
| `tsconfig.json` | TypeScript编译配置 |
|
||
| `tsconfig.test.json` | 测试环境TypeScript配置 |
|
||
| `vitest.config.ts` | Vitest测试框架配置 |
|
||
| `eslint.config.js` | ESLint代码规范配置 |
|
||
| `bunfig.toml` | Bun运行时配置 |
|
||
| `drizzle.config.ts` | Drizzle ORM配置 |
|
||
| `README.md` | 项目说明文档 |
|
||
```
|
||
|
||
### 1.1 接口目录结构
|
||
|
||
```
|
||
src/modules/
|
||
├── index.ts # API总入口:所有的模块文件都由此导出给主程序
|
||
├── [moduleName]/ # 模块目录:每个模块的名称做目录名
|
||
│ ├── [moduleName].docs.md # 接口逻辑规则
|
||
│ ├── [moduleName].schema.ts # Schema定义
|
||
│ ├── [moduleName].response.ts # 响应格式
|
||
│ ├── [moduleName].service.ts # 业务逻辑
|
||
│ ├── [moduleName].controller.ts # 路由控制器
|
||
│ └── [moduleName].test.doc # 测试用例文档
|
||
```
|
||
|
||
### 1.2 接口目录功能
|
||
|
||
- 每个模块目录下的文件,都需要有对应的功能,不能只有docs.md,其他文件缺一不可
|
||
- [moduleName].docs.md:需要包含接口开发的业务逻辑,包括schema的注意点,response格式的注意点,接口的业务流程,分析相关的数据库,性能问题,安全问题等等需要考虑的地方和注意点
|
||
- [moduleName].schema.ts:定义接口的输入参数,包括参数的类型、参数的必填、参数的默认值、参数的描述、参数的示例
|
||
- [moduleName].response.ts:接口返回数据的格式定义文件
|
||
- [moduleName].service.ts:接口的具体业务逻辑方法
|
||
- [moduleName].controller.ts:接口路由定义文件,在这里组合接口的请求方式、参数类型、响应格式
|
||
|
||
### 1.3 插件目录结构
|
||
|
||
```
|
||
src/plugins/
|
||
├── index.ts # 插件总入口
|
||
├── drizzle/ # 数据库ORM插件
|
||
│ ├── drizzle.plugins.ts
|
||
│ ├── drizzle.service.ts
|
||
│ └── README.md
|
||
├── email/ # 邮件插件
|
||
│ ├── email.plugins.ts
|
||
│ ├── email.service.ts
|
||
│ └── README.md
|
||
├── errorHandle/ # 错误处理插件
|
||
│ └── errorHandler.plugins.ts
|
||
├── jwt/ # JWT认证插件
|
||
│ ├── jwt.plugins.ts
|
||
│ └── jwt.service.ts
|
||
├── logger/ # 日志插件
|
||
│ ├── logger.plugins.ts
|
||
│ └── logger.service.ts
|
||
├── redis/ # Redis插件
|
||
│ ├── redis.plugins.ts
|
||
│ └── redis.service.ts
|
||
└── swagger/ # API文档插件
|
||
└── swagger.plugins.ts
|
||
```
|
||
|
||
### 1.4 类型定义 (type/)
|
||
```
|
||
src/type/
|
||
├── config.type.ts # 配置相关类型
|
||
├── drizzle.type.ts # 数据库相关类型
|
||
├── error.type.ts # 错误相关类型
|
||
├── jwt.type.ts # JWT相关类型
|
||
├── logger.type.ts # 日志相关类型
|
||
├── redis.type.ts # Redis相关类型
|
||
└── email.type.ts # 邮件相关类型
|
||
```
|
||
|
||
### 1.5 工具函数 (utils/)
|
||
```
|
||
src/utils/
|
||
├── deviceInfo.ts # 设备信息工具
|
||
├── distributedLock.ts # 分布式锁工具
|
||
├── formatFileSize.ts # 文件大小格式化
|
||
├── formatRoute.ts # 路由格式化
|
||
├── jwt.helper.ts # JWT工具函数
|
||
├── mysql.ts # MySQL工具
|
||
├── pagination.ts # 分页请求参数工厂函数工具
|
||
├── randomChalk.ts # 随机颜色工具
|
||
├── redis.ts # Redis工具
|
||
├── responseFormate.ts # 响应统一格式化处理工具
|
||
├── snowflake.ts # 雪花ID生成器
|
||
└── text.ts # 文本处理工具
|
||
```
|
||
|
||
### 1.6 常量定义 (constants/)
|
||
```
|
||
src/constants/
|
||
├── swaggerTags.ts # Swagger标签定义:所有模块的tag应该集中在此定义
|
||
└── 其他常量
|
||
```
|
||
### 1.7 数据库实体
|
||
|
||
```
|
||
src/eneities/
|
||
├── index.ts # 实体总入口
|
||
├── customType.ts # 自定义类型
|
||
└── [table].ts # 数据表定义
|
||
```
|
||
|
||
### 1.8 文件命名约定
|
||
- 模块名使用 **单数形式**:`auth`、`user`、`product`、`order`
|
||
- 文件名格式:`[module].[type].ts`
|
||
- 导出名格式:`[module][类型名]`
|
||
|
||
## 2. Schema(请求参数类型)
|
||
|
||
**主要指请求参数验证工具,不包括响应**
|
||
|
||
### 2.1 Schema定义规范
|
||
|
||
- 一个模块的所有schema都在一个文件中
|
||
- 使用elysia自带的类型共据`t`
|
||
- 参数一定要明确是必填还是选填,是否有默认值
|
||
- 对必要的数据进行转化,如用户名全小写并且去除两端空格
|
||
- 必须有描述和示例参数
|
||
- 导出类型
|
||
- 注意已经存在的工具函数,比如分页有现成的简化工具,且能够统一请求格式
|
||
|
||
### 2.2 特别注意点
|
||
**必须遵循的命名模式:**
|
||
- Request类型:`[动作][模块]Request` → `RegisterRequest`
|
||
- Schema名:`[动作][模块]Schema` → `RegisterSchema`
|
||
|
||
|
||
### 2.3 代码示例
|
||
|
||
```ts
|
||
/**
|
||
* @file 用户模块Schema定义
|
||
* @author AI Assistant
|
||
* @date 2024-12-19
|
||
* @lastEditor AI Assistant
|
||
* @lastEditTime 2025-01-07
|
||
* @description 定义用户模块的Schema,包括获取当前用户信息、用户列表查询等
|
||
*/
|
||
|
||
import { t, type Static } from 'elysia';
|
||
import { createQuerySchema } from '@/utils/pagination';
|
||
|
||
/**
|
||
* 用户列表查询参数Schema
|
||
* @description 用户列表查询的请求参数验证规则
|
||
*/
|
||
export const UserListQuerySchema = createQuerySchema(t.Object({
|
||
// 用户特有参数
|
||
keyword: t.Optional(t.String({
|
||
minLength: 1,
|
||
maxLength: 100,
|
||
description: '搜索关键词,支持用户名、邮箱模糊搜索',
|
||
examples: ['admin', 'test@example.com']
|
||
})),
|
||
status: t.Optional(t.Union([
|
||
t.Literal('active'),
|
||
t.Literal('inactive'),
|
||
t.Literal('pending')
|
||
], {
|
||
description: '用户状态筛选',
|
||
examples: ['active', 'inactive', 'pending']
|
||
})),
|
||
gender: t.Optional(t.Union([
|
||
t.Literal(0),
|
||
t.Literal(1),
|
||
t.Literal(2),
|
||
t.Literal('0'),
|
||
t.Literal('1'),
|
||
t.Literal('2'),
|
||
], {
|
||
description: '性别筛选:0-未知,1-男,2-女',
|
||
examples: [0, 1, 2]
|
||
})),
|
||
isRoot: t.Optional(t.Boolean({
|
||
description: '是否超级管理员筛选',
|
||
examples: [true, false]
|
||
}))
|
||
}));
|
||
|
||
/** 用户列表查询参数类型 */
|
||
export type UserListQueryRequest = Static<typeof UserListQuerySchema>;
|
||
```
|
||
|
||
## 3. Response(响应数据类型)
|
||
|
||
### 3.1 响应格式定义规范
|
||
|
||
- 使用统一的相应格式工具`responseWrapperSchema`
|
||
- 错误相应只提供示例和描述
|
||
- 导出响应成功类型
|
||
|
||
|
||
### 3.2 特别注意点
|
||
**必须遵循的命名模式:**
|
||
- Response格式定义:`[动作][模块]Response` → `RegisterResponse`
|
||
- Response成功类型:`[动作][模块]SuccessType ` → `RegisterSuccessType `
|
||
|
||
### 3.3 代码示例
|
||
|
||
```ts
|
||
/**
|
||
* @file 用户模块响应格式定义
|
||
* @author AI Assistant
|
||
* @date 2024-12-19
|
||
* @lastEditor AI Assistant
|
||
* @lastEditTime 2025-01-07
|
||
* @description 定义用户模块的响应格式,包括获取当前用户信息、用户列表查询等
|
||
*/
|
||
|
||
import { t, type Static } from 'elysia';
|
||
import { responseWrapperSchema } from '@/utils/responseFormate';
|
||
import { createPaginationResponseSchema } from '@/utils/pagination';
|
||
|
||
|
||
|
||
/**
|
||
* 用户列表项Schema
|
||
* @description 用户列表中单个用户的数据结构
|
||
*/
|
||
export const UserListItemSchema = t.Object({
|
||
/** 用户ID */
|
||
id: t.String({
|
||
description: '用户ID(bigint类型以字符串形式返回防止精度丢失)',
|
||
examples: ['1', '2', '3']
|
||
}),
|
||
/** 用户名 */
|
||
username: t.String({
|
||
description: '用户名',
|
||
examples: ['admin', 'testuser']
|
||
}),
|
||
|
||
/** 更多字段... */
|
||
|
||
/** 更新时间 */
|
||
updatedAt: t.String({
|
||
description: '更新时间',
|
||
examples: ['2024-12-19T10:30:00Z']
|
||
})
|
||
});
|
||
|
||
/**
|
||
* 获取用户列表接口响应组合
|
||
* @description 用于Controller中定义所有可能的响应格式
|
||
*/
|
||
export const GetUserListResponsesSchema = {
|
||
200: responseWrapperSchema(createPaginationResponseSchema(UserListItemSchema)),
|
||
401: responseWrapperSchema(t.Object({
|
||
error: t.String({
|
||
description: '认证失败',
|
||
examples: ['未提供有效的认证令牌', '令牌已过期']
|
||
})
|
||
})),
|
||
400: responseWrapperSchema(t.Object({
|
||
error: t.String({
|
||
description: '参数错误',
|
||
examples: ['分页参数无效', '搜索关键词格式错误']
|
||
})
|
||
})),
|
||
500: responseWrapperSchema(t.Object({
|
||
error: t.String({
|
||
description: '服务器错误',
|
||
examples: ['内部服务器错误']
|
||
})
|
||
}))
|
||
};
|
||
|
||
/** 获取用户列表成功响应数据类型 */
|
||
export type GetUserListSuccessType = Static<typeof GetUserListResponsesSchema[200]>;
|
||
```
|
||
|
||
## 4. Service(业务逻辑层)
|
||
|
||
### 4.1 Service层要求
|
||
|
||
- ✅ 所有方法必须有完整的类型注解
|
||
- ✅ 所有方法必须有详细的JSDoc注释
|
||
- ✅ 必须使用统一的响应格式工具
|
||
- ✅ 必须使用统一的错误码
|
||
- ✅ 必须有详细的日志记录,接口有请求响应记录,无需记录每次请求
|
||
- ✅ 需要有完整的错误处理,服务错误有拦截器处理可以不管,只判断逻辑上的错误
|
||
- ✅ 导出单例实例供controller使用
|
||
|
||
### 4.2 注意点
|
||
|
||
- 开发逻辑时,注意数据库实体,必须严格按照数据库字段进行开发,不能出错
|
||
- 更新时间数据库一般会自动天写,不用手动设置或更新
|
||
- 数据库id一般为bigint,注意类型和精度问题
|
||
- 在写入数据时,必要的增加分布式锁
|
||
- 逻辑要清晰,代码要简洁
|
||
|
||
### 4.3 代码示例
|
||
|
||
```ts
|
||
/**
|
||
* @file 用户模块Service层实现
|
||
* @author AI Assistant
|
||
* @date 2024-12-19
|
||
* @lastEditor AI Assistant
|
||
* @lastEditTime 2025-01-07
|
||
* @description 用户模块的业务逻辑实现,包括获取当前用户信息、用户列表查询等
|
||
*/
|
||
|
||
import { Logger } from '@/plugins/logger/logger.service';
|
||
import { db } from '@/plugins/drizzle/drizzle.service';
|
||
import { sysUsers } from '@/eneities';
|
||
import { eq, like, and, desc, asc, sql } from 'drizzle-orm';
|
||
import { successResponse, errorResponse, BusinessError } from '@/utils/responseFormate';
|
||
|
||
import { calculatePagination, normalizePaginationParams } from '@/utils/pagination';
|
||
import type { GetCurrentUserSuccessType, GetUserListSuccessType } from './user.response';
|
||
import type { UserListQueryRequest, UserListItem } from './user.schema';
|
||
|
||
/**
|
||
* 用户服务类
|
||
* @description 处理用户相关的业务逻辑
|
||
*/
|
||
export class UserService {
|
||
/**
|
||
* 获取当前用户信息
|
||
* @param userId 用户ID
|
||
* @returns Promise<GetCurrentUserSuccessType>
|
||
* @throws BusinessError 业务逻辑错误
|
||
* @type API =====================================================================
|
||
*/
|
||
public async getCurrentUser(userId: string): Promise<GetCurrentUserSuccessType> {
|
||
Logger.info(`获取用户信息:${userId}`);
|
||
// 查询用户信息
|
||
const user = await db()
|
||
.select({
|
||
id: sysUsers.id,
|
||
username: sysUsers.username,
|
||
email: sysUsers.email,
|
||
nickname: sysUsers.nickname,
|
||
avatar: sysUsers.avatar,
|
||
mobile: sysUsers.mobile,
|
||
status: sysUsers.status,
|
||
lastLoginAt: sysUsers.lastLoginAt,
|
||
createdAt: sysUsers.createdAt,
|
||
updatedAt: sysUsers.updatedAt
|
||
})
|
||
.from(sysUsers)
|
||
.where(eq(sysUsers.id, BigInt(userId)))
|
||
.limit(1);
|
||
|
||
if (!user || user.length === 0) {
|
||
Logger.warn(`用户不存在:${userId}`);
|
||
throw new BusinessError(
|
||
`用户不存在:${userId}`,
|
||
404
|
||
);
|
||
}
|
||
|
||
const userData = user[0]!;
|
||
|
||
Logger.info(`获取用户信息成功:${userId} - ${userData.username}`);
|
||
|
||
return successResponse({
|
||
id: userId, // 使用传入的字符串ID,避免精度丢失
|
||
username: userData.username,
|
||
email: userData.email,
|
||
nickname: userData.nickname,
|
||
avatar: userData.avatar,
|
||
phone: userData.mobile,
|
||
status: userData.status,
|
||
lastLoginAt: userData.lastLoginAt || null,
|
||
createdAt: userData.createdAt,
|
||
updatedAt: userData.updatedAt
|
||
}, '获取用户信息成功');
|
||
}
|
||
|
||
}
|
||
|
||
// 导出单例实例
|
||
export const userService = new UserService();
|
||
```
|
||
|
||
## 5. Controllers(接口名称接入)
|
||
|
||
### 5.1 Controllers层要求
|
||
|
||
- ✅ 路由方法必须有完整的JSDoc注释
|
||
- ✅ 必须定义完整的response schema
|
||
- ✅ 必须有适当的tags分类
|
||
- ✅ 必须有operationId用于API文档
|
||
- ✅ 错误处理由Service层统一处理
|
||
|
||
### 5.2 Controllers层注意点
|
||
|
||
- 使用service时要简洁,如`({ body }) => authService.register(body),`
|
||
|
||
### 5.3 Controllers层代码示例
|
||
|
||
```ts
|
||
/**
|
||
* 认证控制器
|
||
* @description 处理用户认证相关的HTTP请求
|
||
*/
|
||
export const authController = new Elysia()
|
||
/**
|
||
* 用户注册接口
|
||
* @route POST /api/auth/register
|
||
* @description 用户注册,包含验证码验证、用户名邮箱唯一性检查等
|
||
*/
|
||
.post(
|
||
'/register',
|
||
({ body }) => authService.register(body),
|
||
{
|
||
body: RegisterSchema,
|
||
detail: {
|
||
summary: '用户注册',
|
||
description: '用户注册接口,需要提供用户名、邮箱、密码和验证码',
|
||
tags: [tags.auth],
|
||
operationId: 'registerUser',
|
||
},
|
||
response: RegisterResponsesSchema,
|
||
}
|
||
);
|
||
```
|
||
## 6. 错误处理
|
||
|
||
### 6.1 错误处理规范
|
||
|
||
- 统一`import { BusinessError } from '@/utils/responseFormate';`引入错误工具抛出错误
|
||
- 错误处理在errorHandle.plugins中统一处理,
|
||
|
||
### 6.2 主动抛出异常示例
|
||
|
||
```typescript
|
||
// ✅ Service层错误处理
|
||
import { BusinessError } from '@/utils/responseFormate';
|
||
|
||
// 抛出业务错误
|
||
throw new BusinessError('消息说明...', 409);
|
||
```
|
||
## 7. 测试用例文档
|
||
|
||
1. 分模块
|
||
2. 分接口
|
||
3. 测试名称
|
||
4. 场景
|
||
5. 方法 |