# Tennis Training Hub - 代码规范文档 ## 项目约定 ### 技术栈版本 | 技术 | 版本 | 用途 | |------|------|------| | React | 19 | UI框架 | | TypeScript | 5.9 | 类型安全 | | Tailwind CSS | 4 | 样式系统 | | tRPC | 11 | 端到端类型安全API | | Drizzle ORM | 0.44 | 数据库ORM | | Vitest | 2.1 | 测试框架 | | Express | 4 | HTTP服务器 | ### 文件命名规范 | 类型 | 规范 | 示例 | |------|------|------| | React页面 | PascalCase | `Dashboard.tsx`, `LiveCamera.tsx` | | React组件 | PascalCase | `DashboardLayout.tsx` | | 工具函数 | camelCase | `db.ts`, `storage.ts` | | 测试文件 | `*.test.ts` | `features.test.ts` | | 数据库迁移 | 自动生成 | `0001_public_prowler.sql` | | 文档 | UPPER_CASE.md | `API.md`, `DATABASE.md` | ### 代码风格 **TypeScript/React:** - 使用函数组件和Hooks,不使用类组件 - 使用 `const` 优先,必要时使用 `let`,禁止 `var` - 使用箭头函数作为回调 - 使用模板字符串而非字符串拼接 - 使用可选链 `?.` 和空值合并 `??` - 导出组件使用 `export default function ComponentName()` - 类型定义使用 `type` 而非 `interface`(除非需要继承) **CSS/Tailwind:** - 优先使用Tailwind工具类 - 颜色使用OKLCH格式(Tailwind 4要求) - 响应式设计使用移动优先策略(`sm:`, `md:`, `lg:`) - 语义化颜色变量定义在 `index.css` 的 `:root` 中 - 避免内联样式,除非动态计算值 **数据库:** - 字段使用camelCase命名 - 主键统一使用 `id: int().autoincrement().primaryKey()` - 时间字段使用 `timestamp` 类型 - JSON字段用于存储结构化但不需要索引的数据 - 所有表包含 `createdAt` 字段 ### tRPC路由规范 ```typescript // 公开接口使用 publicProcedure publicProcedure.query(...) // 需要认证的接口使用 protectedProcedure protectedProcedure.query(...) protectedProcedure.mutation(...) // 输入验证使用 Zod .input(z.object({ field: z.string().min(1).max(64), optional: z.number().optional(), })) ``` ### 测试规范 - 每个API路由至少有一个认证测试 - 输入验证测试覆盖边界值 - 使用 `createMockContext()` 创建测试上下文 - 数据库操作在测试中允许抛出连接错误,但输入验证不应失败 - 测试文件放在 `server/` 目录下 ### Git提交规范 | 前缀 | 用途 | 示例 | |------|------|------| | `feat:` | 新功能 | `feat: 添加排行榜功能` | | `fix:` | 修复 | `fix: 修复打卡连续天数计算` | | `docs:` | 文档 | `docs: 更新API文档` | | `test:` | 测试 | `test: 添加徽章系统测试` | | `refactor:` | 重构 | `refactor: 优化评分计算逻辑` | | `style:` | 样式 | `style: 调整移动端布局` | ### 安全规范 - 所有LLM调用必须在服务端执行 - 文件上传通过服务端中转到S3 - 用户输入使用Zod严格验证 - Session使用HttpOnly Cookie - 敏感操作使用 `protectedProcedure` ### 性能规范 - MediaPipe推理在浏览器端执行,不占用服务器资源 - 视频文件存储在S3,不存入数据库 - 使用tRPC的React Query缓存减少重复请求 - 大列表查询使用 `limit` 参数分页 - 图片和媒体资源使用CDN URL