- Full-stack Node.js/Express application with PostgreSQL - Modern ES modules architecture - AdminJS admin panel with Sequelize ORM - Tourism routes, guides, articles, bookings management - Responsive Bootstrap 5 frontend - Docker containerization with docker-compose - Complete database schema with migrations - Authentication system for admin panel - Dynamic placeholder images for tour categories
155 lines
6.3 KiB
PL/PgSQL
155 lines
6.3 KiB
PL/PgSQL
-- Korea Tourism Agency Database Schema
|
||
-- Создание основных таблиц для туристического агентства
|
||
|
||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||
|
||
-- Таблица администраторов
|
||
CREATE TABLE IF NOT EXISTS admins (
|
||
id SERIAL PRIMARY KEY,
|
||
username VARCHAR(50) UNIQUE NOT NULL,
|
||
password VARCHAR(255) NOT NULL,
|
||
name VARCHAR(100) NOT NULL,
|
||
email VARCHAR(100) UNIQUE NOT NULL,
|
||
role VARCHAR(20) DEFAULT 'admin',
|
||
is_active BOOLEAN DEFAULT true,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Таблица гидов
|
||
CREATE TABLE IF NOT EXISTS guides (
|
||
id SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
email VARCHAR(100) UNIQUE,
|
||
phone VARCHAR(20),
|
||
bio TEXT,
|
||
specialization VARCHAR(20) NOT NULL CHECK (specialization IN ('city', 'mountain', 'fishing')),
|
||
languages TEXT[], -- Массив языков
|
||
experience INTEGER DEFAULT 0, -- Опыт в годах
|
||
image_url VARCHAR(255),
|
||
hourly_rate DECIMAL(8,2),
|
||
is_active BOOLEAN DEFAULT true,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Таблица маршрутов/туров
|
||
CREATE TABLE IF NOT EXISTS routes (
|
||
id SERIAL PRIMARY KEY,
|
||
title VARCHAR(200) NOT NULL,
|
||
description TEXT NOT NULL,
|
||
content TEXT, -- Полное описание
|
||
type VARCHAR(20) NOT NULL CHECK (type IN ('city', 'mountain', 'fishing')),
|
||
price DECIMAL(10,2) NOT NULL,
|
||
duration INTEGER NOT NULL, -- Длительность в часах
|
||
difficulty_level VARCHAR(10) DEFAULT 'easy' CHECK (difficulty_level IN ('easy', 'moderate', 'hard')),
|
||
max_group_size INTEGER DEFAULT 10,
|
||
included_services TEXT[],
|
||
meeting_point TEXT,
|
||
image_url VARCHAR(255),
|
||
guide_id INTEGER REFERENCES guides(id),
|
||
is_featured BOOLEAN DEFAULT false,
|
||
is_active BOOLEAN DEFAULT true,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Таблица статей
|
||
CREATE TABLE IF NOT EXISTS articles (
|
||
id SERIAL PRIMARY KEY,
|
||
title VARCHAR(200) NOT NULL,
|
||
excerpt TEXT,
|
||
content TEXT NOT NULL,
|
||
category VARCHAR(50) NOT NULL CHECK (category IN ('travel-tips', 'culture', 'food', 'nature', 'history')),
|
||
image_url VARCHAR(255),
|
||
author_id INTEGER REFERENCES admins(id),
|
||
views INTEGER DEFAULT 0,
|
||
is_published BOOLEAN DEFAULT false,
|
||
meta_description TEXT,
|
||
meta_keywords TEXT,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Таблица бронирований
|
||
CREATE TABLE IF NOT EXISTS bookings (
|
||
id SERIAL PRIMARY KEY,
|
||
route_id INTEGER REFERENCES routes(id) NOT NULL,
|
||
guide_id INTEGER REFERENCES guides(id),
|
||
customer_name VARCHAR(100) NOT NULL,
|
||
customer_email VARCHAR(100) NOT NULL,
|
||
customer_phone VARCHAR(20),
|
||
preferred_date DATE,
|
||
group_size INTEGER DEFAULT 1,
|
||
total_price DECIMAL(10,2),
|
||
special_requirements TEXT,
|
||
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'cancelled', 'completed')),
|
||
notes TEXT,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Таблица отзывов
|
||
CREATE TABLE IF NOT EXISTS reviews (
|
||
id SERIAL PRIMARY KEY,
|
||
route_id INTEGER REFERENCES routes(id),
|
||
guide_id INTEGER REFERENCES guides(id),
|
||
booking_id INTEGER REFERENCES bookings(id),
|
||
customer_name VARCHAR(100) NOT NULL,
|
||
customer_email VARCHAR(100),
|
||
rating INTEGER CHECK (rating >= 1 AND rating <= 5) NOT NULL,
|
||
comment TEXT,
|
||
is_approved BOOLEAN DEFAULT false,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Таблица сообщений с формы контактов
|
||
CREATE TABLE IF NOT EXISTS contact_messages (
|
||
id SERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
email VARCHAR(100) NOT NULL,
|
||
phone VARCHAR(20),
|
||
subject VARCHAR(200),
|
||
message TEXT NOT NULL,
|
||
status VARCHAR(20) DEFAULT 'unread' CHECK (status IN ('unread', 'read', 'replied')),
|
||
admin_notes TEXT,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Таблица настроек сайта
|
||
CREATE TABLE IF NOT EXISTS site_settings (
|
||
id SERIAL PRIMARY KEY,
|
||
setting_key VARCHAR(100) UNIQUE NOT NULL,
|
||
setting_value TEXT,
|
||
setting_type VARCHAR(20) DEFAULT 'text' CHECK (setting_type IN ('text', 'number', 'boolean', 'json')),
|
||
description TEXT,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
-- Создание индексов для производительности
|
||
CREATE INDEX IF NOT EXISTS idx_routes_type ON routes(type);
|
||
CREATE INDEX IF NOT EXISTS idx_routes_active ON routes(is_active);
|
||
CREATE INDEX IF NOT EXISTS idx_routes_featured ON routes(is_featured);
|
||
CREATE INDEX IF NOT EXISTS idx_guides_specialization ON guides(specialization);
|
||
CREATE INDEX IF NOT EXISTS idx_guides_active ON guides(is_active);
|
||
CREATE INDEX IF NOT EXISTS idx_articles_category ON articles(category);
|
||
CREATE INDEX IF NOT EXISTS idx_articles_published ON articles(is_published);
|
||
CREATE INDEX IF NOT EXISTS idx_bookings_status ON bookings(status);
|
||
CREATE INDEX IF NOT EXISTS idx_bookings_date ON bookings(preferred_date);
|
||
CREATE INDEX IF NOT EXISTS idx_reviews_rating ON reviews(rating);
|
||
|
||
-- Создание триггеров для автоматического обновления 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 TRIGGER update_admins_updated_at BEFORE UPDATE ON admins FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
CREATE TRIGGER update_guides_updated_at BEFORE UPDATE ON guides FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
CREATE TRIGGER update_routes_updated_at BEFORE UPDATE ON routes FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
CREATE TRIGGER update_articles_updated_at BEFORE UPDATE ON articles FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
CREATE TRIGGER update_bookings_updated_at BEFORE UPDATE ON bookings FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
CREATE TRIGGER update_site_settings_updated_at BEFORE UPDATE ON site_settings FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); |