diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..48fe731 --- /dev/null +++ b/.env.example @@ -0,0 +1,20 @@ +NODE_ENV=production +PORT=3000 + +# App auth / storage / database +DATABASE_URL=mysql://user:password@127.0.0.1:4000/tennis_training_hub +JWT_SECRET=replace-with-strong-secret +VITE_APP_ID=tennis-training-hub +OAUTH_SERVER_URL= +OWNER_OPEN_ID= +BUILT_IN_FORGE_API_URL= +BUILT_IN_FORGE_API_KEY= +VITE_OAUTH_PORTAL_URL= +VITE_FRONTEND_FORGE_API_URL= +VITE_FRONTEND_FORGE_API_KEY= + +# Optional direct media URL override for browser builds +VITE_MEDIA_BASE_URL=/media + +# Local app-to-media proxy for development or direct container access +MEDIA_SERVICE_URL=http://127.0.0.1:8081 diff --git a/.gitignore b/.gitignore index 24ab932..41217e6 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,8 @@ pids # Coverage directory used by tools like istanbul coverage/ *.lcov +playwright-report/ +test-results/ # nyc test coverage .nyc_output diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..02f6653 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM node:22-bookworm-slim AS deps +WORKDIR /app +RUN corepack enable +COPY package.json pnpm-lock.yaml ./ +COPY patches ./patches +RUN pnpm install --frozen-lockfile + +FROM node:22-bookworm-slim AS build +WORKDIR /app +RUN corepack enable +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN pnpm build + +FROM node:22-bookworm-slim AS runtime +WORKDIR /app +ENV NODE_ENV=production +RUN corepack enable +COPY package.json pnpm-lock.yaml ./ +COPY patches ./patches +RUN pnpm install --prod --frozen-lockfile +COPY --from=build /app/dist ./dist +EXPOSE 3000 +CMD ["node", "dist/index.js"] diff --git a/README.md b/README.md index af70b19..fa32339 100644 --- a/README.md +++ b/README.md @@ -1,186 +1,102 @@ -# Tennis Training Hub - AI网球训练助手 +# Tennis Training Hub -一个基于AI的在家网球训练平台,通过MediaPipe姿势识别技术帮助用户在只有球拍的条件下进行科学训练,自动分析挥拍姿势并生成个性化训练计划。 +AI 网球训练助手,提供训练计划、姿势分析、实时摄像头分析、在线视频录制与视频库管理。当前版本新增独立 Go 媒体服务,用于处理在线录制、分段上传、实时推流信令和归档回放。 -## 功能概览 +## Architecture -| 功能模块 | 描述 | 技术实现 | -|---------|------|---------| -| 用户名登录 | 无需注册,输入用户名即可使用 | tRPC + JWT Session | -| 训练计划生成 | 根据用户水平(初/中/高级)AI生成训练计划 | LLM结构化输出 | -| 视频上传分析 | 上传训练视频进行姿势识别 | MediaPipe Pose + S3 | -| 实时摄像头分析 | 手机/电脑摄像头实时捕捉分析 | MediaPipe实时推理 | -| 在线录制 | 稳定压缩流录制、断线重连、自动剪辑 | MediaRecorder API | -| 姿势矫正建议 | AI根据分析结果生成矫正方案 | LLM + 姿势数据 | -| NTRP自动评分 | 基于USTA标准的五维度加权评分 | 自动算法 | -| 训练计划自动调整 | 根据分析结果智能调整后续计划 | LLM + 历史数据 | -| 每日打卡 | 连续打卡追踪、训练时长记录 | 日期计算 + 数据库 | -| 成就徽章 | 24种成就徽章激励系统 | 自动检测 + 授予 | -| 社区排行榜 | NTRP评分、训练时长、击球数排名 | 数据库排序查询 | -| 训练进度追踪 | 可视化展示训练历史和改进趋势 | Recharts图表 | -| 视频库管理 | 保存管理所有训练视频及分析结果 | S3 + 数据库 | -| 移动端适配 | 全面响应式设计,手机摄像头优化 | Tailwind响应式 | +- `client/`: React 19 + TypeScript + Tailwind CSS 4 + shadcn/ui +- `server/`: Express + tRPC + Drizzle + MySQL/TiDB,负责业务 API、登录、训练数据与视频库元数据 +- `media/`: Go 媒体服务,负责录制会话、分段上传、WebRTC 信令、关键片段标记与 FFmpeg 归档 +- `docker-compose.yml`: 单机部署编排 +- `deploy/nginx.te.hao.work.conf`: `te.hao.work` 的宿主机 nginx 入口配置 -## 技术栈 +## Online Recording -**前端:** -- React 19 + TypeScript -- Tailwind CSS 4 + shadcn/ui -- MediaPipe Pose(浏览器端姿势识别) -- Recharts(数据可视化) -- Framer Motion(动画效果) -- wouter(路由) +在线录制模块采用双链路设计: -**后端:** -- Express 4 + tRPC 11 -- Drizzle ORM + MySQL/TiDB -- S3文件存储 -- LLM集成(训练计划生成、姿势矫正建议) +- 浏览器端 `MediaRecorder` 本地压缩并每 60 秒自动分段上传 +- 浏览器端 `RTCPeerConnection` 同步建立 WebRTC 低延迟推流链路 +- 客户端运动检测自动写入关键片段 marker,也支持手动标记 +- 摄像头中断后自动重连,保留既有分段与会话 +- 服务端 worker 将分段合并归档,并产出 WebM 回放;FFmpeg 可用时额外生成 MP4 -## 项目结构 +## Quick Start -``` -tennis-training-hub/ -├── client/ -│ ├── src/ -│ │ ├── pages/ -│ │ │ ├── Home.tsx # 落地页 -│ │ │ ├── Login.tsx # 用户名登录 -│ │ │ ├── Dashboard.tsx # 仪表盘 -│ │ │ ├── Training.tsx # 训练计划 -│ │ │ ├── Analysis.tsx # 视频分析(MediaPipe) -│ │ │ ├── LiveCamera.tsx # 实时摄像头分析 -│ │ │ ├── Recorder.tsx # 在线录制 -│ │ │ ├── Videos.tsx # 视频库 -│ │ │ ├── Progress.tsx # 训练进度 -│ │ │ ├── Rating.tsx # NTRP评分详情 -│ │ │ ├── Leaderboard.tsx # 社区排行榜 -│ │ │ └── Checkin.tsx # 每日打卡+徽章 -│ │ ├── components/ -│ │ │ └── DashboardLayout.tsx # 侧边栏导航布局 -│ │ ├── App.tsx # 路由配置 -│ │ └── index.css # 主题样式 -│ └── index.html -├── server/ -│ ├── routers.ts # tRPC路由定义 -│ ├── db.ts # 数据库查询助手 -│ ├── storage.ts # S3存储助手 -│ ├── features.test.ts # 功能测试(47个) -│ └── _core/ # 框架核心(勿修改) -├── drizzle/ -│ └── schema.ts # 数据库表结构 -└── shared/ - └── const.ts # 共享常量 -``` - -## 数据库设计 - -### 核心表 - -| 表名 | 用途 | 关键字段 | -|------|------|---------| -| `users` | 用户信息 | openId, name, skillLevel, ntrpRating, totalSessions, currentStreak | -| `username_accounts` | 用户名登录映射 | username, userId | -| `training_plans` | AI训练计划 | exercises(JSON), skillLevel, durationDays, version | -| `training_videos` | 训练视频 | fileKey, url, format, analysisStatus | -| `pose_analyses` | 姿势分析结果 | overallScore, shotCount, avgSwingSpeed, strokeConsistency | -| `training_records` | 训练记录 | exerciseName, durationMinutes, completed, poseScore | -| `rating_history` | NTRP评分历史 | rating, dimensionScores(JSON), analysisId | -| `daily_checkins` | 每日打卡 | checkinDate, streakCount, minutesTrained | -| `user_badges` | 成就徽章 | badgeKey, earnedAt | - -## NTRP自动评分系统 - -评分基于USTA(美国网球协会)的NTRP标准,范围1.0-5.0,采用五维度加权计算: - -| 维度 | 权重 | 说明 | -|------|------|------| -| 姿势正确性 | 30% | 基于MediaPipe关键点角度分析 | -| 击球一致性 | 25% | 多次挥拍动作的稳定性 | -| 脚步移动 | 20% | 身体重心移动和步法评估 | -| 动作流畅性 | 15% | 挥拍动作的连贯性和自然度 | -| 力量表现 | 10% | 基于挥拍速度估算 | - -**评分映射规则:** -- 0-20分 → NTRP 1.0-1.5(初学者) -- 20-40分 → NTRP 1.5-2.5(初级) -- 40-60分 → NTRP 2.5-3.5(中级) -- 60-80分 → NTRP 3.5-4.5(中高级) -- 80-100分 → NTRP 4.5-5.0(高级) - -评分会根据最近20次视频分析结果自动更新,近期分析权重更高。 - -## 成就徽章系统 - -共24种成就徽章,分为6个类别: - -| 类别 | 徽章数 | 示例 | -|------|--------|------| -| 里程碑 | 1 | 初来乍到(首次登录) | -| 训练 | 6 | 初试身手、十次训练、百次训练、训练时长里程碑 | -| 连续打卡 | 4 | 三日坚持、一周达人、两周勇士、月度冠军 | -| 视频 | 3 | 影像记录、视频达人、视频大师 | -| 分析 | 4 | AI教练、优秀姿势、完美姿势、击球里程碑 | -| 评分 | 3 | NTRP 2.0/3.0/4.0 | - -## 在线录制功能 - -在线录制模块提供专业级录制体验: - -- **稳定压缩流**:使用MediaRecorder API,自适应码率(1-2.5Mbps),支持webm/mp4格式 -- **断线自动重连**:摄像头意外断开时自动检测并重新连接,保存已录制片段 -- **自动剪辑**:基于运动检测自动标记关键时刻,支持手动设置剪辑点 -- **分段录制**:每60秒自动分段,防止数据丢失 -- **手机摄像头优化**:支持前后摄像头切换,自适应分辨率 - -## 移动端适配 - -- 安全区域适配(iPhone X+刘海屏) -- 触摸友好的44px最小点击区域 -- 横屏视频优化 -- 防误触下拉刷新(录制/分析模式) -- 响应式侧边栏导航 -- 移动端底部导航栏 - -## 测试 - -项目包含47个vitest测试用例,覆盖所有核心后端功能: +### Local development ```bash -pnpm test -``` - -测试覆盖范围: -- 认证系统(登录、登出、用户名验证) -- 用户资料管理 -- 训练计划生成(输入验证) -- 视频上传和管理 -- 姿势分析保存和查询 -- 训练记录创建和完成 -- NTRP评分系统 -- 每日打卡系统 -- 成就徽章系统 -- 社区排行榜 - -## 开发 - -```bash -# 安装依赖 pnpm install - -# 启动开发服务器 +cp .env.example .env pnpm dev - -# 运行测试 -pnpm test - -# 类型检查 -pnpm check - -# 构建生产版本 -pnpm build ``` -## 许可证 +本地开发时: -MIT License +- Node 应用默认运行在 `http://localhost:3000` +- 若设置 `MEDIA_SERVICE_URL=http://127.0.0.1:8081`,Express 会把 `/media` 代理到 Go 服务 +- Go 媒体服务可单独启动: + +```bash +cd media +go mod tidy +go run . +``` + +### Checks + +```bash +pnpm check +pnpm test +pnpm test:go +pnpm build +pnpm test:e2e +pnpm verify + +cd media +go build ./... +``` + +首次运行浏览器测试前执行: + +```bash +pnpm exec playwright install chromium +``` + +## Production Deployment + +单机部署推荐: + +1. 宿主机 nginx 处理 `80/443` 和 TLS +2. `docker compose up -d --build` 启动 `app + media + worker` +3. nginx 将 `/` 转发到 `app:3000`,`/media/` 转发到 `media:8081` + +详细步骤见: + +- `docs/deploy.md` +- `docs/media-architecture.md` +- `docs/frontend-recording.md` + +## Documentation Index + +- `docs/FEATURES.md`: 当前功能特性与能力边界 +- `docs/testing.md`: 自动测试分层与运行方式 +- `docs/verified-features.md`: 已验证通过的项目清单 +- `docs/developer-workflow.md`: 阶段可中断的开发与本地提交流程 +- `docs/deploy.md`: 部署指南 +- `docs/media-architecture.md`: 媒体服务架构 +- `docs/frontend-recording.md`: 前端录制与移动端适配说明 + +## Environment + +关键环境变量见 `.env.example`,重点包括: + +- `DATABASE_URL` +- `JWT_SECRET` +- `MEDIA_SERVICE_URL` +- `VITE_MEDIA_BASE_URL` + +## Notes + +- 浏览器兼容目标以 Chrome 为主 +- 录制文件优先产出 WebM,MP4 为服务端可选归档产物 +- 存储策略当前为本地卷优先,适合单机 Compose 部署 diff --git a/client/index.html b/client/index.html index 6310b19..b34d061 100644 --- a/client/index.html +++ b/client/index.html @@ -15,10 +15,6 @@
-