- Implementazione TRASPORTI ! Passo 1
This commit is contained in:
296
src/models/RideRequest.js
Normal file
296
src/models/RideRequest.js
Normal 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;
|
||||
Reference in New Issue
Block a user