智能健身应用:数据结构、数据库架构和服务器设计规范
宇宙文物这是第 2 集“规划应用的系统规范(无代码)”中使用的工件。您可以将此规范作为参考,用于构建 Cloud Firestore 集合、服务器端点和 3 层级智能体内存。
登录即可访问此制品
使用您的 Google 开发者资料登录,即可查看和复制“Build It!”系列中使用的完整工件。
必须提供开发者资料
您不符合创建开发者资料的条件。请通知您的 Workspace 管理员启用此功能。
创建开发者资料
创建免费的 Google 开发者资料,解锁所有 Build It! 提示和工件。
加入 Builder Central 即可解锁
一键领取免费的 Builder 会员徽章,即可解锁所有 Build It! 提示和工件。
状态:进行中(正在使用 Planning Companion 起草)
目标数据库:Google Cloud Firestore
架构目标:对 AI Studio 智能健身原型进行简洁、可维护、可用于生产用途的重构。
0. 核心技术栈和系统架构决策
为确保生产环境的稳定性、性能和清晰的边界,以下架构基准已锁定,适用于所有实现:
- 前端框架:Next.js(App Router、React 19),具有渐进式 Web 应用 (PWA) 离线资源缓存(使用 Serwist /
@ducanh2912/next-pwa)。 - 样式和界面组件:Tailwind CSS v4 + Tailwind Variants(用于变体管理)+ Lucide React(图标)。
- 客户端状态架构:
- Zustand(使用
persist中间件):处理本地、触觉和临时客户端状态(秒表计时器、活动组缓冲区、步数图表和未保存的健身房输入)。 - TanStack Query (React Query):处理所有异步服务器状态、Firestore 文档缓存、后台重新验证、请求去重和乐观变更。
- Zustand(使用
- 后端运行时和 API 托管:Next.js 路由处理程序 (
app/api/...) 托管在 Google Cloud Run 上,与 Firestore 和 Gemini 上下文缓存位于同一区域,内部延迟低于 15 毫秒。 - 身份验证:Firebase Authentication(Google 登录、电子邮件/密码和匿名登录关联)。客户端在所有 API 请求中附加
Authorization: Bearer <Firebase_ID_Token>;服务器使用firebase-admin.auth().verifyIdToken(token)验证身份。 - 主数据库:Google Cloud Firestore。
1. 系统概览和核心用户体验历程
1.1 范围和架构原则
本规范定义了 Smart Fitness 应用的数据模型、Cloud Firestore 架构、服务器端点协定和 AI 智能体内存结构。它为所有锻炼规划、历史练习记录和自主分层教练记忆建立了单一事实来源,并与前端呈现框架完全解耦(前端呈现框架在专门的 UI/UX 设计规范中进行处理)。
1.2 核心数据原则
- 关注点分离:数据协定(后端/数据库)与客户端界面设计之间有严格的界限。
- 混合库架构:静态、不可变的练习百科全书 (
exercises) 与动态 AI 组合的每周时间表 (weekly_plans) 相结合。 - 人机协同分阶段变更:AI 生成的锻炼变更会作为分阶段提案返回;只有在用户明确接受后,才会执行数据库写入操作。
- 连续三层实时记忆:AI 会自主捕获、更新和清除用户事实,涵盖永久性基本信息、会过期的生理状态(具有明确的 TTL)以及瞬时情境。
- 精细的单文档读取:数据被结构化为经过优化的文档(例如每周计划螺旋笔记本和每周聊天存储桶),以最大限度地减少读取延迟并消除冗余查询。
2. 服务器端点和 API 协定
每个端点都必须指定响应来源(
AGENT、DATABASE或HYBRID),以便在实现过程中正确配置由代理支持的端点。
POST /api/coach/chat
- 用途:与 AI 私人教练进行对话式互动,处理指导问题、提供激励支持,以及生成建议的锻炼修改方案(包括每日调整和每周全面调整)。
- 触发条件:用户在“教练”标签页中提交消息,或点按内嵌的符合情境的建议修改条状标签(例如,“将阿诺德推举替换为哑铃肩上推举”)。
- Response Source:
AGENT- 利用 Gemini 的结构化工具/函数调用和流式传输功能。不会直接更改数据库;会返回暂存的提案,供人机协同审核。 - 标头:
Authorization: Bearer <Firebase_ID_Token>、Content-Type: application/json
请求载荷:
{
"message": "string (user input or selected prompt chip text)",
"context": {
"active_tab": "string (enum: today | week | coach)",
"week_start_date": "string (ISO date YYYY-MM-DD)",
"selected_day": "string (e.g. monday, optional)"
}
}响应载荷:
{
"message": "string (conversational coaching response displayed in chat bubble)",
"proposal": {
"id": "string (unique proposal ID, nullable)",
"type": "string (enum: NONE | UPDATE_DAY | UPDATE_WEEK)",
"target_day": "string (e.g. monday, nullable)",
"summary_diff": "string (concise summary of changes, e.g. 'Swapped Arnold Press ➔ DB Shoulder Press', nullable)",
"data": "map (full proposed workout day object or weekly plan object, nullable)"
},
"memory_mutations": [
{
"operation": "string (enum: CREATE | UPDATE | DELETE)",
"target_tier": "string (enum: tier_1_staples | tier_2_physiological | tier_3_situational)",
"memory_id": "string (required for UPDATE and DELETE)",
"entry": "map (memory payload with fact, category, expires_at, etc., required for CREATE and UPDATE)",
"reason": "string (concise rationale for why memory was mutated)"
}
]
}- 双重写入
weekly_chats:在每个轮次中,服务器都会使用FieldValue.arrayUnion()将传入的用户消息 (text) 和教练的回答 (text) 保存在/users/{userId}/weekly_chats/{week_start_date}中。 - 分阶段变更模式:返回提案载荷,而不修改
/weekly_plans,直到用户点按[接受方案]。 - 持续自主内存管理:发出
memory_mutations,以在用户的 3 层级个人资料中创建、更新或完全清除内容。
POST /api/workouts/log
- 用途:将已完成的锻炼课程保存到用户的永久训练档案中,计算最终的训练量/重复次数汇总数据,检查个人记录 (PR),并将预定日期标记为已完成。
- 触发器:用户在锻炼跟踪界面上点按“完成锻炼”。
- 响应来源:
DATABASE- 直接向 Cloud Firestore 写入/执行数据库事务。
请求载荷:
{
"plan_reference": {
"week_start_date": "string (ISO date YYYY-MM-DD)",
"day_of_week": "string (e.g. monday)"
},
"title": "string (e.g. Upper Body Hypertrophy)",
"started_at": "string (ISO timestamp)",
"completed_at": "string (ISO timestamp)",
"duration_seconds": "number (e.g. 3120)",
"exercises_logged": [
{
"exercise_id": "string (canonical slug, e.g. incline-db-bench-press)",
"exercise_name": "string (e.g. Incline DB Bench Press)",
"sets": [
{
"set_number": "number (e.g. 1)",
"weight_lbs": "number (e.g. 60)",
"reps_completed": "number (e.g. 12)",
"completed": "boolean"
}
]
}
],
"user_notes": "string (optional subjective feedback)",
"perceived_exertion": "number (optional 1-10 RPE rating)"
}响应载荷:
{
"success": "boolean",
"log_id": "string (newly created workout log document ID)",
"summary": {
"total_volume_lbs": "number",
"total_sets_completed": "number",
"total_reps_completed": "number"
},
"new_prs": [
{
"exercise_id": "string (canonical slug, e.g. incline-db-bench-press)",
"metric": "string (e.g. '10 Rep Max' or 'Estimated 1RM')",
"value": "string (e.g. '65 lbs' or '105 lbs')"
}
]
}3. Cloud Firestore 数据库架构
收藏集:exercises
层次结构:根集合 (/exercises/{exercise_id}) • 用途:静态练习百科全书,包含多媒体、动作提示、器械要求和说明。
{
"id": "string (canonical slug matching document ID, e.g. incline-db-bench-press)",
"name": "string (e.g. Incline DB Bench Press)",
"category": "string (e.g. Strength, Hypertrophy, Cardio, Mobility)",
"target_muscles": {
"primary": ["string (e.g. Upper Chest)"],
"secondary": ["string (e.g. Front Deltoids, Triceps)"]
},
"media": {
"video_url": "string (URL to demonstration video)",
"poster_url": "string (URL to video poster / thumbnail image)"
},
"instructions": ["string (step-by-step execution guidance)"],
"form_tips": ["string (critical cues and common pitfalls)"],
"equipment_required": ["string (e.g. Dumbbells, Incline Bench)"]
}集合:weekly_plans
层次结构:/users/{userId}/weekly_plans/{week_start_date} • 用途:包含完整每周训练时间表、摘要指标和每日锻炼内容的“螺旋笔记本”。
{
"week_start_date": "string (ISO date YYYY-MM-DD, e.g. 2026-09-07)",
"theme": "string (e.g. Hypertrophy Focus & Upper Body Overload)",
"summary": {
"total_time_minutes": "number (raw minutes, e.g. 180)",
"total_volume_lbs": "number (raw tonnage in lbs, e.g. 38400)",
"frequency": "number (active workout days, e.g. 4)"
},
"suggested_edits": ["string (pre-generated AI prompt suggestion)"],
"days": {
"monday": {
"date": "string (ISO date YYYY-MM-DD)",
"day_of_week": "string (e.g. Monday)",
"is_rest_day": "boolean",
"status": "string (enum: scheduled | completed | skipped)",
"completed_log_id": "string (nullable)",
"completed_at": "string (ISO timestamp, nullable)",
"title": "string (e.g. Upper Body Hypertrophy)",
"focus_tag": "string (e.g. Push & Chest Dominant)",
"duration_minutes": "number",
"estimated_volume_lbs": "number",
"coach_note": "string",
"muscle_focus": [{ "muscle": "string", "intensity": "number (0-100)" }],
"suggested_edits": ["string"],
"blocks": [
{
"label": "string (e.g. Main Compound Block)",
"exercises": [
{
"exercise_id": "string",
"name": "string",
"target_sets": "number",
"target_reps": "string or number",
"rest_seconds": "number"
}
]
}
]
}
}
}收藏集:weekly_chats
层次结构:/users/{userId}/weekly_chats/{week_start_date} • 用途:“每周聊天存储桶”包含特定训练周的完整对话历史记录,可通过单次文档读取操作获取。
{
"week_start_date": "string (ISO date YYYY-MM-DD, e.g. 2026-09-07)",
"messages": [
{
"id": "string (unique message ID)",
"sender": "string (enum: user | coach)",
"text": "string (conversational message body)",
"created_at": "timestamp (server timestamp)",
"proposal": {
"id": "string (proposal UID, nullable)",
"type": "string (enum: UPDATE_DAY | UPDATE_WEEK, nullable)",
"target_day": "string (e.g. monday, nullable)",
"summary_diff": "string (nullable)",
"status": "string (enum: pending | accepted | dismissed)",
"data": "map (workout or week snapshot payload, nullable)"
}
}
]
}收藏集:workout_logs
层次结构:/users/{userId}/workout_logs/{log_id} • 用途:已完成锻炼活动的永久、不可变历史归档。
{
"id": "string (document ID)",
"plan_reference": {
"week_start_date": "string (ISO date YYYY-MM-DD)",
"day_of_week": "string (e.g. monday)"
},
"title": "string (e.g. Upper Body Hypertrophy)",
"started_at": "timestamp",
"completed_at": "timestamp",
"duration_seconds": "number",
"summary": {
"total_volume_lbs": "number",
"total_sets_completed": "number",
"total_reps_completed": "number"
},
"exercises_logged": [
{
"exercise_id": "string",
"exercise_name": "string",
"sets": [
{
"set_number": "number",
"weight_lbs": "number",
"reps_completed": "number",
"completed": "boolean"
}
]
}
],
"user_notes": "string (nullable)",
"perceived_exertion": "number (1-10 RPE, nullable)"
}收藏集:user_profiles
层次结构:/users/{userId}/profile/main • 用途:存储用户 3 层记忆配置文件的“Living Client Chart”。
{
"user_id": "string (document owner UID)",
"display_name": "string (e.g. Alex)",
"email": "string (e.g. alex@example.com)",
"preferences": {
"units": "string (enum: lbs | kg)",
"haptics": "boolean",
"sound": "boolean"
},
"equipment_ids": ["string (e.g. 'dumbbells', 'bench_adjustable', 'pull_up_bar')"],
"onboarding_status": "string (enum: not_started | in_progress | completed)",
"created_at": "timestamp",
"tier_1_staples": [
{
"id": "string (e.g. mem_01)",
"category": "string (enum: equipment | preference | goal | chronic_condition)",
"fact": "string (e.g. Home gym with dumbbells up to 50 lbs and adjustable bench)",
"source": "string (e.g. conversational_onboarding | manual_entry)"
}
],
"tier_2_physiological": [
{
"id": "string (e.g. mem_02)",
"body_part": "string (e.g. hamstrings, shoulder)",
"condition": "string (e.g. soreness, acute_fatigue, minor_strain)",
"fact": "string (e.g. Hamstrings are moderately sore from hill sprints)",
"effective_date": "string (ISO date YYYY-MM-DD)",
"expires_at": "timestamp"
}
],
"tier_3_situational": [
{
"id": "string (e.g. mem_03)",
"event": "string (e.g. golf_competition, travel)",
"fact": "string (e.g. Major golf tournament on Saturday; avoid excessive arm/lat fatigue on Friday)",
"week_start_date": "string (ISO date YYYY-MM-DD)",
"expires_at": "timestamp"
}
]
}收藏集:prs
层次结构:/users/{userId}/prs/{exercise_slug} • 用途:存储历史个人记录、预计 1 次最大重复次数基准和特定重复次数记录。
{
"exercise_id": "string (canonical slug, e.g. incline-db-bench-press)",
"exercise_name": "string (e.g. Incline DB Bench Press)",
"estimated_1rm": "number (calculated using Brzycki formula, e.g. 105)",
"max_weight_set": {
"weight_lbs": "number (e.g. 80)",
"reps": "number (e.g. 10)",
"achieved_at": "timestamp"
},
"rep_records": {
"10": 80,
"8": 85,
"5": 95
},
"last_updated": "timestamp"
}4. 数据生命周期和智能体流水线
4.1 流水线 1:指导、锻炼生成和分阶段修改
- 用户操作:提交自由格式输入内容或点按预生成的上下文编辑条状标签。
- 上下文组装:服务器检索活跃的三层记忆配置文件和本周计划,并剪除过期的记忆项。
- 智能体推理:Gemini 使用结构化工具/函数调用来处理提示和上下文。
- 自主记忆变更:提取的用户事实会应用于
/user_profiles/{userId},并与equipment_ids同步。 - 暂存提案暂存:建议的锻炼修改内容会通过
/weekly_chats/{week_start_date}内的proposal.status = "pending"返回。 - 用户解决和持久化:点按[接受方案]会将建议的数据提交到
/weekly_plans,并将建议标记为"accepted"。
4.2 流水线 2:锻炼执行和归档(标准答案)
- 实时练习记录:客户端在本地界面状态中跟踪实际举起的负重、完成的重复次数和完成标志。
- 提交锻炼时段:客户端在“完成锻炼”时调用
POST /api/workouts/log。 - 汇总计算和验证:服务器计算累计训练量,并使用 Brzycki 公式检查是否达到个人纪录。
- 不可变持久化:在
/workout_logs中写入不可变记录,在/weekly_plans中标记日期"completed",并将新的 PR upsert 到/prs中。
4.3 流水线 3:自主记忆提取和失效
- 提取:在所有对话互动中持续提取事实。
- 分类:第 1 层级(主食,无过期时间)、第 2 层级(生理状态,3-4 天 TTL)、第 3 层级(情境,基于事件的 TTL)。
- 修剪和失效:过期内容 (
current_timestamp > expires_at) 会从提示上下文中过滤掉,并延迟清除。
5. Firestore 安全规则和复合索引规范
5.1 安全规则 (firestore.rules)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
match /profile/{document=**} { allow read, write: if request.auth != null && request.auth.uid == userId; }
match /weekly_plans/{planId} { allow read, write: if request.auth != null && request.auth.uid == userId; }
match /weekly_chats/{chatId} { allow read, write: if request.auth != null && request.auth.uid == userId; }
match /prs/{exerciseSlug} { allow read, write: if request.auth != null && request.auth.uid == userId; }
match /workout_logs/{logId} {
allow read, create: if request.auth != null && request.auth.uid == userId;
allow update, delete: if false;
}
}
match /exercises/{exerciseId} {
allow read: if request.auth != null;
allow write: if false;
}
}
}5.2 复合查询索引 (firestore.indexes.json)
{
"indexes": [
{
"collectionGroup": "workout_logs",
"queryScope": "COLLECTION",
"fields": [{ "fieldPath": "completed_at", "order": "DESCENDING" }]
},
{
"collectionGroup": "weekly_plans",
"queryScope": "COLLECTION",
"fields": [{ "fieldPath": "week_start_date", "order": "DESCENDING" }]
},
{
"collectionGroup": "prs",
"queryScope": "COLLECTION",
"fields": [{ "fieldPath": "last_updated", "order": "DESCENDING" }]
}
]
}6. 更新日志和已锁定的决策
| 日期 | 组件/决策 | 类型 | 锁定者 |
|---|---|---|---|
| 初始设置 | 已初始化空规范框架 | 系统设置 | 团队 |
| 2026-09-04 | 添加了 Firestore 集合:exercises 和 weekly_plans | 架构 | 团队 |
| 2026-09-04 | 添加了 POST /api/coach/chat 端点和 chat_messages 集合 | 端点和架构 | 团队 |
| 2026-09-04 | 已将对话存储重构为 weekly_chats 存储桶架构 | 架构 | 团队 |
| 2026-09-04 | 添加了 POST /api/workouts/log 端点和 workout_logs 集合 | 端点和架构 | 团队 |
| 2026-09-04 | 添加了 user_profiles 3 层记忆架构,并使用 memory_mutations 更新了 POST /api/coach/chat | 端点和架构 | 团队 |
| 2026-09-04 | 已完成第 1 部分(系统范围和原则)和第 4 部分(数据生命周期和流水线) | 完成完整规格 | 团队 |
| 2026-09-22 | 统一规范:Next.js + Tailwind + Zustand 堆栈、不记名身份验证、PR 子集合、Firestore 规则和规范练习标识名 | 全面规范协调 | 架构审核 |