- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren - DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues - .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08) - Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben - docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md - CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht Made-with: Cursor
1759 lines
52 KiB
Markdown
1759 lines
52 KiB
Markdown
# Technisches Datenmodell – Mitai Jinkendo
|
||
|
||
> **Quelle:** Development Database (dev.mitai.jinkendo.de)
|
||
> **Generiert:** 2026-04-02
|
||
> **PostgreSQL Version:** 16-alpine
|
||
> **Anzahl Tabellen:** 43
|
||
|
||
---
|
||
|
||
## Inhaltsverzeichnis
|
||
|
||
1. [Kernmodule](#1-kernmodule)
|
||
2. [Tracking-Module](#2-tracking-module)
|
||
3. [KI & Analysen](#3-ki--analysen)
|
||
4. [Membership & Features](#4-membership--features)
|
||
5. [Ziele & Training](#5-ziele--training)
|
||
6. [System-Tabellen](#6-system-tabellen)
|
||
7. [Backup-Tabellen](#7-backup-tabellen)
|
||
8. [ER-Diagramm](#8-er-diagramm)
|
||
|
||
---
|
||
|
||
## 1. Kernmodule
|
||
|
||
### 1.1 profiles
|
||
|
||
**Beschreibung:** Nutzerstammdaten und Authentifizierung
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK, eindeutige Profil-ID |
|
||
| email | varchar | YES | NULL | E-Mail-Adresse (unique) |
|
||
| pin_hash | varchar(255) | NO | - | bcrypt-gehashtes Passwort |
|
||
| role | varchar(20) | YES | 'user' | Rolle (user, admin) |
|
||
| tier | text | YES | 'free' | Membership-Tier |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
| email_verified | boolean | YES | false | E-Mail-Verifizierung |
|
||
| verification_token | varchar(255) | YES | NULL | Token für E-Mail-Verifizierung |
|
||
| invited_by | uuid | YES | NULL | FK zu profiles.id (Einladung) |
|
||
| quality_filter_level | varchar(20) | YES | 'disabled' | Qualitätsfilter für Aktivitäten |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `email`
|
||
- FK: `invited_by` → `profiles(id)` ON DELETE NO ACTION
|
||
- CHECK: `tier IN ('free', 'plus', 'premium', 'trial')`
|
||
|
||
**Indizes:**
|
||
- `idx_profiles_email` (email) WHERE email IS NOT NULL
|
||
- `idx_profiles_tier` (tier)
|
||
- `idx_profiles_quality_filter` (quality_filter_level)
|
||
- `idx_profiles_verification_token` (verification_token) WHERE verification_token IS NOT NULL
|
||
|
||
**Beziehungen:**
|
||
- 1:N zu allen Tracking-Tabellen (profile_id)
|
||
- 1:N zu sessions
|
||
- 1:N zu ai_insights, ai_usage
|
||
- 1:N zu goals, training_phases
|
||
- 1:1 zu user_stats
|
||
- Self-Reference (invited_by)
|
||
|
||
---
|
||
|
||
### 1.2 sessions
|
||
|
||
**Beschreibung:** Authentifizierungs-Sessions
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| token | varchar(255) | NO | - | PK, Session-Token |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| expires_at | timestamp | NO | - | Ablaufdatum |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `token`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_sessions_profile_id` (profile_id)
|
||
- `idx_sessions_expires_at` (expires_at)
|
||
|
||
---
|
||
|
||
### 1.3 user_stats
|
||
|
||
**Beschreibung:** Aggregierte Nutzerstatistiken
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| profile_id | uuid | NO | - | PK, FK zu profiles.id |
|
||
| total_weight_entries | integer | YES | 0 | Anzahl Gewichtseinträge |
|
||
| total_circumference_entries | integer | YES | 0 | Anzahl Umfangseinträge |
|
||
| total_caliper_entries | integer | YES | 0 | Anzahl Caliper-Einträge |
|
||
| total_activity_entries | integer | YES | 0 | Anzahl Aktivitäten |
|
||
| total_nutrition_entries | integer | YES | 0 | Anzahl Ernährungseinträge |
|
||
| total_photos | integer | YES | 0 | Anzahl Fotos |
|
||
| total_ai_insights | integer | YES | 0 | Anzahl KI-Analysen |
|
||
| last_weight_date | date | YES | NULL | Letztes Gewichtsdatum |
|
||
| last_activity_date | date | YES | NULL | Letzte Aktivität |
|
||
| last_insight_date | timestamp | YES | NULL | Letzte KI-Analyse |
|
||
| updated_at | timestamp | YES | now() | Letztes Update |
|
||
|
||
**Constraints:**
|
||
- PK: `profile_id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
---
|
||
|
||
## 2. Tracking-Module
|
||
|
||
### 2.1 weight_log
|
||
|
||
**Beschreibung:** Gewichtsverlauf
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Messdatum |
|
||
| weight | numeric(5,2) | NO | - | Gewicht in kg |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, date`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_weight_log_profile_date` (profile_id, date DESC)
|
||
- `idx_weight_log_profile_date_unique` UNIQUE (profile_id, date)
|
||
|
||
---
|
||
|
||
### 2.2 circumference_log
|
||
|
||
**Beschreibung:** Umfangsmessungen (8 Messpunkte)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Messdatum |
|
||
| c_neck | numeric(5,2) | YES | NULL | Hals (cm) |
|
||
| c_chest | numeric(5,2) | YES | NULL | Brust (cm) |
|
||
| c_waist | numeric(5,2) | YES | NULL | Taille (cm) |
|
||
| c_hips | numeric(5,2) | YES | NULL | Hüfte (cm) |
|
||
| c_thigh | numeric(5,2) | YES | NULL | Oberschenkel (cm) |
|
||
| c_calf | numeric(5,2) | YES | NULL | Wade (cm) |
|
||
| c_biceps | numeric(5,2) | YES | NULL | Bizeps (cm) |
|
||
| c_forearm | numeric(5,2) | YES | NULL | Unterarm (cm) |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_circumference_profile_date` (profile_id, date DESC)
|
||
|
||
---
|
||
|
||
### 2.3 caliper_log
|
||
|
||
**Beschreibung:** Hautfaltenmessungen (Körperfettanalyse)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Messdatum |
|
||
| chest | numeric(5,2) | YES | NULL | Brust (mm) |
|
||
| abdominal | numeric(5,2) | YES | NULL | Bauch (mm) |
|
||
| thigh | numeric(5,2) | YES | NULL | Oberschenkel (mm) |
|
||
| triceps | numeric(5,2) | YES | NULL | Trizeps (mm) |
|
||
| subscapular | numeric(5,2) | YES | NULL | Schulterblatt (mm) |
|
||
| suprailiac | numeric(5,2) | YES | NULL | Hüfte (mm) |
|
||
| midaxillary | numeric(5,2) | YES | NULL | Mittelachsel (mm) |
|
||
| body_fat_pct | numeric(5,2) | YES | NULL | Körperfett % (berechnet) |
|
||
| formula | varchar(20) | YES | NULL | Berechnungsformel |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_caliper_profile_date` (profile_id, date DESC)
|
||
|
||
---
|
||
|
||
### 2.4 activity_log
|
||
|
||
**Beschreibung:** Trainings- und Aktivitätslog
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Aktivitätsdatum |
|
||
| start_time | time | YES | NULL | Startzeit |
|
||
| duration_minutes | integer | YES | NULL | Dauer (Minuten) |
|
||
| training_type_id | integer | YES | NULL | FK zu training_types.id |
|
||
| training_category | varchar(50) | YES | NULL | Kategorie |
|
||
| kcal | integer | YES | NULL | Kalorienverbrauch |
|
||
| avg_hr | integer | YES | NULL | Durchschnittliche HF |
|
||
| max_hr | integer | YES | NULL | Maximale HF |
|
||
| distance_km | numeric(6,2) | YES | NULL | Distanz (km) |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| evaluation | jsonb | YES | NULL | Qualitätsbewertung |
|
||
| overall_score | integer | YES | NULL | Gesamtbewertung (0-100) |
|
||
| quality_label | varchar(20) | YES | NULL | Qualitätslabel |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
- FK: `training_type_id` → `training_types(id)` ON DELETE NO ACTION
|
||
|
||
**Indizes:**
|
||
- `idx_activity_profile_date` (profile_id, date DESC)
|
||
- `idx_activity_training_type` (training_type_id)
|
||
- `idx_activity_training_category` (training_category)
|
||
- `idx_activity_quality_label` (quality_label) WHERE quality_label IS NOT NULL
|
||
- `idx_activity_overall_score` (overall_score DESC) WHERE overall_score IS NOT NULL
|
||
- `idx_activity_evaluation_passed` (evaluation->'rule_set_results'->'minimum_requirements'->>'passed') WHERE evaluation IS NOT NULL
|
||
|
||
---
|
||
|
||
### 2.5 nutrition_log
|
||
|
||
**Beschreibung:** Ernährungslog (täglich)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Datum |
|
||
| kcal | integer | YES | NULL | Kalorien |
|
||
| protein_g | numeric(6,2) | YES | NULL | Protein (g) |
|
||
| carbs_g | numeric(6,2) | YES | NULL | Kohlenhydrate (g) |
|
||
| fat_g | numeric(6,2) | YES | NULL | Fett (g) |
|
||
| fiber_g | numeric(6,2) | YES | NULL | Ballaststoffe (g) |
|
||
| water_l | numeric(4,2) | YES | NULL | Wasser (Liter) |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_nutrition_profile_date` (profile_id, date DESC)
|
||
|
||
---
|
||
|
||
### 2.6 sleep_log
|
||
|
||
**Beschreibung:** Schlaf-Tracking mit JSONB-Segmenten (Deep, REM, Light, Awake)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Schlafdatum |
|
||
| sleep_start | timestamp | YES | NULL | Schlafbeginn |
|
||
| sleep_end | timestamp | YES | NULL | Schlafende |
|
||
| total_duration_minutes | integer | YES | NULL | Gesamtdauer (Minuten) |
|
||
| sleep_segments | jsonb | YES | NULL | Schlafphasen-Daten |
|
||
| quality_score | integer | YES | NULL | Schlafqualität (0-100) |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**JSONB-Struktur `sleep_segments`:**
|
||
```json
|
||
{
|
||
"deep": 120, // Minuten
|
||
"rem": 90, // Minuten
|
||
"light": 180, // Minuten
|
||
"awake": 15 // Minuten
|
||
}
|
||
```
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, date`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_sleep_profile_date` (profile_id, date DESC)
|
||
- `unique_sleep_per_day` UNIQUE (profile_id, date)
|
||
|
||
---
|
||
|
||
### 2.7 vitals_baseline
|
||
|
||
**Beschreibung:** Morgenmessung Vitalwerte (RHR, HRV, VO2 Max, SpO2, Atemfrequenz)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Messdatum |
|
||
| resting_hr | integer | YES | NULL | Ruhepuls (bpm) |
|
||
| hrv | integer | YES | NULL | HRV (ms) |
|
||
| vo2_max | numeric(5,2) | YES | NULL | VO2 Max (ml/kg/min) |
|
||
| spo2 | integer | YES | NULL | Sauerstoffsättigung (%) |
|
||
| respiratory_rate | integer | YES | NULL | Atemfrequenz (bpm) |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, date`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_vitals_baseline_profile_date` (profile_id, date DESC)
|
||
- `unique_baseline_per_day` UNIQUE (profile_id, date)
|
||
|
||
---
|
||
|
||
### 2.8 blood_pressure_log
|
||
|
||
**Beschreibung:** Blutdruckmessungen (mehrfach täglich mit Context-Tagging)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| measured_at | timestamp | NO | - | Messzeitpunkt |
|
||
| systolic | integer | NO | - | Systolisch (mmHg) |
|
||
| diastolic | integer | NO | - | Diastolisch (mmHg) |
|
||
| pulse | integer | YES | NULL | Puls (bpm) |
|
||
| context | varchar(50) | YES | NULL | Kontext (nüchtern, nach Essen, etc.) |
|
||
| irregular_heartbeat | boolean | YES | false | Unregelmäßiger Herzschlag |
|
||
| afib_detected | boolean | YES | false | Vorhofflimmern erkannt |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, measured_at`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_blood_pressure_profile_datetime` (profile_id, measured_at DESC)
|
||
- `idx_blood_pressure_context` (context) WHERE context IS NOT NULL
|
||
- `unique_bp_measurement` UNIQUE (profile_id, measured_at)
|
||
|
||
---
|
||
|
||
### 2.9 rest_days
|
||
|
||
**Beschreibung:** Multi-dimensionale Ruhetage (Kraft, Cardio, Entspannung)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Ruhetag-Datum |
|
||
| focus | varchar(50) | NO | - | Fokus (strength, cardio, relaxation) |
|
||
| rest_config | jsonb | YES | NULL | Detailkonfiguration |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**JSONB-Struktur `rest_config`:**
|
||
```json
|
||
{
|
||
"strength_rest": true,
|
||
"cardio_rest": false,
|
||
"relaxation": true,
|
||
"preset": "full_rest"
|
||
}
|
||
```
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, date, focus`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_rest_days_profile_date` (profile_id, date DESC)
|
||
- `idx_rest_days_focus` (focus)
|
||
- `idx_rest_days_config` GIN (rest_config)
|
||
- `unique_rest_day_per_focus` UNIQUE (profile_id, date, focus)
|
||
|
||
---
|
||
|
||
### 2.10 photos
|
||
|
||
**Beschreibung:** Progress-Fotos
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Fotodatum |
|
||
| filename | varchar(255) | NO | - | Dateiname |
|
||
| pose | varchar(50) | YES | NULL | Pose (front, side, back) |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_photos_profile_date` (profile_id, date DESC)
|
||
|
||
---
|
||
|
||
## 3. KI & Analysen
|
||
|
||
### 3.1 ai_prompts
|
||
|
||
**Beschreibung:** KI-Prompt-Definitionen (Unified Prompt System)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| slug | varchar(100) | NO | - | Eindeutiger Slug (unique) |
|
||
| title | varchar(255) | NO | - | Titel |
|
||
| type | varchar(20) | YES | 'base' | Typ (base, pipeline) |
|
||
| category | varchar(50) | YES | NULL | Kategorie |
|
||
| stages | jsonb | YES | NULL | Pipeline-Stages |
|
||
| output_format | varchar(20) | YES | NULL | Output-Format (json, text) |
|
||
| output_schema | jsonb | YES | NULL | JSON-Schema für Output |
|
||
| active | boolean | YES | true | Aktiv |
|
||
| sort_order | integer | YES | 0 | Sortierung |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
| updated_at | timestamp | YES | now() | Letztes Update |
|
||
|
||
**JSONB-Struktur `stages`:**
|
||
```json
|
||
[
|
||
{
|
||
"name": "Stage 1",
|
||
"prompts": [
|
||
{
|
||
"type": "reference",
|
||
"slug": "body_summary"
|
||
},
|
||
{
|
||
"type": "inline",
|
||
"template": "Analyse {{weight_latest}}...",
|
||
"output_format": "json",
|
||
"output_schema": {...}
|
||
}
|
||
]
|
||
}
|
||
]
|
||
```
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `slug`
|
||
|
||
**Indizes:**
|
||
- `idx_ai_prompts_slug` (slug)
|
||
- `ai_prompts_slug_key` UNIQUE (slug)
|
||
- `idx_ai_prompts_type` (type)
|
||
- `idx_ai_prompts_category` (category)
|
||
- `idx_ai_prompts_active_sort` (active, sort_order)
|
||
- `idx_ai_prompts_stages` GIN (stages)
|
||
|
||
---
|
||
|
||
### 3.2 ai_insights
|
||
|
||
**Beschreibung:** Gespeicherte KI-Analysen
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| scope | varchar(100) | YES | NULL | Prompt-Slug |
|
||
| content | text | NO | - | KI-Antwort |
|
||
| metadata | jsonb | YES | NULL | Platzhalter-Werte |
|
||
| created | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**JSONB-Struktur `metadata`:**
|
||
```json
|
||
{
|
||
"placeholders": {
|
||
"weight_latest": 85.2,
|
||
"goal_weight": 80.0,
|
||
"activity_days": 28
|
||
},
|
||
"categories": {
|
||
"PROFIL": [...],
|
||
"KÖRPER": [...]
|
||
}
|
||
}
|
||
```
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_ai_insights_profile_scope` (profile_id, scope, created DESC)
|
||
|
||
---
|
||
|
||
### 3.3 ai_usage
|
||
|
||
**Beschreibung:** Tägliche KI-Nutzung (Rate Limiting)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Nutzungsdatum |
|
||
| count | integer | YES | 0 | Anzahl Calls |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, date`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_ai_usage_profile_date` (profile_id, date)
|
||
- `ai_usage_profile_id_date_key` UNIQUE (profile_id, date)
|
||
|
||
---
|
||
|
||
### 3.4 pipeline_configs
|
||
|
||
**Beschreibung:** Pipeline-Konfigurationen (deprecated, ersetzt durch ai_prompts)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| name | varchar(100) | NO | - | Name (unique) |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| stages | jsonb | NO | - | Stage-Konfiguration |
|
||
| active | boolean | YES | true | Aktiv |
|
||
| is_default | boolean | YES | false | Standard-Pipeline |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `name`
|
||
|
||
**Indizes:**
|
||
- `idx_pipeline_configs_active` (active)
|
||
- `idx_pipeline_configs_default` (is_default) WHERE is_default = true
|
||
- `idx_pipeline_configs_single_default` UNIQUE (is_default) WHERE is_default = true
|
||
- `pipeline_configs_name_key` UNIQUE (name)
|
||
|
||
---
|
||
|
||
## 4. Membership & Features
|
||
|
||
### 4.1 tiers
|
||
|
||
**Beschreibung:** Membership-Tiers (free, plus, premium, trial)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | text | NO | - | PK (free, plus, premium, trial) |
|
||
| name | text | NO | - | Display-Name |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| price_monthly | numeric(10,2) | YES | NULL | Preis/Monat |
|
||
| price_yearly | numeric(10,2) | YES | NULL | Preis/Jahr |
|
||
| trial_days | integer | YES | NULL | Trial-Dauer (Tage) |
|
||
| is_active | boolean | YES | true | Aktiv |
|
||
| sort_order | integer | YES | 0 | Sortierung |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
|
||
---
|
||
|
||
### 4.2 features
|
||
|
||
**Beschreibung:** Feature-Definitionen
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | text | NO | - | PK (feature_key) |
|
||
| name | text | NO | - | Display-Name |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| category | text | YES | NULL | Kategorie |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
|
||
---
|
||
|
||
### 4.3 tier_limits
|
||
|
||
**Beschreibung:** Feature-Limits pro Tier
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| tier_id | text | NO | - | FK zu tiers.id |
|
||
| feature_id | text | NO | - | FK zu features.id |
|
||
| limit_value | integer | YES | NULL | Limit-Wert |
|
||
| limit_type | text | YES | NULL | Limit-Typ (daily, monthly, total) |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `tier_id, feature_id`
|
||
- FK: `tier_id` → `tiers(id)` ON DELETE CASCADE
|
||
- FK: `feature_id` → `features(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_tier_limits_tier` (tier_id)
|
||
- `idx_tier_limits_feature` (feature_id)
|
||
- `tier_limits_tier_id_feature_id_key` UNIQUE (tier_id, feature_id)
|
||
|
||
---
|
||
|
||
### 4.4 coupons
|
||
|
||
**Beschreibung:** Gutschein-Codes
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| code | varchar(50) | NO | - | Gutschein-Code (unique) |
|
||
| tier_id | text | YES | NULL | FK zu tiers.id |
|
||
| duration_days | integer | YES | NULL | Laufzeit (Tage) |
|
||
| max_uses | integer | YES | NULL | Max. Einlösungen |
|
||
| current_uses | integer | YES | 0 | Aktuelle Einlösungen |
|
||
| valid_from | timestamp | YES | NULL | Gültig ab |
|
||
| valid_until | timestamp | YES | NULL | Gültig bis |
|
||
| is_active | boolean | YES | true | Aktiv |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `code`
|
||
- FK: `tier_id` → `tiers(id)` ON DELETE SET NULL
|
||
|
||
**Indizes:**
|
||
- `idx_coupons_code` (code)
|
||
- `coupons_code_key` UNIQUE (code)
|
||
|
||
---
|
||
|
||
### 4.5 coupon_redemptions
|
||
|
||
**Beschreibung:** Gutschein-Einlösungen
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| coupon_id | uuid | NO | - | FK zu coupons.id |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| redeemed_at | timestamp | YES | now() | Einlösungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `coupon_id, profile_id`
|
||
- FK: `coupon_id` → `coupons(id)` ON DELETE CASCADE
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_coupon_redemptions_profile` (profile_id)
|
||
- `coupon_redemptions_coupon_id_profile_id_key` UNIQUE (coupon_id, profile_id)
|
||
|
||
---
|
||
|
||
### 4.6 access_grants
|
||
|
||
**Beschreibung:** Zeitgebundene Tier-Zugriffe
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| tier_id | text | NO | - | FK zu tiers.id |
|
||
| granted_by | text | YES | NULL | Vergeben durch |
|
||
| coupon_id | uuid | YES | NULL | FK zu coupons.id |
|
||
| valid_from | timestamp | NO | - | Gültig ab |
|
||
| valid_until | timestamp | NO | - | Gültig bis |
|
||
| is_active | boolean | YES | true | Aktiv |
|
||
| paused_by | uuid | YES | NULL | Pausiert von |
|
||
| paused_at | timestamp | YES | NULL | Pausiert am |
|
||
| remaining_days | integer | YES | NULL | Verbleibende Tage |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
- FK: `tier_id` → `tiers(id)` ON DELETE CASCADE
|
||
- FK: `coupon_id` → `coupons(id)` ON DELETE SET NULL
|
||
|
||
**Indizes:**
|
||
- `idx_access_grants_profile` (profile_id, valid_until DESC)
|
||
- `idx_access_grants_active` (profile_id, is_active, valid_until DESC)
|
||
|
||
---
|
||
|
||
### 4.7 user_feature_restrictions
|
||
|
||
**Beschreibung:** Nutzer-spezifische Feature-Restriktionen
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| feature_id | text | NO | - | FK zu features.id |
|
||
| custom_limit | integer | YES | NULL | Custom Limit |
|
||
| is_disabled | boolean | YES | false | Feature deaktiviert |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, feature_id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
- FK: `feature_id` → `features(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_user_restrictions_profile` (profile_id)
|
||
- `user_feature_restrictions_profile_id_feature_id_key` UNIQUE (profile_id, feature_id)
|
||
|
||
---
|
||
|
||
### 4.8 user_feature_usage
|
||
|
||
**Beschreibung:** Feature-Nutzungszähler
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| feature_id | text | NO | - | FK zu features.id |
|
||
| usage_count | integer | YES | 0 | Nutzungszähler |
|
||
| last_used_at | timestamp | YES | NULL | Letzte Nutzung |
|
||
| reset_at | timestamp | YES | NULL | Reset-Zeitpunkt |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, feature_id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
- FK: `feature_id` → `features(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_user_usage_profile` (profile_id)
|
||
- `user_feature_usage_profile_id_feature_id_key` UNIQUE (profile_id, feature_id)
|
||
|
||
---
|
||
|
||
### 4.9 user_activity_log
|
||
|
||
**Beschreibung:** Audit-Log für Nutzeraktionen
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| action | text | NO | - | Aktion |
|
||
| details | jsonb | YES | NULL | Details |
|
||
| created | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_activity_log_profile` (profile_id, created DESC)
|
||
- `idx_activity_log_action` (action, created DESC)
|
||
|
||
---
|
||
|
||
## 5. Ziele & Training
|
||
|
||
### 5.1 goals
|
||
|
||
**Beschreibung:** Nutzer-definierte Ziele
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| title | varchar(255) | NO | - | Zieltitel |
|
||
| goal_type | varchar(50) | NO | - | Zieltyp (weight, body_fat, etc.) |
|
||
| category | varchar(50) | YES | NULL | Kategorie |
|
||
| start_date | date | YES | NULL | Startdatum |
|
||
| target_date | date | YES | NULL | Zieldatum |
|
||
| start_value | numeric(10,2) | YES | NULL | Startwert |
|
||
| target_value | numeric(10,2) | NO | - | Zielwert |
|
||
| current_value | numeric(10,2) | YES | NULL | Aktueller Wert |
|
||
| source_table | varchar(50) | YES | NULL | Datenquelle-Tabelle |
|
||
| source_column | varchar(50) | YES | NULL | Datenquelle-Spalte |
|
||
| status | varchar(20) | YES | 'active' | Status (active, completed, paused) |
|
||
| is_primary | boolean | YES | false | Primäres Ziel |
|
||
| priority | integer | YES | 0 | Priorität |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
| updated_at | timestamp | YES | now() | Letztes Update |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_goals_profile` (profile_id)
|
||
- `idx_goals_status` (profile_id, status)
|
||
- `idx_goals_primary` (profile_id, is_primary) WHERE is_primary = true
|
||
- `idx_goals_category_priority` (profile_id, category, priority)
|
||
|
||
---
|
||
|
||
### 5.2 goal_type_definitions
|
||
|
||
**Beschreibung:** Zieltyp-Definitionen (weight, body_fat, vo2max, etc.)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| type_key | varchar(50) | NO | - | Zieltyp-Key (unique) |
|
||
| display_name | varchar(100) | NO | - | Display-Name |
|
||
| category | varchar(50) | NO | - | Kategorie |
|
||
| unit | varchar(20) | YES | NULL | Einheit |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| is_active | boolean | YES | true | Aktiv |
|
||
| sort_order | integer | YES | 0 | Sortierung |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `type_key`
|
||
|
||
**Indizes:**
|
||
- `idx_goal_type_definitions_category` (category)
|
||
- `idx_goal_type_definitions_active` (is_active) WHERE is_active = true
|
||
- `goal_type_definitions_type_key_key` UNIQUE (type_key)
|
||
|
||
---
|
||
|
||
### 5.3 goal_focus_contributions
|
||
|
||
**Beschreibung:** M:N Relationship Goals ↔ Focus Areas
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| goal_id | uuid | NO | - | PK, FK zu goals.id |
|
||
| focus_area_id | integer | NO | - | PK, FK zu focus_area_definitions.id |
|
||
| contribution_weight | numeric(5,2) | YES | 100 | Gewichtung (%) |
|
||
|
||
**Constraints:**
|
||
- PK: `goal_id, focus_area_id`
|
||
- FK: `goal_id` → `goals(id)` ON DELETE CASCADE
|
||
- FK: `focus_area_id` → `focus_area_definitions(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_gfc_goal` (goal_id)
|
||
- `idx_gfc_focus_area` (focus_area_id)
|
||
|
||
---
|
||
|
||
### 5.4 goal_progress_log
|
||
|
||
**Beschreibung:** Tägliche Fortschrittswerte für Custom Goals
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| goal_id | uuid | NO | - | FK zu goals.id |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Datum |
|
||
| value | numeric(10,2) | NO | - | Wert |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `goal_id, date`
|
||
- FK: `goal_id` → `goals(id)` ON DELETE CASCADE
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_goal_progress_goal_date` (goal_id, date DESC)
|
||
- `idx_goal_progress_profile` (profile_id)
|
||
- `unique_progress_per_day` UNIQUE (goal_id, date)
|
||
|
||
---
|
||
|
||
### 5.5 focus_area_definitions
|
||
|
||
**Beschreibung:** Focus Area Definitionen (26 Bereiche in 7 Kategorien)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | serial | NO | - | PK |
|
||
| key | varchar(50) | NO | - | Focus Area Key (unique) |
|
||
| name | varchar(100) | NO | - | Display-Name |
|
||
| category | varchar(50) | NO | - | Kategorie |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| sort_order | integer | YES | 0 | Sortierung |
|
||
| is_active | boolean | YES | true | Aktiv |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `key`
|
||
|
||
**Indizes:**
|
||
- `idx_focus_area_key` (key)
|
||
- `idx_focus_area_category` (category)
|
||
- `focus_area_definitions_key_key` UNIQUE (key)
|
||
|
||
---
|
||
|
||
### 5.6 user_focus_area_weights
|
||
|
||
**Beschreibung:** User-spezifische Focus Area Gewichtungen
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| profile_id | uuid | NO | - | PK, FK zu profiles.id |
|
||
| focus_area_id | integer | NO | - | PK, FK zu focus_area_definitions.id |
|
||
| weight | numeric(5,2) | YES | 0 | Gewichtung (0-100) |
|
||
| updated_at | timestamp | YES | now() | Letztes Update |
|
||
|
||
**Constraints:**
|
||
- PK: `profile_id, focus_area_id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
- FK: `focus_area_id` → `focus_area_definitions(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_user_focus_weights_profile` (profile_id)
|
||
- `idx_user_focus_weights_area` (focus_area_id)
|
||
|
||
---
|
||
|
||
### 5.7 user_focus_preferences
|
||
|
||
**Beschreibung:** User Focus Preferences (deprecated/legacy)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| focus_areas | jsonb | YES | NULL | Focus Areas (legacy) |
|
||
| active | boolean | YES | true | Aktiv |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
| updated_at | timestamp | YES | now() | Letztes Update |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_focus_areas_profile_active` UNIQUE (profile_id) WHERE active = true
|
||
|
||
---
|
||
|
||
### 5.8 training_types
|
||
|
||
**Beschreibung:** Trainingstypen (29 Typen in 7 Kategorien)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | serial | NO | - | PK |
|
||
| name | varchar(100) | NO | - | Typname |
|
||
| category | varchar(50) | NO | - | Kategorie |
|
||
| color | varchar(7) | YES | NULL | Farbe (Hex) |
|
||
| icon | varchar(50) | YES | NULL | Icon-Name |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| abilities | jsonb | YES | NULL | Abilities-Matrix |
|
||
| profile | jsonb | YES | NULL | Profil-Einstellungen |
|
||
|
||
**JSONB-Struktur `abilities`:**
|
||
```json
|
||
{
|
||
"kraft": 80,
|
||
"ausdauer": 20,
|
||
"beweglichkeit": 40,
|
||
"schnelligkeit": 10,
|
||
"koordination": 30,
|
||
"balance": 20,
|
||
"geist": 10
|
||
}
|
||
```
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
|
||
**Indizes:**
|
||
- `idx_training_types_category` (category)
|
||
- `idx_training_types_abilities` GIN (abilities)
|
||
- `idx_training_types_profile_enabled` (profile->'rule_sets'->'minimum_requirements'->>'enabled') WHERE profile IS NOT NULL
|
||
|
||
---
|
||
|
||
### 5.9 activity_type_mappings
|
||
|
||
**Beschreibung:** Lernendes Mapping-System (Workout-Name → Training Type)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| activity_type | varchar(255) | NO | - | Workout-Name (unique per profile) |
|
||
| training_type_id | integer | NO | - | FK zu training_types.id |
|
||
| profile_id | uuid | YES | NULL | NULL = global, sonst user-spezifisch |
|
||
| is_global | boolean | YES | true | Global oder user-spezifisch |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `activity_type, profile_id`
|
||
- FK: `training_type_id` → `training_types(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_activity_type_mappings_type` (activity_type)
|
||
- `idx_activity_type_mappings_profile` (profile_id)
|
||
- `unique_activity_type_per_profile` UNIQUE (activity_type, profile_id)
|
||
|
||
---
|
||
|
||
### 5.10 training_phases
|
||
|
||
**Beschreibung:** Trainingsphasen (Auto-Detection Framework)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| phase_type | varchar(50) | NO | - | Phasentyp |
|
||
| start_date | date | NO | - | Startdatum |
|
||
| end_date | date | YES | NULL | Enddatum |
|
||
| status | varchar(20) | YES | 'suggested' | Status |
|
||
| confidence | numeric(5,2) | YES | NULL | Confidence (0-100) |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_training_phases_profile` (profile_id)
|
||
- `idx_training_phases_status` (profile_id, status)
|
||
- `idx_training_phases_dates` (profile_id, start_date, end_date)
|
||
|
||
---
|
||
|
||
### 5.11 fitness_tests
|
||
|
||
**Beschreibung:** Standardisierte Fitness-Tests
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| test_date | date | NO | - | Testdatum |
|
||
| test_type | varchar(50) | NO | - | Testtyp |
|
||
| result_value | numeric(10,2) | NO | - | Ergebnis |
|
||
| result_unit | varchar(20) | YES | NULL | Einheit |
|
||
| norm_category | varchar(50) | YES | NULL | Norm-Kategorie |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_fitness_tests_profile` (profile_id)
|
||
- `idx_fitness_tests_type` (profile_id, test_type)
|
||
- `idx_fitness_tests_date` (profile_id, test_date)
|
||
|
||
---
|
||
|
||
### 5.12 training_parameters
|
||
|
||
**Beschreibung:** Globale Trainings-Parameter (Quality-Filter Settings)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| key | varchar(100) | NO | - | Parameter-Key (unique) |
|
||
| category | varchar(50) | NO | - | Kategorie |
|
||
| value | text | NO | - | Wert |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| is_active | boolean | YES | true | Aktiv |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
| updated_at | timestamp | YES | now() | Letztes Update |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `key`
|
||
|
||
**Indizes:**
|
||
- `idx_training_parameters_key` (key) WHERE is_active = true
|
||
- `idx_training_parameters_category` (category) WHERE is_active = true
|
||
- `training_parameters_key_key` UNIQUE (key)
|
||
|
||
---
|
||
|
||
### 5.13 weekly_goals
|
||
|
||
**Beschreibung:** Wöchentliche Ziele (legacy)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| week_start | date | NO | - | Wochenbeginn |
|
||
| goals | jsonb | YES | NULL | Ziele |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, week_start`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `idx_weekly_goals_profile_week` (profile_id, week_start DESC)
|
||
- `unique_weekly_goal_per_profile` UNIQUE (profile_id, week_start)
|
||
|
||
---
|
||
|
||
## 6. System-Tabellen
|
||
|
||
### 6.1 schema_migrations
|
||
|
||
**Beschreibung:** Migrations-Tracking
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | serial | NO | - | PK |
|
||
| filename | varchar(255) | NO | - | Migrations-Dateiname (unique) |
|
||
| applied_at | timestamp | YES | now() | Anwendungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `filename`
|
||
|
||
**Indizes:**
|
||
- `schema_migrations_filename_key` UNIQUE (filename)
|
||
|
||
---
|
||
|
||
### 6.2 app_settings
|
||
|
||
**Beschreibung:** Globale App-Einstellungen
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| key | varchar(100) | NO | - | PK, Setting-Key |
|
||
| value | text | YES | NULL | Setting-Value |
|
||
| updated_at | timestamp | YES | now() | Letztes Update |
|
||
|
||
**Constraints:**
|
||
- PK: `key`
|
||
|
||
---
|
||
|
||
## 7. Backup-Tabellen
|
||
|
||
### 7.1 pipeline_configs_backup_pre_020
|
||
|
||
**Beschreibung:** Backup vor Migration 020 (Unified Prompt System)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | YES | - | Original ID |
|
||
| name | varchar(100) | YES | - | Name |
|
||
| description | text | YES | NULL | Beschreibung |
|
||
| stages | jsonb | YES | - | Stages |
|
||
| active | boolean | YES | true | Aktiv |
|
||
| is_default | boolean | YES | false | Default |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Keine Constraints oder Indizes.**
|
||
|
||
---
|
||
|
||
### 7.2 vitals_log_backup_pre_015
|
||
|
||
**Beschreibung:** Backup vor Vitals-Refactoring (Migration 015)
|
||
|
||
| Spalte | Typ | Nullable | Default | Beschreibung |
|
||
|--------|-----|----------|---------|--------------|
|
||
| id | uuid | NO | uuid_generate_v4() | PK |
|
||
| profile_id | uuid | NO | - | FK zu profiles.id |
|
||
| date | date | NO | - | Messdatum |
|
||
| resting_hr | integer | YES | NULL | Ruhepuls |
|
||
| hrv | integer | YES | NULL | HRV |
|
||
| systolic | integer | YES | NULL | Systolisch |
|
||
| diastolic | integer | YES | NULL | Diastolisch |
|
||
| pulse | integer | YES | NULL | Puls |
|
||
| vo2_max | numeric(5,2) | YES | NULL | VO2 Max |
|
||
| spo2 | integer | YES | NULL | SpO2 |
|
||
| respiratory_rate | integer | YES | NULL | Atemfrequenz |
|
||
| source | varchar(50) | YES | 'manual' | Datenquelle |
|
||
| notes | text | YES | NULL | Notizen |
|
||
| created_at | timestamp | YES | now() | Erstellungsdatum |
|
||
|
||
**Constraints:**
|
||
- PK: `id`
|
||
- UNIQUE: `profile_id, date`
|
||
- FK: `profile_id` → `profiles(id)` ON DELETE CASCADE
|
||
|
||
**Indizes:**
|
||
- `unique_vitals_per_day` UNIQUE (profile_id, date)
|
||
|
||
---
|
||
|
||
## 8. ER-Diagramm
|
||
|
||
### 8.1 Zentrale Entität: profiles
|
||
|
||
```
|
||
profiles (1) → (N) weight_log
|
||
profiles (1) → (N) circumference_log
|
||
profiles (1) → (N) caliper_log
|
||
profiles (1) → (N) activity_log
|
||
profiles (1) → (N) nutrition_log
|
||
profiles (1) → (N) sleep_log
|
||
profiles (1) → (N) vitals_baseline
|
||
profiles (1) → (N) blood_pressure_log
|
||
profiles (1) → (N) rest_days
|
||
profiles (1) → (N) photos
|
||
profiles (1) → (N) sessions
|
||
profiles (1) → (N) ai_insights
|
||
profiles (1) → (N) ai_usage
|
||
profiles (1) → (N) goals
|
||
profiles (1) → (N) goal_progress_log
|
||
profiles (1) → (N) training_phases
|
||
profiles (1) → (N) fitness_tests
|
||
profiles (1) → (N) access_grants
|
||
profiles (1) → (N) coupon_redemptions
|
||
profiles (1) → (N) user_feature_restrictions
|
||
profiles (1) → (N) user_feature_usage
|
||
profiles (1) → (N) user_activity_log
|
||
profiles (1) → (1) user_stats
|
||
profiles (1) → (N) user_focus_area_weights
|
||
profiles (1) → (N) user_focus_preferences
|
||
profiles (1) → (N) activity_type_mappings
|
||
profiles (1) → (N) weekly_goals
|
||
profiles (self) → (N) profiles (invited_by)
|
||
```
|
||
|
||
### 8.2 Trainings-Typen-Beziehungen
|
||
|
||
```
|
||
training_types (1) → (N) activity_log
|
||
training_types (1) → (N) activity_type_mappings
|
||
```
|
||
|
||
### 8.3 Membership-Beziehungen
|
||
|
||
```
|
||
tiers (1) → (N) tier_limits
|
||
tiers (1) → (N) coupons
|
||
tiers (1) → (N) access_grants
|
||
|
||
features (1) → (N) tier_limits
|
||
features (1) → (N) user_feature_restrictions
|
||
features (1) → (N) user_feature_usage
|
||
|
||
coupons (1) → (N) coupon_redemptions
|
||
coupons (1) → (N) access_grants
|
||
```
|
||
|
||
### 8.4 Ziele-Beziehungen
|
||
|
||
```
|
||
goals (1) → (N) goal_progress_log
|
||
goals (N) → (M) focus_area_definitions (via goal_focus_contributions)
|
||
|
||
focus_area_definitions (N) → (M) profiles (via user_focus_area_weights)
|
||
```
|
||
|
||
### 8.5 Kardinalitäten
|
||
|
||
| Beziehung | Typ | ON DELETE |
|
||
|-----------|-----|-----------|
|
||
| profiles → sessions | 1:N | CASCADE |
|
||
| profiles → weight_log | 1:N | CASCADE |
|
||
| profiles → activity_log | 1:N | CASCADE |
|
||
| profiles → goals | 1:N | CASCADE |
|
||
| profiles → access_grants | 1:N | CASCADE |
|
||
| training_types → activity_log | 1:N | NO ACTION |
|
||
| tiers → tier_limits | 1:N | CASCADE |
|
||
| goals → goal_focus_contributions | 1:N | CASCADE |
|
||
| focus_area_definitions → goal_focus_contributions | 1:N | CASCADE |
|
||
|
||
---
|
||
|
||
## 9. Datenbank-Statistiken
|
||
|
||
### 9.1 Tabellen-Übersicht
|
||
|
||
| Kategorie | Anzahl Tabellen |
|
||
|-----------|-----------------|
|
||
| Kernmodule | 3 (profiles, sessions, user_stats) |
|
||
| Tracking | 10 (weight, circumference, caliper, activity, nutrition, sleep, vitals_baseline, blood_pressure, rest_days, photos) |
|
||
| KI & Analysen | 4 (ai_prompts, ai_insights, ai_usage, pipeline_configs) |
|
||
| Membership | 9 (tiers, features, tier_limits, coupons, coupon_redemptions, access_grants, user_feature_restrictions, user_feature_usage, user_activity_log) |
|
||
| Ziele & Training | 13 (goals, goal_type_definitions, goal_progress_log, goal_focus_contributions, focus_area_definitions, user_focus_area_weights, user_focus_preferences, training_types, activity_type_mappings, training_phases, fitness_tests, training_parameters, weekly_goals) |
|
||
| System | 2 (schema_migrations, app_settings) |
|
||
| Backup | 2 (pipeline_configs_backup_pre_020, vitals_log_backup_pre_015) |
|
||
| **Gesamt** | **43** |
|
||
|
||
### 9.2 Foreign Keys
|
||
|
||
| Tabelle | Anzahl FKs |
|
||
|---------|------------|
|
||
| access_grants | 3 |
|
||
| activity_log | 2 |
|
||
| activity_type_mappings | 1 |
|
||
| ai_insights | 1 |
|
||
| ai_usage | 1 |
|
||
| blood_pressure_log | 1 |
|
||
| caliper_log | 1 |
|
||
| circumference_log | 1 |
|
||
| coupon_redemptions | 2 |
|
||
| coupons | 1 |
|
||
| fitness_tests | 1 |
|
||
| goal_focus_contributions | 2 |
|
||
| goal_progress_log | 2 |
|
||
| goals | 1 |
|
||
| nutrition_log | 1 |
|
||
| photos | 1 |
|
||
| profiles | 1 (self-reference) |
|
||
| rest_days | 1 |
|
||
| sessions | 1 |
|
||
| sleep_log | 1 |
|
||
| tier_limits | 2 |
|
||
| training_phases | 1 |
|
||
| user_activity_log | 1 |
|
||
| user_feature_restrictions | 2 |
|
||
| user_feature_usage | 2 |
|
||
| user_focus_area_weights | 2 |
|
||
| user_focus_preferences | 1 |
|
||
| user_stats | 1 |
|
||
| vitals_baseline | 1 |
|
||
| vitals_log_backup_pre_015 | 1 |
|
||
| weekly_goals | 1 |
|
||
| weight_log | 1 |
|
||
| **Gesamt** | **42** |
|
||
|
||
### 9.3 Indizes
|
||
|
||
| Typ | Anzahl |
|
||
|-----|--------|
|
||
| Primary Keys | 43 |
|
||
| Unique Constraints | 36 |
|
||
| B-Tree Indizes | 86 |
|
||
| GIN Indizes (JSONB) | 7 |
|
||
| Partial Indizes | 12 |
|
||
| **Gesamt** | **184** |
|
||
|
||
---
|
||
|
||
## 10. Wichtige Design-Patterns
|
||
|
||
### 10.1 Profile-ID Isolation
|
||
|
||
**Regel:** Jede Tabelle mit Nutzerdaten hat `profile_id` als Foreign Key.
|
||
|
||
**Vorteile:**
|
||
- Strikte Datenisolation pro Nutzer
|
||
- Kein Zugriff auf fremde Daten möglich
|
||
- Performante Queries durch Index auf profile_id
|
||
|
||
**Beispiel Query:**
|
||
```sql
|
||
SELECT * FROM weight_log
|
||
WHERE profile_id = '...' AND date >= '2026-01-01'
|
||
ORDER BY date DESC;
|
||
```
|
||
|
||
### 10.2 Source-Tracking
|
||
|
||
**Regel:** Tabellen mit Import-Daten haben `source` Spalte.
|
||
|
||
**Werte:**
|
||
- `'manual'` – manuell erfasst
|
||
- `'apple_health'` – Apple Health Import
|
||
- `'garmin'` – Garmin Import
|
||
- `'withings'` – Withings Import
|
||
|
||
**Regel bei Reimport:**
|
||
```sql
|
||
INSERT INTO sleep_log (...)
|
||
ON CONFLICT (profile_id, date)
|
||
DO UPDATE SET ... WHERE sleep_log.source != 'manual';
|
||
```
|
||
|
||
**Schutz:** Manuelle Einträge werden niemals überschrieben.
|
||
|
||
### 10.3 JSONB für flexible Daten
|
||
|
||
**Verwendung:**
|
||
- `ai_prompts.stages` – dynamische Pipeline-Stages
|
||
- `ai_insights.metadata` – Platzhalter-Werte
|
||
- `activity_log.evaluation` – Qualitätsbewertungen
|
||
- `sleep_log.sleep_segments` – Schlafphasen
|
||
- `rest_days.rest_config` – Ruhetag-Konfiguration
|
||
- `training_types.abilities` – Abilities-Matrix
|
||
- `training_parameters.value` – Parameter-Werte
|
||
|
||
**Indizes:**
|
||
- GIN-Indizes für JSONB-Suchen
|
||
- Partial-Indizes mit JSONB-Extraktion
|
||
|
||
**Beispiel:**
|
||
```sql
|
||
-- GIN Index für vollständige JSONB-Suche
|
||
CREATE INDEX idx_ai_prompts_stages ON ai_prompts USING gin (stages);
|
||
|
||
-- Partial Index für spezifischen JSONB-Wert
|
||
CREATE INDEX idx_training_types_profile_enabled
|
||
ON training_types (((profile->'rule_sets'->'minimum_requirements'->>'enabled')))
|
||
WHERE profile IS NOT NULL;
|
||
```
|
||
|
||
### 10.4 Composite Primary Keys
|
||
|
||
**M:N Beziehungen:**
|
||
- `goal_focus_contributions` (goal_id, focus_area_id)
|
||
- `user_focus_area_weights` (profile_id, focus_area_id)
|
||
|
||
**Zeitbasierte Uniqueness:**
|
||
- `weight_log` (profile_id, date)
|
||
- `sleep_log` (profile_id, date)
|
||
- `vitals_baseline` (profile_id, date)
|
||
- `blood_pressure_log` (profile_id, measured_at)
|
||
|
||
### 10.5 Soft Deletes & Status-Felder
|
||
|
||
**Status-Felder:**
|
||
- `goals.status` (active, completed, paused)
|
||
- `training_phases.status` (suggested, accepted, active, completed)
|
||
- `ai_prompts.active` (true/false)
|
||
- `coupons.is_active` (true/false)
|
||
|
||
**Keine Hard Deletes bei:**
|
||
- ai_insights (Historische Analysen behalten)
|
||
- goals (Abgeschlossene Ziele für Statistiken)
|
||
- training_phases (Phasen-Historie)
|
||
|
||
### 10.6 Auto-Updated Timestamps
|
||
|
||
**Pattern:**
|
||
```sql
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
```
|
||
|
||
**Trigger für auto-update:**
|
||
```sql
|
||
CREATE TRIGGER update_timestamp
|
||
BEFORE UPDATE ON tabelle
|
||
FOR EACH ROW
|
||
EXECUTE FUNCTION update_updated_at_column();
|
||
```
|
||
|
||
---
|
||
|
||
## 11. Performance-Optimierungen
|
||
|
||
### 11.1 Index-Strategie
|
||
|
||
**Profile-ID + Date Indizes:**
|
||
```sql
|
||
idx_weight_log_profile_date (profile_id, date DESC)
|
||
idx_activity_profile_date (profile_id, date DESC)
|
||
idx_sleep_profile_date (profile_id, date DESC)
|
||
```
|
||
|
||
**Grund:** Häufigste Query-Pattern = "alle Einträge für User X, neuste zuerst"
|
||
|
||
### 11.2 Partial Indizes
|
||
|
||
**Beispiele:**
|
||
```sql
|
||
-- Nur aktive Prompts indizieren
|
||
CREATE INDEX idx_ai_prompts_active_sort
|
||
ON ai_prompts (active, sort_order);
|
||
|
||
-- Nur Einträge mit Quality-Label
|
||
CREATE INDEX idx_activity_quality_label
|
||
ON activity_log (quality_label)
|
||
WHERE quality_label IS NOT NULL;
|
||
|
||
-- Nur verifizierte E-Mails
|
||
CREATE INDEX idx_profiles_verification_token
|
||
ON profiles (verification_token)
|
||
WHERE verification_token IS NOT NULL;
|
||
```
|
||
|
||
**Vorteil:** Kleinere Index-Größe, schnellere Updates
|
||
|
||
### 11.3 Composite Indizes
|
||
|
||
**Multi-Column Indizes:**
|
||
```sql
|
||
idx_access_grants_active (profile_id, is_active, valid_until DESC)
|
||
idx_goals_category_priority (profile_id, category, priority)
|
||
idx_tier_limits_tier (tier_id, feature_id)
|
||
```
|
||
|
||
**Regel:** Häufigste WHERE-Klauseln in Index-Spalten-Reihenfolge
|
||
|
||
### 11.4 GIN-Indizes für JSONB
|
||
|
||
**JSONB-Suchen beschleunigen:**
|
||
```sql
|
||
CREATE INDEX idx_ai_prompts_stages ON ai_prompts USING gin (stages);
|
||
CREATE INDEX idx_training_types_abilities ON training_types USING gin (abilities);
|
||
CREATE INDEX idx_rest_days_config ON rest_days USING gin (rest_config);
|
||
```
|
||
|
||
**Verwendung:**
|
||
```sql
|
||
SELECT * FROM ai_prompts
|
||
WHERE stages @> '[{"name": "Stage 1"}]'::jsonb;
|
||
```
|
||
|
||
---
|
||
|
||
## 12. Migration-Historie
|
||
|
||
### Migrations-Log
|
||
|
||
| Migration | Datum | Beschreibung |
|
||
|-----------|-------|--------------|
|
||
| 001-010 | 2025-2026 | Basis-Tabellen (weight, circumference, caliper, nutrition, activity, photos, ai) |
|
||
| 011-012 | 2026-02 | Membership-System (tiers, features, access_grants) |
|
||
| 013-014 | 2026-03 | Sleep + Rest Days |
|
||
| 015 | 2026-03 | Vitals-Refactoring (vitals_baseline + blood_pressure_log) |
|
||
| 016-021 | 2026-03 | Training Types + Activity Mappings |
|
||
| 022 | 2026-03 | Goal System (goals, training_phases, fitness_tests) |
|
||
| 023-028 | 2026-03 | Goal System Extensions (auto-population, time-based tracking) |
|
||
| 029-030 | 2026-03 | Dynamic Focus Areas v2.0 (focus_area_definitions, user_focus_area_weights) |
|
||
| 031-032 | 2026-03 | Goal Focus Contributions (M:N Relationship) |
|
||
|
||
**Aktueller Stand:** Migration 032
|
||
**Tracking-Tabelle:** `schema_migrations`
|
||
|
||
---
|
||
|
||
## 13. Bekannte Deprecated-Elemente
|
||
|
||
### 13.1 Deprecated Tabellen
|
||
|
||
| Tabelle | Ersetzt durch | Migration |
|
||
|---------|---------------|-----------|
|
||
| vitals_log | vitals_baseline + blood_pressure_log | 015 |
|
||
| pipeline_configs | ai_prompts (Unified System) | 020 |
|
||
|
||
**Backup-Tabellen behalten für Rollback-Szenarien.**
|
||
|
||
### 13.2 Deprecated Felder
|
||
|
||
| Tabelle | Feld | Grund |
|
||
|---------|------|-------|
|
||
| profiles | goal_mode | Ersetzt durch Dynamic Focus Areas |
|
||
| user_focus_preferences | focus_areas | Ersetzt durch user_focus_area_weights |
|
||
|
||
---
|
||
|
||
## 14. Datenintegrität
|
||
|
||
### 14.1 Foreign Key Constraints
|
||
|
||
**ON DELETE CASCADE:**
|
||
- Alle Tracking-Daten → profiles
|
||
- sessions → profiles
|
||
- goals → profiles
|
||
- access_grants → profiles
|
||
|
||
**ON DELETE SET NULL:**
|
||
- access_grants → coupons
|
||
- coupons → tiers
|
||
|
||
**ON DELETE NO ACTION:**
|
||
- activity_log → training_types
|
||
- profiles → profiles (invited_by)
|
||
|
||
### 14.2 Check Constraints
|
||
|
||
**Beispiele:**
|
||
```sql
|
||
-- profiles.tier
|
||
CHECK (tier IN ('free', 'plus', 'premium', 'trial'))
|
||
|
||
-- goals.status
|
||
CHECK (status IN ('active', 'completed', 'paused'))
|
||
|
||
-- training_phases.phase_type
|
||
CHECK (phase_type IN ('calorie_deficit', 'calorie_surplus', 'deload', 'maintenance', 'periodization'))
|
||
```
|
||
|
||
### 14.3 Unique Constraints
|
||
|
||
**Pro User + Datum:**
|
||
- `weight_log` (profile_id, date)
|
||
- `sleep_log` (profile_id, date)
|
||
- `vitals_baseline` (profile_id, date)
|
||
- `blood_pressure_log` (profile_id, measured_at)
|
||
- `rest_days` (profile_id, date, focus)
|
||
|
||
**Pro User + Feature:**
|
||
- `user_feature_restrictions` (profile_id, feature_id)
|
||
- `user_feature_usage` (profile_id, feature_id)
|
||
- `ai_usage` (profile_id, date)
|
||
|
||
**Global:**
|
||
- `profiles` (email)
|
||
- `ai_prompts` (slug)
|
||
- `coupons` (code)
|
||
- `focus_area_definitions` (key)
|
||
|
||
---
|
||
|
||
## 15. Nächste Schritte (Roadmap)
|
||
|
||
### Phase 0c: Data Layer & Charts ✅ COMPLETED
|
||
- ✅ Data Layer Migration (97 Funktionen)
|
||
- ✅ Chart Endpoints (20 Endpoints E1-E5, A1-A8, R1-R5, C1-C4)
|
||
- 🔲 Frontend Chart Integration (IN PROGRESS)
|
||
|
||
### Phase 1: Advanced Goals & Tracking
|
||
- 🔲 Goal Templates System
|
||
- 🔲 Automated Training Phase Detection
|
||
- 🔲 Goal-Aware Placeholders (120+)
|
||
|
||
### Phase 2: Integrations
|
||
- 🔲 Apple Health Connector (persistent sync)
|
||
- 🔲 Garmin Connect Integration
|
||
- 🔲 Withings API Integration
|
||
- 🔲 Oura Ring Support
|
||
|
||
### Phase 3: Advanced Features
|
||
- 🔲 Multi-Profile Support (Trainer → Clients)
|
||
- 🔲 Team/Group Features
|
||
- 🔲 Stripe Payment Integration
|
||
- 🔲 Advanced Correlations (Lag-Analysis)
|
||
|
||
---
|
||
|
||
## Anhang
|
||
|
||
### A. Tabellen-Index (alphabetisch)
|
||
|
||
```
|
||
access_grants
|
||
activity_log
|
||
activity_type_mappings
|
||
ai_insights
|
||
ai_prompts
|
||
ai_usage
|
||
app_settings
|
||
blood_pressure_log
|
||
caliper_log
|
||
circumference_log
|
||
coupon_redemptions
|
||
coupons
|
||
features
|
||
fitness_tests
|
||
focus_area_definitions
|
||
goal_focus_contributions
|
||
goal_progress_log
|
||
goal_type_definitions
|
||
goals
|
||
nutrition_log
|
||
photos
|
||
pipeline_configs
|
||
pipeline_configs_backup_pre_020
|
||
profiles
|
||
rest_days
|
||
schema_migrations
|
||
sessions
|
||
sleep_log
|
||
tier_limits
|
||
tiers
|
||
training_parameters
|
||
training_phases
|
||
training_types
|
||
user_activity_log
|
||
user_feature_restrictions
|
||
user_feature_usage
|
||
user_focus_area_weights
|
||
user_focus_preferences
|
||
user_stats
|
||
vitals_baseline
|
||
vitals_log_backup_pre_015
|
||
weekly_goals
|
||
weight_log
|
||
```
|
||
|
||
### B. Query-Beispiele
|
||
|
||
**Gewichtsverlauf (letzte 90 Tage):**
|
||
```sql
|
||
SELECT date, weight
|
||
FROM weight_log
|
||
WHERE profile_id = '...'
|
||
AND date >= CURRENT_DATE - INTERVAL '90 days'
|
||
ORDER BY date DESC;
|
||
```
|
||
|
||
**Aktivitäten mit Quality-Label "high":**
|
||
```sql
|
||
SELECT a.date, a.description, t.name AS training_type, a.overall_score
|
||
FROM activity_log a
|
||
JOIN training_types t ON a.training_type_id = t.id
|
||
WHERE a.profile_id = '...'
|
||
AND a.quality_label = 'high'
|
||
ORDER BY a.date DESC;
|
||
```
|
||
|
||
**Aktuelle Tier-Limits für User:**
|
||
```sql
|
||
SELECT f.name, tl.limit_value, tl.limit_type
|
||
FROM profiles p
|
||
JOIN tier_limits tl ON p.tier = tl.tier_id
|
||
JOIN features f ON tl.feature_id = f.id
|
||
WHERE p.id = '...';
|
||
```
|
||
|
||
**Schlafqualität (7 Tage):**
|
||
```sql
|
||
SELECT date,
|
||
total_duration_minutes,
|
||
(sleep_segments->>'deep')::int AS deep_minutes,
|
||
(sleep_segments->>'rem')::int AS rem_minutes,
|
||
quality_score
|
||
FROM sleep_log
|
||
WHERE profile_id = '...'
|
||
AND date >= CURRENT_DATE - INTERVAL '7 days'
|
||
ORDER BY date DESC;
|
||
```
|
||
|
||
**Aktive Ziele mit Progress:**
|
||
```sql
|
||
SELECT title, goal_type, target_value, current_value,
|
||
ROUND((current_value / target_value) * 100, 1) AS progress_pct
|
||
FROM goals
|
||
WHERE profile_id = '...'
|
||
AND status = 'active'
|
||
ORDER BY priority DESC, created_at ASC;
|
||
```
|
||
|
||
---
|
||
|
||
**Ende des Datenmodells**
|