Mise à jour

This commit is contained in:
2026-06-03 14:47:35 +02:00
parent 085cf33114
commit ad0d86e734
16 changed files with 669 additions and 889 deletions

View File

@@ -1,13 +1,3 @@
/**
* ============================================
* DATABASE.JS - Gestion MariaDB
* Smart Parking v3.0
* AJOUTÉ : checkReservationConflict()
* → vérifie les conflits d'horaire
* au lieu de bloquer toute la place
* ============================================
*/
const mysql = require('mysql2/promise');
const bcrypt = require('bcryptjs');
require('dotenv').config();
@@ -23,9 +13,6 @@ const pool = mysql.createPool({
queueLimit: 0
});
// ============================================
// INITIALISATION
// ============================================
async function initDatabase() {
try {
@@ -105,7 +92,6 @@ async function initDatabase() {
console.log('✅ Tables vérifiées/créées');
// Admin par défaut
const [rows] = await pool.query('SELECT id FROM users WHERE email = ?', ['admin@smartparking.fr']);
if (rows.length === 0) {
const hashed = await bcrypt.hash('admin123', 10);
@@ -116,7 +102,7 @@ async function initDatabase() {
console.log('✅ Admin par défaut créé');
}
// 10 places par défaut
const [spots] = await pool.query('SELECT COUNT(*) AS count FROM spots');
if (spots[0].count === 0) {
for (let i = 1; i <= 10; i++) {
@@ -134,9 +120,6 @@ async function initDatabase() {
}
}
// ============================================
// UTILISATEURS
// ============================================
async function createUser(name, email, phone, hashedPassword, role = 'client') {
const [result] = await pool.query(
@@ -180,9 +163,6 @@ async function deleteUser(id) {
return { deleted: result.affectedRows };
}
// ============================================
// PLACES
// ============================================
async function createSpot(number, sensorId, status = 'free') {
const [result] = await pool.query(
@@ -215,9 +195,6 @@ async function deleteAllSpots() {
return { deleted: result.affectedRows };
}
// ============================================
// RÉSERVATIONS
// ============================================
async function createReservation(userId, spotId, date, startTime, endTime, duration, vehicle, price, paymentCode) {
const [result] = await pool.query(
@@ -229,21 +206,7 @@ async function createReservation(userId, spotId, date, startTime, endTime, durat
return { id: result.insertId };
}
/**
* ⭐ NOUVELLE FONCTION — Vérification des conflits d'horaire
*
* Problème corrigé : avant, quand une place était réservée,
* elle restait bloquée pour TOUS les jours et TOUTES les heures.
*
* Maintenant on vérifie uniquement s'il y a une réservation
* qui se chevauche sur la MÊME date et le MÊME créneau horaire.
*
* Exemple :
* Place 2 réservée aujourd'hui 10h-11h ✅
* Place 2 réservée aujourd'hui 14h-15h ✅ (pas de conflit)
* Place 2 réservée demain 10h-11h ✅ (pas de conflit)
* Place 2 réservée aujourd'hui 10h30-11h30 ❌ (conflit !)
*/
async function checkReservationConflict(spotId, date, startTime, endTime) {
const [rows] = await pool.query(`
SELECT id FROM reservations
@@ -254,7 +217,7 @@ async function checkReservationConflict(spotId, date, startTime, endTime) {
AND end_time > ?
`, [spotId, date, endTime, startTime]);
return rows.length > 0; // true = conflit, false = créneau libre
return rows.length > 0;
}
async function getReservationById(id) {
@@ -299,10 +262,7 @@ async function updateReservationStatus(id, status) {
return { changed: result.affectedRows };
}
/**
* Expiration automatique des réservations
* Appelée toutes les 60 secondes par server.js
*/
async function expireReservations() {
const [expiredRows] = await pool.query(`
SELECT r.id, r.spot_id, r.user_id, s.number AS spot_number
@@ -334,9 +294,36 @@ async function expireReservations() {
return expiredRows.length;
}
// ============================================
// HISTORIQUE
// ============================================
async function cleanupStaleReservedSpots() {
try {
const [staleSpots] = await pool.query(`
SELECT s.id, s.number FROM spots s
WHERE s.status = 'reserved'
AND s.id NOT IN (
SELECT r.spot_id FROM reservations r
WHERE r.status IN ('active', 'pending')
AND r.date = CURDATE()
AND r.start_time <= CURTIME()
AND r.end_time > CURTIME()
)
`);
for (const spot of staleSpots) {
await pool.query(
"UPDATE spots SET status = 'free', last_update = NOW() WHERE id = ?",
[spot.id]
);
console.log(`🧹 Place ${spot.number} nettoyée : reserved → free (pas de réservation en cours)`);
}
return staleSpots.length;
} catch (err) {
console.error('❌ Erreur cleanup stale spots:', err.message);
return 0;
}
}
async function addHistory(action, details, userId = null) {
const [result] = await pool.query(
@@ -358,9 +345,6 @@ async function getHistory(limit = 50) {
return rows;
}
// ============================================
// STATISTIQUES
// ============================================
async function recordStats(total, free, occupied, reserved) {
const rate = total > 0 ? Math.round(((occupied + reserved) / total) * 100) : 0;
@@ -381,9 +365,7 @@ async function getStats(days = 7) {
return rows;
}
// ============================================
// MQTT
// ============================================
async function recordMqttEvent(topic, message) {
const [result] = await pool.query(
@@ -393,9 +375,6 @@ async function recordMqttEvent(topic, message) {
return { id: result.insertId };
}
// ============================================
// FERMETURE
// ============================================
async function closeDatabase() {
await pool.end();
@@ -411,7 +390,7 @@ module.exports = {
createReservation, checkReservationConflict,
getReservationById, getReservationsByUser,
getAllReservations, updateReservationStatus,
expireReservations,
expireReservations, cleanupStaleReservedSpots,
addHistory, getHistory,
recordStats, getStats,
recordMqttEvent