Initial commit

This commit is contained in:
abakumov-a-a committed 2025-12-07 13:34:02 +03:00
commit 38b382bdc9
190 files changed
+37216

No files matched your search

+385
View File
@@ -0,0 +1,385 @@
-- Схема базы данных для интернет-магазина
-- 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,
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';
+678
View File
@@ -0,0 +1,678 @@
-- Начальные данные для базы данных интернет-магазина
-- PostgreSQL
--
-- ВАЖНО: Этот файл должен выполняться ПОСЛЕ schema.sql
-- Сначала создайте структуру базы данных (schema.sql), затем заполните данными (seed_data.sql)
-- Этот файл можно безопасно выполнять многократно - все INSERT используют ON CONFLICT
-- ============================================================================
-- 1. РЕСУРСЫ (RESOURCES)
-- ============================================================================
-- Основные ресурсы системы
INSERT INTO resources (name, slug, description) VALUES
('Товары', 'products', 'Управление товарами магазина')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Заказы', 'orders', 'Управление заказами покупателей')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Категории', 'categories', 'Управление категориями товаров')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Отзывы', 'reviews', 'Управление отзывами на товары')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Корзина', 'cart', 'Управление корзиной покупок')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Пользователи', 'users', 'Управление пользователями системы')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Все ресурсы', '*', 'Все ресурсы системы (для администратора)')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- Дополнительные ресурсы
INSERT INTO resources (name, slug, description) VALUES
('Платежи', 'payments', 'Управление платежами по заказам')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Адреса', 'addresses', 'Управление адресами доставки')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Роли', 'roles', 'Управление ролями пользователей')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO resources (name, slug, description) VALUES
('Права доступа', 'permissions', 'Управление правами доступа')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- ============================================================================
-- 2. ДЕЙСТВИЯ (ACTIONS)
-- ============================================================================
-- CRUD операции
INSERT INTO actions (name, slug, description) VALUES
('Просмотр', 'view', 'Просмотр списка и деталей записей (Read)')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO actions (name, slug, description) VALUES
('Создание', 'create', 'Создание новых записей (Create)')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO actions (name, slug, description) VALUES
('Редактирование', 'edit', 'Редактирование существующих записей (Update)')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO actions (name, slug, description) VALUES
('Удаление', 'delete', 'Удаление записей (Delete)')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- Дополнительные действия
INSERT INTO actions (name, slug, description) VALUES
('Модерация', 'moderate', 'Одобрение и отклонение записей')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO actions (name, slug, description) VALUES
('Управление', 'manage', 'Полное управление ресурсом')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO actions (name, slug, description) VALUES
('Просмотр своих', 'view_own', 'Просмотр только своих записей')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO actions (name, slug, description) VALUES
('Все действия', '*', 'Все действия (для администратора)')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- ============================================================================
-- 3. ПРАВА ДОСТУПА (PERMISSIONS)
-- ============================================================================
-- Products
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT
'Просмотр товаров',
'products.view',
r.id,
a.id,
'Просмотр списка и деталей товаров'
FROM resources r, actions a
WHERE r.slug = 'products' AND a.slug = 'view'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Создание товаров', 'products.create', r.id, a.id, 'Создание новых товаров'
FROM resources r, actions a
WHERE r.slug = 'products' AND a.slug = 'create'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Редактирование товаров', 'products.edit', r.id, a.id, 'Редактирование существующих товаров'
FROM resources r, actions a
WHERE r.slug = 'products' AND a.slug = 'edit'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Удаление товаров', 'products.delete', r.id, a.id, 'Удаление товаров'
FROM resources r, actions a
WHERE r.slug = 'products' AND a.slug = 'delete'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- Orders
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Просмотр заказов', 'orders.view', r.id, a.id, 'Просмотр списка и деталей заказов'
FROM resources r, actions a
WHERE r.slug = 'orders' AND a.slug = 'view'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Создание заказов', 'orders.create', r.id, a.id, 'Создание новых заказов'
FROM resources r, actions a
WHERE r.slug = 'orders' AND a.slug = 'create'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Редактирование заказов', 'orders.edit', r.id, a.id, 'Редактирование заказов'
FROM resources r, actions a
WHERE r.slug = 'orders' AND a.slug = 'edit'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Просмотр своих заказов', 'orders.view_own', r.id, a.id, 'Просмотр только своих заказов'
FROM resources r, actions a
WHERE r.slug = 'orders' AND a.slug = 'view_own'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- Categories
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Просмотр категорий', 'categories.view', r.id, a.id, 'Просмотр категорий'
FROM resources r, actions a
WHERE r.slug = 'categories' AND a.slug = 'view'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Создание категорий', 'categories.create', r.id, a.id, 'Создание новых категорий'
FROM resources r, actions a
WHERE r.slug = 'categories' AND a.slug = 'create'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Редактирование категорий', 'categories.edit', r.id, a.id, 'Редактирование категорий'
FROM resources r, actions a
WHERE r.slug = 'categories' AND a.slug = 'edit'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Удаление категорий', 'categories.delete', r.id, a.id, 'Удаление категорий'
FROM resources r, actions a
WHERE r.slug = 'categories' AND a.slug = 'delete'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- Reviews
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Просмотр отзывов', 'reviews.view', r.id, a.id, 'Просмотр отзывов'
FROM resources r, actions a
WHERE r.slug = 'reviews' AND a.slug = 'view'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Создание отзывов', 'reviews.create', r.id, a.id, 'Создание отзывов на товары'
FROM resources r, actions a
WHERE r.slug = 'reviews' AND a.slug = 'create'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Модерация отзывов', 'reviews.moderate', r.id, a.id, 'Одобрение и отклонение отзывов'
FROM resources r, actions a
WHERE r.slug = 'reviews' AND a.slug = 'moderate'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Удаление отзывов', 'reviews.delete', r.id, a.id, 'Удаление отзывов'
FROM resources r, actions a
WHERE r.slug = 'reviews' AND a.slug = 'delete'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- Cart
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Управление корзиной', 'cart.manage', r.id, a.id, 'Добавление и удаление товаров из корзины'
FROM resources r, actions a
WHERE r.slug = 'cart' AND a.slug = 'manage'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- Users
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Просмотр пользователей', 'users.view', r.id, a.id, 'Просмотр списка пользователей'
FROM resources r, actions a
WHERE r.slug = 'users' AND a.slug = 'view'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Редактирование пользователей', 'users.edit', r.id, a.id, 'Редактирование данных пользователей'
FROM resources r, actions a
WHERE r.slug = 'users' AND a.slug = 'edit'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Удаление пользователей', 'users.delete', r.id, a.id, 'Удаление пользователей'
FROM resources r, actions a
WHERE r.slug = 'users' AND a.slug = 'delete'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- Admin (полные права)
INSERT INTO permissions (name, slug, resource_id, action_id, description)
SELECT 'Полные права', 'admin.all', r.id, a.id, 'Полные права администратора на все ресурсы'
FROM resources r, actions a
WHERE r.slug = '*' AND a.slug = '*'
ON CONFLICT (resource_id, action_id) DO UPDATE SET
name = EXCLUDED.name,
slug = EXCLUDED.slug,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- ============================================================================
-- 4. РОЛИ (ROLES)
-- ============================================================================
INSERT INTO roles (name, slug, description) VALUES
('Администратор', 'admin', 'Администратор системы с полными правами доступа ко всем ресурсам')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO roles (name, slug, description) VALUES
('Владелец', 'owner', 'Владелец магазина с расширенными правами управления')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO roles (name, slug, description) VALUES
('Клиент', 'customer', 'Обычный покупатель магазина с базовыми правами')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO roles (name, slug, description) VALUES
('Сотрудник', 'employee', 'Сотрудник магазина с правами на управление товарами и заказами')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO roles (name, slug, description) VALUES
('Менеджер', 'manager', 'Менеджер магазина')
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
updated_at = CURRENT_TIMESTAMP;
-- ============================================================================
-- 5. СВЯЗЬ РОЛЕЙ И ПРАВ (ROLE_PERMISSIONS)
-- ============================================================================
-- Администратор (admin) - все права
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'admin'
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Владелец (owner) - расширенные права (почти как администратор, но без управления ролями и правами)
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'owner'
AND p.slug NOT LIKE 'roles.%'
AND p.slug NOT LIKE 'permissions.%'
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Сотрудник (employee) - управление товарами и заказами
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'employee'
AND p.slug IN (
'products.view', 'products.create', 'products.edit', 'products.delete',
'orders.view', 'orders.create', 'orders.edit',
'categories.view', 'categories.create', 'categories.edit', 'categories.delete',
'reviews.view', 'reviews.moderate', 'reviews.delete',
'users.view',
'cart.manage'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Менеджер (manager) - аналогично сотруднику
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'manager'
AND p.slug IN (
'products.view', 'products.create', 'products.edit', 'products.delete',
'orders.view', 'orders.edit',
'categories.view', 'categories.create', 'categories.edit', 'categories.delete',
'reviews.view', 'reviews.moderate', 'reviews.delete',
'users.view',
'cart.manage'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Клиент (customer) - базовые права
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'customer'
AND p.slug IN (
'products.view',
'orders.create',
'orders.view_own',
'cart.manage',
'reviews.create',
'categories.view'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- ============================================================================
-- 6. СТАТУСЫ ЗАКАЗОВ (ORDER_STATUSES)
-- ============================================================================
INSERT INTO order_statuses (name, slug, description, color, sort_order) VALUES
('Ожидает обработки', 'pending', 'Заказ создан и ожидает обработки', '#FFA500', 1)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO order_statuses (name, slug, description, color, sort_order) VALUES
('В обработке', 'processing', 'Заказ обрабатывается', '#4169E1', 2)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO order_statuses (name, slug, description, color, sort_order) VALUES
('Отправлен', 'shipped', 'Заказ отправлен покупателю', '#32CD32', 3)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO order_statuses (name, slug, description, color, sort_order) VALUES
('Доставлен', 'delivered', 'Заказ доставлен покупателю', '#228B22', 4)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO order_statuses (name, slug, description, color, sort_order) VALUES
('Отменен', 'cancelled', 'Заказ отменен', '#DC143C', 5)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO order_statuses (name, slug, description, color, sort_order) VALUES
('Возвращен', 'refunded', 'Заказ возвращен, средства возвращены', '#8B0000', 6)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
-- ============================================================================
-- 7. СПОСОБЫ ОПЛАТЫ (PAYMENT_METHODS)
-- ============================================================================
INSERT INTO payment_methods (name, slug, description, sort_order) VALUES
('Банковская карта', 'card', 'Оплата банковской картой онлайн', 1)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO payment_methods (name, slug, description, sort_order) VALUES
('Электронный кошелек', 'e-wallet', 'Оплата через электронный кошелек (Яндекс.Деньги, Qiwi и т.д.)', 2)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO payment_methods (name, slug, description, sort_order) VALUES
('Наличные при получении', 'cash_on_delivery', 'Оплата наличными при получении заказа', 3)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO payment_methods (name, slug, description, sort_order) VALUES
('Банковский перевод', 'bank_transfer', 'Оплата через банковский перевод', 4)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO payment_methods (name, slug, description, sort_order) VALUES
('Криптовалюта', 'crypto', 'Оплата криптовалютой', 5)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
-- ============================================================================
-- 8. СТАТУСЫ ПЛАТЕЖЕЙ (PAYMENT_STATUSES)
-- ============================================================================
INSERT INTO payment_statuses (name, slug, description, color, sort_order) VALUES
('Ожидает оплаты', 'pending', 'Платеж ожидает оплаты', '#FFA500', 1)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO payment_statuses (name, slug, description, color, sort_order) VALUES
('Оплачен', 'paid', 'Платеж успешно оплачен', '#32CD32', 2)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO payment_statuses (name, slug, description, color, sort_order) VALUES
('Завершен', 'completed', 'Платеж завершен', '#228B22', 3)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO payment_statuses (name, slug, description, color, sort_order) VALUES
('Ошибка оплаты', 'failed', 'Ошибка при обработке платежа', '#DC143C', 4)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
INSERT INTO payment_statuses (name, slug, description, color, sort_order) VALUES
('Возвращен', 'refunded', 'Платеж возвращен покупателю', '#8B0000', 5)
ON CONFLICT (slug) DO UPDATE SET
name = EXCLUDED.name,
description = EXCLUDED.description,
color = EXCLUDED.color,
sort_order = EXCLUDED.sort_order,
updated_at = CURRENT_TIMESTAMP;
-- ============================================================================
-- 9. ОБНОВЛЕНИЕ ПРАВ ДЛЯ СУЩЕСТВУЮЩИХ РОЛЕЙ
-- ============================================================================
-- Этот раздел гарантирует, что сотрудники и менеджеры имеют все необходимые права
-- Можно безопасно выполнять многократно благодаря ON CONFLICT
-- Убедиться, что сотрудники (employee) имеют право на управление корзиной
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'employee'
AND p.slug = 'cart.manage'
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Убедиться, что менеджеры (manager) имеют право на управление корзиной
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'manager'
AND p.slug = 'cart.manage'
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Убедиться, что сотрудники (employee) имеют право на редактирование заказов
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'employee'
AND p.slug = 'orders.edit'
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Убедиться, что менеджеры (manager) имеют право на редактирование заказов
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug = 'manager'
AND p.slug = 'orders.edit'
ON CONFLICT (role_id, permission_id) DO NOTHING;
-- Убедиться, что все роли имеют право на создание заказов
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.slug IN ('customer', 'employee', 'manager', 'owner', 'admin')
AND p.slug = 'orders.create'
ON CONFLICT (role_id, permission_id) DO NOTHING;
+96
View File
@@ -0,0 +1,96 @@
-- Триггеры для базы данных интернет-магазина
-- PostgreSQL
--
-- ВАЖНО: Этот файл должен выполняться ПОСЛЕ schema.sql
-- Сначала создайте структуру базы данных (schema.sql), затем создайте триггеры (triggers.sql)
-- Функции должны быть созданы в schema.sql перед выполнением этого файла
-- Триггеры для автоматического обновления updated_at
DROP TRIGGER IF EXISTS update_resources_updated_at ON resources;
CREATE TRIGGER update_resources_updated_at BEFORE UPDATE ON resources
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_actions_updated_at ON actions;
CREATE TRIGGER update_actions_updated_at BEFORE UPDATE ON actions
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_permissions_updated_at ON permissions;
CREATE TRIGGER update_permissions_updated_at BEFORE UPDATE ON permissions
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_roles_updated_at ON roles;
CREATE TRIGGER update_roles_updated_at BEFORE UPDATE ON roles
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_categories_updated_at ON categories;
CREATE TRIGGER update_categories_updated_at BEFORE UPDATE ON categories
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_order_statuses_updated_at ON order_statuses;
CREATE TRIGGER update_order_statuses_updated_at BEFORE UPDATE ON order_statuses
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_payment_methods_updated_at ON payment_methods;
CREATE TRIGGER update_payment_methods_updated_at BEFORE UPDATE ON payment_methods
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_payment_statuses_updated_at ON payment_statuses;
CREATE TRIGGER update_payment_statuses_updated_at BEFORE UPDATE ON payment_statuses
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_products_updated_at ON products;
CREATE TRIGGER update_products_updated_at BEFORE UPDATE ON products
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_addresses_updated_at ON addresses;
CREATE TRIGGER update_addresses_updated_at BEFORE UPDATE ON addresses
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_orders_updated_at ON orders;
CREATE TRIGGER update_orders_updated_at BEFORE UPDATE ON orders
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_cart_items_updated_at ON cart_items;
CREATE TRIGGER update_cart_items_updated_at BEFORE UPDATE ON cart_items
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_reviews_updated_at ON reviews;
CREATE TRIGGER update_reviews_updated_at BEFORE UPDATE ON reviews
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
DROP TRIGGER IF EXISTS update_payments_updated_at ON payments;
CREATE TRIGGER update_payments_updated_at BEFORE UPDATE ON payments
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Триггеры для обновления рейтинга товара
DROP TRIGGER IF EXISTS update_product_rating_on_review_insert ON reviews;
CREATE TRIGGER update_product_rating_on_review_insert
AFTER INSERT ON reviews
FOR EACH ROW
WHEN (NEW.is_approved = TRUE)
EXECUTE FUNCTION update_product_rating();
DROP TRIGGER IF EXISTS update_product_rating_on_review_update ON reviews;
CREATE TRIGGER update_product_rating_on_review_update
AFTER UPDATE ON reviews
FOR EACH ROW
WHEN (NEW.is_approved != OLD.is_approved OR NEW.rating != OLD.rating)
EXECUTE FUNCTION update_product_rating();
DROP TRIGGER IF EXISTS update_product_rating_on_review_delete ON reviews;
CREATE TRIGGER update_product_rating_on_review_delete
AFTER DELETE ON reviews
FOR EACH ROW
EXECUTE FUNCTION update_product_rating();
-- Триггер для автоматической генерации номера заказа
DROP TRIGGER IF EXISTS generate_order_number_trigger ON orders;
CREATE TRIGGER generate_order_number_trigger
BEFORE INSERT ON orders
FOR EACH ROW
EXECUTE FUNCTION generate_order_number();
+100
View File
@@ -0,0 +1,100 @@
-- Индексы для оптимизации запросов
-- PostgreSQL
--
-- ВАЖНО: Этот файл должен выполняться ПОСЛЕ schema.sql
-- Сначала создайте структуру базы данных (schema.sql), затем создайте индексы (indexes.sql)
-- Индексы для таблицы resources
CREATE INDEX IF NOT EXISTS idx_resources_slug ON resources(slug);
CREATE INDEX IF NOT EXISTS idx_resources_is_active ON resources(is_active);
-- Индексы для таблицы actions
CREATE INDEX IF NOT EXISTS idx_actions_slug ON actions(slug);
CREATE INDEX IF NOT EXISTS idx_actions_is_active ON actions(is_active);
-- Индексы для таблицы permissions
CREATE INDEX IF NOT EXISTS idx_permissions_slug ON permissions(slug);
CREATE INDEX IF NOT EXISTS idx_permissions_resource_id ON permissions(resource_id);
CREATE INDEX IF NOT EXISTS idx_permissions_action_id ON permissions(action_id);
CREATE INDEX IF NOT EXISTS idx_permissions_resource_action ON permissions(resource_id, action_id);
CREATE INDEX IF NOT EXISTS idx_permissions_is_active ON permissions(is_active);
-- Индексы для таблицы roles
CREATE INDEX IF NOT EXISTS idx_roles_slug ON roles(slug);
CREATE INDEX IF NOT EXISTS idx_roles_is_active ON roles(is_active);
-- Индексы для таблицы role_permissions
CREATE INDEX IF NOT EXISTS idx_role_permissions_role_id ON role_permissions(role_id);
CREATE INDEX IF NOT EXISTS idx_role_permissions_permission_id ON role_permissions(permission_id);
-- Индексы для таблицы users
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_users_telegram_id ON users(telegram_id);
CREATE INDEX IF NOT EXISTS idx_users_role_id ON users(role_id);
CREATE INDEX IF NOT EXISTS idx_users_is_active ON users(is_active);
-- Индексы для таблицы categories
CREATE INDEX IF NOT EXISTS idx_categories_parent_id ON categories(parent_id);
CREATE INDEX IF NOT EXISTS idx_categories_slug ON categories(slug);
CREATE INDEX IF NOT EXISTS idx_categories_is_active ON categories(is_active);
-- Индексы для таблицы products
CREATE INDEX IF NOT EXISTS idx_products_category_id ON products(category_id);
CREATE INDEX IF NOT EXISTS idx_products_slug ON products(slug);
CREATE INDEX IF NOT EXISTS idx_products_sku ON products(sku);
CREATE INDEX IF NOT EXISTS idx_products_is_active ON products(is_active);
CREATE INDEX IF NOT EXISTS idx_products_is_featured ON products(is_featured);
CREATE INDEX IF NOT EXISTS idx_products_price ON products(price);
CREATE INDEX IF NOT EXISTS idx_products_discount ON products(discount_percent) WHERE discount_percent > 0;
-- Индексы для таблицы product_images
CREATE INDEX IF NOT EXISTS idx_product_images_product_id ON product_images(product_id);
CREATE INDEX IF NOT EXISTS idx_product_images_is_primary ON product_images(is_primary);
-- Индексы для таблицы addresses
CREATE INDEX IF NOT EXISTS idx_addresses_user_id ON addresses(user_id);
CREATE INDEX IF NOT EXISTS idx_addresses_is_default ON addresses(is_default);
-- Индексы для таблицы order_statuses
CREATE INDEX IF NOT EXISTS idx_order_statuses_slug ON order_statuses(slug);
CREATE INDEX IF NOT EXISTS idx_order_statuses_is_active ON order_statuses(is_active);
-- Индексы для таблицы payment_methods
CREATE INDEX IF NOT EXISTS idx_payment_methods_slug ON payment_methods(slug);
CREATE INDEX IF NOT EXISTS idx_payment_methods_is_active ON payment_methods(is_active);
-- Индексы для таблицы payment_statuses
CREATE INDEX IF NOT EXISTS idx_payment_statuses_slug ON payment_statuses(slug);
CREATE INDEX IF NOT EXISTS idx_payment_statuses_is_active ON payment_statuses(is_active);
-- Индексы для таблицы orders
CREATE INDEX IF NOT EXISTS idx_orders_user_id ON orders(user_id);
CREATE INDEX IF NOT EXISTS idx_orders_order_number ON orders(order_number);
CREATE INDEX IF NOT EXISTS idx_orders_status_id ON orders(status_id);
CREATE INDEX IF NOT EXISTS idx_orders_payment_method_id ON orders(payment_method_id);
CREATE INDEX IF NOT EXISTS idx_orders_payment_status_id ON orders(payment_status_id);
CREATE INDEX IF NOT EXISTS idx_orders_created_at ON orders(created_at);
-- Индексы для таблицы order_items
CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items(order_id);
CREATE INDEX IF NOT EXISTS idx_order_items_product_id ON order_items(product_id);
-- Индексы для таблицы cart_items
CREATE INDEX IF NOT EXISTS idx_cart_items_user_id ON cart_items(user_id);
CREATE INDEX IF NOT EXISTS idx_cart_items_product_id ON cart_items(product_id);
-- Индексы для таблицы reviews
CREATE INDEX IF NOT EXISTS idx_reviews_product_id ON reviews(product_id);
CREATE INDEX IF NOT EXISTS idx_reviews_user_id ON reviews(user_id);
CREATE INDEX IF NOT EXISTS idx_reviews_is_approved ON reviews(is_approved);
CREATE INDEX IF NOT EXISTS idx_reviews_rating ON reviews(rating);
-- Индексы для таблицы payments
CREATE INDEX IF NOT EXISTS idx_payments_order_id ON payments(order_id);
CREATE INDEX IF NOT EXISTS idx_payments_payment_method_id ON payments(payment_method_id);
CREATE INDEX IF NOT EXISTS idx_payments_status_id ON payments(status_id);
CREATE INDEX IF NOT EXISTS idx_payments_transaction_id ON payments(transaction_id);
-- Индексы для таблицы settings
CREATE INDEX IF NOT EXISTS idx_settings_key ON settings(key);
+7
View File
@@ -0,0 +1,7 @@
-- Инициализация настроек системы
-- Выполнить после создания основной схемы
-- Настройка для включения/отключения рейтингов
INSERT INTO settings (key, value, description, type)
VALUES ('ratings_enabled', 'true', 'Включить/выключить функционал рейтингов и отзывов', 'boolean')
ON CONFLICT (key) DO NOTHING;
+757
View File
@@ -0,0 +1,757 @@
# Документация базы данных интернет-магазина
## Описание проекта
База данных для интернет-магазина на PostgreSQL, включающая систему управления пользователями с ролями и правами доступа, каталог товаров, систему заказов, корзину покупок, отзывы и платежи.
## Структура файлов
Проект состоит из четырех SQL-файлов, которые должны выполняться в следующем порядке:
1. **schema.sql** - Создание структуры базы данных (таблицы, функции)
2. **indexes.sql** - Создание индексов для оптимизации запросов
3. **triggers.sql** - Создание триггеров для автоматизации процессов
4. **seed_data.sql** - Заполнение начальными данными
## Порядок установки
```sql
-- 1. Создание структуры
\i schema.sql
-- 2. Создание индексов
\i indexes.sql
-- 3. Создание триггеров
\i triggers.sql
-- 4. Заполнение данными
\i seed_data.sql
```
## Архитектура базы данных
### Система авторизации и прав доступа
База данных использует гибкую систему ролей и прав доступа (RBAC - Role-Based Access Control):
- **Ресурсы (resources)** - объекты системы (товары, заказы, категории и т.д.)
- **Действия (actions)** - операции над ресурсами (просмотр, создание, редактирование, удаление)
- **Права доступа (permissions)** - комбинация ресурса и действия
- **Роли (roles)** - наборы прав доступа
- **Пользователи (users)** - привязаны к ролям
### Основные модули
1. **Управление товарами** - категории, товары, изображения
2. **Управление заказами** - заказы, элементы заказов, статусы
3. **Корзина покупок** - временное хранение товаров перед оформлением заказа
4. **Отзывы и рейтинги** - система отзывов с автоматическим расчетом рейтинга
5. **Платежи** - управление платежами по заказам
6. **Адреса доставки** - адреса пользователей
---
## Описание таблиц
### 1. Система авторизации
#### `resources` - Ресурсы системы
Хранит список ресурсов (объектов), к которым можно предоставить доступ.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(100) | Название ресурса |
| slug | VARCHAR(50) | Уникальный идентификатор (URL-friendly) |
| description | TEXT | Описание ресурса |
| is_active | BOOLEAN | Активен ли ресурс |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Примеры ресурсов:** products, orders, categories, reviews, cart, users
#### `actions` - Действия над ресурсами
Хранит список действий, которые можно выполнять над ресурсами.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(100) | Название действия |
| slug | VARCHAR(50) | Уникальный идентификатор |
| description | TEXT | Описание действия |
| is_active | BOOLEAN | Активно ли действие |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Примеры действий:** view, create, edit, delete, moderate, manage
#### `permissions` - Права доступа
Комбинация ресурса и действия. Определяет конкретное право доступа.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(100) | Название права |
| slug | VARCHAR(100) | Уникальный идентификатор (формат: resource.action) |
| resource_id | INTEGER | Ссылка на ресурс |
| action_id | INTEGER | Ссылка на действие |
| description | TEXT | Описание права |
| is_active | BOOLEAN | Активно ли право |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Ограничения:** UNIQUE(resource_id, action_id) - одна комбинация ресурса и действия
#### `roles` - Роли пользователей
Роли, которые могут быть назначены пользователям.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(100) | Название роли |
| slug | VARCHAR(50) | Уникальный идентификатор |
| description | TEXT | Описание роли |
| is_active | BOOLEAN | Активна ли роль |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Предустановленные роли:**
- `admin` - Администратор (полные права)
- `owner` - Владелец магазина
- `employee` - Сотрудник
- `manager` - Менеджер
- `customer` - Клиент (покупатель)
#### `role_permissions` - Связь ролей и прав
Связывает роли с правами доступа (многие-ко-многим).
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| role_id | INTEGER | Ссылка на роль |
| permission_id | INTEGER | Ссылка на право доступа |
| created_at | TIMESTAMP | Дата создания |
**Ограничения:** UNIQUE(role_id, permission_id)
#### `users` - Пользователи системы
Пользователи системы (покупатели и администраторы).
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| email | VARCHAR(255) | Email (уникальный) |
| password_hash | VARCHAR(255) | Хеш пароля |
| first_name | VARCHAR(100) | Имя |
| last_name | VARCHAR(100) | Фамилия |
| phone | VARCHAR(20) | Телефон |
| telegram_id | BIGINT | ID в Telegram (уникальный) |
| role_id | INTEGER | Ссылка на роль |
| is_active | BOOLEAN | Активен ли пользователь |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Особенности:**
- Поддержка авторизации через email или Telegram
- Связь с ролью через `role_id`
---
### 2. Каталог товаров
#### `categories` - Категории товаров
Иерархическая структура категорий товаров.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(255) | Название категории |
| slug | VARCHAR(255) | Уникальный идентификатор (URL-friendly) |
| description | TEXT | Описание категории |
| parent_id | INTEGER | Ссылка на родительскую категорию (для иерархии) |
| image_url | VARCHAR(500) | URL изображения категории |
| is_active | BOOLEAN | Активна ли категория |
| sort_order | INTEGER | Порядок сортировки |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Особенности:**
- Поддержка вложенных категорий через `parent_id`
- Возможность сортировки через `sort_order`
#### `products` - Товары
Основная информация о товарах магазина.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(255) | Название товара |
| slug | VARCHAR(255) | Уникальный идентификатор (URL-friendly) |
| description | TEXT | Полное описание |
| short_description | TEXT | Краткое описание |
| sku | VARCHAR(100) | Артикул товара (уникальный) |
| price | DECIMAL(10, 2) | Цена продажи |
| compare_at_price | DECIMAL(10, 2) | Старая цена (для отображения скидки) |
| cost_price | DECIMAL(10, 2) | Себестоимость |
| category_id | INTEGER | Ссылка на категорию |
| stock_quantity | INTEGER | Количество на складе |
| weight | DECIMAL(8, 2) | Вес товара |
| dimensions | VARCHAR(100) | Размеры товара |
| is_active | BOOLEAN | Активен ли товар |
| is_featured | BOOLEAN | Рекомендуемый товар |
| rating | DECIMAL(3, 2) | Средний рейтинг (0-5) |
| review_count | INTEGER | Количество одобренных отзывов |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Ограничения:**
- `price >= 0`
- `compare_at_price >= 0`
- `cost_price >= 0`
- `stock_quantity >= 0`
- `rating >= 0 AND rating <= 5`
**Особенности:**
- Рейтинг и количество отзывов обновляются автоматически через триггеры
#### `product_images` - Изображения товаров
Изображения товаров с поддержкой множественных изображений.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| product_id | INTEGER | Ссылка на товар |
| image_url | VARCHAR(500) | URL изображения |
| alt_text | VARCHAR(255) | Альтернативный текст для изображения |
| is_primary | BOOLEAN | Основное изображение |
| sort_order | INTEGER | Порядок сортировки |
| created_at | TIMESTAMP | Дата создания |
**Особенности:**
- Поддержка нескольких изображений на товар
- Одно изображение может быть помечено как основное (`is_primary = TRUE`)
---
### 3. Система заказов
#### `order_statuses` - Статусы заказов
Предустановленные статусы заказов.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(100) | Название статуса |
| slug | VARCHAR(50) | Уникальный идентификатор |
| description | TEXT | Описание статуса |
| color | VARCHAR(20) | Цвет для отображения в интерфейсе |
| sort_order | INTEGER | Порядок сортировки |
| is_active | BOOLEAN | Активен ли статус |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Предустановленные статусы:**
- `pending` - Ожидает обработки
- `processing` - В обработке
- `shipped` - Отправлен
- `delivered` - Доставлен
- `cancelled` - Отменен
- `refunded` - Возвращен
#### `payment_methods` - Способы оплаты
Справочник способов оплаты.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(100) | Название способа оплаты |
| slug | VARCHAR(50) | Уникальный идентификатор |
| description | TEXT | Описание способа оплаты |
| is_active | BOOLEAN | Активен ли способ оплаты |
| sort_order | INTEGER | Порядок сортировки |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Предустановленные способы оплаты:**
- `card` - Банковская карта
- `e-wallet` - Электронный кошелек
- `cash_on_delivery` - Наличные при получении
- `bank_transfer` - Банковский перевод
- `crypto` - Криптовалюта
#### `payment_statuses` - Статусы платежей
Справочник статусов платежей.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| name | VARCHAR(100) | Название статуса |
| slug | VARCHAR(50) | Уникальный идентификатор |
| description | TEXT | Описание статуса |
| color | VARCHAR(20) | Цвет для отображения в интерфейсе |
| sort_order | INTEGER | Порядок сортировки |
| is_active | BOOLEAN | Активен ли статус |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Предустановленные статусы:**
- `pending` - Ожидает оплаты
- `paid` - Оплачен
- `completed` - Завершен
- `failed` - Ошибка оплаты
- `refunded` - Возвращен
#### `orders` - Заказы
Основная информация о заказах покупателей.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| user_id | INTEGER | Ссылка на пользователя |
| order_number | VARCHAR(50) | Уникальный номер заказа |
| status_id | INTEGER | Ссылка на статус заказа |
| subtotal | DECIMAL(10, 2) | Сумма товаров |
| tax_amount | DECIMAL(10, 2) | Сумма налогов |
| shipping_cost | DECIMAL(10, 2) | Стоимость доставки |
| discount_amount | DECIMAL(10, 2) | Сумма скидки |
| total_amount | DECIMAL(10, 2) | Итоговая сумма |
| shipping_address_id | INTEGER | Ссылка на адрес доставки |
| billing_address_id | INTEGER | Ссылка на адрес для выставления счета |
| payment_method_id | INTEGER | Ссылка на способ оплаты |
| payment_status_id | INTEGER | Ссылка на статус платежа |
| notes | TEXT | Примечания к заказу |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Ограничения:**
- `subtotal >= 0`
- `tax_amount >= 0`
- `shipping_cost >= 0`
- `discount_amount >= 0`
- `total_amount >= 0`
**Особенности:**
- Номер заказа генерируется автоматически через триггер в формате: `ORD-YYYYMMDD-XXXXXX`
#### `order_items` - Элементы заказа
Товары, входящие в заказ.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| order_id | INTEGER | Ссылка на заказ |
| product_id | INTEGER | Ссылка на товар |
| product_name | VARCHAR(255) | Название товара на момент заказа |
| product_sku | VARCHAR(100) | Артикул товара на момент заказа |
| quantity | INTEGER | Количество |
| price | DECIMAL(10, 2) | Цена за единицу на момент заказа |
| subtotal | DECIMAL(10, 2) | Сумма по позиции |
| created_at | TIMESTAMP | Дата создания |
**Ограничения:**
- `quantity > 0`
- `price >= 0`
- `subtotal >= 0`
**Особенности:**
- Сохраняет название, артикул и цену товара на момент заказа (на случай изменения товара в будущем)
---
### 4. Корзина покупок
#### `cart_items` - Корзина покупок
Временное хранение товаров перед оформлением заказа.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| user_id | INTEGER | Ссылка на пользователя |
| product_id | INTEGER | Ссылка на товар |
| quantity | INTEGER | Количество |
| created_at | TIMESTAMP | Дата добавления |
| updated_at | TIMESTAMP | Дата обновления |
**Ограничения:**
- `quantity > 0`
- UNIQUE(user_id, product_id) - один товар может быть в корзине только один раз
---
### 5. Отзывы и рейтинги
#### `reviews` - Отзывы на товары
Отзывы покупателей на товары.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| product_id | INTEGER | Ссылка на товар |
| user_id | INTEGER | Ссылка на пользователя |
| rating | INTEGER | Оценка (1-5) |
| title | VARCHAR(255) | Заголовок отзыва |
| comment | TEXT | Текст отзыва |
| is_approved | BOOLEAN | Одобрен ли отзыв модератором |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Ограничения:**
- `rating >= 1 AND rating <= 5`
- UNIQUE(product_id, user_id) - один пользователь может оставить только один отзыв на товар
**Особенности:**
- Отзывы требуют модерации (`is_approved = FALSE` по умолчанию)
- Рейтинг товара обновляется автоматически при одобрении/изменении отзывов
---
### 6. Платежи
#### `payments` - Платежи по заказам
Информация о платежах по заказам.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| order_id | INTEGER | Ссылка на заказ |
| payment_method_id | INTEGER | Ссылка на способ оплаты |
| transaction_id | VARCHAR(255) | ID транзакции в платежной системе (уникальный) |
| amount | DECIMAL(10, 2) | Сумма платежа |
| status_id | INTEGER | Ссылка на статус платежа |
| payment_date | TIMESTAMP | Дата платежа |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
**Ограничения:**
- `amount >= 0`
---
### 7. Адреса доставки
#### `addresses` - Адреса пользователей
Адреса доставки пользователей.
| Поле | Тип | Описание |
|------|-----|----------|
| id | SERIAL | Первичный ключ |
| user_id | INTEGER | Ссылка на пользователя |
| first_name | VARCHAR(100) | Имя получателя |
| last_name | VARCHAR(100) | Фамилия получателя |
| phone | VARCHAR(20) | Телефон |
| country | VARCHAR(100) | Страна |
| city | VARCHAR(100) | Город |
| street | VARCHAR(255) | Улица |
| building | VARCHAR(50) | Номер дома |
| apartment | VARCHAR(50) | Номер квартиры |
| postal_code | VARCHAR(20) | Почтовый индекс |
| is_default | BOOLEAN | Адрес по умолчанию |
| created_at | TIMESTAMP | Дата создания |
| updated_at | TIMESTAMP | Дата обновления |
---
## Функции базы данных
### `update_updated_at_column()`
Автоматически обновляет поле `updated_at` при изменении записи.
**Использование:** Применяется через триггеры на всех таблицах с полем `updated_at`.
### `update_product_rating()`
Автоматически пересчитывает рейтинг и количество отзывов товара при изменении отзывов.
**Логика:**
- Рассчитывает средний рейтинг из одобренных отзывов
- Подсчитывает количество одобренных отзывов
- Обновляет поля `rating` и `review_count` в таблице `products`
**Триггеры:**
- Срабатывает при INSERT, UPDATE и DELETE в таблице `reviews`
### `generate_order_number()`
Автоматически генерирует уникальный номер заказа.
**Формат:** `ORD-YYYYMMDD-XXXXXX`
**Пример:** `ORD-20240115-000001`
**Триггер:** Срабатывает перед INSERT в таблицу `orders`, если `order_number` не указан.
### `get_order_status_id(status_slug VARCHAR)`
Вспомогательная функция для получения ID статуса заказа по его slug.
**Возвращает:** INTEGER (ID статуса) или NULL
### `get_payment_status_id(status_slug VARCHAR)`
Вспомогательная функция для получения ID статуса платежа по его slug.
**Возвращает:** INTEGER (ID статуса) или NULL
### `get_payment_method_id(method_slug VARCHAR)`
Вспомогательная функция для получения ID способа оплаты по его slug.
**Возвращает:** INTEGER (ID способа оплаты) или NULL
---
## Триггеры
### Триггеры обновления `updated_at`
Автоматически обновляют поле `updated_at` при изменении записи в следующих таблицах:
- `resources`
- `actions`
- `permissions`
- `roles`
- `users`
- `categories`
- `order_statuses`
- `products`
- `addresses`
- `orders`
- `cart_items`
- `reviews`
- `payments`
### Триггеры обновления рейтинга товара
- **`update_product_rating_on_review_insert`** - после добавления отзыва (если он одобрен)
- **`update_product_rating_on_review_update`** - после изменения отзыва (если изменился рейтинг или статус одобрения)
- **`update_product_rating_on_review_delete`** - после удаления отзыва
### Триггер генерации номера заказа
- **`generate_order_number_trigger`** - перед вставкой нового заказа генерирует номер, если он не указан
---
## Индексы
Индексы созданы для оптимизации наиболее частых запросов:
### Индексы по slug
- `resources.slug`
- `actions.slug`
- `permissions.slug`
- `roles.slug`
- `categories.slug`
- `products.slug`
- `order_statuses.slug`
### Индексы по внешним ключам
- `permissions.resource_id`, `permissions.action_id`
- `role_permissions.role_id`, `role_permissions.permission_id`
- `users.role_id`
- `categories.parent_id`
- `products.category_id`
- `product_images.product_id`
- `addresses.user_id`
- `orders.user_id`, `orders.status_id`, `orders.payment_method_id`, `orders.payment_status_id`
- `order_items.order_id`, `order_items.product_id`
- `cart_items.user_id`, `cart_items.product_id`
- `reviews.product_id`, `reviews.user_id`
- `payments.order_id`, `payments.payment_method_id`, `payments.status_id`
### Индексы по статусам и флагам
- `resources.is_active`
- `actions.is_active`
- `permissions.is_active`
- `roles.is_active`
- `users.is_active`
- `categories.is_active`
- `products.is_active`, `products.is_featured`
- `order_statuses.is_active`
- `payment_methods.is_active`
- `payment_statuses.is_active`
- `reviews.is_approved`
### Индексы по уникальным полям
- `users.email`
- `users.telegram_id`
- `products.sku`
- `orders.order_number`
- `payments.transaction_id`
### Индексы для сортировки и фильтрации
- `products.price`
- `orders.created_at`
- `orders.payment_status_id`
- `reviews.rating`
---
## Начальные данные (seed_data.sql)
### Ресурсы
- products, orders, categories, reviews, cart, users
- payments, addresses, roles, permissions
- * (все ресурсы для администратора)
### Действия
- view, create, edit, delete
- moderate, manage, view_own
- * (все действия для администратора)
### Права доступа
Создаются комбинации ресурсов и действий, например:
- `products.view`, `products.create`, `products.edit`, `products.delete`
- `orders.view`, `orders.create`, `orders.edit`, `orders.view_own`
- `categories.view`, `categories.create`, `categories.edit`, `categories.delete`
- `reviews.view`, `reviews.create`, `reviews.moderate`, `reviews.delete`
- `cart.manage`
- `users.view`, `users.edit`, `users.delete`
- `admin.all` (полные права)
### Роли и их права
#### Администратор (admin)
- Все права доступа (`admin.all`)
#### Владелец (owner)
- Все права, кроме управления ролями и правами доступа
#### Сотрудник (employee)
- Просмотр, создание, редактирование, удаление товаров
- Просмотр и редактирование заказов
- Полное управление категориями
- Просмотр, модерация и удаление отзывов
- Просмотр пользователей
#### Менеджер (manager)
- Те же права, что и у сотрудника
#### Клиент (customer)
- Просмотр товаров и категорий
- Создание заказов и просмотр своих заказов
- Управление корзиной
- Создание отзывов
### Статусы заказов
1. **pending** - Ожидает обработки (#FFA500)
2. **processing** - В обработке (#4169E1)
3. **shipped** - Отправлен (#32CD32)
4. **delivered** - Доставлен (#228B22)
5. **cancelled** - Отменен (#DC143C)
6. **refunded** - Возвращен (#8B0000)
### Способы оплаты
1. **card** - Банковская карта
2. **e-wallet** - Электронный кошелек
3. **cash_on_delivery** - Наличные при получении
4. **bank_transfer** - Банковский перевод
5. **crypto** - Криптовалюта
### Статусы платежей
1. **pending** - Ожидает оплаты (#FFA500)
2. **paid** - Оплачен (#32CD32)
3. **completed** - Завершен (#228B22)
4. **failed** - Ошибка оплаты (#DC143C)
5. **refunded** - Возвращен (#8B0000)
---
## Диаграмма связей
```
users ──┬──> roles ──> role_permissions ──> permissions ──┬──> resources
│ └──> actions
│
├──> addresses
├──> cart_items ──> products ──> categories
├──> orders ──> order_statuses
│ └──> order_items ──> products
│ └──> payments
└──> reviews ──> products
```
---
## Примеры использования
### Получение всех прав пользователя
```sql
SELECT p.slug, p.name
FROM users u
JOIN roles r ON u.role_id = r.id
JOIN role_permissions rp ON r.id = rp.role_id
JOIN permissions p ON rp.permission_id = p.id
WHERE u.id = 1 AND p.is_active = TRUE;
```
### Получение товаров с рейтингом
```sql
SELECT p.*, c.name as category_name
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.is_active = TRUE
ORDER BY p.rating DESC, p.review_count DESC;
```
### Получение заказов пользователя
```sql
SELECT o.*,
os.name as status_name, os.color as status_color,
pm.name as payment_method_name,
ps.name as payment_status_name, ps.color as payment_status_color
FROM orders o
JOIN order_statuses os ON o.status_id = os.id
LEFT JOIN payment_methods pm ON o.payment_method_id = pm.id
LEFT JOIN payment_statuses ps ON o.payment_status_id = ps.id
WHERE o.user_id = 1
ORDER BY o.created_at DESC;
```
### Получение элементов заказа
```sql
SELECT oi.*, p.name as current_product_name, p.price as current_price
FROM order_items oi
LEFT JOIN products p ON oi.product_id = p.id
WHERE oi.order_id = 1;
```
### Получение корзины пользователя
```sql
SELECT ci.*, p.name, p.price, p.stock_quantity, pi.image_url
FROM cart_items ci
JOIN products p ON ci.product_id = p.id
LEFT JOIN product_images pi ON p.id = pi.product_id AND pi.is_primary = TRUE
WHERE ci.user_id = 1;
```
---
## Рекомендации по использованию
1. **Безопасность:**
- Всегда проверяйте права доступа перед выполнением операций
- Используйте параметризованные запросы для предотвращения SQL-инъекций
- Храните пароли только в виде хешей
2. **Производительность:**
- Используйте индексы для часто запрашиваемых полей
- При больших объемах данных рассмотрите партиционирование таблиц `orders` и `order_items`
- Регулярно анализируйте и оптимизируйте медленные запросы
3. **Целостность данных:**
- Используйте транзакции при создании заказов (заказ + элементы заказа)
- Проверяйте наличие товара на складе перед добавлением в корзину
- Обновляйте количество товара на складе при создании заказа
4. **Масштабирование:**
- Рассмотрите возможность добавления таблицы для истории изменений заказов
- Для больших каталогов товаров используйте полнотекстовый поиск (PostgreSQL tsvector)
- Кэшируйте часто запрашиваемые данные (категории, популярные товары)
---
## Версия
**Версия базы данных:** 1.0
**СУБД:** PostgreSQL
**Дата создания:** 2024
---
## Лицензия
Данная схема базы данных предоставляется "как есть" для использования в проектах интернет-магазинов.