تطبيق اللياقة البدنية الذكي: مواصفات تصميم الخادم ومخطط قاعدة البيانات وبنية البيانات
القطع الأثريةهذا هو العنصر المستخدم في الحلقة 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) مع ميزة تخزين مواد العرض بلا إنترنت في "تطبيقات الويب التقدّمية" (PWA) (باستخدام Serwist /
@ducanh2912/next-pwa). - التنسيق ومكوّنات واجهة المستخدم: Tailwind CSS v4 وTailwind Variants (لإدارة الصيغ) وLucide React (الرموز).
- بنية حالة العميل:
- Zustand (مع برمجيات
persistالوسيطة): يعالج حالة العميل المحلية والملموسة والمؤقتة (مؤقتات ساعة الإيقاف، ومخازن المجموعات النشطة، والرسم البياني للخطوات، والمدخلات غير المحفوظة في صالة الألعاب الرياضية). - TanStack Query (React Query): يعالج جميع حالات الخادم غير المتزامنة، وتخزين مستندات Firestore مؤقتًا، وإعادة التحقّق في الخلفية، وإزالة الطلبات المكرّرة، والتغييرات المتفائلة.
- Zustand (مع برمجيات
- وقت التشغيل في الخلفية واستضافة واجهة برمجة التطبيقات: معالِجات مسارات Next.js (
app/api/...) المستضافة على Google Cloud Run في المنطقة نفسها التي تتوفّر فيها ميزتا Firestore و"التخزين المؤقت للسياق" في Gemini لتقليل وقت الاستجابة الداخلي إلى أقل من 15 ملي ثانية. - المصادقة: مصادقة Firebase (تسجيل الدخول باستخدام حساب Google، والبريد الإلكتروني/كلمة المرور، وربط الإعدادات المجهولة الهوية). يُرفق العميل
Authorization: Bearer <Firebase_ID_Token>في جميع طلبات البيانات من واجهة برمجة التطبيقات، ويتحقّق الخادم من الهوية باستخدامfirebase-admin.auth().verifyIdToken(token). - قاعدة البيانات الأساسية: Google Cloud Firestore
1. نظرة عامة على النظام وتجربة المستخدم الأساسية
1.1 النطاق ومبادئ البنية
تحدّد هذه المواصفات نماذج البيانات ومخططات Cloud Firestore وعقود نقاط نهاية الخادم وبُنى ذاكرة وكيل الذكاء الاصطناعي لتطبيق Smart Fitness. ويوفّر مصدرًا وحيدًا للحقيقة لجميع عمليات تخطيط التمارين وتسجيل الجلسات السابقة وذاكرة التدريب المستقلة المتعدّدة المستويات، وهو منفصل تمامًا عن أُطر عمل العرض في الواجهة الأمامية (التي يتم تناولها في مواصفات مخصّصة لتصميم واجهة المستخدم/تجربة المستخدم).
1.2 المبادئ الأساسية للبيانات
- فصل الاهتمامات: حدود صارمة بين عقود البيانات (الواجهة الخلفية/قاعدة البيانات) وتصميم واجهة المستخدم للعميل.
- بنية المكتبة المختلطة: موسوعة تمارين ثابتة وغير قابلة للتغيير (
exercises) مقترنة بجدول أسبوعي ديناميكي من إنشاء الذكاء الاصطناعي (weekly_plans). - التعديلات المرحلية بمشاركة بشرية: يتم عرض التعديلات التي ينشئها الذكاء الاصطناعي على التمارين الرياضية كاقتراحات مرحلية، ولا تتم عمليات الكتابة في قاعدة البيانات إلا بعد موافقة المستخدم الصريحة.
- ذاكرة حيّة مستمرة من 3 مستويات: يلتقط الذكاء الاصطناعي بشكل مستقل حقائق المستخدم ويحدّثها ويحذفها من خلال العناصر الأساسية الدائمة والحالات الفسيولوجية المنتهية الصلاحية (مع مدة صلاحية صريحة) والسياق الظرفي العابر.
- قراءة المستندات الفردية بشكل دقيق: يتم تنظيم البيانات في مستندات محسّنة (مثل دفاتر الملاحظات الحلزونية للخطة الأسبوعية ومجموعات المحادثات الأسبوعية) لتقليل وقت استجابة القراءة وإزالة طلبات البحث المكرّرة.
2. نقاط نهاية الخادم وعقود واجهة برمجة التطبيقات
يجب أن تحدّد كل نقطة نهاية مصدر الردّ (
AGENTأوDATABASEأوHYBRID) لكي يتم ضبط نقاط النهاية المستندة إلى الوكيل بشكل مناسب أثناء التنفيذ.
POST /api/coach/chat
- الغرض: التفاعل الحواري مع المدرّب الشخصي المستنِد إلى الذكاء الاصطناعي، والتعامل مع أسئلة التدريب، وتقديم الدعم التحفيزي، وإنشاء تعديلات مقترَحة على التمارين (سواء كانت تعديلات يومية أو إصلاحات أسبوعية).
- الإجراء: يرسل المستخدم رسالة في علامة التبويب "المدرّب" أو ينقر على شريحة تعديل مقترَحة في السياق (مثلاً، "استبدال تمرين أرنولد بتمرين الضغط بالدمبل").
- مصدر الردّ:
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)"
}
}Response Payload:
{
"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: في كل دور، يحفظ الخادم رسالة المستخدم الواردة (text) وردّ المدرّب (text) في/users/{userId}/weekly_chats/{week_start_date}باستخدامFieldValue.arrayUnion(). - نمط التغيير المرحلي: يعرض حمولة العرض بدون تعديل
/weekly_plansإلى أن ينقر المستخدم على [قبول الخطة]. - إدارة الذاكرة المستقلة المستمرة: تُصدر
memory_mutationsلإنشاء عناصر أو تعديلها أو إزالتها من ملف المستخدم الشخصي المكوّن من 3 مستويات.
POST /api/workouts/log
- الغرض: حفظ جلسة تمرين مكتملة في أرشيف التدريب الدائم للمستخدم، وحساب إجمالي عدد المجموعات/التكرارات النهائية، والتحقّق من الأرقام القياسية الشخصية، ووضع علامة على اليوم المجدوَل على أنّه مكتمل.
- المشغّل: ينقر المستخدم على "إنهاء التمرين" في شاشة تتبُّع التمرين.
- مصدر الردّ:
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)"
}Response Payload:
{
"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 • الغرض: "مخطّط العميل الحي" الذي يخزّن ملف تعريف الذاكرة الثلاثي المستويات للمستخدم.
{
"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} • الغرض: يخزّن الأرقام القياسية الشخصية على الإطلاق، والمقاييس المقدَّرة للحد الأقصى لعدد التكرارات، والأرقام القياسية الخاصة بالتكرارات.
{
"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: التدريب وإنشاء التمارين والتعديل المرحلي
- إجراء المستخدم: يرسل المستخدم إدخالاً حرًا أو ينقر على شريحة تعديل سياقية تم إنشاؤها مسبقًا.
- تجميع السياق: يسترد الخادم ملف الذاكرة النشط المكوّن من 3 مستويات وخطة الأسبوع الحالي، مع إزالة عناصر الذاكرة منتهية الصلاحية.
- استدلال الوكيل: يعالج Gemini الطلب والسياق باستخدام استدعاء الدوال أو الأدوات المنظَّمة.
- تعديل الذاكرة المستقل: يتم تطبيق الحقائق المستخرَجة عن المستخدم على "
/user_profiles/{userId}" ومزامنتها مع "equipment_ids". - Staged Proposal Staging: يتم عرض التعديلات المقترَحة على التمرين باستخدام
proposal.status = "pending"داخل/weekly_chats/{week_start_date}. - حلّ المشاكل من قِبل المستخدم وحفظ البيانات: يؤدي النقر على [قبول الخطة] إلى حفظ البيانات المقترَحة في
/weekly_plansووضع علامة"accepted"على الاقتراح.
4.2 المسار 2: تنفيذ التمارين وأرشفتها (المعلومات الفعلية)
- تسجيل الجلسة المباشرة: يتتبّع العميل الوزن الفعلي الذي تم رفعه، وعدد التكرارات المكتملة، وعلامات الإكمال في حالة واجهة المستخدم المحلية.
- إرسال الجلسة: يطلب العميل
POST /api/workouts/logعند "إنهاء التمرين". - احتساب الإجمالي والتحقّق منه: يحتسب الخادم إجمالي حجم الجلسة ويتحقّق من الأرقام القياسية الشخصية باستخدام صيغة Brzycki.
- Immutable Persistence: Writes an immutable record in
/workout_logs, marks the day"completed"in/weekly_plans, and upserts new PRs into/prs.
4.3 المسار 3: استيعاب الذاكرة وإبطالها بشكل مستقل
- الاستيعاب: يتم استخراج الحقائق باستمرار من جميع التفاعلات في المحادثات.
- التصنيف: الفئة 1 (الاحتياجات الأساسية، بدون انتهاء صلاحية)، والفئة 2 (الحالة الفسيولوجية، مدة بقاء من 3 إلى 4 أيام)، والفئة 3 (السياق الظرفي، مدة بقاء مستندة إلى الأحداث).
- التقليم والإبطال: تتم فلترة العناصر المنتهية الصلاحية (
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 | تمت إضافة مخطط الذاكرة المكوّن من 3 مستويات لـ user_profiles وتعديل POST /api/coach/chat باستخدام memory_mutations | نقطة النهاية والمخطط | الفريق |
| 2026-09-04 | إكمال القسم 1 (نطاق النظام ومبادئه) والقسم 4 (مراحل نشاط البيانات ومساراتها) | إكمال المواصفات بالكامل | الفريق |
| 2026-09-22 | المواصفات الموحّدة: حزمة Next.js وTailwind وZustand، ومصادقة Bearer، ومجموعة فرعية من طلبات الدمج، وقواعد Firestore، وعناوين URL الأساسية للتمارين | المواءمة الكاملة للمواصفات | مراجعة البنية |