Compare commits

..

2 Commits

Author SHA1 Message Date
HeXiaoLong:Suanier
863b376020 fix: 修改代码格局分布 2025-07-07 14:29:09 +08:00
HeXiaoLong:Suanier
7c0bc201a5 docs: 优化规则 2025-07-07 13:59:42 +08:00
22 changed files with 1743 additions and 2070 deletions

View File

@ -1,379 +0,0 @@
---
description: "全局规则"
globs: ["**/*"]
alwaysApply: true
---
# 🤖 AI友好的Elysia开发规则体系
## 快速开始 🚀
### 1. 对于开发者
**首次使用:**
1. 阅读 `ai-friendly-elysia-rules.md` 了解规范
2. 查看 `ai-development-workflow.md` 学习如何与AI协作
3. 在项目中导入支持文件
**日常开发:**
```typescript
// 使用标准错误码
import { ERROR_CODES } from '@/constants/error-codes';
// 使用响应工具
import { successResponse, errorResponse, BusinessError } from '@/utils/response.helper';
```
### 2. 对于AI助手
**工作原则:**
- 严格遵循 `ai-friendly-elysia-rules.md` 规范
- 按照 `ai-development-workflow.md` 工作流程
- 确保代码质量和一致性
**开发顺序:**
1. Schema定义最高优先级
2. Response格式
3. Service层实现
4. Controller层实现
5. 测试用例
## 核心优势 💪
### 1. 一致性
- 统一的文件结构
- 标准的命名规范
- 一致的错误处理
### 2. 类型安全
- TypeBox Schema + TypeScript类型
- 编译时类型检查
- 运行时参数验证
### 3. 可维护性
- 清晰的模块划分
- 完整的文档注释
- 系统化的测试
### 4. AI友好
- 语义清晰的代码结构
- 明确的依赖关系
- 标准化的模式
## 使用示例 📝
### 创建新模块
当你需要创建新的业务模块时告诉AI助手
```
功能:用户管理
模块user
接口:注册、登录、获取个人信息、修改个人信息
认证登录后的接口需要JWT认证
特殊要求:密码需要加密存储
```
AI助手会自动创建
- `src/modules/user/user.schema.ts`
- `src/modules/user/user.response.ts`
- `src/modules/user/user.service.ts`
- `src/modules/user/user.controller.ts`
- `src/modules/user/user.test.ts`
### 修改现有功能
```
修改用户查询接口添加分页功能每页最多50条记录
```
AI助手会
1. 分析现有代码
2. 更新Schema定义
3. 修改Service逻辑
4. 更新Controller
5. 补充测试用例
## 代码质量保证 ✅
### 自动检查项
- [ ] TypeScript类型正确性
- [ ] Schema验证完整性
- [ ] 错误处理覆盖
- [ ] 响应格式一致性
- [ ] 命名规范符合标准
- [ ] 文档注释完整
### 人工Review项
- [ ] 业务逻辑正确性
- [ ] 安全性考虑
- [ ] 性能优化
- [ ] 用户体验
## 最佳实践 🌟
### 1. 需求描述
```
✅ 清晰:实现用户注册接口,包含邮箱验证和密码强度检查
❌ 模糊:做一个用户功能
```
### 2. 错误处理
```typescript
// ✅ 使用标准错误类
throw new BusinessError('用户名已存在', ERROR_CODES.USER_ALREADY_EXISTS);
// ❌ 直接抛出Error
throw new Error('用户名已存在');
```
### 3. 响应格式
```typescript
// ✅ 使用工具函数
return successResponse(userData, '查询成功');
// ❌ 手动构造
return { code: 'SUCCESS', message: '查询成功', data: userData };
```
## 性能考虑 ⚡
### 数据库优化
- 使用索引优化查询
- 避免N+1查询问题
- 合理使用连接池
### 缓存策略
- 热点数据Redis缓存
- 适当的缓存过期时间
- 缓存穿透防护
### 并发处理
- 避免阻塞操作
- 合理的超时设置
- 资源清理
## 故障排除 🔧
### 常见问题
1. **类型错误**
- 检查Schema定义
- 确认类型导出
- 验证导入路径
2. **运行时错误**
- 查看错误日志
- 检查参数验证
- 确认业务逻辑
3. **性能问题**
- 分析慢查询
- 检查缓存命中率
- 监控资源使用
### 调试技巧
```typescript
// 添加调试日志
Logger.debug(`处理用户请求: ${JSON.stringify(params)}`);
// 性能监控
const startTime = Date.now();
// ... 业务逻辑
Logger.info(`操作耗时: ${Date.now() - startTime}ms`);
```
## 版本历史 📚
- **v1.0.0** - 初始版本,包含核心规范
- **v1.1.0** - 添加AI协作指南
- **v1.2.0** - 完善错误处理和响应格式
记住:好的规范不是限制,而是让团队更高效协作的基础! 🎯 # 🤖 AI友好的Elysia开发规则体系
## 快速开始 🚀
### 1. 对于开发者
**首次使用:**
1. 阅读 `ai-friendly-elysia-rules.md` 了解规范
2. 查看 `ai-development-workflow.md` 学习如何与AI协作
3. 在项目中导入支持文件
**日常开发:**
```typescript
// 使用标准错误码
import { ERROR_CODES } from '@/constants/error-codes';
// 使用响应工具
import { successResponse, errorResponse, BusinessError } from '@/utils/response.helper';
```
### 2. 对于AI助手
**工作原则:**
- 严格遵循 `ai-friendly-elysia-rules.md` 规范
- 按照 `ai-development-workflow.md` 工作流程
- 确保代码质量和一致性
**开发顺序:**
1. Schema定义最高优先级
2. Response格式
3. Service层实现
4. Controller层实现
5. 测试用例
## 核心优势 💪
### 1. 一致性
- 统一的文件结构
- 标准的命名规范
- 一致的错误处理
### 2. 类型安全
- TypeBox Schema + TypeScript类型
- 编译时类型检查
- 运行时参数验证
### 3. 可维护性
- 清晰的模块划分
- 完整的文档注释
- 系统化的测试
### 4. AI友好
- 语义清晰的代码结构
- 明确的依赖关系
- 标准化的模式
## 使用示例 📝
### 创建新模块
当你需要创建新的业务模块时告诉AI助手
```
功能:用户管理
模块user
接口:注册、登录、获取个人信息、修改个人信息
认证登录后的接口需要JWT认证
特殊要求:密码需要加密存储
```
AI助手会自动创建
- `src/modules/user/user.schema.ts`
- `src/modules/user/user.response.ts`
- `src/modules/user/user.service.ts`
- `src/modules/user/user.controller.ts`
- `src/modules/user/user.test.ts`
### 修改现有功能
```
修改用户查询接口添加分页功能每页最多50条记录
```
AI助手会
1. 分析现有代码
2. 更新Schema定义
3. 修改Service逻辑
4. 更新Controller
5. 补充测试用例
## 代码质量保证 ✅
### 自动检查项
- [ ] TypeScript类型正确性
- [ ] Schema验证完整性
- [ ] 错误处理覆盖
- [ ] 响应格式一致性
- [ ] 命名规范符合标准
- [ ] 文档注释完整
### 人工Review项
- [ ] 业务逻辑正确性
- [ ] 安全性考虑
- [ ] 性能优化
- [ ] 用户体验
## 最佳实践 🌟
### 1. 需求描述
```
✅ 清晰:实现用户注册接口,包含邮箱验证和密码强度检查
❌ 模糊:做一个用户功能
```
### 2. 错误处理
```typescript
// ✅ 使用标准错误类
throw new BusinessError('用户名已存在', ERROR_CODES.USER_ALREADY_EXISTS);
// ❌ 直接抛出Error
throw new Error('用户名已存在');
```
### 3. 响应格式
```typescript
// ✅ 使用工具函数
return successResponse(userData, '查询成功');
// ❌ 手动构造
return { code: 'SUCCESS', message: '查询成功', data: userData };
```
## 性能考虑 ⚡
### 数据库优化
- 使用索引优化查询
- 避免N+1查询问题
- 合理使用连接池
### 缓存策略
- 热点数据Redis缓存
- 适当的缓存过期时间
- 缓存穿透防护
### 并发处理
- 避免阻塞操作
- 合理的超时设置
- 资源清理
## 故障排除 🔧
### 常见问题
1. **类型错误**
- 检查Schema定义
- 确认类型导出
- 验证导入路径
2. **运行时错误**
- 查看错误日志
- 检查参数验证
- 确认业务逻辑
3. **性能问题**
- 分析慢查询
- 检查缓存命中率
- 监控资源使用
### 调试技巧
```typescript
// 添加调试日志
Logger.debug(`处理用户请求: ${JSON.stringify(params)}`);
// 性能监控
const startTime = Date.now();
// ... 业务逻辑
Logger.info(`操作耗时: ${Date.now() - startTime}ms`);
```
## 版本历史 📚
- **v1.0.0** - 初始版本,包含核心规范
- **v1.1.0** - 添加AI协作指南
- **v1.2.0** - 完善错误处理和响应格式
记住:好的规范不是限制,而是让团队更高效协作的基础! 🎯

