246 lines
7.8 KiB
JavaScript
246 lines
7.8 KiB
JavaScript
![]() |
const Report = require('../models/Report');
|
|||
|
const User = require('../models/User');
|
|||
|
|
|||
|
// @desc Подать жалобу на пользователя
|
|||
|
// @route POST /api/reports
|
|||
|
// @access Private
|
|||
|
const createReport = async (req, res, next) => {
|
|||
|
try {
|
|||
|
const { reportedUserId, reason, description } = req.body;
|
|||
|
const reporterId = req.user._id;
|
|||
|
|
|||
|
// Проверяем, что пользователь не подает жалобу на самого себя
|
|||
|
if (reporterId.equals(reportedUserId)) {
|
|||
|
const error = new Error('Вы не можете подать жалобу на самого себя');
|
|||
|
error.statusCode = 400;
|
|||
|
return next(error);
|
|||
|
}
|
|||
|
|
|||
|
// Проверяем, существует ли пользователь, на которого подается жалоба
|
|||
|
const reportedUser = await User.findById(reportedUserId);
|
|||
|
if (!reportedUser) {
|
|||
|
const error = new Error('Пользователь не найден');
|
|||
|
error.statusCode = 404;
|
|||
|
return next(error);
|
|||
|
}
|
|||
|
|
|||
|
// Проверяем, нет ли уже жалобы от этого пользователя на этого же пользователя
|
|||
|
const existingReport = await Report.findOne({
|
|||
|
reporter: reporterId,
|
|||
|
reportedUser: reportedUserId
|
|||
|
});
|
|||
|
|
|||
|
if (existingReport) {
|
|||
|
const error = new Error('Вы уже подавали жалобу на этого пользователя');
|
|||
|
error.statusCode = 400;
|
|||
|
return next(error);
|
|||
|
}
|
|||
|
|
|||
|
// Создаем новую жалобу
|
|||
|
const newReport = new Report({
|
|||
|
reporter: reporterId,
|
|||
|
reportedUser: reportedUserId,
|
|||
|
reason,
|
|||
|
description: description || ''
|
|||
|
});
|
|||
|
|
|||
|
await newReport.save();
|
|||
|
|
|||
|
console.log(`[REPORT_CTRL] Создана жалоба ${newReport._id} от пользователя ${reporterId} на пользователя ${reportedUserId}`);
|
|||
|
|
|||
|
res.status(201).json({
|
|||
|
message: 'Жалоба успешно подана. Мы рассмотрим её в ближайшее время.',
|
|||
|
reportId: newReport._id
|
|||
|
});
|
|||
|
|
|||
|
} catch (error) {
|
|||
|
console.error('[REPORT_CTRL] Ошибка при создании жалобы:', error.message);
|
|||
|
next(error);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// @desc Получить список всех жалоб (для админов)
|
|||
|
// @route GET /api/admin/reports
|
|||
|
// @access Admin
|
|||
|
const getAllReports = async (req, res, next) => {
|
|||
|
try {
|
|||
|
const { page = 1, limit = 20, status = 'all' } = req.query;
|
|||
|
const skip = (page - 1) * limit;
|
|||
|
|
|||
|
// Строим фильтр
|
|||
|
let filter = {};
|
|||
|
if (status !== 'all') {
|
|||
|
filter.status = status;
|
|||
|
}
|
|||
|
|
|||
|
// Получаем жалобы с информацией о пользователях
|
|||
|
const reports = await Report.find(filter)
|
|||
|
.populate('reporter', 'name email')
|
|||
|
.populate('reportedUser', 'name email isActive')
|
|||
|
.populate('reviewedBy', 'name email')
|
|||
|
.skip(skip)
|
|||
|
.limit(parseInt(limit))
|
|||
|
.sort({ createdAt: -1 });
|
|||
|
|
|||
|
// Получаем общее количество жалоб
|
|||
|
const total = await Report.countDocuments(filter);
|
|||
|
|
|||
|
res.json({
|
|||
|
reports,
|
|||
|
pagination: {
|
|||
|
page: parseInt(page),
|
|||
|
limit: parseInt(limit),
|
|||
|
total,
|
|||
|
pages: Math.ceil(total / limit)
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
} catch (error) {
|
|||
|
console.error('[REPORT_CTRL] Ошибка при получении списка жалоб:', error.message);
|
|||
|
next(error);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// @desc Получить детали жалобы
|
|||
|
// @route GET /api/admin/reports/:id
|
|||
|
// @access Admin
|
|||
|
const getReportById = async (req, res, next) => {
|
|||
|
try {
|
|||
|
const { id } = req.params;
|
|||
|
|
|||
|
const report = await Report.findById(id)
|
|||
|
.populate('reporter', 'name email photos')
|
|||
|
.populate('reportedUser', 'name email photos isActive')
|
|||
|
.populate('reviewedBy', 'name email');
|
|||
|
|
|||
|
if (!report) {
|
|||
|
const error = new Error('Жалоба не найдена');
|
|||
|
error.statusCode = 404;
|
|||
|
return next(error);
|
|||
|
}
|
|||
|
|
|||
|
res.json(report);
|
|||
|
|
|||
|
} catch (error) {
|
|||
|
console.error('[REPORT_CTRL] Ошибка при получении деталей жалобы:', error.message);
|
|||
|
next(error);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// @desc Обновить статус жалобы
|
|||
|
// @route PUT /api/admin/reports/:id
|
|||
|
// @access Admin
|
|||
|
const updateReportStatus = async (req, res, next) => {
|
|||
|
try {
|
|||
|
const { id } = req.params;
|
|||
|
const { status, adminComment, actionTaken } = req.body;
|
|||
|
const adminId = req.user._id;
|
|||
|
|
|||
|
const report = await Report.findById(id);
|
|||
|
if (!report) {
|
|||
|
const error = new Error('Жалоба не найдена');
|
|||
|
error.statusCode = 404;
|
|||
|
return next(error);
|
|||
|
}
|
|||
|
|
|||
|
// Обновляем жалобу
|
|||
|
report.status = status;
|
|||
|
report.reviewedBy = adminId;
|
|||
|
report.reviewedAt = new Date();
|
|||
|
|
|||
|
if (adminComment) {
|
|||
|
report.adminComment = adminComment;
|
|||
|
}
|
|||
|
|
|||
|
if (actionTaken) {
|
|||
|
report.actionTaken = actionTaken;
|
|||
|
}
|
|||
|
|
|||
|
await report.save();
|
|||
|
|
|||
|
// Если было предпринято действие, применяем его к пользователю
|
|||
|
if (actionTaken && actionTaken !== 'none') {
|
|||
|
const reportedUser = await User.findById(report.reportedUser);
|
|||
|
if (reportedUser) {
|
|||
|
switch (actionTaken) {
|
|||
|
case 'temporary_ban':
|
|||
|
case 'permanent_ban':
|
|||
|
case 'profile_removal':
|
|||
|
reportedUser.isActive = false;
|
|||
|
await reportedUser.save();
|
|||
|
console.log(`[REPORT_CTRL] Пользователь ${reportedUser._id} заблокирован по жалобе ${id}`);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
console.log(`[REPORT_CTRL] Жалоба ${id} обновлена администратором ${adminId}`);
|
|||
|
|
|||
|
res.json({
|
|||
|
message: 'Жалоба успешно обновлена',
|
|||
|
report: await Report.findById(id)
|
|||
|
.populate('reporter', 'name email')
|
|||
|
.populate('reportedUser', 'name email isActive')
|
|||
|
.populate('reviewedBy', 'name email')
|
|||
|
});
|
|||
|
|
|||
|
} catch (error) {
|
|||
|
console.error('[REPORT_CTRL] Ошибка при обновлении жалобы:', error.message);
|
|||
|
next(error);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// @desc Получить статистику жалоб
|
|||
|
// @route GET /api/admin/reports/stats
|
|||
|
// @access Admin
|
|||
|
const getReportsStats = async (req, res, next) => {
|
|||
|
try {
|
|||
|
// Общее количество жалоб
|
|||
|
const totalReports = await Report.countDocuments();
|
|||
|
|
|||
|
// Жалобы по статусам
|
|||
|
const pendingReports = await Report.countDocuments({ status: 'pending' });
|
|||
|
const reviewedReports = await Report.countDocuments({ status: 'reviewed' });
|
|||
|
const resolvedReports = await Report.countDocuments({ status: 'resolved' });
|
|||
|
const dismissedReports = await Report.countDocuments({ status: 'dismissed' });
|
|||
|
|
|||
|
// Жалобы за последние 30 дней
|
|||
|
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|||
|
const recentReports = await Report.countDocuments({
|
|||
|
createdAt: { $gte: thirtyDaysAgo }
|
|||
|
});
|
|||
|
|
|||
|
// Топ причин жалоб
|
|||
|
const topReasons = await Report.aggregate([
|
|||
|
{ $group: { _id: '$reason', count: { $sum: 1 } } },
|
|||
|
{ $sort: { count: -1 } },
|
|||
|
{ $limit: 5 }
|
|||
|
]);
|
|||
|
|
|||
|
const stats = {
|
|||
|
total: totalReports,
|
|||
|
byStatus: {
|
|||
|
pending: pendingReports,
|
|||
|
reviewed: reviewedReports,
|
|||
|
resolved: resolvedReports,
|
|||
|
dismissed: dismissedReports
|
|||
|
},
|
|||
|
recent: recentReports,
|
|||
|
topReasons
|
|||
|
};
|
|||
|
|
|||
|
res.json(stats);
|
|||
|
|
|||
|
} catch (error) {
|
|||
|
console.error('[REPORT_CTRL] Ошибка при получении статистики жалоб:', error.message);
|
|||
|
next(error);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
module.exports = {
|
|||
|
createReport,
|
|||
|
getAllReports,
|
|||
|
getReportById,
|
|||
|
updateReportStatus,
|
|||
|
getReportsStats
|
|||
|
};
|