- Implementazione TRASPORTI ! Passo 1

This commit is contained in:
Surya Paolo
2025-12-22 01:19:28 +01:00
parent afeedf27a5
commit 2e7801b4ba
17 changed files with 7078 additions and 2546 deletions

296
src/models/RideRequest.js Normal file
View File

@@ -0,0 +1,296 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Schema per le coordinate
const CoordinatesSchema = new Schema({
lat: {
type: Number,
required: true
},
lng: {
type: Number,
required: true
}
}, { _id: false });
// Schema per località
const LocationSchema = new Schema({
city: {
type: String,
required: true,
trim: true
},
address: {
type: String,
trim: true
},
province: {
type: String,
trim: true
},
coordinates: {
type: CoordinatesSchema,
required: true
}
}, { _id: false });
const RideRequestSchema = new Schema({
idapp: {
type: String,
required: true,
index: true
},
rideId: {
type: Schema.Types.ObjectId,
ref: 'Ride',
required: true,
index: true
},
passengerId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
index: true
},
driverId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
index: true
},
message: {
type: String,
trim: true,
maxlength: 500
},
pickupPoint: {
type: LocationSchema
},
dropoffPoint: {
type: LocationSchema
},
useOriginalRoute: {
type: Boolean,
default: true
// true = usa partenza/destinazione originali del ride
},
seatsRequested: {
type: Number,
required: true,
min: 1,
default: 1
},
hasLuggage: {
type: Boolean,
default: false
},
luggageSize: {
type: String,
enum: ['small', 'medium', 'large'],
default: 'small'
},
hasPackages: {
type: Boolean,
default: false
},
packageDescription: {
type: String,
trim: true,
maxlength: 200
},
hasPets: {
type: Boolean,
default: false
},
petType: {
type: String,
trim: true
},
petSize: {
type: String,
enum: ['small', 'medium', 'large']
},
specialNeeds: {
type: String,
trim: true,
maxlength: 300
},
status: {
type: String,
enum: ['pending', 'accepted', 'rejected', 'cancelled', 'expired', 'completed'],
default: 'pending',
index: true
},
responseMessage: {
type: String,
trim: true,
maxlength: 500
},
respondedAt: {
type: Date
},
contribution: {
agreed: {
type: Boolean,
default: false
},
contribTypeId: {
type: Schema.Types.ObjectId,
ref: 'Contribtype'
},
amount: {
type: Number,
min: 0
},
notes: {
type: String,
trim: true
}
},
cancelledBy: {
type: String,
enum: ['passenger', 'driver']
},
cancellationReason: {
type: String,
trim: true
},
cancelledAt: {
type: Date
},
completedAt: {
type: Date
},
feedbackGiven: {
type: Boolean,
default: false
}
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
// Indici composti per ricerche ottimizzate
RideRequestSchema.index({ rideId: 1, status: 1 });
RideRequestSchema.index({ passengerId: 1, status: 1 });
RideRequestSchema.index({ driverId: 1, status: 1 });
RideRequestSchema.index({ idapp: 1, createdAt: -1 });
// Virtual per verificare se la richiesta può essere cancellata
RideRequestSchema.virtual('canCancel').get(function() {
return ['pending', 'accepted'].includes(this.status);
});
// Virtual per verificare se è in attesa
RideRequestSchema.virtual('isPending').get(function() {
return this.status === 'pending';
});
// Metodo per accettare la richiesta
RideRequestSchema.methods.accept = async function(responseMessage = '') {
this.status = 'accepted';
this.responseMessage = responseMessage;
this.respondedAt = new Date();
// Aggiorna il ride con il passeggero confermato
const Ride = mongoose.model('Ride');
const ride = await Ride.findById(this.rideId);
if (ride) {
ride.confirmedPassengers.push({
userId: this.passengerId,
seats: this.seatsRequested,
pickupPoint: this.pickupPoint || ride.departure,
dropoffPoint: this.dropoffPoint || ride.destination,
confirmedAt: new Date()
});
await ride.updateAvailableSeats();
}
return this.save();
};
// Metodo per rifiutare la richiesta
RideRequestSchema.methods.reject = function(responseMessage = '') {
this.status = 'rejected';
this.responseMessage = responseMessage;
this.respondedAt = new Date();
return this.save();
};
// Metodo per cancellare la richiesta
RideRequestSchema.methods.cancel = async function(cancelledBy, reason = '') {
this.status = 'cancelled';
this.cancelledBy = cancelledBy;
this.cancellationReason = reason;
this.cancelledAt = new Date();
// Se era accettata, rimuovi il passeggero dal ride
if (this.status === 'accepted') {
const Ride = mongoose.model('Ride');
const ride = await Ride.findById(this.rideId);
if (ride) {
ride.confirmedPassengers = ride.confirmedPassengers.filter(
p => p.userId.toString() !== this.passengerId.toString()
);
await ride.updateAvailableSeats();
}
}
return this.save();
};
// Metodo statico per ottenere richieste pendenti di un conducente
RideRequestSchema.statics.getPendingForDriver = function(idapp, driverId) {
return this.find({
idapp,
driverId,
status: 'pending'
})
.populate('passengerId', 'username name surname email')
.populate('rideId', 'departure destination dateTime')
.sort({ createdAt: -1 });
};
// Metodo statico per ottenere richieste di un passeggero
RideRequestSchema.statics.getByPassenger = function(idapp, passengerId, status = null) {
const query = { idapp, passengerId };
if (status) {
query.status = status;
}
return this.find(query)
.populate('rideId')
.populate('driverId', 'username name surname')
.sort({ createdAt: -1 });
};
// Pre-save hook per validazioni
RideRequestSchema.pre('save', async function(next) {
if (this.isNew) {
// Verifica che il ride esista e abbia posti disponibili
const Ride = mongoose.model('Ride');
const ride = await Ride.findById(this.rideId);
if (!ride) {
throw new Error('Viaggio non trovato');
}
if (ride.type === 'offer' && ride.passengers.available < this.seatsRequested) {
throw new Error('Posti non sufficienti per questo viaggio');
}
if (ride.userId.toString() === this.passengerId.toString()) {
throw new Error('Non puoi richiedere un passaggio per il tuo stesso viaggio');
}
// Imposta il driverId dal ride
this.driverId = ride.userId;
}
next();
});
const RideRequest = mongoose.model('RideRequest', RideRequestSchema);
module.exports = RideRequest;