1017 lines
30 KiB
Plaintext
1017 lines
30 KiB
Plaintext
---
|
||
description: "Bun Elysia框架业务开发规则"
|
||
globs: ["**/*"]
|
||
alwaysApply: true
|
||
---
|
||
|
||
# Bun Elysia框架业务开发规则
|
||
|
||
|
||
## 0. 概览
|
||
|
||
这是一个基于 **Bun + Elysia** 的现代化后端API项目,采用TypeScript开发,集成了MySQL、Redis、JWT认证、Swagger文档等功能。
|
||
|
||
- **运行时**:Bun
|
||
- **框架**:Elysia
|
||
- **语言**:TypeScript
|
||
- **数据库**:MySQL + Drizzle ORM
|
||
- **缓存**:Redis
|
||
- **认证**:JWT
|
||
- **测试**:Vitest
|
||
- **文档**:Swagger
|
||
- **日志**:Winston
|
||
- **代码规范**:ESLint + Prettier
|
||
|
||
### 📂 根目录结构
|
||
|
||
```
|
||
project/
|
||
├── 📋 配置文件(config/)
|
||
├── 📁 源代码 (src/)
|
||
├── 📁 文档 (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` | 项目说明文档 |
|
||
|
||
### 应用入口
|
||
```
|
||
src/
|
||
├── app.ts # Elysia应用主入口
|
||
└── server.ts # 服务器启动文件
|
||
```
|
||
|
||
### 配置管理 (config/)
|
||
```
|
||
src/config/
|
||
├── index.ts # 配置总入口
|
||
├── db.config.ts # 数据库配置
|
||
├── redis.config.ts # Redis配置
|
||
├── jwt.config.ts # JWT配置
|
||
├── logger.config.ts # 日志配置
|
||
└── email.config.ts # 邮件配置
|
||
```
|
||
|
||
### 数据实体 (eneities/)
|
||
```
|
||
src/eneities/
|
||
├── index.ts # 实体总入口
|
||
├── customType.ts # 自定义类型
|
||
└── [table].ts # 数据表定义
|
||
```
|
||
|
||
### 业务模块 (modules/)
|
||
```
|
||
src/modules/
|
||
├── index.ts # 模块总入口
|
||
├── tags.ts # Swagger标签定义
|
||
├── auth/ # 认证模块
|
||
│ ├── auth.schema.ts # Schema定义
|
||
│ ├── auth.response.ts # 响应格式
|
||
│ ├── auth.service.ts # 业务逻辑
|
||
│ ├── auth.controller.ts # 路由控制器
|
||
│ └── auth.test.ts # 测试用例
|
||
├── captcha/ # 验证码模块
|
||
│ ├── captcha.schema.ts
|
||
│ ├── captcha.service.ts
|
||
│ ├── captcha.controller.ts
|
||
│ └── captcha.test.ts
|
||
├── health/ # 健康检查模块
|
||
│ ├── health.controller.ts
|
||
│ └── health.service.ts
|
||
└── test/ # 测试模块
|
||
└── test.controller.ts
|
||
```
|
||
|
||
### 插件系统 (plugins/)
|
||
```
|
||
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
|
||
```
|
||
|
||
### 类型定义 (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 # 邮件相关类型
|
||
```
|
||
|
||
### 工具函数 (utils/)
|
||
```
|
||
src/utils/
|
||
├── deviceInfo.ts # 设备信息工具
|
||
├── formatFileSize.ts # 文件大小格式化
|
||
├── formatRoute.ts # 路由格式化
|
||
├── jwt.helper.ts # JWT工具函数
|
||
├── mysql.ts # MySQL工具
|
||
├── randomChalk.ts # 随机颜色工具
|
||
├── redis.ts # Redis工具
|
||
├── text.ts # 文本处理工具
|
||
├── responseFormate.ts # 响应格式化工具
|
||
└── snowflake.ts # 雪花ID生成器
|
||
```
|
||
|
||
### 测试文件 (tests/)
|
||
```
|
||
src/tests/
|
||
├── app.test.ts # 应用测试
|
||
├── health.test.ts # 健康检查测试
|
||
├── mysql.test.ts # MySQL测试
|
||
├── redis.test.ts # Redis测试
|
||
├── email.test.ts # 邮件测试
|
||
├── swagger.test.ts # Swagger测试
|
||
└── demo/
|
||
├── testLogger.ts # 日志测试演示
|
||
└── emailDemo.ts # 邮件测试演示
|
||
```
|
||
|
||
### 常量定义 (constants/)
|
||
```
|
||
src/constants/
|
||
```
|
||
|
||
## 1. 文件组织规范
|
||
|
||
### 1.1 必须的文件结构
|
||
每个业务模块必须包含以下文件,**按照固定顺序**:
|
||
|
||
```
|
||
src/modules/[module]/
|
||
├── [module].schema.ts # 1️⃣ 数据结构定义(最高优先级)
|
||
├── [module].response.ts # 2️⃣ 响应格式定义
|
||
├── [module].service.ts # 3️⃣ 业务逻辑实现
|
||
├── [module].controller.ts # 4️⃣ 路由控制器
|
||
└── [module].test.ts # 5️⃣ 测试用例
|
||
└── [module].test.md # 5️⃣ 测试用例文档
|
||
```
|
||
|
||
### 1.2 文件命名约定
|
||
- 模块名使用 **单数形式**:`auth`、`user`、`product`、`order`
|
||
- 文件名格式:`[module].[type].ts`
|
||
- 导出名格式:`[module][类型名]`
|
||
|
||
## 2. Schema & 类型系统(🔥 重点优化)
|
||
|
||
### 2.1 Schema定义规范
|
||
|
||
```typescript
|
||
/**
|
||
* @file 认证模块Schema定义
|
||
* @author AI Assistant
|
||
* @date 2024-12-19
|
||
* @lastEditor AI Assistant
|
||
* @lastEditTime 2025-07-06
|
||
* @description 定义认证模块的Schema,包括用户注册、邮箱激活等
|
||
*/
|
||
|
||
import { t, type Static } from 'elysia';
|
||
|
||
/**
|
||
* 用户注册Schema
|
||
* @description 用户注册请求参数验证规则,基于sys_users表结构
|
||
*/
|
||
export const RegisterSchema = t.Object({
|
||
/** 用户名,2-50字符,对应sys_users.username */
|
||
username: t.String({
|
||
minLength: 2,
|
||
maxLength: 50,
|
||
description: '用户名,2-50字符',
|
||
examples: ['root', 'testuser']
|
||
}),
|
||
/** 邮箱地址,对应sys_users.email */
|
||
email: t.String({
|
||
format: 'email',
|
||
maxLength: 100,
|
||
description: '邮箱地址',
|
||
examples: ['x71291@outlook.com']
|
||
}),
|
||
/** 密码,6-50字符 */
|
||
password: t.String({
|
||
minLength: 6,
|
||
maxLength: 50,
|
||
description: '密码,6-50字符',
|
||
examples: ['password123']
|
||
}),
|
||
/** 图形验证码 */
|
||
captcha: t.String({
|
||
minLength: 4,
|
||
maxLength: 6,
|
||
description: '图形验证码',
|
||
examples: ['a1b2']
|
||
}),
|
||
/** 验证码会话ID */
|
||
captchaId: t.String({
|
||
description: '验证码会话ID',
|
||
examples: ['cap']
|
||
})
|
||
});
|
||
|
||
/**
|
||
* 邮箱激活Schema
|
||
* @description 邮箱激活请求参数验证规则
|
||
*/
|
||
export const ActivateSchema = t.Object({
|
||
/** 激活令牌,JWT格式 */
|
||
token: t.String({
|
||
minLength: 10,
|
||
maxLength: 1000,
|
||
description: '邮箱激活令牌,JWT格式,24小时有效',
|
||
examples: ['eyJhbGciOiJIUzI1NiI']
|
||
})
|
||
});
|
||
|
||
/**
|
||
* 用户登录Schema
|
||
* @description 用户登录请求参数验证规则
|
||
*/
|
||
export const LoginSchema = t.Object({
|
||
/** 用户名/邮箱地址,2-50字符,对应sys_users.username */
|
||
identifier: t.String({
|
||
minLength: 2,
|
||
maxLength: 100,
|
||
description: '用户名/邮箱地址,100字符',
|
||
examples: ['root', 'testuser', 'x71291@outlook.com']
|
||
}),
|
||
/** 图形验证码(可选) */
|
||
captcha: t.Optional(t.String({
|
||
minLength: 4,
|
||
maxLength: 6,
|
||
description: '图形验证码,登录失败次数过多时需要',
|
||
examples: ['a1b2']
|
||
})),
|
||
/** 密码,6-50字符 */
|
||
password: t.String({
|
||
minLength: 6,
|
||
maxLength: 50,
|
||
description: '密码,6-50字符',
|
||
examples: ['password123']
|
||
}),
|
||
/** 验证码会话ID(可选) */
|
||
captchaId: t.Optional(t.String({
|
||
description: '验证码会话ID,与captcha配对使用',
|
||
examples: ['cap']
|
||
})),
|
||
/** 是否记住登录状态 */
|
||
rememberMe: t.Optional(t.Boolean({
|
||
description: '是否记住登录状态,影响token过期时间',
|
||
examples: [true, false],
|
||
default: false
|
||
}))
|
||
});
|
||
|
||
/** 用户注册请求类型 */
|
||
export type RegisterRequest = Static<typeof RegisterSchema>;
|
||
|
||
/** 邮箱激活请求类型 */
|
||
export type ActivateRequest = Static<typeof ActivateSchema>;
|
||
|
||
/** 用户登录请求类型 */
|
||
export type LoginRequest = Static<typeof LoginSchema>;
|
||
```
|
||
|
||
### 2.2 类型导出规范
|
||
|
||
**必须遵循的命名模式:**
|
||
- Request类型:`[动作][模块]Request` → `RegisterRequest`
|
||
- Response类型:`[动作][模块]Response` → `RegisterResponse`
|
||
- Schema名:`[动作][模块]Schema` → `RegisterSchema`
|
||
- 数据模型:`[模块]Data` → `UserData`
|
||
|
||
### 2.3 Schema定义最佳实践
|
||
|
||
```typescript
|
||
// ✅ 完整的Schema定义示例
|
||
export const CreateUserSchema = t.Object({
|
||
username: t.String({
|
||
minLength: 2,
|
||
maxLength: 50,
|
||
pattern: '^[a-zA-Z0-9_-]+$', // 添加正则验证
|
||
description: '用户名,2-50字符,仅允许字母、数字、下划线、连字符',
|
||
examples: ['admin', 'user123', 'test_user']
|
||
}),
|
||
email: t.String({
|
||
format: 'email',
|
||
maxLength: 100,
|
||
description: '邮箱地址',
|
||
examples: ['user@example.com', 'admin@company.com']
|
||
}),
|
||
age: t.Optional(t.Number({
|
||
minimum: 0,
|
||
maximum: 150,
|
||
description: '年龄,可选字段',
|
||
examples: [25, 30, 35]
|
||
})),
|
||
tags: t.Optional(t.Array(t.String({
|
||
maxLength: 20
|
||
}), {
|
||
description: '标签列表',
|
||
examples: [['developer', 'admin'], ['user']]
|
||
}))
|
||
});
|
||
```
|
||
|
||
## 3. 响应格式规范(🔥 重点优化)
|
||
|
||
### 3.1 统一响应格式工具
|
||
|
||
```typescript
|
||
// ✅ 使用 responseFormate.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';
|
||
|
||
// ========== 邮箱注册相关响应格式 ==========
|
||
|
||
/**
|
||
* 用户注册接口响应组合
|
||
* @description 用于Controller中定义所有可能的响应格式
|
||
*/
|
||
export const RegisterResponsesSchema = {
|
||
200: responseWrapperSchema(t.Object({
|
||
/** 用户ID */
|
||
id: t.String({
|
||
description: '用户ID(bigint类型以字符串形式返回防止精度丢失)',
|
||
examples: ['1', '2', '3']
|
||
}),
|
||
/** 用户名 */
|
||
username: t.String({
|
||
description: '用户名',
|
||
examples: ['admin', 'testuser']
|
||
}),
|
||
/** 邮箱地址 */
|
||
email: t.String({
|
||
description: '邮箱地址',
|
||
examples: ['user@example.com']
|
||
}),
|
||
/** 账号状态 */
|
||
status: t.String({
|
||
description: '账号状态',
|
||
examples: ['pending', 'active']
|
||
}),
|
||
/** 创建时间 */
|
||
createdAt: t.String({
|
||
description: '创建时间',
|
||
examples: ['2024-12-19T10:30:00Z']
|
||
})
|
||
})),
|
||
};
|
||
/** 用户注册成功响应数据类型 */
|
||
export type RegisterResponsesType = Static<typeof RegisterResponsesSchema[200]>;
|
||
|
||
// ========== 邮箱激活相关响应格式 ==========
|
||
|
||
/**
|
||
* 邮箱激活接口响应组合
|
||
* @description 用于Controller中定义所有可能的响应格式
|
||
*/
|
||
export const ActivateResponsesSchema = {
|
||
200: responseWrapperSchema(t.Object({
|
||
/** 用户ID */
|
||
id: t.String({
|
||
description: '用户ID(bigint类型以字符串形式返回防止精度丢失)',
|
||
examples: ['1', '2', '3']
|
||
}),
|
||
/** 用户名 */
|
||
username: t.String({
|
||
description: '用户名',
|
||
examples: ['admin', 'testuser']
|
||
}),
|
||
/** 邮箱地址 */
|
||
email: t.String({
|
||
description: '邮箱地址',
|
||
examples: ['user@example.com']
|
||
}),
|
||
/** 账号状态 */
|
||
status: t.String({
|
||
description: '账号状态',
|
||
examples: ['active']
|
||
}),
|
||
/** 激活时间 */
|
||
updatedAt: t.String({
|
||
description: '激活时间',
|
||
examples: ['2024-12-19T10:30:00Z']
|
||
}),
|
||
/** 激活成功标识 */
|
||
activated: t.Boolean({
|
||
description: '是否已激活',
|
||
examples: [true]
|
||
})
|
||
})),
|
||
};
|
||
|
||
/** 邮箱激活成功响应数据类型 */
|
||
export type ActivateSuccessType = Static<typeof ActivateResponsesSchema[200]>;
|
||
|
||
// ========== 用户登录相关响应格式 ==========
|
||
|
||
/**
|
||
* 用户登录接口响应组合
|
||
* @description 用于Controller中定义所有可能的响应格式
|
||
*/
|
||
export const LoginResponsesSchema = {
|
||
200: responseWrapperSchema(t.Object({
|
||
/** 用户基本信息 */
|
||
user: t.Object({
|
||
/** 用户ID */
|
||
id: t.String({
|
||
description: '用户ID(bigint类型以字符串形式返回防止精度丢失)',
|
||
examples: ['1', '2', '3']
|
||
}),
|
||
/** 用户名 */
|
||
username: t.String({
|
||
description: '用户名',
|
||
examples: ['admin', 'testuser']
|
||
}),
|
||
/** 邮箱地址 */
|
||
email: t.String({
|
||
description: '邮箱地址',
|
||
examples: ['user@example.com']
|
||
}),
|
||
/** 账号状态 */
|
||
status: t.String({
|
||
description: '账号状态',
|
||
examples: ['active']
|
||
}),
|
||
/** 最后登录时间 */
|
||
lastLoginAt: t.Union([t.String(), t.Null()], {
|
||
description: '最后登录时间',
|
||
examples: ['2024-12-19T10:30:00Z', null]
|
||
})
|
||
}),
|
||
/** 认证令牌信息 */
|
||
tokens: t.Object({
|
||
/** 访问令牌 */
|
||
accessToken: t.String({
|
||
description: 'JWT访问令牌',
|
||
examples: ['eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...']
|
||
}),
|
||
/** 刷新令牌 */
|
||
refreshToken: t.String({
|
||
description: 'JWT刷新令牌',
|
||
examples: ['eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...']
|
||
}),
|
||
/** 令牌类型 */
|
||
tokenType: t.String({
|
||
description: '令牌类型',
|
||
examples: ['Bearer']
|
||
}),
|
||
/** 过期时间(秒) */
|
||
expiresIn: t.String({
|
||
description: '访问令牌过期时间(秒)',
|
||
examples: [7200, 86400]
|
||
}),
|
||
/** 刷新令牌过期时间(秒) */
|
||
refreshExpiresIn: t.String({
|
||
description: '刷新令牌过期时间(秒)',
|
||
examples: [2592000]
|
||
})
|
||
})
|
||
})),
|
||
};
|
||
|
||
/** 用户登录成功响应数据类型 */
|
||
export type LoginSuccessType = Static<typeof LoginResponsesSchema[200]>;
|
||
```
|
||
|
||
### 3.2 Response定义规范
|
||
|
||
```typescript
|
||
// ✅ 优化示例 - auth.response.ts
|
||
/**
|
||
* @file 认证模块响应格式定义
|
||
* @author 开发者姓名
|
||
* @date 2024-12-19
|
||
* @description 定义认证模块的响应格式,包括成功和错误响应
|
||
*/
|
||
|
||
import { t, type Static } from 'elysia';
|
||
import { responseWrapperSchema } from '@/utils/responseFormate';
|
||
|
||
/**
|
||
* 用户注册接口响应组合
|
||
* @description 用于Controller中定义所有可能的响应格式
|
||
*/
|
||
export const RegisterResponsesSchema = {
|
||
200: responseWrapperSchema(t.Object({
|
||
/** 用户ID,以字符串形式返回避免精度丢失 */
|
||
id: t.String({
|
||
description: '用户ID',
|
||
examples: ['1', '2', '3']
|
||
}),
|
||
/** 用户名 */
|
||
username: t.String({
|
||
description: '用户名',
|
||
examples: ['admin', 'testuser']
|
||
}),
|
||
/** 邮箱地址 */
|
||
email: t.String({
|
||
description: '邮箱地址',
|
||
examples: ['user@example.com']
|
||
}),
|
||
/** 账号状态 */
|
||
status: t.String({
|
||
description: '账号状态',
|
||
examples: ['pending', 'active']
|
||
}),
|
||
/** 创建时间 */
|
||
createdAt: t.String({
|
||
description: '创建时间',
|
||
examples: ['2024-12-19T10:30:00Z']
|
||
})
|
||
})),
|
||
400: responseWrapperSchema(t.Object({
|
||
error: t.String({
|
||
description: '错误信息',
|
||
examples: ['用户名已存在', '邮箱已被注册']
|
||
})
|
||
})),
|
||
500: responseWrapperSchema(t.Object({
|
||
error: t.String({
|
||
description: '服务器错误',
|
||
examples: ['内部服务器错误']
|
||
})
|
||
}))
|
||
};
|
||
// 注意,不需要定义错误响应的具体格式,只用给出可能错误的情况的说明和示例即可,一下就是正确的错误示例
|
||
`
|
||
500: responseWrapperSchema(t.Object({
|
||
error: t.String({
|
||
description: '服务器错误',
|
||
examples: ['内部服务器错误']
|
||
})
|
||
}))
|
||
`
|
||
/** 用户注册成功响应数据类型 */
|
||
export type RegisterSuccessResponse = Static<typeof RegisterResponsesSchema[200]>;
|
||
```
|
||
|
||
## 4. Service层规范(🔥 重点优化)
|
||
|
||
### 4.1 Service类定义
|
||
|
||
```typescript
|
||
// ✅ 优化示例 - auth.service.ts
|
||
/**
|
||
* @file 认证模块Service层实现
|
||
* @author 开发者姓名
|
||
* @date 2024-12-19
|
||
* @description 认证模块的业务逻辑实现,包括用户注册、登录等
|
||
*/
|
||
|
||
import { Logger } from '@/plugins/logger/logger.service';
|
||
import { successResponse, errorResponse, BusinessError } from '@/utils/responseFormate';
|
||
import { ERROR_CODES } from '@/constants/error-codes';
|
||
import type { RegisterRequest } from './auth.schema';
|
||
import type { RegisterSuccessResponse } from './auth.response';
|
||
|
||
/**
|
||
* 认证服务类
|
||
* @description 处理用户注册、登录等认证相关业务逻辑
|
||
*/
|
||
export class AuthService {
|
||
/**
|
||
* 用户注册
|
||
* @param request 用户注册请求参数
|
||
* @returns Promise<RegisterSuccessResponse>
|
||
* @throws BusinessError 业务逻辑错误
|
||
* @type API =====================================================================
|
||
*/
|
||
public async register(request: RegisterRequest): Promise<RegisterSuccessResponse> {
|
||
Logger.info(`用户注册请求:${JSON.stringify({ ...request, password: '***' })}`);
|
||
|
||
try {
|
||
// 1. 验证验证码
|
||
await this.validateCaptcha(request.captcha, request.captchaId);
|
||
|
||
// 2. 检查用户名是否已存在
|
||
await this.checkUsernameExists(request.username);
|
||
|
||
// 3. 检查邮箱是否已存在
|
||
await this.checkEmailExists(request.email);
|
||
|
||
// 4. 创建用户
|
||
const user = await this.createUser(request);
|
||
|
||
Logger.info(`用户注册成功:${user.id} - ${user.username}`);
|
||
|
||
return successResponse({
|
||
id: user.id,
|
||
username: user.username,
|
||
email: user.email,
|
||
status: user.status,
|
||
createdAt: user.createdAt
|
||
}, '用户注册成功,请查收激活邮件');
|
||
|
||
} catch (error) {
|
||
Logger.error(new Error(`用户注册失败:${error}`));
|
||
|
||
if (error instanceof BusinessError) {
|
||
throw error;
|
||
}
|
||
|
||
throw new BusinessError(
|
||
ERROR_CODES.INTERNAL_ERROR,
|
||
500
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证验证码
|
||
* @param captcha 验证码
|
||
* @param captchaId 验证码ID
|
||
* @throws BusinessError 验证失败时抛出
|
||
*/
|
||
private async validateCaptcha(captcha: string, captchaId: string): Promise<void> {
|
||
// 验证逻辑...
|
||
if (!isValidCaptcha) {
|
||
throw new BusinessError(
|
||
ERROR_CODES.CAPTCHA_ERROR,
|
||
400
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查用户名是否已存在
|
||
* @param username 用户名
|
||
* @throws BusinessError 用户名已存在时抛出
|
||
*/
|
||
private async checkUsernameExists(username: string): Promise<void> {
|
||
// 检查逻辑...
|
||
if (exists) {
|
||
throw new BusinessError(
|
||
ERROR_CODES.USERNAME_EXISTS,
|
||
409
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 导出单例实例
|
||
export const authService = new AuthService();
|
||
```
|
||
|
||
### 4.2 Service层要求
|
||
|
||
- ✅ 所有方法必须有完整的类型注解
|
||
- ✅ 所有方法必须有详细的JSDoc注释
|
||
- ✅ 必须使用统一的响应格式工具
|
||
- ✅ 必须使用统一的错误码
|
||
- ✅ 必须有详细的日志记录
|
||
- ✅ 必须有完整的错误处理
|
||
- ✅ 导出单例实例供controller使用
|
||
|
||
## 5. Controller层规范(🔥 重点优化)
|
||
|
||
### 5.1 Controller定义
|
||
|
||
```typescript
|
||
// ✅ 优化示例 - auth.controller.ts
|
||
/**
|
||
* @file 认证模块Controller层实现
|
||
* @author 开发者姓名
|
||
* @date 2024-12-19
|
||
* @description 认证模块的路由控制器,处理HTTP请求
|
||
*/
|
||
|
||
import { Elysia } from 'elysia';
|
||
import { RegisterSchema } from './auth.schema';
|
||
import { RegisterResponsesSchema } from './auth.response';
|
||
import { authService } from './auth.service';
|
||
import { tags } from '@/modules/tags';
|
||
|
||
/**
|
||
* 认证控制器
|
||
* @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,
|
||
}
|
||
);
|
||
```
|
||
|
||
### 5.2 Controller层要求
|
||
|
||
- ✅ 路由方法必须有完整的JSDoc注释
|
||
- ✅ 必须定义完整的response schema
|
||
- ✅ 必须有适当的tags分类
|
||
- ✅ 必须有operationId用于API文档
|
||
- ✅ 错误处理由Service层统一处理
|
||
|
||
## 6. 错误处理规范(🔥 重点优化)
|
||
|
||
### 6.2 错误处理最佳实践
|
||
|
||
```typescript
|
||
// ✅ Service层错误处理
|
||
import { BusinessError } from '@/utils/responseFormate';
|
||
|
||
// 抛出业务错误
|
||
throw new BusinessError(ERROR_CODES.USERNAME_EXISTS, 409);
|
||
|
||
// ✅ 统一错误响应格式
|
||
export const errorResponse = (code: number, message: string, type: string, data: any = null) => {
|
||
const response = {
|
||
code,
|
||
message,
|
||
data,
|
||
type,
|
||
timestamp: new Date().toISOString(),
|
||
};
|
||
Logger.warn(response);
|
||
return response;
|
||
};
|
||
```
|
||
|
||
## 7. 测试规范
|
||
|
||
### 7.1 测试文件结构
|
||
|
||
```typescript
|
||
// ✅ 优化示例 - auth.test.ts
|
||
/**
|
||
* @file 认证模块测试用例
|
||
* @author 开发者姓名
|
||
* @date 2024-12-19
|
||
* @description 认证模块的单元测试和集成测试
|
||
*/
|
||
|
||
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||
import { app } from '@/app';
|
||
import type { RegisterRequest } from './auth.schema';
|
||
|
||
describe('Auth API', () => {
|
||
beforeAll(async () => {
|
||
// 测试环境初始化
|
||
});
|
||
|
||
afterAll(async () => {
|
||
// 测试环境清理
|
||
});
|
||
|
||
describe('POST /api/auth/register', () => {
|
||
it('应该成功注册用户', async () => {
|
||
const payload: RegisterRequest = {
|
||
username: 'testuser',
|
||
email: 'test@example.com',
|
||
password: 'password123',
|
||
captcha: 'a1b2',
|
||
captchaId: 'test_captcha_id'
|
||
};
|
||
|
||
const response = await app
|
||
.handle(new Request('http://localhost/api/auth/register', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(payload),
|
||
}));
|
||
|
||
expect(response.status).toBe(200);
|
||
const result = await response.json();
|
||
expect(result.code).toBe(200);
|
||
expect(result.message).toContain('注册成功');
|
||
expect(result.data.username).toBe(payload.username);
|
||
});
|
||
|
||
it('应该验证必填字段', async () => {
|
||
const payload = { username: 'test' }; // 缺少必要字段
|
||
|
||
const response = await app
|
||
.handle(new Request('http://localhost/api/auth/register', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(payload),
|
||
}));
|
||
|
||
expect(response.status).toBe(400);
|
||
});
|
||
|
||
it('应该检查用户名重复', async () => {
|
||
// 测试用户名重复的情况
|
||
});
|
||
|
||
it('应该验证验证码', async () => {
|
||
// 测试验证码验证
|
||
});
|
||
});
|
||
});
|
||
```
|
||
|
||
## 8. AI助手协作规范
|
||
|
||
### 8.1 注释规范(关键❗️)
|
||
|
||
#### 1. 文件头部注释
|
||
```typescript
|
||
/**
|
||
* @file 文件简要说明
|
||
* @author 创建者姓名
|
||
* @date 创建时间(如:2024-12-19)
|
||
* @lastEditor 最后修改人
|
||
* @lastEditTime 最后修改时间
|
||
* @description 文件详细描述
|
||
*/
|
||
```
|
||
|
||
#### 2. 函数/方法注释(JSDoc)
|
||
```typescript
|
||
/**
|
||
* 方法功能说明
|
||
* @param paramName 参数说明
|
||
* @returns 返回值类型和说明
|
||
* @throws ErrorType 可能抛出的异常说明
|
||
* @example
|
||
* const result = await someMethod('example');
|
||
* @modification 修改人 修改时间 修改说明
|
||
*/
|
||
```
|
||
|
||
#### 3. 变量注释
|
||
```typescript
|
||
/**
|
||
* 变量说明
|
||
* @type {string} 变量类型
|
||
* @description 详细描述
|
||
* @example 'example_value'
|
||
*/
|
||
```
|
||
|
||
### 8.2 代码组织原则
|
||
|
||
1. **单一职责**:每个文件只负责一个清晰的功能
|
||
2. **类型优先**:先定义Schema和类型,再实现逻辑
|
||
3. **错误优先**:优先考虑错误处理和边界情况
|
||
4. **测试驱动**:每个功能都有对应的测试用例
|
||
5. **依赖注入**:通过导入明确依赖关系
|
||
|
||
### 8.3 命名约定总结
|
||
|
||
| 类型 | 格式 | 示例 |
|
||
|------|------|------|
|
||
| 文件名 | `[module].[type].ts` | `auth.controller.ts` |
|
||
| Schema | `[Action][Module]Schema` | `RegisterSchema` |
|
||
| Request类型 | `[Action][Module]Request` | `RegisterRequest` |
|
||
| Response类型 | `[Action][Module]Response` | `RegisterResponse` |
|
||
| Service类 | `[Module]Service` | `AuthService` |
|
||
| Service实例 | `[module]Service` | `authService` |
|
||
| Controller | `[module]Controller` | `authController` |
|
||
|
||
## 9. 快速检查清单
|
||
|
||
开发新功能时,按此顺序检查:
|
||
|
||
- [ ] 1. Schema定义完整(包含验证规则、描述、示例)
|
||
- [ ] 2. 类型导出正确(Request/Response类型)
|
||
- [ ] 3. Response格式使用responseWrapperSchema
|
||
- [ ] 4. Service使用successResponse/errorResponse
|
||
- [ ] 5. 错误处理使用统一错误码
|
||
- [ ] 6. Controller定义完整的response schema
|
||
- [ ] 7. 测试用例覆盖主要场景
|
||
- [ ] 8. JSDoc注释完整
|
||
- [ ] 9. 日志记录到位
|
||
- [ ] 10. 使用统一的工具函数
|
||
|
||
## 10. 最佳实践
|
||
|
||
### 10.1 响应格式优化
|
||
```typescript
|
||
// ✅ 使用统一的响应格式
|
||
import { successResponse, errorResponse } from '@/utils/responseFormate';
|
||
|
||
// 成功响应
|
||
return successResponse(data, '操作成功');
|
||
|
||
// 错误响应
|
||
return errorResponse(400, '参数错误', 'VALIDATION_ERROR');
|
||
```
|
||
|
||
### 10.2 错误处理优化
|
||
```typescript
|
||
// ✅ 使用BusinessError类
|
||
import { BusinessError } from '@/utils/responseFormate';
|
||
import { ERROR_CODES } from '@/constants/error-codes';
|
||
|
||
throw new BusinessError(ERROR_CODES.USERNAME_EXISTS, 409);
|
||
```
|
||
|
||
### 10.3 Schema验证优化
|
||
```typescript
|
||
// ✅ 完整的Schema定义
|
||
export const UserSchema = t.Object({
|
||
id: t.String({ description: '用户ID' }),
|
||
username: t.String({
|
||
description: '用户名',
|
||
examples: ['admin', 'user123']
|
||
}),
|
||
email: t.String({
|
||
format: 'email',
|
||
description: '邮箱地址',
|
||
examples: ['user@example.com']
|
||
})
|
||
});
|
||
```
|
||
|
||
### 10.4 性能优化
|
||
- 使用连接池管理数据库连接
|
||
- 实现合理的缓存策略(Redis)
|
||
- 避免N+1查询问题
|
||
- 使用雪花ID算法生成唯一ID
|
||
|
||
### 10.5 安全考虑
|
||
- 输入验证和清理
|
||
- 密码加密存储(bcrypt)
|
||
- JWT令牌认证
|
||
- 敏感信息不记录日志
|
||
- 对于必要的写入操作,需要增加分布式锁,分布式锁键名在常量中统一定义
|
||
|
||
## 11. 模块引入规范
|
||
|
||
### 11.1 路径别名
|
||
```typescript
|
||
// ✅ 使用路径别名
|
||
import { Logger } from '@/plugins/logger/logger.service';
|
||
import { db } from '@/plugins/drizzle/drizzle.service';
|
||
import { ERROR_CODES } from '@/constants/error-codes';
|
||
import { successResponse } from '@/utils/responseFormate';
|
||
```
|
||
|
||
### 11.2 配置文件更新
|
||
确保更新以下配置文件中的路径别名:
|
||
- `tsconfig.json`
|
||
- `bunfig.toml`
|
||
- `vitest.config.ts`
|
||
|
||
---
|
||
|
||
**请严格遵守以上规范,确保代码的一致性、可维护性和AI友好性。这套规则经过实际项目验证,能够显著提升开发效率和代码质量。**
|
||
- 注意更新 tsconfig.json bunfig.toml 等配置中关于路径别名的配置
|
||
|
||
---
|
||
|
||
**请所有开发者严格遵守以上规范,保障 Elysia 接口的一致性、后端服务的健壮性、安全性与可维护性。** |