import express from 'express'; import db from '../config/database.js'; const router = express.Router(); // Получение расписания гидов за месяц router.get('/', async (req, res) => { try { const { year, month, guide_id } = req.query; let query = ` SELECT gwd.*, g.name as guide_name, g.specialization FROM guide_working_days gwd LEFT JOIN guides g ON gwd.guide_id = g.id WHERE 1=1 `; const params = []; if (year && month) { query += ` AND EXTRACT(YEAR FROM gwd.work_date) = $${params.length + 1} AND EXTRACT(MONTH FROM gwd.work_date) = $${params.length + 2}`; params.push(year, month); } if (guide_id) { query += ` AND gwd.guide_id = $${params.length + 1}`; params.push(guide_id); } query += ` ORDER BY gwd.work_date, g.name`; const result = await db.query(query, params); res.json({ success: true, data: result.rows }); } catch (error) { console.error('Ошибка получения расписания:', error); res.status(500).json({ success: false, error: 'Ошибка получения расписания' }); } }); // Массовое создание/обновление расписания гидов за месяц router.put('/', async (req, res) => { try { const { year, month } = req.query; const { schedules } = req.body; if (!year || !month || !schedules || !Array.isArray(schedules)) { return res.status(400).json({ success: false, error: 'Требуются параметры year, month и массив schedules' }); } await db.query('BEGIN'); // Удаляем существующее расписание за этот месяц await db.query(` DELETE FROM guide_working_days WHERE EXTRACT(YEAR FROM work_date) = $1 AND EXTRACT(MONTH FROM work_date) = $2 `, [year, month]); // Добавляем новое расписание if (schedules.length > 0) { const values = schedules.map((schedule, index) => { const baseIndex = index * 2; return `($${baseIndex + 1}, $${baseIndex + 2})`; }).join(', '); const params = schedules.flatMap(schedule => [ schedule.guide_id, schedule.work_date ]); await db.query(` INSERT INTO guide_schedules (guide_id, work_date) VALUES ${values} `, params); } await db.query('COMMIT'); res.json({ success: true, message: `Расписание за ${year}-${month.toString().padStart(2, '0')} обновлено`, count: schedules.length }); } catch (error) { await db.query('ROLLBACK'); console.error('Ошибка обновления расписания:', error); res.status(500).json({ success: false, error: 'Ошибка обновления расписания' }); } }); // Массовое добавление расписания (для копирования) router.post('/batch', async (req, res) => { try { const { schedules } = req.body; if (!schedules || !Array.isArray(schedules)) { return res.status(400).json({ success: false, error: 'Требуется массив schedules' }); } if (schedules.length === 0) { return res.json({ success: true, message: 'Нет расписания для добавления' }); } await db.query('BEGIN'); // Проверяем существующие записи и добавляем только новые const existingQuery = ` SELECT guide_id, work_date FROM guide_working_days WHERE (guide_id, work_date) IN (${schedules.map((_, index) => { const baseIndex = index * 2; return `($${baseIndex + 1}, $${baseIndex + 2})`; }).join(', ')}) `; const existingParams = schedules.flatMap(schedule => [ schedule.guide_id, schedule.work_date ]); const existingResult = await db.query(existingQuery, existingParams); const existingSet = new Set( existingResult.rows.map(row => `${row.guide_id}-${row.work_date}`) ); // Фильтруем новые записи const newSchedules = schedules.filter(schedule => { const key = `${schedule.guide_id}-${schedule.work_date}`; return !existingSet.has(key); }); if (newSchedules.length > 0) { const values = newSchedules.map((schedule, index) => { const baseIndex = index * 2; return `($${baseIndex + 1}, $${baseIndex + 2})`; }).join(', '); const params = newSchedules.flatMap(schedule => [ schedule.guide_id, schedule.work_date ]); await db.query(` INSERT INTO guide_schedules (guide_id, work_date) VALUES ${values} `, params); } await db.query('COMMIT'); res.json({ success: true, message: 'Расписание добавлено', added: newSchedules.length, skipped: schedules.length - newSchedules.length }); } catch (error) { await db.query('ROLLBACK'); console.error('Ошибка массового добавления расписания:', error); res.status(500).json({ success: false, error: 'Ошибка добавления расписания' }); } }); // Добавление одного рабочего дня router.post('/', async (req, res) => { try { const { guide_id, work_date } = req.body; if (!guide_id || !work_date) { return res.status(400).json({ success: false, error: 'Требуются параметры guide_id и work_date' }); } // Проверяем, что запись не существует const existingResult = await db.query( 'SELECT id FROM guide_working_days WHERE guide_id = $1 AND work_date = $2', [guide_id, work_date] ); if (existingResult.rows.length > 0) { return res.json({ success: true, message: 'Рабочий день уже существует' }); } const result = await db.query(` INSERT INTO guide_working_days (guide_id, work_date) VALUES ($1, $2) RETURNING * `, [guide_id, work_date]); res.json({ success: true, data: result.rows[0], message: 'Рабочий день добавлен' }); } catch (error) { console.error('Ошибка добавления рабочего дня:', error); res.status(500).json({ success: false, error: 'Ошибка добавления рабочего дня' }); } }); // Удаление рабочего дня router.delete('/', async (req, res) => { try { const { guide_id, work_date } = req.body; if (!guide_id || !work_date) { return res.status(400).json({ success: false, error: 'Требуются параметры guide_id и work_date' }); } const result = await db.query(` DELETE FROM guide_working_days WHERE guide_id = $1 AND work_date = $2 RETURNING * `, [guide_id, work_date]); if (result.rows.length === 0) { return res.status(404).json({ success: false, error: 'Рабочий день не найден' }); } res.json({ success: true, message: 'Рабочий день удален' }); } catch (error) { console.error('Ошибка удаления рабочего дня:', error); res.status(500).json({ success: false, error: 'Ошибка удаления рабочего дня' }); } }); // Получение статистики работы гидов router.get('/stats', async (req, res) => { try { const { year, month } = req.query; let dateFilter = ''; const params = []; if (year && month) { dateFilter = 'WHERE EXTRACT(YEAR FROM gwd.work_date) = $1 AND EXTRACT(MONTH FROM gwd.work_date) = $2'; params.push(year, month); } const statsQuery = ` SELECT g.id, g.name, g.specialization, COUNT(gwd.work_date) as working_days, MIN(gwd.work_date) as first_work_date, MAX(gwd.work_date) as last_work_date FROM guides g LEFT JOIN guide_working_days gwd ON g.id = gwd.guide_id ${dateFilter} GROUP BY g.id, g.name, g.specialization ORDER BY working_days DESC, g.name `; const result = await db.query(statsQuery, params); // Общая статистика const totalStats = await db.query(` SELECT COUNT(DISTINCT gwd.guide_id) as active_guides, COUNT(gwd.work_date) as total_working_days, ROUND(AVG(guide_days.working_days), 1) as avg_days_per_guide FROM ( SELECT guide_id, COUNT(work_date) as working_days FROM guide_working_days gwd ${dateFilter} GROUP BY guide_id ) guide_days RIGHT JOIN guides g ON guide_days.guide_id = g.id `, params); res.json({ success: true, data: { guides: result.rows, summary: totalStats.rows[0] } }); } catch (error) { console.error('Ошибка получения статистики:', error); res.status(500).json({ success: false, error: 'Ошибка получения статистики' }); } }); export default router;