View File

@ -1,294 +0,0 @@
---
description: "全局规则"
globs: ["**/*"]
alwaysApply: true
---
# AI助手开发工作流程指南 🤖
## 快速开始
当你需要我帮你开发新功能时,请按照以下格式提供信息:
```
功能:[功能名称]
模块:[模块名,如 user、product]
接口:[接口列表,如 创建用户、查询用户列表]
认证:[是否需要JWT认证]
特殊要求:[任何特殊要求]
```
## 我的工作流程
### 1. 分析阶段30秒内
- 理解功能需求
- 分析现有代码结构
- 确定模块依赖关系
- 制定开发计划
### 2. 设计阶段(按优先级)
1. **Schema设计**(最优先)
- 定义请求参数Schema
- 定义响应数据Schema
- 导出TypeScript类型
2. **Response格式设计**
- 成功响应格式
- 错误响应格式
- 组合响应定义
3. **Service层设计**
- 业务逻辑接口
- 数据访问逻辑
- 错误处理逻辑
4. **Controller层设计**
- 路由定义
- 参数验证
- 错误处理
5. **测试用例设计**
- 正常流程测试
- 异常流程测试
- 边界条件测试
### 3. 实现阶段(批量操作)
我会同时创建/修改多个文件:
```typescript
// 1. 同时读取相关文件了解现状
// 2. 并行创建所有必需的文件
// 3. 更新路由和导出文件
// 4. 验证代码完整性
```
### 4. 验证阶段
- 检查类型安全性
- 确认错误处理完整性
- 验证响应格式一致性
- 检查测试覆盖率
## 交互模式
### 快速开发模式 ⚡
适用于标准CRUD操作、常见业务场景
**你只需要说:**
```
"帮我实现用户模块的CRUD接口"
"添加产品管理功能"
"实现订单状态查询"
```
**AI会自动**
- 按照规范创建完整的5个文件
- 集成到现有路由系统
- 提供完整的类型安全
- 包含基础测试用例
### 定制开发模式 🔧
适用于:复杂业务逻辑、特殊需求
**你需要提供:**
- 详细的业务规则
- 特殊的验证要求
- 复杂的数据关联
- 性能要求
**AI会**
- 详细分析需求
- 提供设计方案
- 征求确认后实现
- 优化性能和安全性
## 我擅长处理的场景
### ✅ 高效处理
- 标准REST API开发
- CRUD操作实现
- 数据验证和类型安全
- 错误处理和响应格式
- JWT认证集成
- 数据库操作Drizzle ORM
- Redis缓存集成
- 测试用例编写
- API文档生成
### ⚡ 批量操作
- 多文件同时创建/修改
- 路由批量注册
- 类型定义批量导出
- 测试用例批量生成
### 🔍 代码分析
- 现有代码结构理解
- 依赖关系分析
- 潜在问题识别
- 优化建议提供
## 沟通最佳实践
### 清晰描述需求
```
❌ "做一个用户功能"
✅ "实现用户注册、登录、个人信息查询和修改功能需要JWT认证"
❌ "这个接口有问题"
✅ "用户登录接口返回401错误但是用户名密码正确"
```
### 提供上下文信息
```
✅ "在现有的用户模块基础上添加头像上传功能"
✅ "这个接口需要管理员权限才能访问"
✅ "数据需要缓存到Redis缓存时间1小时"
```
### 明确期望结果
```
✅ "创建完整的用户CRUD接口包含测试"
✅ "只需要修改现有的查询接口,添加分页功能"
✅ "优化这个接口的性能响应时间控制在100ms内"
```
## 错误处理和调试
### 当代码出现问题时
1. **我会主动分析**
- 检查类型错误
- 验证语法正确性
- 确认导入导出关系
2. **提供修复方案**
- 直接修复简单问题
- 解释复杂问题的原因
- 提供多种解决方案
3. **验证修复结果**
- 确保修复后代码可运行
- 检查是否引入新问题
- 验证功能完整性
### 性能优化建议
我会在适当时候提供:
- 数据库查询优化
- 缓存策略建议
- 并发处理优化
- 内存使用优化
## 质量保证
### 代码质量检查
- [ ] 类型安全性
- [ ] 错误处理完整性
- [ ] 响应格式一致性
- [ ] 命名规范符合标准
- [ ] 注释文档完整
### 功能完整性检查
- [ ] Schema定义完整
- [ ] Response格式正确
- [ ] Service逻辑完整
- [ ] Controller路由正确
- [ ] 测试用例覆盖
### 安全性检查
- [ ] 参数验证到位
- [ ] 认证授权正确
- [ ] 敏感信息保护
- [ ] SQL注入防护
- [ ] XSS攻击防护
记住:我的目标是让你专注于业务逻辑,而我来确保代码的规范性、安全性和可维护性! 🎯 # AI助手开发工作流程指南 🤖
## 快速开始
当你需要我帮你开发新功能时,请按照以下格式提供信息:
```
功能:[功能名称]
模块:[模块名,如 user、product]
接口:[接口列表,如 创建用户、查询用户列表]
认证:[是否需要JWT认证]
特殊要求:[任何特殊要求]
```
## 我的工作流程
### 1. 分析阶段30秒内
- 理解功能需求
- 分析现有代码结构
- 确定模块依赖关系
- 制定开发计划
### 2. 设计阶段(按优先级)
1. **Schema设计**(最优先)
- 定义请求参数Schema
- 定义响应数据Schema
- 导出TypeScript类型
2. **Response格式设计**
- 成功响应格式
- 错误响应格式
- 组合响应定义
3. **Service层设计**
- 业务逻辑接口
- 数据访问逻辑
- 错误处理逻辑
4. **Controller层设计**
- 路由定义
- 参数验证
- 错误处理
5. **测试用例设计**
- 正常流程测试
- 异常流程测试
- 边界条件测试
### 3. 实现阶段(批量操作)
我会同时创建/修改多个文件:
```typescript
// 1. 同时读取相关文件了解现状
// 2. 并行创建所有必需的文件
// 3. 更新路由和导出文件
// 4. 验证代码完整性
```
### 4. 验证阶段
- 检查类型安全性
- 确认错误处理完整性
- 验证响应格式一致性
- 检查测试覆盖率
### 代码质量检查
- [ ] 类型安全性
- [ ] 错误处理完整性
- [ ] 响应格式一致性
- [ ] 命名规范符合标准
- [ ] 注释文档完整
### 功能完整性检查
- [ ] Schema定义完整
- [ ] Response格式正确
- [ ] Service逻辑完整
- [ ] Controller路由正确
- [ ] 测试用例覆盖
### 安全性检查
- [ ] 参数验证到位
- [ ] 认证授权正确
- [ ] 敏感信息保护
- [ ] SQL注入防护
- [ ] XSS攻击防护
记住:我的目标是让你专注于业务逻辑,而我来确保代码的规范性、安全性和可维护性! 🎯

