Files
tourrism_site/database/run-migration.js
Andrey K. Choi a461fea9d9 Компактные hero секции и улучшенная инициализация БД
🎨 UI улучшения:
- Уменьшена высота синих панелей с 100vh до 70vh на главной
- Добавлен класс .compact (25vh) для всех остальных страниц
- Улучшена адаптивность для мобильных устройств
- Обновлены все шаблоны с hero секциями

🚀 Инфраструктура:
- Автоматическая инициализация базы данных при деплое
- Улучшены мокапные данные (больше отзывов, бронирований, сообщений)
- Добавлены настройки сайта в базу данных
- Создан скрипт автоматического деплоя deploy.sh

📦 Система сборки:
- Обновлен .gitignore с полным покрытием файлов
- Добавлена папка для загрузок с .gitkeep
- Улучшен README с инструкциями по запуску
- ES модули для инициализации базы данных

🐛 Исправления:
- Совместимость с ES модулями в Node.js
- Правильная обработка ошибок инициализации БД
- Корректные SQL запросы для PostgreSQL
2025-11-29 18:47:42 +09:00

106 lines
4.0 KiB
JavaScript

import pool from '../src/config/database.js';
const migrateRatingSystem = async () => {
try {
console.log('🔄 Выполняю миграцию для системы рейтингов...');
// Создание таблицы ratings
await pool.query(`
CREATE TABLE IF NOT EXISTS ratings (
id SERIAL PRIMARY KEY,
user_ip VARCHAR(45) NOT NULL,
target_id INTEGER NOT NULL,
target_type VARCHAR(20) NOT NULL CHECK (target_type IN ('route', 'guide', 'article')),
rating INTEGER NOT NULL CHECK (rating IN (1, -1)),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_ip, target_id, target_type)
);
`);
// Создание таблицы guide_schedules
await pool.query(`
CREATE TABLE IF NOT EXISTS guide_schedules (
id SERIAL PRIMARY KEY,
guide_id INTEGER NOT NULL REFERENCES guides(id) ON DELETE CASCADE,
monday BOOLEAN DEFAULT true,
tuesday BOOLEAN DEFAULT true,
wednesday BOOLEAN DEFAULT true,
thursday BOOLEAN DEFAULT true,
friday BOOLEAN DEFAULT true,
saturday BOOLEAN DEFAULT false,
sunday BOOLEAN DEFAULT false,
start_time TIME DEFAULT '09:00',
end_time TIME DEFAULT '18:00',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(guide_id)
);
`);
// Создание таблицы holidays
await pool.query(`
CREATE TABLE IF NOT EXISTS holidays (
id SERIAL PRIMARY KEY,
date DATE NOT NULL,
title VARCHAR(255) NOT NULL,
type VARCHAR(20) NOT NULL CHECK (type IN ('public', 'guide_personal')),
guide_id INTEGER REFERENCES guides(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(date, guide_id)
);
`);
// Добавление колонок в bookings (если не существуют)
try {
await pool.query('ALTER TABLE bookings ADD COLUMN guide_id INTEGER REFERENCES guides(id);');
} catch (e) {
console.log('Колонка guide_id уже существует');
}
try {
await pool.query('ALTER TABLE bookings ADD COLUMN route_id INTEGER REFERENCES routes(id);');
} catch (e) {
console.log('Колонка route_id уже существует');
}
// Создание индексов
await pool.query('CREATE INDEX IF NOT EXISTS idx_ratings_target ON ratings(target_type, target_id);');
await pool.query('CREATE INDEX IF NOT EXISTS idx_ratings_user_ip ON ratings(user_ip);');
await pool.query('CREATE INDEX IF NOT EXISTS idx_bookings_date ON bookings(preferred_date);');
await pool.query('CREATE INDEX IF NOT EXISTS idx_bookings_guide ON bookings(guide_id);');
await pool.query('CREATE INDEX IF NOT EXISTS idx_holidays_date ON holidays(date);');
// Функция для подсчета рейтинга
await pool.query(`
CREATE OR REPLACE FUNCTION calculate_rating(target_type_param VARCHAR, target_id_param INTEGER)
RETURNS TABLE(
likes_count BIGINT,
dislikes_count BIGINT,
total_votes BIGINT,
rating_percentage NUMERIC(5,2)
) AS $$
BEGIN
RETURN QUERY
SELECT
COUNT(CASE WHEN rating = 1 THEN 1 END) as likes_count,
COUNT(CASE WHEN rating = -1 THEN 1 END) as dislikes_count,
COUNT(*) as total_votes,
CASE
WHEN COUNT(*) = 0 THEN 0
ELSE ROUND((COUNT(CASE WHEN rating = 1 THEN 1 END)::NUMERIC / COUNT(*)::NUMERIC) * 100, 2)
END as rating_percentage
FROM ratings
WHERE target_type = target_type_param AND target_id = target_id_param;
END;
$$ LANGUAGE plpgsql;
`);
console.log('✅ Миграция выполнена успешно!');
process.exit(0);
} catch (error) {
console.error('❌ Ошибка миграции:', error);
process.exit(1);
}
};
migrateRatingSystem();