Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
148 lines
6.9 KiB
SQL
148 lines
6.9 KiB
SQL
-- =============================================================
|
|
-- MIGRATION: 012-wellness-ecosystem.sql
|
|
-- ADR-027: Wellness Ecosystem — Тамагочи для Человека
|
|
-- Phase 1: Foundation (Profile, Vitals, Gamification)
|
|
-- =============================================================
|
|
|
|
-- Профиль пользователя (физические параметры)
|
|
CREATE TABLE IF NOT EXISTS wellness_profiles (
|
|
id SERIAL PRIMARY KEY,
|
|
space_id INTEGER REFERENCES spaces(id) NOT NULL UNIQUE,
|
|
gender VARCHAR(10), -- male, female, other
|
|
birth_date DATE,
|
|
height_cm INTEGER,
|
|
target_weight_kg DECIMAL(5,2),
|
|
activity_level VARCHAR(20), -- sedentary, light, moderate, active, very_active
|
|
bmr INTEGER, -- Basal Metabolic Rate (calculated)
|
|
tdee INTEGER, -- Total Daily Energy Expenditure
|
|
timezone VARCHAR(50) DEFAULT 'UTC',
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
-- =============================================================
|
|
-- VITALS MODULE
|
|
-- =============================================================
|
|
|
|
-- Показатели здоровья
|
|
CREATE TABLE IF NOT EXISTS wellness_vitals (
|
|
id SERIAL PRIMARY KEY,
|
|
space_id INTEGER REFERENCES spaces(id) NOT NULL,
|
|
measured_at TIMESTAMP NOT NULL,
|
|
vital_type VARCHAR(50) NOT NULL, -- weight, heart_rate, blood_pressure_sys,
|
|
-- blood_pressure_dia, temperature, spo2,
|
|
-- blood_glucose, body_fat_pct, body_battery
|
|
value DECIMAL(10,3) NOT NULL,
|
|
unit VARCHAR(20), -- kg, bpm, mmHg, C, %, mg/dL
|
|
source VARCHAR(50) DEFAULT 'manual', -- manual, garmin, apple_health, oura
|
|
notes TEXT,
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
-- Индекс для быстрого поиска последних значений
|
|
CREATE INDEX IF NOT EXISTS idx_vitals_latest ON wellness_vitals(space_id, vital_type, measured_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_vitals_space ON wellness_vitals(space_id);
|
|
|
|
-- =============================================================
|
|
-- GAMIFICATION MODULE
|
|
-- =============================================================
|
|
|
|
-- Очки
|
|
CREATE TABLE IF NOT EXISTS wellness_points (
|
|
id SERIAL PRIMARY KEY,
|
|
space_id INTEGER REFERENCES spaces(id) NOT NULL,
|
|
earned_at TIMESTAMP DEFAULT NOW(),
|
|
points INTEGER NOT NULL,
|
|
source_type VARCHAR(50) NOT NULL, -- vital, workout, habit, goal, achievement, streak
|
|
source_id INTEGER,
|
|
reason VARCHAR(255),
|
|
metadata JSONB
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_points_space ON wellness_points(space_id, earned_at DESC);
|
|
|
|
-- Уровни пользователя
|
|
CREATE TABLE IF NOT EXISTS wellness_levels (
|
|
id SERIAL PRIMARY KEY,
|
|
space_id INTEGER REFERENCES spaces(id) NOT NULL UNIQUE,
|
|
current_level INTEGER DEFAULT 1,
|
|
total_xp INTEGER DEFAULT 0,
|
|
level_xp INTEGER DEFAULT 0, -- XP in current level
|
|
title VARCHAR(100) DEFAULT 'Beginner',
|
|
avatar_url TEXT,
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
-- Справочник достижений (глобальный)
|
|
CREATE TABLE IF NOT EXISTS wellness_achievements (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
icon VARCHAR(100),
|
|
category VARCHAR(50), -- fitness, nutrition, consistency, health
|
|
tier VARCHAR(20) DEFAULT 'bronze', -- bronze, silver, gold, platinum
|
|
condition JSONB NOT NULL, -- {"type": "streak", "streak_type": "workout", "count": 7}
|
|
points_reward INTEGER DEFAULT 50,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
-- Достижения пользователей
|
|
CREATE TABLE IF NOT EXISTS wellness_user_achievements (
|
|
id SERIAL PRIMARY KEY,
|
|
space_id INTEGER REFERENCES spaces(id) NOT NULL,
|
|
achievement_id INTEGER REFERENCES wellness_achievements(id),
|
|
earned_at TIMESTAMP,
|
|
progress DECIMAL(5,2) DEFAULT 0, -- 0-100%
|
|
UNIQUE(space_id, achievement_id)
|
|
);
|
|
|
|
-- Streaks
|
|
CREATE TABLE IF NOT EXISTS wellness_streaks (
|
|
id SERIAL PRIMARY KEY,
|
|
space_id INTEGER REFERENCES spaces(id) NOT NULL,
|
|
streak_type VARCHAR(50) NOT NULL, -- workout, vitals_logged, nutrition_logged, water_goal
|
|
current_count INTEGER DEFAULT 0,
|
|
longest_count INTEGER DEFAULT 0,
|
|
last_activity_date DATE,
|
|
started_at DATE,
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
UNIQUE(space_id, streak_type)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_streaks_space ON wellness_streaks(space_id);
|
|
|
|
-- =============================================================
|
|
-- SEED DATA: Default Achievements
|
|
-- =============================================================
|
|
|
|
INSERT INTO wellness_achievements (name, description, icon, category, tier, condition, points_reward) VALUES
|
|
-- Vitals Achievements
|
|
('First Check-in', 'Log your first vital sign', '❤️', 'health', 'bronze', '{"type": "count", "table": "wellness_vitals", "count": 1}', 10),
|
|
('Health Monitor', 'Log vitals for 7 days in a row', '📊', 'health', 'silver', '{"type": "streak", "streak_type": "vitals_logged", "count": 7}', 50),
|
|
('Vital Master', 'Log vitals for 30 days in a row', '🏆', 'health', 'gold', '{"type": "streak", "streak_type": "vitals_logged", "count": 30}', 200),
|
|
|
|
-- Fitness Achievements (cross-reference with ADR-025)
|
|
('First Workout', 'Complete your first workout', '💪', 'fitness', 'bronze', '{"type": "count", "table": "fitness_workouts", "count": 1}', 10),
|
|
('Week Warrior', 'Work out 7 days in a row', '🔥', 'fitness', 'silver', '{"type": "streak", "streak_type": "workout", "count": 7}', 50),
|
|
('Month Master', 'Work out 30 days in a row', '🏆', 'fitness', 'gold', '{"type": "streak", "streak_type": "workout", "count": 30}', 200),
|
|
('Century Club', 'Complete 100 workouts', '💯', 'fitness', 'platinum', '{"type": "count", "table": "fitness_workouts", "count": 100}', 500),
|
|
|
|
-- Level Achievements
|
|
('Level 5', 'Reach Level 5', '⭐', 'consistency', 'bronze', '{"type": "level", "level": 5}', 25),
|
|
('Level 10', 'Reach Level 10', '🌟', 'consistency', 'silver', '{"type": "level", "level": 10}', 75),
|
|
('Level 25', 'Reach Level 25', '✨', 'consistency', 'gold', '{"type": "level", "level": 25}', 150),
|
|
('Level 50', 'Reach Level 50', '🎖️', 'consistency', 'platinum', '{"type": "level", "level": 50}', 500)
|
|
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- =============================================================
|
|
-- COMMENT: Level Thresholds
|
|
-- =============================================================
|
|
|
|
-- XP requirements per level (exponential curve)
|
|
-- Level 1: 0 XP, Level 2: 100 XP, Level 3: ~283 XP, Level 4: ~400 XP...
|
|
-- Formula: required_xp = round(100 * (level ^ 1.5))
|
|
|
|
COMMENT ON TABLE wellness_levels IS 'Level XP formula: required_xp = round(100 * (level ^ 1.5))';
|
|
COMMENT ON TABLE wellness_vitals IS 'Supported vital_types: weight, heart_rate, blood_pressure_sys, blood_pressure_dia, temperature, spo2, blood_glucose, body_fat_pct, body_battery';
|