8. 健身记录App(technical-architecture)
1. 架构设计
2. 技术描述
- 前端: React@18 + TypeScript + TailwindCSS@3 + Vite
- 初始化工具: vite-init
- 图表库: Recharts@2
- 表单验证: React Hook Form + Zod
- 状态管理: React Context + useReducer
- 后端: Supabase (提供认证、数据库、存储服务)
- UI组件库: HeadlessUI (可选,用于复杂组件)
3. 路由定义
| 路由 | 用途 |
|---|---|
| / | 健身首页,展示训练概览和今日计划 |
| /plans | 训练计划页面,管理所有训练计划 |
| /exercises | 动作库页面,浏览和搜索训练动作 |
| /record | 记录训练页面,进行训练记录 |
| /statistics | 数据统计页面,查看训练数据分析 |
| /diet | 饮食记录页面,管理饮食信息 |
| /profile | 个人资料页面,管理用户信息 |
| /login | 登录页面 |
| /register | 注册页面 |
4. 数据模型
4.1 数据模型定义
4.2 数据定义语言
用户表 (users)
-- 创建表
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
avatar_url TEXT,
fitness_goal VARCHAR(50),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 创建索引
CREATE INDEX idx_users_email ON users(email);
训练计划表 (training_plans)
-- 创建表
CREATE TABLE training_plans (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(200) NOT NULL,
description TEXT,
duration_weeks INTEGER DEFAULT 4,
difficulty VARCHAR(20) CHECK (difficulty IN ('beginner', 'intermediate', 'advanced')),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 创建索引
CREATE INDEX idx_training_plans_user_id ON training_plans(user_id);
CREATE INDEX idx_training_plans_active ON training_plans(is_active);
动作表 (exercises)
-- 创建表
CREATE TABLE exercises (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(200) NOT NULL,
category VARCHAR(50) NOT NULL,
muscle_group VARCHAR(100),
equipment VARCHAR(100),
description TEXT,
video_url TEXT,
image_url TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 创建索引
CREATE INDEX idx_exercises_category ON exercises(category);
CREATE INDEX idx_exercises_muscle_group ON exercises(muscle_group);
训练记录表 (training_records)
-- 创建表
CREATE TABLE training_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
plan_id UUID REFERENCES training_plans(id) ON DELETE SET NULL,
training_date DATE NOT NULL,
total_sets INTEGER DEFAULT 0,
total_reps INTEGER DEFAULT 0,
total_weight FLOAT DEFAULT 0,
duration_minutes INTEGER DEFAULT 0,
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 创建索引
CREATE INDEX idx_training_records_user_id ON training_records(user_id);
CREATE INDEX idx_training_records_date ON training_records(training_date);
身体数据表 (body_data)
-- 创建表
CREATE TABLE body_data (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
record_date DATE NOT NULL,
weight FLOAT,
height FLOAT,
body_fat_percentage FLOAT,
muscle_mass FLOAT,
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(user_id, record_date)
);
-- 创建索引
CREATE INDEX idx_body_data_user_id ON body_data(user_id);
CREATE INDEX idx_body_data_date ON body_data(record_date);
4.3 Supabase权限设置
-- 基本权限设置
GRANT SELECT ON exercises TO anon;
GRANT ALL PRIVILEGES ON exercises TO authenticated;
GRANT SELECT ON training_plans TO anon;
GRANT ALL PRIVILEGES ON training_plans TO authenticated;
GRANT SELECT ON training_records TO anon;
GRANT ALL PRIVILEGES ON training_records TO authenticated;
GRANT SELECT ON body_data TO anon;
GRANT ALL PRIVILEGES ON body_data TO authenticated;
-- RLS (Row Level Security) 策略
ALTER TABLE training_plans ENABLE ROW LEVEL SECURITY;
ALTER TABLE training_records ENABLE ROW LEVEL SECURITY;
ALTER TABLE body_data ENABLE ROW LEVEL SECURITY;
-- 用户只能查看和修改自己的数据
CREATE POLICY "用户只能查看自己的训练计划" ON training_plans
FOR ALL USING (auth.uid() = user_id);
CREATE POLICY "用户只能查看自己的训练记录" ON training_records
FOR ALL USING (auth.uid() = user_id);
CREATE POLICY "用户只能查看自己的身体数据" ON body_data
FOR ALL USING (auth.uid() = user_id);
5. 前端架构设计
5.1 组件结构
src/
├── components/ # 通用组件
│ ├── common/ # 基础组件
│ │ ├── Button.tsx
│ │ ├── Card.tsx
│ │ ├── Input.tsx
│ │ └── Loading.tsx
│ ├── layout/ # 布局组件
│ │ ├── Header.tsx
│ │ ├── BottomNav.tsx
│ │ └── Layout.tsx
│ └── charts/ # 图表组件
│ ├── ProgressChart.tsx
│ ├── WeightChart.tsx
│ └── StatisticsChart.tsx
├── pages/ # 页面组件
│ ├── Home.tsx # 健身首页
│ ├── Plans.tsx # 训练计划
│ ├── Exercises.tsx # 动作库
│ ├── Record.tsx # 记录训练
│ ├── Statistics.tsx # 数据统计
│ ├── Diet.tsx # 饮食记录
│ └── Profile.tsx # 个人资料
├── hooks/ # 自定义Hook
│ ├── useAuth.ts
│ ├── useTraining.ts
│ └── useStatistics.ts
├── context/ # 上下文管理
│ ├── AuthContext.tsx
│ └── TrainingContext.tsx
├── utils/ # 工具函数
│ ├── validation.ts # 表单验证
│ ├── date.ts # 日期处理
│ └── calculation.ts # 数据计算
└── types/ # TypeScript类型定义
├── user.ts
├── training.ts
└── exercise.ts
5.2 状态管理
- 认证状态: 使用React Context管理用户登录状态
- 训练数据: 使用useReducer管理复杂的训练记录状态
- UI状态: 使用useState管理组件级别的UI状态
5.3 表单验证
使用React Hook Form + Zod进行表单验证:
// 示例:训练记录表单验证
const trainingRecordSchema = z.object({
exerciseId: z.string().uuid(),
sets: z.array(z.object({
reps: z.number().min(1).max(100),
weight: z.number().min(0).max(500),
isCompleted: z.boolean()
})).min(1),
notes: z.string().optional()
});
6. 性能优化
6.1 数据获取优化
- 使用React Query进行数据缓存和同步
- 实现分页加载,避免一次性加载大量数据
- 使用Supabase的实时订阅功能,保持数据实时更新
6.2 组件优化
- 使用React.memo进行组件记忆化
- 实现虚拟滚动列表,优化长列表性能
- 使用懒加载策略,按需加载页面和组件
6.3 图片优化
- 使用WebP格式图片,减少加载时间
- 实现图片懒加载,提升首屏加载速度
- 使用CDN加速静态资源访问
20大项目拆解:从PRD到架构 文章被收录于专栏
想独立做出一个完整的项目却不知从何下手?本专栏是你的终极路线图。我们由浅入深,通过20个经典项目案例,手把手带你走过产品构思、需求撰写、功能设计、技术选型、架构搭建的全过程。从“音乐播放器”到“企业后台”,你将逐步建立对软件系统的完整认知,完成从理论到实践、从单一技能到复合能力的飞跃。