View File

@ -3,7 +3,7 @@ description: "全局规则"
globs: ["**/*"]
alwaysApply: true
---
# 规则:从PRD生成任务清单
# 从PRD生成任务清单
## 目标 (Goal)

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +0,0 @@
---
description: "全局规则"
globs: ["**/*"]
alwaysApply: true
---
# 特殊要求
1. 删除文件时一定要请求确认
2. **不允许执行脚本和命令,告诉我命令即可,由我手动执行**

View File

@ -1,59 +0,0 @@
---
description: "全局规则"
globs: ["**/*"]
alwaysApply: true
---
描述 (description):
全局模式 (globs):
始终应用 (alwaysApply): false
---
# 任务清单管理
管理Markdown文件中的任务清单以跟踪完成PRD进度的指南。
## 任务实施 (Task Implementation)
* **一次处理一个子任务:** 在询问用户并获得“yes”或“y”的许可之前**不要**开始下一个子任务。********非常重要必须执行*********
* **完成协议 (Completion protocol)**
1. 当你完成一个**子任务**时,立即通过将 `[ ]` 改为 `[x]` 将其标记为已完成。
2. 如果一个父任务下的**所有**子任务现在都是 `[x]`,则按以下顺序执行:
* **首先:** 运行完整的测试套件(`pytest`, `npm test`, `bin/rails test` 等)。
* **仅当所有测试通过时:** 暂存更改 (`git add .`)。
* **清理:** 提交前删除所有临时文件和临时代码。
* **提交 (Commit)** 使用描述性的提交消息:
* 采用常规提交格式 (`feat:`, `fix:`, `refactor:`, 等)。
* 总结父任务中完成的内容。
* 列出关键的更改和新增。
* 引用任务编号和PRD上下文。
* **使用 `-m` 标志将消息格式化为单行命令**,例如:
```
git commit -m "feat: 添加支付验证逻辑" -m "- 验证卡类型和有效期" -m "- 为边界情况添加单元测试" -m "关联PRD中的T123"
```
3. 一旦所有子任务标记为完成且更改已提交,将**父任务**标记为完成 (`[x]`)。
* 完成每个子任务后停止并等待用户的批准 (`go-ahead`)。
## 任务清单维护 (Task List Maintenance)
1. **边工作边更新任务清单:**
* 按照上述协议将任务和子任务标记为完成 (`[x]`)。
* 添加新出现的任务。
* **注意,每完成一个子任务时,都要更新任务清单状态**
2. **维护“相关文件”部分:**
* 列出每个创建或修改的文件。
* 为每个文件提供一行其用途描述。
## AI 指令 (AI Instructions)
当使用任务清单时AI 必须:
1. 完成任何重要工作后定期更新任务清单文件。
2. 遵守完成协议:
* 将每个完成的**子任务**标记为 `[x]`。
* 一旦其**所有**子任务都是 `[x]`,将**父任务**标记为 `[x]`。
3. 添加新发现的任务。
4. 保持“相关文件”准确且最新。
5. 开始工作前,检查下一个子任务是什么。
6. 实现一个子任务后,更新文件,然后暂停等待用户批准。
7. 在执行任务前请扫描项目目录
8. 完成一个子任务后提交git
9. 将已完成的任务归档到tasks的归档目录archive并修改文件名文件名前添加时间
10. 将需求prd也归档

