-- Схема базы данных для интернет-магазина -- PostgreSQL -- Таблица ресурсов CREATE TABLE IF NOT EXISTS resources ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, slug VARCHAR(50) UNIQUE NOT NULL, description TEXT, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица действий CREATE TABLE IF NOT EXISTS actions ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, slug VARCHAR(50) UNIQUE NOT NULL, description TEXT, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица прав доступа CREATE TABLE IF NOT EXISTS permissions ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, slug VARCHAR(100) UNIQUE NOT NULL, resource_id INTEGER NOT NULL REFERENCES resources(id) ON DELETE RESTRICT, action_id INTEGER NOT NULL REFERENCES actions(id) ON DELETE RESTRICT, description TEXT, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(resource_id, action_id) ); -- Таблица ролей CREATE TABLE IF NOT EXISTS roles ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, slug VARCHAR(50) UNIQUE NOT NULL, description TEXT, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица связи ролей и прав (многие-ко-многим) CREATE TABLE IF NOT EXISTS role_permissions ( id SERIAL PRIMARY KEY, role_id INTEGER NOT NULL REFERENCES roles(id) ON DELETE CASCADE, permission_id INTEGER NOT NULL REFERENCES permissions(id) ON DELETE CASCADE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(role_id, permission_id) ); -- Таблица пользователей CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE, password_hash VARCHAR(255), first_name VARCHAR(100), last_name VARCHAR(100), phone VARCHAR(20), telegram_id BIGINT UNIQUE, telegram_user VARCHAR(100), role_id INTEGER REFERENCES roles(id) ON DELETE SET NULL, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица аватаров пользователей CREATE TABLE IF NOT EXISTS user_avatars ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, image_url TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id) ); -- Таблица категорий товаров CREATE TABLE IF NOT EXISTS categories ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, slug VARCHAR(255) UNIQUE NOT NULL, description TEXT, parent_id INTEGER REFERENCES categories(id) ON DELETE SET NULL, image_url VARCHAR(500), is_active BOOLEAN DEFAULT TRUE, sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица товаров CREATE TABLE IF NOT EXISTS products ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, slug VARCHAR(255) UNIQUE NOT NULL, description TEXT, short_description TEXT, sku VARCHAR(100) UNIQUE NOT NULL, price DECIMAL(10, 2) NOT NULL CHECK (price >= 0), compare_at_price DECIMAL(10, 2) CHECK (compare_at_price >= 0), cost_price DECIMAL(10, 2) CHECK (cost_price >= 0), discount_percent DECIMAL(5, 2) DEFAULT 0 CHECK (discount_percent >= 0 AND discount_percent <= 100), category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL, stock_quantity INTEGER DEFAULT 0 CHECK (stock_quantity >= 0), weight DECIMAL(8, 2), dimensions VARCHAR(100), is_active BOOLEAN DEFAULT TRUE, is_featured BOOLEAN DEFAULT FALSE, rating DECIMAL(3, 2) DEFAULT 0 CHECK (rating >= 0 AND rating <= 5), review_count INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица изображений товаров CREATE TABLE IF NOT EXISTS product_images ( id SERIAL PRIMARY KEY, product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE, image_url TEXT NOT NULL, alt_text VARCHAR(255), is_primary BOOLEAN DEFAULT FALSE, sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица адресов доставки CREATE TABLE IF NOT EXISTS addresses ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, first_name VARCHAR(100) NOT NULL, last_name VARCHAR(100) NOT NULL, phone VARCHAR(20), country VARCHAR(100) NOT NULL, city VARCHAR(100) NOT NULL, street VARCHAR(255) NOT NULL, building VARCHAR(50), apartment VARCHAR(50), postal_code VARCHAR(20), is_default BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица статусов заказов CREATE TABLE IF NOT EXISTS order_statuses ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, slug VARCHAR(50) UNIQUE NOT NULL, description TEXT, color VARCHAR(20), sort_order INTEGER DEFAULT 0, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица способов оплаты CREATE TABLE IF NOT EXISTS payment_methods ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, slug VARCHAR(50) UNIQUE NOT NULL, description TEXT, is_active BOOLEAN DEFAULT TRUE, sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица статусов платежей CREATE TABLE IF NOT EXISTS payment_statuses ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, slug VARCHAR(50) UNIQUE NOT NULL, description TEXT, color VARCHAR(20), sort_order INTEGER DEFAULT 0, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица заказов CREATE TABLE IF NOT EXISTS orders ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE SET NULL, order_number VARCHAR(50) UNIQUE NOT NULL, status_id INTEGER NOT NULL REFERENCES order_statuses(id) ON DELETE RESTRICT, subtotal DECIMAL(10, 2) NOT NULL CHECK (subtotal >= 0), tax_amount DECIMAL(10, 2) DEFAULT 0 CHECK (tax_amount >= 0), shipping_cost DECIMAL(10, 2) DEFAULT 0 CHECK (shipping_cost >= 0), discount_amount DECIMAL(10, 2) DEFAULT 0 CHECK (discount_amount >= 0), total_amount DECIMAL(10, 2) NOT NULL CHECK (total_amount >= 0), shipping_address_id INTEGER REFERENCES addresses(id) ON DELETE SET NULL, billing_address_id INTEGER REFERENCES addresses(id) ON DELETE SET NULL, payment_method_id INTEGER REFERENCES payment_methods(id) ON DELETE SET NULL, payment_status_id INTEGER REFERENCES payment_statuses(id) ON DELETE RESTRICT, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица элементов заказа CREATE TABLE IF NOT EXISTS order_items ( id SERIAL PRIMARY KEY, order_id INTEGER NOT NULL REFERENCES orders(id) ON DELETE CASCADE, product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE RESTRICT, product_name VARCHAR(255) NOT NULL, product_sku VARCHAR(100) NOT NULL, quantity INTEGER NOT NULL CHECK (quantity > 0), price DECIMAL(10, 2) NOT NULL CHECK (price >= 0), subtotal DECIMAL(10, 2) NOT NULL CHECK (subtotal >= 0), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Таблица корзины CREATE TABLE IF NOT EXISTS cart_items ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE, quantity INTEGER NOT NULL CHECK (quantity > 0), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, product_id) ); -- Таблица отзывов CREATE TABLE IF NOT EXISTS reviews ( id SERIAL PRIMARY KEY, product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5), title VARCHAR(255), comment TEXT, is_approved BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(product_id, user_id) ); -- Таблица платежей CREATE TABLE IF NOT EXISTS payments ( id SERIAL PRIMARY KEY, order_id INTEGER NOT NULL REFERENCES orders(id) ON DELETE CASCADE, payment_method_id INTEGER NOT NULL REFERENCES payment_methods(id) ON DELETE RESTRICT, transaction_id VARCHAR(255) UNIQUE, amount DECIMAL(10, 2) NOT NULL CHECK (amount >= 0), status_id INTEGER NOT NULL REFERENCES payment_statuses(id) ON DELETE RESTRICT, payment_date TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Функция для автоматического обновления updated_at CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = CURRENT_TIMESTAMP; RETURN NEW; END; $$ language 'plpgsql'; -- Функция для обновления рейтинга товара при добавлении/изменении отзыва CREATE OR REPLACE FUNCTION update_product_rating() RETURNS TRIGGER AS $$ BEGIN UPDATE products SET rating = ( SELECT COALESCE(AVG(rating), 0) FROM reviews WHERE product_id = COALESCE(NEW.product_id, OLD.product_id) AND is_approved = TRUE ), review_count = ( SELECT COUNT(*) FROM reviews WHERE product_id = COALESCE(NEW.product_id, OLD.product_id) AND is_approved = TRUE ) WHERE id = COALESCE(NEW.product_id, OLD.product_id); RETURN COALESCE(NEW, OLD); END; $$ language 'plpgsql'; -- Функция для генерации номера заказа CREATE OR REPLACE FUNCTION generate_order_number() RETURNS TRIGGER AS $$ BEGIN IF NEW.order_number IS NULL OR NEW.order_number = '' THEN NEW.order_number := 'ORD-' || TO_CHAR(NOW(), 'YYYYMMDD') || '-' || LPAD(NEW.id::TEXT, 6, '0'); END IF; RETURN NEW; END; $$ language 'plpgsql'; -- Функция для получения ID статуса заказа по slug CREATE OR REPLACE FUNCTION get_order_status_id(status_slug VARCHAR) RETURNS INTEGER AS $$ DECLARE status_id INTEGER; BEGIN SELECT id INTO status_id FROM order_statuses WHERE slug = status_slug AND is_active = TRUE LIMIT 1; RETURN status_id; END; $$ language 'plpgsql'; -- Функция для получения ID статуса платежа по slug CREATE OR REPLACE FUNCTION get_payment_status_id(status_slug VARCHAR) RETURNS INTEGER AS $$ DECLARE status_id INTEGER; BEGIN SELECT id INTO status_id FROM payment_statuses WHERE slug = status_slug AND is_active = TRUE LIMIT 1; RETURN status_id; END; $$ language 'plpgsql'; -- Функция для получения ID способа оплаты по slug CREATE OR REPLACE FUNCTION get_payment_method_id(method_slug VARCHAR) RETURNS INTEGER AS $$ DECLARE method_id INTEGER; BEGIN SELECT id INTO method_id FROM payment_methods WHERE slug = method_slug AND is_active = TRUE LIMIT 1; RETURN method_id; END; $$ language 'plpgsql'; -- Комментарии к таблицам COMMENT ON TABLE resources IS 'Ресурсы системы (products, orders, categories и т.д.)'; COMMENT ON TABLE actions IS 'Действия над ресурсами (view, create, edit, delete и т.д.)'; COMMENT ON TABLE permissions IS 'Права доступа к ресурсам системы'; COMMENT ON TABLE roles IS 'Роли пользователей системы'; COMMENT ON TABLE role_permissions IS 'Связь ролей и прав доступа (многие-ко-многим)'; COMMENT ON TABLE users IS 'Пользователи системы (покупатели и администраторы)'; COMMENT ON TABLE categories IS 'Категории товаров с поддержкой иерархии'; COMMENT ON TABLE order_statuses IS 'Статусы заказов'; COMMENT ON TABLE payment_methods IS 'Способы оплаты'; COMMENT ON TABLE payment_statuses IS 'Статусы платежей'; COMMENT ON TABLE products IS 'Товары магазина'; COMMENT ON TABLE product_images IS 'Изображения товаров'; COMMENT ON TABLE addresses IS 'Адреса доставки пользователей'; COMMENT ON TABLE orders IS 'Заказы покупателей'; COMMENT ON TABLE order_items IS 'Элементы заказа (товары в заказе)'; COMMENT ON TABLE cart_items IS 'Корзина покупок'; COMMENT ON TABLE reviews IS 'Отзывы на товары'; COMMENT ON TABLE payments IS 'Платежи по заказам'; -- Таблица настроек системы CREATE TABLE IF NOT EXISTS settings ( id SERIAL PRIMARY KEY, key VARCHAR(100) UNIQUE NOT NULL, value TEXT, description TEXT, type VARCHAR(50) DEFAULT 'string' CHECK (type IN ('string', 'boolean', 'number', 'json')), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); COMMENT ON TABLE settings IS 'Настройки системы'; COMMENT ON COLUMN settings.key IS 'Уникальный ключ настройки'; COMMENT ON COLUMN settings.value IS 'Значение настройки (текст)'; COMMENT ON COLUMN settings.type IS 'Тип значения: string, boolean, number, json';