151 lines
5.9 KiB
SQL
151 lines
5.9 KiB
SQL
-- Fitness Tracking App - Production Database Schema
|
|
-- Database: fitness_app
|
|
-- Created: 2026-04-21
|
|
|
|
-- Create Database
|
|
CREATE DATABASE IF NOT EXISTS fitness_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
USE fitness_app;
|
|
|
|
-- Users Table
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
uuid CHAR(36) UNIQUE NOT NULL COMMENT 'Unique identifier for the user',
|
|
username VARCHAR(50) UNIQUE NOT NULL,
|
|
email VARCHAR(100) UNIQUE NOT NULL,
|
|
password_hash VARCHAR(255) NOT NULL COMMENT 'bcrypt hash',
|
|
api_key VARCHAR(64) UNIQUE NOT NULL COMMENT 'API key for client authentication',
|
|
api_secret VARCHAR(64) NOT NULL COMMENT 'Secret for HMAC signature',
|
|
full_name VARCHAR(100),
|
|
avatar_url VARCHAR(255),
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
INDEX idx_uuid (uuid),
|
|
INDEX idx_api_key (api_key),
|
|
INDEX idx_created_at (created_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- Workouts Table
|
|
CREATE TABLE IF NOT EXISTS workouts (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
workout_uuid CHAR(36) UNIQUE NOT NULL COMMENT 'Unique identifier for the workout',
|
|
user_id INT NOT NULL,
|
|
workout_type ENUM('running', 'walking') NOT NULL,
|
|
distance_meters INT NOT NULL COMMENT 'Total distance in meters',
|
|
duration_seconds INT NOT NULL COMMENT 'Total duration in seconds',
|
|
elevation_gain_meters INT DEFAULT 0 COMMENT 'Elevation gain in meters',
|
|
elevation_loss_meters INT DEFAULT 0 COMMENT 'Elevation loss in meters',
|
|
calories_burned FLOAT DEFAULT 0,
|
|
average_pace_mps FLOAT COMMENT 'Average pace in meters per second',
|
|
max_speed_mps FLOAT DEFAULT 0 COMMENT 'Max speed in meters per second',
|
|
route_polyline LONGTEXT NOT NULL COMMENT 'Encoded polyline format (Google Maps Polyline Algorithm)',
|
|
coordinate_count INT NOT NULL COMMENT 'Total number of GPS coordinates recorded',
|
|
start_lat DECIMAL(10, 8) NOT NULL,
|
|
start_lng DECIMAL(11, 8) NOT NULL,
|
|
end_lat DECIMAL(10, 8) NOT NULL,
|
|
end_lng DECIMAL(11, 8) NOT NULL,
|
|
start_time DATETIME NOT NULL COMMENT 'ISO 8601 datetime when workout started',
|
|
end_time DATETIME NOT NULL COMMENT 'ISO 8601 datetime when workout ended',
|
|
weather_condition VARCHAR(50),
|
|
temperature_celsius FLOAT,
|
|
notes TEXT,
|
|
is_public BOOLEAN DEFAULT FALSE,
|
|
synced_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
INDEX idx_workout_uuid (workout_uuid),
|
|
INDEX idx_user_id (user_id),
|
|
INDEX idx_workout_type (workout_type),
|
|
INDEX idx_synced_at (synced_at),
|
|
INDEX idx_created_at (created_at),
|
|
INDEX idx_user_created (user_id, created_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- Workout Segments Table (for detailed route tracking if needed)
|
|
CREATE TABLE IF NOT EXISTS workout_segments (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
workout_id INT NOT NULL,
|
|
segment_order INT NOT NULL COMMENT 'Order of segment in workout',
|
|
duration_seconds INT,
|
|
distance_meters INT,
|
|
average_pace_mps FLOAT,
|
|
index_in_polyline INT COMMENT 'Start index in polyline',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (workout_id) REFERENCES workouts(id) ON DELETE CASCADE,
|
|
INDEX idx_workout_id (workout_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- API Logs Table (for debugging and analytics)
|
|
CREATE TABLE IF NOT EXISTS api_logs (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT,
|
|
endpoint VARCHAR(255),
|
|
method VARCHAR(10),
|
|
status_code INT,
|
|
request_hash VARCHAR(64) COMMENT 'Hash of request for deduplication',
|
|
ip_address VARCHAR(45),
|
|
user_agent VARCHAR(255),
|
|
response_time_ms INT,
|
|
error_message TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_user_id (user_id),
|
|
INDEX idx_created_at (created_at),
|
|
INDEX idx_endpoint (endpoint),
|
|
INDEX idx_request_hash (request_hash)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- Statistics Cache Table (for performance optimization)
|
|
CREATE TABLE IF NOT EXISTS user_stats_cache (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT UNIQUE NOT NULL,
|
|
total_workouts INT DEFAULT 0,
|
|
total_distance_meters INT DEFAULT 0,
|
|
total_duration_seconds INT DEFAULT 0,
|
|
total_calories_burned FLOAT DEFAULT 0,
|
|
average_pace_mps FLOAT,
|
|
last_workout_date DATETIME,
|
|
streak_days INT DEFAULT 0,
|
|
cached_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
INDEX idx_user_id (user_id),
|
|
INDEX idx_cached_at (cached_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- Create Triggers for audit trail
|
|
DELIMITER $$
|
|
|
|
CREATE TRIGGER workout_audit_insert AFTER INSERT ON workouts
|
|
FOR EACH ROW
|
|
BEGIN
|
|
INSERT INTO api_logs (endpoint, method, status_code, error_message, created_at)
|
|
VALUES ('workouts', 'INSERT', 201, NULL, NOW());
|
|
END $$
|
|
|
|
DELIMITER ;
|
|
|
|
-- Stored Procedure to get user workout statistics
|
|
DELIMITER $$
|
|
|
|
CREATE PROCEDURE GetUserStats(IN p_user_id INT)
|
|
BEGIN
|
|
SELECT
|
|
COUNT(*) as total_workouts,
|
|
SUM(distance_meters) as total_distance,
|
|
SUM(duration_seconds) as total_duration,
|
|
SUM(calories_burned) as total_calories,
|
|
AVG(average_pace_mps) as avg_pace,
|
|
MAX(created_at) as last_workout,
|
|
workout_type
|
|
FROM workouts
|
|
WHERE user_id = p_user_id
|
|
GROUP BY workout_type
|
|
ORDER BY created_at DESC;
|
|
END $$
|
|
|
|
DELIMITER ;
|
|
|
|
-- Initial indexes for optimal query performance
|
|
CREATE INDEX idx_workouts_stats ON workouts(user_id, workout_type, created_at);
|
|
CREATE INDEX idx_users_active ON users(is_active, created_at);
|