View File

@ -0,0 +1,46 @@
---
description: "全局规则"
globs: ["**/*"]
alwaysApply: true
---
# 任务执行流程
管理Markdown文件中的任务清单以跟踪完成PRD进度的指南。
## 任务实施 (Task Implementation)
1. 在执行大任务 `x.0`时先扫描项目理解AI规则
2. 每一个大任务包含多个子任务,如 `1.1`、`1.2`、`1.3`等
3. 在执行子任务之前,要深刻理解任务需求,给出详细的任务执行逻辑,确保执行步骤正确,**必须等待取人才能进行下一步需要等我的输入如y和go等**
4. 每一个子任务执行完之后都必须暂停,等待我的验证,坚决不允许任何连续执行多个任务
6. 每个大任务执行前给出git commit提交文本
* **一次处理一个子任务:** 在询问用户并获得“yes”或“y”的许可之前**不要**开始下一个子任务。********非常重要必须执行*********
* 完成每个子任务后停止并等待用户的批准 (`go-ahead`)。
## 任务清单维护 (Task List Maintenance)
1. **边工作边更新任务清单:**
* 按照上述协议将任务和子任务标记为完成 (`[x]`)。
* 添加新出现的任务。
* **注意,每完成一个子任务时,都要更新任务清单状态**
2. **维护“相关文件”部分:**
* 列出每个创建或修改的文件。
* 为每个文件提供一行其用途描述。
## AI 指令 (AI Instructions)
当使用任务清单时AI 必须:
1. 完成任何重要工作后定期更新任务清单文件。
2. 遵守完成协议:
* 将每个完成的**子任务**标记为 `[x]`。
* 一旦其**所有**子任务都是 `[x]`,将**父任务**标记为 `[x]`。
3. 添加新发现的任务。
4. 保持“相关文件”准确且最新。
5. 开始工作前,检查下一个子任务是什么。
6. 实现一个子任务后,更新文件,然后暂停等待用户批准。
7. 在执行任务前请扫描项目目录
8. 完成一个子任务后提交git
9. 将已完成的任务归档到tasks的归档目录archive并修改文件名文件名前添加时间
10. 将需求prd也归档

