const { Router } = require('express') const mongoose = require('mongoose') const { getAnswer } = require('../../utils/common') const { CigaretteModel } = require('./model/cigarette') const { authMiddleware } = require('./middleware/auth') const router = Router() // Все эндпоинты статистики требуют авторизации router.use(authMiddleware) // Агрегация по дням: количество сигарет в день для построения графика router.get('/daily', async (req, res, next) => { try { const user = req.user const { from, to } = req.query const now = new Date() const defaultFrom = new Date(now) defaultFrom.setDate(defaultFrom.getDate() - 30) const fromDate = from ? new Date(from) : defaultFrom const toDate = to ? new Date(to) : now const match = { userId: new mongoose.Types.ObjectId(user.id), smokedAt: { $gte: fromDate, $lte: toDate, }, } // Отладка: проверяем, сколько записей попадает в фильтр const totalCount = await CigaretteModel.countDocuments(match) console.log('[STATS] Match filter:', JSON.stringify(match, null, 2)) console.log('[STATS] Total cigarettes in range:', totalCount) const data = await CigaretteModel.aggregate([ { $match: match }, { $group: { _id: { $dateToString: { format: '%Y-%m-%d', date: '$smokedAt', timezone: 'UTC' }, }, count: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]) console.log('[STATS] Aggregation result:', data) const result = data.map((item) => ({ date: item._id, count: item.count, })) res.json(getAnswer(null, result)) } catch (err) { next(err) } }) // Сводная статистика: среднее в день, по дням недели, общее по всем пользователям router.get('/summary', async (req, res, next) => { try { const user = req.user const { from, to } = req.query const now = new Date() const defaultFrom = new Date(now) defaultFrom.setDate(defaultFrom.getDate() - 30) const fromDate = from ? new Date(from) : defaultFrom const toDate = to ? new Date(to) : now // Фильтр для текущего пользователя const userMatch = { userId: new mongoose.Types.ObjectId(user.id), smokedAt: { $gte: fromDate, $lte: toDate, }, } // Фильтр для всех пользователей (общая статистика) const globalMatch = { smokedAt: { $gte: fromDate, $lte: toDate, }, } // 1. Статистика по дням (для текущего пользователя) const dailyStats = await CigaretteModel.aggregate([ { $match: userMatch }, { $group: { _id: { $dateToString: { format: '%Y-%m-%d', date: '$smokedAt', timezone: 'UTC' }, }, count: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]) const dailyData = dailyStats.map((item) => ({ date: item._id, count: item.count, })) // 2. Среднее количество в день (для текущего пользователя) const totalCigarettes = dailyStats.reduce((sum, item) => sum + item.count, 0) const daysWithData = dailyStats.length const averagePerDay = daysWithData > 0 ? (totalCigarettes / daysWithData).toFixed(2) : 0 // 3. Статистика по дням недели (для текущего пользователя) const weekdayStats = await CigaretteModel.aggregate([ { $match: userMatch }, { $group: { _id: { $dayOfWeek: '$smokedAt' }, // 1 = воскресенье, 2 = понедельник, ..., 7 = суббота count: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]) const weekdayNames = ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'] const weekdayData = weekdayStats.map((item) => { const dayIndex = item._id - 1 // MongoDB возвращает 1-7, приводим к 0-6 return { dayOfWeek: item._id, dayName: weekdayNames[dayIndex], count: item.count, } }) // Вычисляем среднее для каждого дня недели const weekdayAverages = weekdayData.map((day) => { // Считаем, сколько раз встречается этот день недели в периоде const occurrences = Math.floor( (toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24 * 7) ) + 1 return { ...day, average: occurrences > 0 ? (day.count / occurrences).toFixed(2) : day.count, } }) // 4. Определяем активных пользователей (от 2 до 40 сигарет в день в среднем) const MIN_CIGARETTES_PER_DAY = 2 const MAX_CIGARETTES_PER_DAY = 40 const periodDays = Math.ceil((toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24)) // Получаем статистику по каждому пользователю const userStats = await CigaretteModel.aggregate([ { $match: globalMatch }, { $group: { _id: '$userId', total: { $sum: 1 }, }, }, ]) // Фильтруем активных пользователей (исключаем слишком низкие и слишком высокие значения) const activeUserIds = userStats .filter((stat) => { const avgPerDay = stat.total / periodDays return avgPerDay > MIN_CIGARETTES_PER_DAY && avgPerDay <= MAX_CIGARETTES_PER_DAY }) .map((stat) => stat._id) const filteredLow = userStats.filter((stat) => stat.total / periodDays <= MIN_CIGARETTES_PER_DAY).length const filteredHigh = userStats.filter((stat) => stat.total / periodDays > MAX_CIGARETTES_PER_DAY).length console.log('[STATS] Total users:', userStats.length) console.log('[STATS] Active users (2-40 cigs/day):', activeUserIds.length) console.log('[STATS] Filtered out (too low):', filteredLow) console.log('[STATS] Filtered out (too high):', filteredHigh) // Фильтр только для активных пользователей const activeGlobalMatch = { ...globalMatch, userId: { $in: activeUserIds }, } // Общая статистика по активным пользователям const globalDailyStats = await CigaretteModel.aggregate([ { $match: activeGlobalMatch }, { $group: { _id: { $dateToString: { format: '%Y-%m-%d', date: '$smokedAt', timezone: 'UTC' }, }, count: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]) const globalDailyData = globalDailyStats.map((item) => ({ date: item._id, count: item.count, })) const globalTotalCigarettes = globalDailyStats.reduce((sum, item) => sum + item.count, 0) const globalDaysWithData = globalDailyStats.length const globalAveragePerDay = globalDaysWithData > 0 ? (globalTotalCigarettes / globalDaysWithData).toFixed(2) : 0 // Общая статистика по дням недели (активные пользователи) const globalWeekdayStats = await CigaretteModel.aggregate([ { $match: activeGlobalMatch }, { $group: { _id: { $dayOfWeek: '$smokedAt' }, count: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]) const globalWeekdayData = globalWeekdayStats.map((item) => { const dayIndex = item._id - 1 return { dayOfWeek: item._id, dayName: weekdayNames[dayIndex], count: item.count, } }) const globalWeekdayAverages = globalWeekdayData.map((day) => { const occurrences = Math.floor( (toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24 * 7) ) + 1 return { ...day, average: occurrences > 0 ? (day.count / occurrences).toFixed(2) : day.count, } }) // Количество активных пользователей const activeUsers = activeUserIds const result = { user: { daily: dailyData, averagePerDay: parseFloat(averagePerDay), weekday: weekdayAverages, total: totalCigarettes, daysWithData, }, global: { daily: globalDailyData, averagePerDay: parseFloat(globalAveragePerDay), weekday: globalWeekdayAverages, total: globalTotalCigarettes, daysWithData: globalDaysWithData, activeUsers: activeUsers.length, }, period: { from: fromDate.toISOString(), to: toDate.toISOString(), }, } res.json(getAnswer(null, result)) } catch (err) { next(err) } }) module.exports = router