- Implementazione TRASPORTI ! Passo 1
This commit is contained in:
926
src/controllers/feedbackController.js
Normal file
926
src/controllers/feedbackController.js
Normal file
@@ -0,0 +1,926 @@
|
||||
const Feedback = require('../models/Feedback');
|
||||
const Ride = require('../models/Ride');
|
||||
const RideRequest = require('../models/RideRequest');
|
||||
const { User } = require('../models/User');
|
||||
|
||||
/**
|
||||
* @desc Crea un feedback per un viaggio
|
||||
* @route POST /api/trasporti/feedback
|
||||
* @access Private
|
||||
*/
|
||||
const createFeedback = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
idapp,
|
||||
rideId,
|
||||
rideRequestId,
|
||||
toUserId,
|
||||
role,
|
||||
rating,
|
||||
categories,
|
||||
comment,
|
||||
pros,
|
||||
cons,
|
||||
tags,
|
||||
isPublic
|
||||
} = req.body;
|
||||
|
||||
const fromUserId = req.user._id;
|
||||
|
||||
// Validazione base
|
||||
if (!idapp || !rideId || !toUserId || !role || !rating) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Campi obbligatori: idapp, rideId, toUserId, role, rating'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica rating valido
|
||||
if (rating < 1 || rating > 5) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Il rating deve essere tra 1 e 5'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che il ride esista e sia completato
|
||||
const ride = await Ride.findById(rideId);
|
||||
if (!ride) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Viaggio non trovato'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che l'utente abbia partecipato al viaggio
|
||||
const wasDriver = ride.userId.toString() === fromUserId.toString();
|
||||
const wasPassenger = ride.confirmedPassengers.some(
|
||||
p => p.userId.toString() === fromUserId.toString()
|
||||
);
|
||||
|
||||
if (!wasDriver && !wasPassenger) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Non hai partecipato a questo viaggio'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che non stia valutando se stesso
|
||||
if (fromUserId.toString() === toUserId.toString()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Non puoi valutare te stesso'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che non esista già un feedback
|
||||
const existingFeedback = await Feedback.findOne({
|
||||
rideId,
|
||||
fromUserId,
|
||||
toUserId
|
||||
});
|
||||
|
||||
if (existingFeedback) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Hai già lasciato un feedback per questo utente in questo viaggio'
|
||||
});
|
||||
}
|
||||
|
||||
// Crea il feedback
|
||||
const feedbackData = {
|
||||
idapp,
|
||||
rideId,
|
||||
fromUserId,
|
||||
toUserId,
|
||||
role,
|
||||
rating
|
||||
};
|
||||
|
||||
if (rideRequestId) feedbackData.rideRequestId = rideRequestId;
|
||||
if (categories) feedbackData.categories = categories;
|
||||
if (comment) feedbackData.comment = comment;
|
||||
if (pros) feedbackData.pros = pros;
|
||||
if (cons) feedbackData.cons = cons;
|
||||
if (tags) feedbackData.tags = tags;
|
||||
if (isPublic !== undefined) feedbackData.isPublic = isPublic;
|
||||
|
||||
// Verifica automatica se il viaggio è completato
|
||||
if (ride.status === 'completed') {
|
||||
feedbackData.isVerified = true;
|
||||
}
|
||||
|
||||
const feedback = new Feedback(feedbackData);
|
||||
await feedback.save();
|
||||
|
||||
// Aggiorna la media rating dell'utente destinatario
|
||||
await updateUserRating(idapp, toUserId);
|
||||
|
||||
// Aggiorna flag nella richiesta se presente
|
||||
if (rideRequestId) {
|
||||
await RideRequest.findByIdAndUpdate(rideRequestId, {
|
||||
feedbackGiven: true
|
||||
});
|
||||
}
|
||||
|
||||
await feedback.populate('fromUserId', 'username name surname profile.img');
|
||||
await feedback.populate('toUserId', 'username name surname');
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Feedback inviato con successo!',
|
||||
data: feedback
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore creazione feedback:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore nella creazione del feedback',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Ottieni i feedback ricevuti da un utente
|
||||
* @route GET /api/trasporti/feedback/user/:userId
|
||||
* @access Public
|
||||
*/
|
||||
const getUserFeedback = async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
const { idapp, role, page = 1, limit = 10 } = req.query;
|
||||
|
||||
if (!idapp) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'idapp è obbligatorio'
|
||||
});
|
||||
}
|
||||
|
||||
const query = {
|
||||
idapp,
|
||||
toUserId: userId,
|
||||
isPublic: true
|
||||
};
|
||||
|
||||
if (role) {
|
||||
query.role = role;
|
||||
}
|
||||
|
||||
const skip = (parseInt(page) - 1) * parseInt(limit);
|
||||
|
||||
const [feedbacks, total, stats] = await Promise.all([
|
||||
Feedback.find(query)
|
||||
.populate('fromUserId', 'username name surname profile.img')
|
||||
.populate('rideId', 'departure destination dateTime')
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(parseInt(limit)),
|
||||
Feedback.countDocuments(query),
|
||||
getStatsForUser(idapp, userId)
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: feedbacks,
|
||||
stats,
|
||||
pagination: {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total,
|
||||
pages: Math.ceil(total / parseInt(limit))
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore recupero feedbacks:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore nel recupero dei feedback',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Ottieni statistiche feedback per un utente
|
||||
* @route GET /api/trasporti/feedback/user/:userId/stats
|
||||
* @access Public
|
||||
*/
|
||||
const getUserFeedbackStats = async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
const { idapp } = req.query;
|
||||
|
||||
if (!idapp) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'idapp è obbligatorio'
|
||||
});
|
||||
}
|
||||
|
||||
const [stats, distribution] = await Promise.all([
|
||||
getStatsForUser(idapp, userId),
|
||||
getRatingDistribution(idapp, userId)
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
...stats,
|
||||
distribution
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore recupero stats:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore nel recupero delle statistiche',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Ottieni i feedback per un viaggio
|
||||
* @route GET /api/trasporti/feedback/ride/:rideId
|
||||
* @access Public (con info limitate) / Private (info complete)
|
||||
*/
|
||||
const getRideFeedback = async (req, res) => {
|
||||
try {
|
||||
const { rideId } = req.params;
|
||||
const { idapp } = req.query;
|
||||
const userId = req.user?._id;
|
||||
|
||||
if (!idapp) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'idapp è obbligatorio'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che il ride esista
|
||||
const ride = await Ride.findById(rideId);
|
||||
if (!ride) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Viaggio non trovato'
|
||||
});
|
||||
}
|
||||
|
||||
// Query base per feedback pubblici
|
||||
const query = {
|
||||
idapp,
|
||||
rideId,
|
||||
isPublic: true
|
||||
};
|
||||
|
||||
const feedbacks = await Feedback.find(query)
|
||||
.populate('fromUserId', 'username name surname profile.img')
|
||||
.populate('toUserId', 'username name surname profile.img')
|
||||
.sort({ createdAt: -1 });
|
||||
|
||||
// Se l'utente è autenticato e ha partecipato, mostra info aggiuntive
|
||||
let pendingFeedbacks = [];
|
||||
let myFeedbacks = [];
|
||||
|
||||
if (userId) {
|
||||
const wasDriver = ride.userId.toString() === userId.toString();
|
||||
const wasPassenger = ride.confirmedPassengers.some(
|
||||
p => p.userId.toString() === userId.toString()
|
||||
);
|
||||
|
||||
if (wasDriver || wasPassenger) {
|
||||
myFeedbacks = feedbacks.filter(
|
||||
f => f.fromUserId._id.toString() === userId.toString()
|
||||
);
|
||||
|
||||
if (wasDriver) {
|
||||
// Il conducente deve dare feedback ai passeggeri
|
||||
const feedbackGivenTo = myFeedbacks.map(f => f.toUserId._id.toString());
|
||||
pendingFeedbacks = ride.confirmedPassengers
|
||||
.filter(p => !feedbackGivenTo.includes(p.userId.toString()))
|
||||
.map(p => ({ userId: p.userId, role: 'passenger' }));
|
||||
} else {
|
||||
// Il passeggero deve dare feedback al conducente
|
||||
const hasGivenToDriver = myFeedbacks.some(
|
||||
f => f.toUserId._id.toString() === ride.userId.toString()
|
||||
);
|
||||
if (!hasGivenToDriver) {
|
||||
pendingFeedbacks.push({ userId: ride.userId, role: 'driver' });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
feedbacks,
|
||||
pendingFeedbacks,
|
||||
myFeedbacks
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore recupero feedbacks viaggio:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Verifica se l'utente può lasciare un feedback
|
||||
* @route GET /api/trasporti/feedback/can-leave/:rideId/:toUserId
|
||||
* @access Private
|
||||
* @note NUOVA FUNZIONE - Era mancante!
|
||||
*/
|
||||
const canLeaveFeedback = async (req, res) => {
|
||||
try {
|
||||
const { rideId, toUserId } = req.params;
|
||||
const { idapp } = req.query;
|
||||
const fromUserId = req.user._id;
|
||||
|
||||
if (!idapp) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'idapp è obbligatorio'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che il ride esista
|
||||
const ride = await Ride.findById(rideId);
|
||||
if (!ride) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Viaggio non trovato'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che l'utente abbia partecipato al viaggio
|
||||
const wasDriver = ride.userId.toString() === fromUserId.toString();
|
||||
const wasPassenger = ride.confirmedPassengers.some(
|
||||
p => p.userId.toString() === fromUserId.toString()
|
||||
);
|
||||
|
||||
if (!wasDriver && !wasPassenger) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
canLeave: false,
|
||||
reason: 'Non hai partecipato a questo viaggio'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che non stia valutando se stesso
|
||||
if (fromUserId.toString() === toUserId.toString()) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
canLeave: false,
|
||||
reason: 'Non puoi valutare te stesso'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che toUserId abbia partecipato al viaggio
|
||||
const toUserWasDriver = ride.userId.toString() === toUserId.toString();
|
||||
const toUserWasPassenger = ride.confirmedPassengers.some(
|
||||
p => p.userId.toString() === toUserId.toString()
|
||||
);
|
||||
|
||||
if (!toUserWasDriver && !toUserWasPassenger) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
canLeave: false,
|
||||
reason: 'L\'utente destinatario non ha partecipato a questo viaggio'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che il viaggio sia completato
|
||||
if (ride.status !== 'completed') {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
canLeave: false,
|
||||
reason: 'Il viaggio non è ancora stato completato',
|
||||
rideStatus: ride.status
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che non esista già un feedback
|
||||
const existingFeedback = await Feedback.findOne({
|
||||
rideId,
|
||||
fromUserId,
|
||||
toUserId
|
||||
});
|
||||
|
||||
if (existingFeedback) {
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
canLeave: false,
|
||||
reason: 'Hai già lasciato un feedback per questo utente in questo viaggio',
|
||||
existingFeedbackId: existingFeedback._id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Determina il ruolo del destinatario
|
||||
const toUserRole = toUserWasDriver ? 'driver' : 'passenger';
|
||||
|
||||
// Recupera info utente destinatario
|
||||
const toUser = await User.findById(toUserId)
|
||||
.select('username name surname profile.img');
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
canLeave: true,
|
||||
toUser: {
|
||||
_id: toUser._id,
|
||||
username: toUser.username,
|
||||
name: toUser.name,
|
||||
surname: toUser.surname,
|
||||
img: toUser.profile?.img
|
||||
},
|
||||
toUserRole,
|
||||
ride: {
|
||||
_id: ride._id,
|
||||
departure: ride.departure,
|
||||
destination: ride.destination,
|
||||
dateTime: ride.dateTime
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore verifica canLeaveFeedback:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore nella verifica',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Rispondi a un feedback ricevuto
|
||||
* @route POST /api/trasporti/feedback/:id/response
|
||||
* @access Private
|
||||
*/
|
||||
const respondToFeedback = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { text } = req.body;
|
||||
const userId = req.user._id;
|
||||
|
||||
if (!text) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Il testo della risposta è obbligatorio'
|
||||
});
|
||||
}
|
||||
|
||||
const feedback = await Feedback.findById(id);
|
||||
|
||||
if (!feedback) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Feedback non trovato'
|
||||
});
|
||||
}
|
||||
|
||||
// Solo chi ha ricevuto il feedback può rispondere
|
||||
if (feedback.toUserId.toString() !== userId.toString()) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Non sei autorizzato a rispondere a questo feedback'
|
||||
});
|
||||
}
|
||||
|
||||
// Verifica che non abbia già risposto
|
||||
if (feedback.response && feedback.response.text) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Hai già risposto a questo feedback'
|
||||
});
|
||||
}
|
||||
|
||||
// Aggiungi la risposta
|
||||
feedback.response = {
|
||||
text,
|
||||
createdAt: new Date()
|
||||
};
|
||||
await feedback.save();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Risposta aggiunta',
|
||||
data: feedback
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore risposta feedback:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Segna un feedback come utile
|
||||
* @route POST /api/trasporti/feedback/:id/helpful
|
||||
* @access Private
|
||||
*/
|
||||
const markAsHelpful = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const userId = req.user._id;
|
||||
|
||||
const feedback = await Feedback.findById(id);
|
||||
|
||||
if (!feedback) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Feedback non trovato'
|
||||
});
|
||||
}
|
||||
|
||||
// Inizializza helpful se non esiste
|
||||
if (!feedback.helpful) {
|
||||
feedback.helpful = { count: 0, users: [] };
|
||||
}
|
||||
|
||||
// Verifica se l'utente ha già segnato come utile
|
||||
const userIdStr = userId.toString();
|
||||
const alreadyMarked = feedback.helpful.users.some(
|
||||
u => u.toString() === userIdStr
|
||||
);
|
||||
|
||||
if (alreadyMarked) {
|
||||
// Rimuovi il voto
|
||||
feedback.helpful.users = feedback.helpful.users.filter(
|
||||
u => u.toString() !== userIdStr
|
||||
);
|
||||
feedback.helpful.count = Math.max(0, feedback.helpful.count - 1);
|
||||
} else {
|
||||
// Aggiungi il voto
|
||||
feedback.helpful.users.push(userId);
|
||||
feedback.helpful.count += 1;
|
||||
}
|
||||
|
||||
await feedback.save();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: alreadyMarked ? 'Voto rimosso' : 'Feedback segnato come utile',
|
||||
data: {
|
||||
helpfulCount: feedback.helpful.count,
|
||||
isHelpful: !alreadyMarked
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore mark helpful:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Segnala un feedback inappropriato
|
||||
* @route POST /api/trasporti/feedback/:id/report
|
||||
* @access Private
|
||||
*/
|
||||
const reportFeedback = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { reason } = req.body;
|
||||
const userId = req.user._id;
|
||||
|
||||
if (!reason) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'La motivazione è obbligatoria'
|
||||
});
|
||||
}
|
||||
|
||||
const feedback = await Feedback.findById(id);
|
||||
|
||||
if (!feedback) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Feedback non trovato'
|
||||
});
|
||||
}
|
||||
|
||||
// Inizializza reports se non esiste
|
||||
if (!feedback.reports) {
|
||||
feedback.reports = [];
|
||||
}
|
||||
|
||||
// Verifica se l'utente ha già segnalato
|
||||
const alreadyReported = feedback.reports.some(
|
||||
r => r.userId.toString() === userId.toString()
|
||||
);
|
||||
|
||||
if (alreadyReported) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Hai già segnalato questo feedback'
|
||||
});
|
||||
}
|
||||
|
||||
// Aggiungi la segnalazione
|
||||
feedback.reports.push({
|
||||
userId,
|
||||
reason,
|
||||
createdAt: new Date()
|
||||
});
|
||||
|
||||
// Se ci sono troppe segnalazioni, nascondi automaticamente
|
||||
if (feedback.reports.length >= 3) {
|
||||
feedback.isPublic = false;
|
||||
feedback.hiddenReason = 'Nascosto automaticamente per multiple segnalazioni';
|
||||
}
|
||||
|
||||
await feedback.save();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Feedback segnalato. Lo esamineremo al più presto.'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore segnalazione:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Ottieni i miei feedback dati
|
||||
* @route GET /api/trasporti/feedback/my/given
|
||||
* @access Private
|
||||
*/
|
||||
const getMyGivenFeedback = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user._id;
|
||||
const { idapp, page = 1, limit = 20 } = req.query;
|
||||
|
||||
if (!idapp) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'idapp è obbligatorio'
|
||||
});
|
||||
}
|
||||
|
||||
const skip = (parseInt(page) - 1) * parseInt(limit);
|
||||
|
||||
const [feedbacks, total] = await Promise.all([
|
||||
Feedback.find({ idapp, fromUserId: userId })
|
||||
.populate('toUserId', 'username name surname profile.img')
|
||||
.populate('rideId', 'departure destination dateTime')
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(parseInt(limit)),
|
||||
Feedback.countDocuments({ idapp, fromUserId: userId })
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: feedbacks,
|
||||
pagination: {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total,
|
||||
pages: Math.ceil(total / parseInt(limit))
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore recupero feedback dati:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc Ottieni i miei feedback ricevuti
|
||||
* @route GET /api/trasporti/feedback/my/received
|
||||
* @access Private
|
||||
*/
|
||||
const getMyReceivedFeedback = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user._id;
|
||||
const { idapp, page = 1, limit = 20 } = req.query;
|
||||
|
||||
if (!idapp) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'idapp è obbligatorio'
|
||||
});
|
||||
}
|
||||
|
||||
const skip = (parseInt(page) - 1) * parseInt(limit);
|
||||
|
||||
const [feedbacks, total, stats] = await Promise.all([
|
||||
Feedback.find({ idapp, toUserId: userId })
|
||||
.populate('fromUserId', 'username name surname profile.img')
|
||||
.populate('rideId', 'departure destination dateTime')
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(parseInt(limit)),
|
||||
Feedback.countDocuments({ idapp, toUserId: userId }),
|
||||
getStatsForUser(idapp, userId)
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: feedbacks,
|
||||
stats,
|
||||
pagination: {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
total,
|
||||
pages: Math.ceil(total / parseInt(limit))
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Errore recupero feedback ricevuti:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Errore',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ============================================================
|
||||
// 🔧 HELPER FUNCTIONS
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Calcola le statistiche feedback per un utente
|
||||
*/
|
||||
async function getStatsForUser(idapp, userId) {
|
||||
try {
|
||||
const result = await Feedback.aggregate([
|
||||
{
|
||||
$match: {
|
||||
idapp,
|
||||
toUserId: typeof userId === 'string'
|
||||
? require('mongoose').Types.ObjectId(userId)
|
||||
: userId
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
averageRating: { $avg: '$rating' },
|
||||
totalFeedback: { $sum: 1 },
|
||||
asDriver: {
|
||||
$sum: { $cond: [{ $eq: ['$role', 'driver'] }, 1, 0] }
|
||||
},
|
||||
asPassenger: {
|
||||
$sum: { $cond: [{ $eq: ['$role', 'passenger'] }, 1, 0] }
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
if (result.length === 0) {
|
||||
return {
|
||||
averageRating: 0,
|
||||
totalFeedback: 0,
|
||||
asDriver: 0,
|
||||
asPassenger: 0
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
averageRating: Math.round(result[0].averageRating * 10) / 10,
|
||||
totalFeedback: result[0].totalFeedback,
|
||||
asDriver: result[0].asDriver,
|
||||
asPassenger: result[0].asPassenger
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Errore calcolo stats:', error);
|
||||
return {
|
||||
averageRating: 0,
|
||||
totalFeedback: 0,
|
||||
asDriver: 0,
|
||||
asPassenger: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcola la distribuzione dei rating per un utente
|
||||
*/
|
||||
async function getRatingDistribution(idapp, userId) {
|
||||
try {
|
||||
const result = await Feedback.aggregate([
|
||||
{
|
||||
$match: {
|
||||
idapp,
|
||||
toUserId: typeof userId === 'string'
|
||||
? require('mongoose').Types.ObjectId(userId)
|
||||
: userId
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: '$rating',
|
||||
count: { $sum: 1 }
|
||||
}
|
||||
},
|
||||
{ $sort: { _id: -1 } }
|
||||
]);
|
||||
|
||||
// Crea distribuzione completa 1-5
|
||||
const distribution = {};
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
distribution[i] = 0;
|
||||
}
|
||||
|
||||
result.forEach(r => {
|
||||
distribution[r._id] = r.count;
|
||||
});
|
||||
|
||||
return distribution;
|
||||
} catch (error) {
|
||||
console.error('Errore calcolo distribuzione:', error);
|
||||
return { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggiorna la media rating nel profilo utente
|
||||
*/
|
||||
async function updateUserRating(idapp, userId) {
|
||||
try {
|
||||
const stats = await getStatsForUser(idapp, userId);
|
||||
|
||||
await User.findByIdAndUpdate(userId, {
|
||||
'profile.driverProfile.averageRating': stats.averageRating,
|
||||
'profile.driverProfile.totalFeedback': stats.totalFeedback
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Errore aggiornamento rating utente:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// 📤 EXPORTS
|
||||
// ============================================================
|
||||
|
||||
module.exports = {
|
||||
// Funzioni principali (nomi corretti per le routes)
|
||||
createFeedback,
|
||||
getUserFeedback, // GET /feedback/user/:userId
|
||||
getUserFeedbackStats, // GET /feedback/user/:userId/stats
|
||||
getRideFeedback, // GET /feedback/ride/:rideId
|
||||
canLeaveFeedback, // GET /feedback/can-leave/:rideId/:toUserId ← NUOVA!
|
||||
respondToFeedback, // POST /feedback/:id/response
|
||||
reportFeedback, // POST /feedback/:id/report
|
||||
markAsHelpful, // POST /feedback/:id/helpful
|
||||
getMyGivenFeedback, // GET /feedback/my/given
|
||||
getMyReceivedFeedback, // GET /feedback/my/received
|
||||
|
||||
// Alias per compatibilità (vecchi nomi)
|
||||
getFeedbacksForUser: getUserFeedback,
|
||||
getFeedbacksForRide: getRideFeedback,
|
||||
getMyGivenFeedbacks: getMyGivenFeedback,
|
||||
getMyReceivedFeedbacks: getMyReceivedFeedback,
|
||||
|
||||
// Helper functions (esportate per uso in altri moduli)
|
||||
getStatsForUser,
|
||||
getRatingDistribution,
|
||||
updateUserRating
|
||||
};
|
||||
Reference in New Issue
Block a user