Compare commits
2 Commits
926564b144
...
863b376020
Author | SHA1 | Date | |
---|---|---|---|
![]() |
863b376020 | ||
![]() |
7c0bc201a5 |
@ -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** - 完善错误处理和响应格式
|
||||
|
||||
记住:好的规范不是限制,而是让团队更高效协作的基础! 🎯
|
@ -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攻击防护
|
||||
|
||||
记住:我的目标是让你专注于业务逻辑,而我来确保代码的规范性、安全性和可维护性! 🎯
|
@ -1,61 +1,61 @@
|
||||
---
|
||||
description: "全局规则"
|
||||
globs: ["**/*"]
|
||||
alwaysApply: true
|
||||
---
|
||||
# 规则:生成产品需求文档 (PRD)
|
||||
|
||||
## 目标 (Goal)
|
||||
|
||||
指导 AI 助手基于用户的初始提示创建详细的产品需求文档 (PRD)(Markdown 格式)。PRD 应清晰、可操作,并适合初级开发人员理解和实施该功能。
|
||||
|
||||
## 流程 (Process)
|
||||
|
||||
1. **接收初始提示:** 用户提供新功能或功能的简要描述或请求。
|
||||
2. **提出澄清性问题:** 在编写 PRD *之前*,AI *必须*提出澄清性问题以收集足够的细节。目标是理解功能的“是什么”和“为什么”,而不是“如何做”(开发人员将负责解决)。务必以字母/数字列表的形式提供选项,以便我可以用选择轻松回复。
|
||||
3. **生成 PRD:** 基于初始提示和用户对澄清性问题的回答,使用下面概述的结构生成 PRD。
|
||||
4. **保存 PRD:** 将生成的文档保存为 `/tasks` 目录中的 `prd-[功能名称].md`。
|
||||
|
||||
## 澄清性问题 (示例) (Clarifying Questions (Examples))
|
||||
|
||||
AI 应根据提示调整其问题,但以下是一些常见的探索领域:
|
||||
|
||||
* **问题/目标:** “这个功能为用户解决了什么问题?” 或 “我们希望通过这个功能实现的主要目标是什么?”
|
||||
* **目标用户:** “这个功能的主要用户是谁?”
|
||||
* **核心功能:** “您能描述用户应该能够使用此功能执行的关键操作吗?”
|
||||
* **用户故事:** “您能提供一些用户故事吗?(例如:作为 [用户类型],我希望 [执行操作],以便 [获得收益]。)”
|
||||
* **验收标准:** “我们如何知道此功能已成功实施?关键的成功标准是什么?”
|
||||
* **范围/边界:** “有没有此功能*不应该*做的特定事情(非目标)?”
|
||||
* **数据需求:** “此功能需要显示或操作什么类型的数据?”
|
||||
* **设计/UI:** “是否有现有的设计稿或 UI 指南需要遵循?” 或 “您能描述期望的外观和感觉吗?”
|
||||
* **边界情况:** “是否有任何潜在的边界情况或错误条件我们需要考虑?”
|
||||
|
||||
## PRD 结构 (PRD Structure)
|
||||
|
||||
生成的 PRD 应包含以下部分:
|
||||
|
||||
1. **引言/概述:** 简要描述该功能及其解决的问题。陈述目标。
|
||||
2. **目标:** 列出此功能的具体、可衡量的目标。
|
||||
3. **用户故事:** 详细描述描述功能使用和收益的用户叙事。
|
||||
4. **功能需求:** 列出该功能必须具备的具体功能。使用清晰、简洁的语言(例如:“系统必须允许用户上传个人资料图片。”)。对这些需求进行编号。
|
||||
5. **非目标(范围之外):** 明确说明此功能*不会*包含的内容,以管理范围。
|
||||
6. **设计考虑(可选):** 如果适用,链接到设计稿,描述 UI/UX 要求,或提及相关组件/样式。
|
||||
7. **技术考虑(可选):** 提及任何已知的技术限制、依赖项或建议(例如:“应与现有的 Auth 模块集成”)。
|
||||
8. **成功指标:** 如何衡量此功能的成功?(例如:“用户参与度提高 10%”、“减少与 X 相关的支持工单”)。
|
||||
9. **待解决问题:** 列出任何剩余问题或需要进一步澄清的领域。
|
||||
|
||||
## 目标受众 (Target Audience)
|
||||
|
||||
假设 PRD 的主要读者是**初级开发人员**。因此,需求应明确、无歧义,并尽可能避免行话。提供足够的细节供他们理解功能的目的和核心逻辑。
|
||||
|
||||
## 输出 (Output)
|
||||
|
||||
* **格式:** Markdown (`.md`)
|
||||
* **位置:** `/tasks/`
|
||||
* **文件名:** `prd-[功能名称].md`
|
||||
|
||||
## 最终指令 (Final instructions)
|
||||
|
||||
1. 不要开始实施 PRD。
|
||||
2. 务必向用户提出澄清性问题。
|
||||
---
|
||||
description: "全局规则"
|
||||
globs: ["**/*"]
|
||||
alwaysApply: true
|
||||
---
|
||||
# 规则:生成产品需求文档 (PRD)
|
||||
|
||||
## 目标 (Goal)
|
||||
|
||||
指导 AI 助手基于用户的初始提示创建详细的产品需求文档 (PRD)(Markdown 格式)。PRD 应清晰、可操作,并适合初级开发人员理解和实施该功能。
|
||||
|
||||
## 流程 (Process)
|
||||
|
||||
1. **接收初始提示:** 用户提供新功能或功能的简要描述或请求。
|
||||
2. **提出澄清性问题:** 在编写 PRD *之前*,AI *必须*提出澄清性问题以收集足够的细节。目标是理解功能的“是什么”和“为什么”,而不是“如何做”(开发人员将负责解决)。务必以字母/数字列表的形式提供选项,以便我可以用选择轻松回复。
|
||||
3. **生成 PRD:** 基于初始提示和用户对澄清性问题的回答,使用下面概述的结构生成 PRD。
|
||||
4. **保存 PRD:** 将生成的文档保存为 `/tasks` 目录中的 `prd-[功能名称].md`。
|
||||
|
||||
## 澄清性问题 (示例) (Clarifying Questions (Examples))
|
||||
|
||||
AI 应根据提示调整其问题,但以下是一些常见的探索领域:
|
||||
|
||||
* **问题/目标:** “这个功能为用户解决了什么问题?” 或 “我们希望通过这个功能实现的主要目标是什么?”
|
||||
* **目标用户:** “这个功能的主要用户是谁?”
|
||||
* **核心功能:** “您能描述用户应该能够使用此功能执行的关键操作吗?”
|
||||
* **用户故事:** “您能提供一些用户故事吗?(例如:作为 [用户类型],我希望 [执行操作],以便 [获得收益]。)”
|
||||
* **验收标准:** “我们如何知道此功能已成功实施?关键的成功标准是什么?”
|
||||
* **范围/边界:** “有没有此功能*不应该*做的特定事情(非目标)?”
|
||||
* **数据需求:** “此功能需要显示或操作什么类型的数据?”
|
||||
* **设计/UI:** “是否有现有的设计稿或 UI 指南需要遵循?” 或 “您能描述期望的外观和感觉吗?”
|
||||
* **边界情况:** “是否有任何潜在的边界情况或错误条件我们需要考虑?”
|
||||
|
||||
## PRD 结构 (PRD Structure)
|
||||
|
||||
生成的 PRD 应包含以下部分:
|
||||
|
||||
1. **引言/概述:** 简要描述该功能及其解决的问题。陈述目标。
|
||||
2. **目标:** 列出此功能的具体、可衡量的目标。
|
||||
3. **用户故事:** 详细描述描述功能使用和收益的用户叙事。
|
||||
4. **功能需求:** 列出该功能必须具备的具体功能。使用清晰、简洁的语言(例如:“系统必须允许用户上传个人资料图片。”)。对这些需求进行编号。
|
||||
5. **非目标(范围之外):** 明确说明此功能*不会*包含的内容,以管理范围。
|
||||
6. **设计考虑(可选):** 如果适用,链接到设计稿,描述 UI/UX 要求,或提及相关组件/样式。
|
||||
7. **技术考虑(可选):** 提及任何已知的技术限制、依赖项或建议(例如:“应与现有的 Auth 模块集成”)。
|
||||
8. **成功指标:** 如何衡量此功能的成功?(例如:“用户参与度提高 10%”、“减少与 X 相关的支持工单”)。
|
||||
9. **待解决问题:** 列出任何剩余问题或需要进一步澄清的领域。
|
||||
|
||||
## 目标受众 (Target Audience)
|
||||
|
||||
假设 PRD 的主要读者是**初级开发人员**。因此,需求应明确、无歧义,并尽可能避免行话。提供足够的细节供他们理解功能的目的和核心逻辑。
|
||||
|
||||
## 输出 (Output)
|
||||
|
||||
* **格式:** Markdown (`.md`)
|
||||
* **位置:** `/tasks/`
|
||||
* **文件名:** `prd-[功能名称].md`
|
||||
|
||||
## 最终指令 (Final instructions)
|
||||
|
||||
1. 不要开始实施 PRD。
|
||||
2. 务必向用户提出澄清性问题。
|
||||
3. 获取用户对澄清性问题的答案,并据此改进 PRD。
|
@ -1,54 +1,54 @@
|
||||
---
|
||||
description: "全局规则"
|
||||
globs: ["**/*"]
|
||||
alwaysApply: true
|
||||
---
|
||||
# 规则:从PRD生成任务清单
|
||||
|
||||
## 目标 (Goal)
|
||||
|
||||
指导AI助手基于现有的产品需求文档(PRD)创建一个详细的、逐步执行的任务清单(Markdown格式)。该任务清单应指导开发人员进行实现。
|
||||
|
||||
## 输出 (Output)
|
||||
|
||||
* **格式:** Markdown (`.md`)
|
||||
* **位置:** `/tasks/`
|
||||
* **文件名:** `tasks-[prd文件名].md` (例如:`tasks-prd-user-profile-editing.md`)
|
||||
|
||||
## 流程 (Process)
|
||||
|
||||
1. **接收PRD引用:** 用户指定一个特定的PRD文件。
|
||||
2. **分析PRD:** AI阅读并分析指定PRD中的功能需求、用户故事和其他部分。
|
||||
3. **阶段1:生成父任务:** 基于PRD分析,创建文件并生成实现该功能所需的主要、高级别任务。根据你的判断决定使用多少高级别任务(大约5个)。以指定格式(尚未包含子任务)将这些任务呈现给用户。通知用户:“我已根据PRD生成了高级别任务。准备好生成子任务了吗?请回复 'Go' 以继续。”
|
||||
4. **等待确认:** 暂停并等待用户回复“Go”。
|
||||
5. **阶段2:生成子任务:** 一旦用户确认,将每个父任务分解为完成父任务所需的更小、可操作的子任务。确保子任务逻辑上源自父任务,并涵盖PRD中隐含的实现细节。
|
||||
6. **识别相关文件:** 基于任务和PRD,识别需要创建或修改的潜在文件。在 `Relevant Files` 部分列出这些文件,如果适用,包括对应的测试文件。
|
||||
7. **生成最终输出:** 将父任务、子任务、相关文件和备注合并到最终的Markdown结构中。
|
||||
8. **保存任务清单:** 将生成的文档保存在 `/tasks/` 目录中,文件名为 `tasks-[prd文件名].md`,其中 `[prd文件名]` 与输入PRD文件的基本名称匹配(例如,如果输入是 `prd-user-profile-editing.md`,则输出为 `tasks-prd-user-profile-editing.md`)。
|
||||
|
||||
## 输出格式 (Output Format)
|
||||
|
||||
生成的任务清单*必须*遵循以下结构:
|
||||
|
||||
```markdown
|
||||
## 相关文件 (Relevant Files)
|
||||
|
||||
- `path/to/potential/file1.ts` - 该文件相关性的简要说明(例如:包含此功能的主要组件)。
|
||||
- `path/to/file1.test.ts` - `file1.ts` 的单元测试。
|
||||
- `path/to/another/file.tsx` - 简要说明(例如:用于数据提交的API路由处理器)。
|
||||
- `path/to/another/file.test.tsx` - `another/file.tsx` 的单元测试。
|
||||
- `lib/utils/helpers.ts` - 简要说明(例如:计算所需的工具函数)。
|
||||
- `lib/utils/helpers.test.ts` - `helpers.ts` 的单元测试。
|
||||
|
||||
### 备注 (Notes)
|
||||
|
||||
- 单元测试通常应放置在与它们测试的代码文件相同的目录中(例如:在同一目录下的 `MyComponent.tsx` 和 `MyComponent.test.tsx`)。
|
||||
|
||||
## 任务 (Tasks)
|
||||
|
||||
- [ ] 1.0 父任务标题
|
||||
- [ ] 1.1 [子任务描述 1.1]
|
||||
- [ ] 1.2 [子任务描述 1.2]
|
||||
- [ ] 2.0 父任务标题
|
||||
- [ ] 2.1 [子任务描述 2.1]
|
||||
---
|
||||
description: "全局规则"
|
||||
globs: ["**/*"]
|
||||
alwaysApply: true
|
||||
---
|
||||
# 从PRD生成任务清单
|
||||
|
||||
## 目标 (Goal)
|
||||
|
||||
指导AI助手基于现有的产品需求文档(PRD)创建一个详细的、逐步执行的任务清单(Markdown格式)。该任务清单应指导开发人员进行实现。
|
||||
|
||||
## 输出 (Output)
|
||||
|
||||
* **格式:** Markdown (`.md`)
|
||||
* **位置:** `/tasks/`
|
||||
* **文件名:** `tasks-[prd文件名].md` (例如:`tasks-prd-user-profile-editing.md`)
|
||||
|
||||
## 流程 (Process)
|
||||
|
||||
1. **接收PRD引用:** 用户指定一个特定的PRD文件。
|
||||
2. **分析PRD:** AI阅读并分析指定PRD中的功能需求、用户故事和其他部分。
|
||||
3. **阶段1:生成父任务:** 基于PRD分析,创建文件并生成实现该功能所需的主要、高级别任务。根据你的判断决定使用多少高级别任务(大约5个)。以指定格式(尚未包含子任务)将这些任务呈现给用户。通知用户:“我已根据PRD生成了高级别任务。准备好生成子任务了吗?请回复 'Go' 以继续。”
|
||||
4. **等待确认:** 暂停并等待用户回复“Go”。
|
||||
5. **阶段2:生成子任务:** 一旦用户确认,将每个父任务分解为完成父任务所需的更小、可操作的子任务。确保子任务逻辑上源自父任务,并涵盖PRD中隐含的实现细节。
|
||||
6. **识别相关文件:** 基于任务和PRD,识别需要创建或修改的潜在文件。在 `Relevant Files` 部分列出这些文件,如果适用,包括对应的测试文件。
|
||||
7. **生成最终输出:** 将父任务、子任务、相关文件和备注合并到最终的Markdown结构中。
|
||||
8. **保存任务清单:** 将生成的文档保存在 `/tasks/` 目录中,文件名为 `tasks-[prd文件名].md`,其中 `[prd文件名]` 与输入PRD文件的基本名称匹配(例如,如果输入是 `prd-user-profile-editing.md`,则输出为 `tasks-prd-user-profile-editing.md`)。
|
||||
|
||||
## 输出格式 (Output Format)
|
||||
|
||||
生成的任务清单*必须*遵循以下结构:
|
||||
|
||||
```markdown
|
||||
## 相关文件 (Relevant Files)
|
||||
|
||||
- `path/to/potential/file1.ts` - 该文件相关性的简要说明(例如:包含此功能的主要组件)。
|
||||
- `path/to/file1.test.ts` - `file1.ts` 的单元测试。
|
||||
- `path/to/another/file.tsx` - 简要说明(例如:用于数据提交的API路由处理器)。
|
||||
- `path/to/another/file.test.tsx` - `another/file.tsx` 的单元测试。
|
||||
- `lib/utils/helpers.ts` - 简要说明(例如:计算所需的工具函数)。
|
||||
- `lib/utils/helpers.test.ts` - `helpers.ts` 的单元测试。
|
||||
|
||||
### 备注 (Notes)
|
||||
|
||||
- 单元测试通常应放置在与它们测试的代码文件相同的目录中(例如:在同一目录下的 `MyComponent.tsx` 和 `MyComponent.test.tsx`)。
|
||||
|
||||
## 任务 (Tasks)
|
||||
|
||||
- [ ] 1.0 父任务标题
|
||||
- [ ] 1.1 [子任务描述 1.1]
|
||||
- [ ] 1.2 [子任务描述 1.2]
|
||||
- [ ] 2.0 父任务标题
|
||||
- [ ] 2.1 [子任务描述 2.1]
|
||||
- [ ] 3.0 父任务标题 (如果纯粹是结构性的或配置性的,则可能不需要子任务)
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +0,0 @@
|
||||
---
|
||||
description: "全局规则"
|
||||
globs: ["**/*"]
|
||||
alwaysApply: true
|
||||
---
|
||||
# 特殊要求
|
||||
|
||||
1. 删除文件时一定要请求确认
|
||||
|
||||
2. **不允许执行脚本和命令,告诉我命令即可,由我手动执行**
|
@ -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也归档
|
46
.cursor/rules/task-execution.mdc
Normal file
46
.cursor/rules/task-execution.mdc
Normal 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也归档
|
@ -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`.
|
503
.trae/rules/project_rules.md
Normal file
503
.trae/rules/project_rules.md
Normal 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: '用户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. 方法
|
18
bun.lock
18
bun.lock
@ -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=="],
|
||||
|
503
docs/elysiaAPI开发流程.md
Normal file
503
docs/elysiaAPI开发流程.md
Normal 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: '用户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. 方法
|
@ -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"
|
||||
},
|
||||
|
@ -1,36 +1,36 @@
|
||||
/**
|
||||
* @file API文档标签定义
|
||||
* @author hotok
|
||||
* @date 2025-06-29
|
||||
* @lastEditor hotok
|
||||
* @lastEditTime 2025-06-29
|
||||
* @description 统一管理Swagger文档中的标签,便于接口分类和文档组织
|
||||
*/
|
||||
|
||||
/**
|
||||
* API文档标签枚举
|
||||
* @description 用于Swagger文档的接口分类
|
||||
*/
|
||||
export const tags = {
|
||||
/** 用户相关接口 */
|
||||
user: 'User',
|
||||
/** 认证相关接口 */
|
||||
auth: 'Auth',
|
||||
/** 健康检查接口 */
|
||||
health: 'Health',
|
||||
/** 测试接口 */
|
||||
test: 'Test',
|
||||
/** 文件上传接口 */
|
||||
upload: 'Upload',
|
||||
/** 系统管理接口 */
|
||||
system: 'System',
|
||||
/** 权限管理接口 */
|
||||
permission: 'Permission',
|
||||
/** 验证码相关接口 */
|
||||
captcha: 'Captcha',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 标签类型定义
|
||||
*/
|
||||
/**
|
||||
* @file API文档标签定义
|
||||
* @author hotok
|
||||
* @date 2025-06-29
|
||||
* @lastEditor hotok
|
||||
* @lastEditTime 2025-06-29
|
||||
* @description 统一管理Swagger文档中的标签,便于接口分类和文档组织
|
||||
*/
|
||||
|
||||
/**
|
||||
* API文档标签枚举
|
||||
* @description 用于Swagger文档的接口分类
|
||||
*/
|
||||
export const tags = {
|
||||
/** 用户相关接口 */
|
||||
user: 'User',
|
||||
/** 认证相关接口 */
|
||||
auth: 'Auth',
|
||||
/** 健康检查接口 */
|
||||
health: 'Health',
|
||||
/** 测试接口 */
|
||||
test: 'Test',
|
||||
/** 文件上传接口 */
|
||||
upload: 'Upload',
|
||||
/** 系统管理接口 */
|
||||
system: 'System',
|
||||
/** 权限管理接口 */
|
||||
permission: 'Permission',
|
||||
/** 验证码相关接口 */
|
||||
captcha: 'Captcha',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 标签类型定义
|
||||
*/
|
||||
export type ApiTag = typeof tags[keyof typeof tags];
|
@ -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';
|
||||
|
||||
/**
|
||||
* 认证控制器
|
||||
|
@ -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()
|
||||
/**
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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: '用户ID(bigint类型以字符串形式返回防止精度丢失)',
|
||||
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: '用户ID(bigint类型以字符串形式返回防止精度丢失)',
|
||||
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]>;
|
||||
|
@ -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: '用户ID(bigint类型以字符串形式返回防止精度丢失)',
|
||||
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: '用户ID(bigint类型以字符串形式返回防止精度丢失)',
|
||||
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>;
|
@ -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';
|
||||
|
||||
/**
|
||||
* 用户服务类
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -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 - 实现创建用户业务逻辑
|
||||
|
Loading…
Reference in New Issue
Block a user