View File

@ -1,111 +0,0 @@
---
description: Use Bun instead of Node.js, npm, pnpm, or vite.
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
alwaysApply: false
---
Default to using Bun instead of Node.js.
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
- Bun automatically loads .env, so don't use dotenv.
## APIs
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
- `Bun.redis` for Redis. Don't use `ioredis`.
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
- `WebSocket` is built-in. Don't use `ws`.
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
- Bun.$`ls` instead of execa.
## Testing
Use `bun test` to run tests.
```ts#index.test.ts
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
```
## Frontend
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
Server:
```ts#index.ts
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
```
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
```html#index.html
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
```
With the following `frontend.tsx`:
```tsx#frontend.tsx
import React from "react";
// import .css files directly and it works
import './index.css';
import { createRoot } from "react-dom/client";
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
```
Then, run index.ts
```sh
bun --hot ./index.ts
```
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.

View File

@ -0,0 +1,503 @@
# 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: '用户IDbigint类型以字符串形式返回防止精度丢失',
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. 方法

View File

@ -5,28 +5,24 @@
"name": "cursor-init",
"dependencies": {
"@elysiajs/swagger": "^1.3.0",
"@types/ua-parser-js": "^0.7.39",
"bcrypt": "^6.0.0",
"canvas": "^3.1.2",
"chalk": "^5.4.1",
"drizzle-orm": "^0.44.2",
"jsonwebtoken": "^9.0.2",
"mysql2": "^3.14.1",
"nanoid": "^5.1.5",
"nodemailer": "^7.0.4",
"picocolors": "^1.1.1",
"redis": "^5.5.6",
"ua-parser-js": "^2.0.4",
"undici": "^7.11.0",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0",
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/bun": "^1.0.25",
"@types/jsonwebtoken": "^9.0.10",
"@types/nodemailer": "^6.4.17",
"@types/redis": "^4.0.11",
"@types/ua-parser-js": "^0.7.39",
"@types/winston": "^2.4.4",
"@typescript-eslint/eslint-plugin": "^8.35.0",
"@typescript-eslint/parser": "^8.35.0",
@ -210,10 +206,6 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/jsonwebtoken": ["@types/jsonwebtoken@9.0.10", "", { "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA=="],
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
"@types/node": ["@types/node@24.0.4", "https://registry.npmmirror.com/@types/node/-/node-24.0.4.tgz", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="],
"@types/node-fetch": ["@types/node-fetch@2.6.12", "https://registry.npmmirror.com/@types/node-fetch/-/node-fetch-2.6.12.tgz", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA=="],
@ -590,7 +582,7 @@
"named-placeholders": ["named-placeholders@1.1.3", "https://registry.npmmirror.com/named-placeholders/-/named-placeholders-1.1.3.tgz", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="],
"nanoid": ["nanoid@5.1.5", "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.5.tgz", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw=="],
"nanoid": ["nanoid@3.3.11", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
@ -752,8 +744,6 @@
"uint8array-extras": ["uint8array-extras@1.4.0", "https://registry.npmmirror.com/uint8array-extras/-/uint8array-extras-1.4.0.tgz", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="],
"undici": ["undici@7.11.0", "https://registry.npmmirror.com/undici/-/undici-7.11.0.tgz", {}, "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg=="],
"undici-types": ["undici-types@7.8.0", "https://registry.npmmirror.com/undici-types/-/undici-types-7.8.0.tgz", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
"uri-js": ["uri-js@4.4.1", "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
@ -818,8 +808,6 @@
"micromatch/picomatch": ["picomatch@2.3.1", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"postcss/nanoid": ["nanoid@3.3.11", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
@ -868,6 +856,8 @@
"@scalar/themes/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "https://registry.npmmirror.com/@scalar/openapi-types/-/openapi-types-0.2.0.tgz", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],
"@scalar/themes/@scalar/types/nanoid": ["nanoid@5.1.5", "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.5.tgz", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"color/color-convert/color-name": ["color-name@1.1.3", "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],

View File

@ -0,0 +1,503 @@
# 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: '用户IDbigint类型以字符串形式返回防止精度丢失',
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. 方法

View File

@ -18,6 +18,7 @@
"@types/bun": "^1.0.25",
"@types/nodemailer": "^6.4.17",
"@types/redis": "^4.0.11",
"@types/ua-parser-js": "^0.7.39",
"@types/winston": "^2.4.4",
"@typescript-eslint/eslint-plugin": "^8.35.0",
"@typescript-eslint/parser": "^8.35.0",
@ -29,21 +30,16 @@
"vitest": "^3.2.4"
},
"dependencies": {
"@elysiajs/jwt": "^1.3.1",
"@elysiajs/swagger": "^1.3.0",
"@types/ua-parser-js": "^0.7.39",
"bcrypt": "^6.0.0",
"canvas": "^3.1.2",
"chalk": "^5.4.1",
"drizzle-orm": "^0.44.2",
"jsonwebtoken": "^9.0.2",
"mysql2": "^3.14.1",
"nanoid": "^5.1.5",
"nodemailer": "^7.0.4",
"picocolors": "^1.1.1",
"redis": "^5.5.6",
"ua-parser-js": "^2.0.4",
"undici": "^7.11.0",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0"
},

View File

@ -11,7 +11,7 @@ import { Elysia } from 'elysia';
import { RegisterSchema, ActivateSchema, LoginSchema, RefreshSchema, ResetPasswordRequestSchema, ResetPasswordConfirmSchema } from './auth.schema';
import { RegisterResponsesSchema, ActivateResponsesSchema, LoginResponsesSchema, RefreshResponsesSchema, ResetPasswordRequestResponsesSchema, ResetPasswordConfirmResponsesSchema } from './auth.response';
import { authService } from './auth.service';
import { tags } from '@/modules/tags';
import { tags } from '@/constants/swaggerTags';
/**
*

View File

@ -9,7 +9,7 @@ import { Elysia, t } from 'elysia';
import { GenerateCaptchaSchema, VerifyCaptchaSchema } from './captcha.schema';
import { responseWrapperSchema } from '@/utils/responseFormate';
import { captchaService } from './captcha.service';
import { tags } from '@/modules/tags';
import { tags } from '@/constants/swaggerTags';
export const captchaController = new Elysia()
/**

View File

@ -11,7 +11,7 @@ import { Elysia } from 'elysia';
import { userService } from './user.service';
import { GetCurrentUserResponsesSchema, GetUserListResponsesSchema } from './user.response';
import { UserListQuerySchema } from './user.schema';
import { tags } from '@/modules/tags';
import { tags } from '@/constants/swaggerTags';
import { jwtAuthPlugin } from '@/plugins/jwt/jwt.plugins';
import type { JwtUserType } from '@/type/jwt.type';
@ -26,37 +26,29 @@ export const userController = new Elysia()
* @description JWT认证
*/
.use(jwtAuthPlugin)
.get(
'/me',
({ user }: { user: JwtUserType }) => userService.getCurrentUser(user.userId),
{
detail: {
summary: '获取当前用户信息',
description: '获取当前登录用户的详细信息,包括基本信息、状态、时间等',
tags: [tags.user],
operationId: 'getCurrentUser',
security: [{ bearerAuth: [] }]
},
response: GetCurrentUserResponsesSchema,
}
)
.get('/me', ({ user }: { user: JwtUserType }) => userService.getCurrentUser(user.userId), {
detail: {
summary: '获取当前用户信息',
description: '获取当前登录用户的详细信息,包括基本信息、状态、时间等',
tags: [tags.user],
operationId: 'getCurrentUser',
security: [{ bearerAuth: [] }],
},
response: GetCurrentUserResponsesSchema,
})
/**
*
* @route GET /api/users
* @description JWT认证
*/
.get(
'/list',
({ query }) => userService.getUserList(query),
{
query: UserListQuerySchema,
detail: {
summary: '获取用户列表',
description: '获取用户列表,支持分页查询、关键词搜索、状态筛选、排序等功能',
tags: [tags.user],
operationId: 'getUserList',
security: [{ bearerAuth: [] }]
},
response: GetUserListResponsesSchema,
}
);
.get('/list', ({ query }) => userService.getUserList(query), {
query: UserListQuerySchema,
detail: {
summary: '获取用户列表',
description: '获取用户列表,支持分页查询、关键词搜索、状态筛选、排序等功能',
tags: [tags.user],
operationId: 'getUserList',
security: [{ bearerAuth: [] }],
},
response: GetUserListResponsesSchema,
});

View File

@ -9,7 +9,64 @@
import { t, type Static } from 'elysia';
import { responseWrapperSchema } from '@/utils/responseFormate';
import { CurrentUserSchema, UserListResponseSchema } from './user.schema';
import { createPaginationResponseSchema } from '@/utils/pagination';
/**
* Schema
* @description
*/
export const CurrentUserSchema = t.Object({
/** 用户ID */
id: t.String({
description: '用户IDbigint类型以字符串形式返回防止精度丢失',
examples: ['1', '2', '3'],
}),
/** 用户名 */
username: t.String({
description: '用户名',
examples: ['admin', 'testuser'],
}),
/** 邮箱地址 */
email: t.String({
description: '邮箱地址',
examples: ['user@example.com'],
}),
/** 昵称 */
nickname: t.Union([t.String(), t.Null()], {
description: '用户昵称',
examples: ['管理员', '测试用户', null],
}),
/** 头像URL */
avatar: t.Union([t.String(), t.Null()], {
description: '用户头像URL',
examples: ['https://example.com/avatar.jpg', null],
}),
/** 手机号 */
phone: t.Union([t.String(), t.Null()], {
description: '手机号码',
examples: ['13800138000', null],
}),
/** 账号状态 */
status: t.String({
description: '账号状态',
examples: ['active', 'inactive', 'pending'],
}),
/** 最后登录时间 */
lastLoginAt: t.Union([t.String(), t.Null()], {
description: '最后登录时间',
examples: ['2024-12-19T10:30:00Z', null],
}),
/** 创建时间 */
createdAt: t.String({
description: '创建时间',
examples: ['2024-12-19T10:30:00Z'],
}),
/** 更新时间 */
updatedAt: t.String({
description: '更新时间',
examples: ['2024-12-19T10:30:00Z'],
}),
});
/**
*
@ -17,60 +74,172 @@ import { CurrentUserSchema, UserListResponseSchema } from './user.schema';
*/
export const GetCurrentUserResponsesSchema = {
200: responseWrapperSchema(CurrentUserSchema),
401: responseWrapperSchema(t.Object({
error: t.String({
description: '认证失败',
examples: ['未提供有效的认证令牌', '令牌已过期']
})
})),
404: responseWrapperSchema(t.Object({
error: t.String({
description: '用户不存在',
examples: ['用户不存在或已被删除']
})
})),
500: responseWrapperSchema(t.Object({
error: t.String({
description: '服务器错误',
examples: ['内部服务器错误']
})
}))
401: responseWrapperSchema(
t.Object({
error: t.String({
description: '认证失败',
examples: ['未提供有效的认证令牌', '令牌已过期'],
}),
}),
),
404: responseWrapperSchema(
t.Object({
error: t.String({
description: '用户不存在',
examples: ['用户不存在或已被删除'],
}),
}),
),
500: responseWrapperSchema(
t.Object({
error: t.String({
description: '服务器错误',
examples: ['内部服务器错误'],
}),
}),
),
};
/**
* Schema
* @description
*/
export const UserListItemSchema = t.Object({
/** 用户ID */
id: t.String({
description: '用户IDbigint类型以字符串形式返回防止精度丢失',
examples: ['1', '2', '3'],
}),
/** 用户名 */
username: t.String({
description: '用户名',
examples: ['admin', 'testuser'],
}),
/** 邮箱地址 */
email: t.String({
description: '邮箱地址',
examples: ['user@example.com'],
}),
/** 手机号 */
mobile: t.Union([t.String(), t.Null()], {
description: '手机号码',
examples: ['13800138000', null],
}),
/** 昵称 */
nickname: t.Union([t.String(), t.Null()], {
description: '用户昵称',
examples: ['管理员', '测试用户', null],
}),
/** 头像URL */
avatar: t.Union([t.String(), t.Null()], {
description: '用户头像URL',
examples: ['https://example.com/avatar.jpg', null],
}),
/** 账号状态 */
status: t.String({
description: '账号状态',
examples: ['active', 'inactive', 'pending'],
}),
/** 性别 */
gender: t.Union([t.Number(), t.Null()], {
description: '性别0-未知1-男2-女',
examples: [0, 1, 2, null],
}),
/** 生日 */
birthday: t.Union([t.String(), t.Null()], {
description: '生日',
examples: ['1990-01-01', null],
}),
/** 个人简介 */
bio: t.Union([t.String(), t.Null()], {
description: '个人简介',
examples: ['这是一段个人简介', null],
}),
/** 登录次数 */
loginCount: t.Number({
description: '登录次数',
examples: [0, 10, 100],
}),
/** 最后登录时间 */
lastLoginAt: t.Union([t.String(), t.Null()], {
description: '最后登录时间',
examples: ['2024-12-19T10:30:00Z', null],
}),
/** 最后登录IP */
lastLoginIp: t.Union([t.String(), t.Null()], {
description: '最后登录IP',
examples: ['192.168.1.1', null],
}),
/** 失败尝试次数 */
failedAttempts: t.Number({
description: '失败尝试次数',
examples: [0, 1, 5],
}),
/** 是否超级管理员 */
isRoot: t.Boolean({
description: '是否超级管理员',
examples: [true, false],
}),
/** 创建时间 */
createdAt: t.String({
description: '创建时间',
examples: ['2024-12-19T10:30:00Z'],
}),
/** 更新时间 */
updatedAt: t.String({
description: '更新时间',
examples: ['2024-12-19T10:30:00Z'],
}),
});
/**
*
* @description Controller中定义所有可能的响应格式
*/
export const GetUserListResponsesSchema = {
200: responseWrapperSchema(UserListResponseSchema),
401: responseWrapperSchema(t.Object({
error: t.String({
description: '认证失败',
examples: ['未提供有效的认证令牌', '令牌已过期']
})
})),
403: 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: ['内部服务器错误']
})
}))
200: responseWrapperSchema(createPaginationResponseSchema(UserListItemSchema)),
401: responseWrapperSchema(
t.Object({
error: t.String({
description: '认证失败',
examples: ['未提供有效的认证令牌', '令牌已过期'],
}),
}),
),
403: 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 CurrentUserResponse = Static<typeof CurrentUserSchema>;
/** 用户列表项类型 */
export type UserListItem = Static<typeof UserListItemSchema>;
/** 获取当前用户信息成功响应数据类型 */
export type GetCurrentUserSuccessType = Static<typeof GetCurrentUserResponsesSchema[200]>;
export type GetCurrentUserSuccessType = Static<(typeof GetCurrentUserResponsesSchema)[200]>;
/** 获取用户列表成功响应数据类型 */
export type GetUserListSuccessType = Static<typeof GetUserListResponsesSchema[200]>;
export type GetUserListSuccessType = Static<(typeof GetUserListResponsesSchema)[200]>;

View File

@ -10,62 +10,6 @@
import { t, type Static } from 'elysia';
import { createPaginationResponseSchema, createQuerySchema } from '@/utils/pagination';
/**
* Schema
* @description
*/
export const CurrentUserSchema = t.Object({
/** 用户ID */
id: t.String({
description: '用户IDbigint类型以字符串形式返回防止精度丢失',
examples: ['1', '2', '3']
}),
/** 用户名 */
username: t.String({
description: '用户名',
examples: ['admin', 'testuser']
}),
/** 邮箱地址 */
email: t.String({
description: '邮箱地址',
examples: ['user@example.com']
}),
/** 昵称 */
nickname: t.Union([t.String(), t.Null()], {
description: '用户昵称',
examples: ['管理员', '测试用户', null]
}),
/** 头像URL */
avatar: t.Union([t.String(), t.Null()], {
description: '用户头像URL',
examples: ['https://example.com/avatar.jpg', null]
}),
/** 手机号 */
phone: t.Union([t.String(), t.Null()], {
description: '手机号码',
examples: ['13800138000', null]
}),
/** 账号状态 */
status: t.String({
description: '账号状态',
examples: ['active', 'inactive', 'pending']
}),
/** 最后登录时间 */
lastLoginAt: t.Union([t.String(), t.Null()], {
description: '最后登录时间',
examples: ['2024-12-19T10:30:00Z', null]
}),
/** 创建时间 */
createdAt: t.String({
description: '创建时间',
examples: ['2024-12-19T10:30:00Z']
}),
/** 更新时间 */
updatedAt: t.String({
description: '更新时间',
examples: ['2024-12-19T10:30:00Z']
})
});
/**
* Schema
@ -104,112 +48,9 @@ export const UserListQuerySchema = createQuerySchema(t.Object({
}))
}));
/**
* Schema
* @description
*/
export const UserListItemSchema = t.Object({
/** 用户ID */
id: t.String({
description: '用户IDbigint类型以字符串形式返回防止精度丢失',
examples: ['1', '2', '3']
}),
/** 用户名 */
username: t.String({
description: '用户名',
examples: ['admin', 'testuser']
}),
/** 邮箱地址 */
email: t.String({
description: '邮箱地址',
examples: ['user@example.com']
}),
/** 手机号 */
mobile: t.Union([t.String(), t.Null()], {
description: '手机号码',
examples: ['13800138000', null]
}),
/** 昵称 */
nickname: t.Union([t.String(), t.Null()], {
description: '用户昵称',
examples: ['管理员', '测试用户', null]
}),
/** 头像URL */
avatar: t.Union([t.String(), t.Null()], {
description: '用户头像URL',
examples: ['https://example.com/avatar.jpg', null]
}),
/** 账号状态 */
status: t.String({
description: '账号状态',
examples: ['active', 'inactive', 'pending']
}),
/** 性别 */
gender: t.Union([t.Number(), t.Null()], {
description: '性别0-未知1-男2-女',
examples: [0, 1, 2, null]
}),
/** 生日 */
birthday: t.Union([t.String(), t.Null()], {
description: '生日',
examples: ['1990-01-01', null]
}),
/** 个人简介 */
bio: t.Union([t.String(), t.Null()], {
description: '个人简介',
examples: ['这是一段个人简介', null]
}),
/** 登录次数 */
loginCount: t.Number({
description: '登录次数',
examples: [0, 10, 100]
}),
/** 最后登录时间 */
lastLoginAt: t.Union([t.String(), t.Null()], {
description: '最后登录时间',
examples: ['2024-12-19T10:30:00Z', null]
}),
/** 最后登录IP */
lastLoginIp: t.Union([t.String(), t.Null()], {
description: '最后登录IP',
examples: ['192.168.1.1', null]
}),
/** 失败尝试次数 */
failedAttempts: t.Number({
description: '失败尝试次数',
examples: [0, 1, 5]
}),
/** 是否超级管理员 */
isRoot: t.Boolean({
description: '是否超级管理员',
examples: [true, false]
}),
/** 创建时间 */
createdAt: t.String({
description: '创建时间',
examples: ['2024-12-19T10:30:00Z']
}),
/** 更新时间 */
updatedAt: t.String({
description: '更新时间',
examples: ['2024-12-19T10:30:00Z']
})
});
/**
* Schema
* @description
*/
export const UserListResponseSchema = createPaginationResponseSchema(UserListItemSchema);
/** 当前用户信息响应类型 */
export type CurrentUserResponse = Static<typeof CurrentUserSchema>;
/** 用户列表查询参数类型 */
export type UserListQueryRequest = Static<typeof UserListQuerySchema>;
/** 用户列表项类型 */
export type UserListItem = Static<typeof UserListItemSchema>;
/** 用户列表响应类型 */
export type UserListResponse = Static<typeof UserListResponseSchema>;

View File

@ -15,7 +15,8 @@ import { successResponse, errorResponse, BusinessError } from '@/utils/responseF
import { calculatePagination, normalizePaginationParams } from '@/utils/pagination';
import type { GetCurrentUserSuccessType, GetUserListSuccessType } from './user.response';
import type { UserListQueryRequest, UserListItem } from './user.schema';
import type { UserListQueryRequest } from './user.schema';
import type { UserListItem } from './user.response';
/**
*

View File

@ -1 +0,0 @@

View File

@ -141,7 +141,7 @@
- [x] 10.5 扩展user.test.md - 编写用户列表测试用例文档
- [ ] 11.0 POST /users - 创建用户接口
- [ ] Before 整理输入此接口的逻辑必须等待用户确认后进行需要输入go才能进行下一步
- [ ] 0.0 整理输入此接口的逻辑必须等待用户确认后进行需要输入go才能进行下一步
- [ ] 11.1 扩展user.schema.ts - 定义创建用户Schema
- [ ] 11.2 扩展user.response.ts - 定义创建用户响应格式
- [ ] 11.3 扩展user.service.ts - 实现创建用户业务逻辑