hotel-backend/
├── config/
│ └── config.js
├── controllers/
│ ├── authController.js
│ ├── guestController.js
│ ├── paymentController.js
│ ├── reservationController.js
│ ├── reviewController.js
│ └── roomController.js
├── middleware/
│ └── authMiddleware.js
├── models/
│ ├── guest.js
│ ├── payment.js
│ ├── reservation.js
│ ├── review.js
│ ├── room.js
│ └── index.js
├── routes/
│ ├── auth.js
│ ├── guests.js
│ ├── payments.js
│ ├── reservations.js
│ ├── reviews.js
│ └── rooms.js
├── package.json
├── .env
└── server.js

module.exports = {
HOST: "localhost",
PORT: 1369,
USER: "root",
PASSWORD: "в1369sukAnigghaD1ck",
DB: "hotel",
dialect: "mysql",


pool: {
  max: 5,
  min: 0,
  acquire: 30000,
  idle: 10000
}
};

// controllers/authController.js
const db = require('../models');
const Guest = db.Guests;
const jwt = require('jsonwebtoken');

// Регистрация гостя
exports.register = async (req, res) => {
try {
const { name, email, phone_number, password } = req.body;


// Проверка наличия пользователя
let guest = await Guest.findOne({ where: { email } });
if (guest) {
  return res.status(400).json({ message: 'Пользователь с таким email уже существует' });
}

guest = await Guest.create({ name, email, phone_number, password });
res.status(201).json({ message: 'Регистрация успешна' });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// Вход гостя
exports.login = async (req, res) => {
try {
const { email, password } = req.body;


const guest = await Guest.findOne({ where: { email } });
if (!guest) {
  return res.status(400).json({ message: 'Неверный email или пароль' });
}

const isMatch = await guest.validPassword(password);
if (!isMatch) {
  return res.status(400).json({ message: 'Неверный email или пароль' });
}

const payload = { id: guest.guest_id, name: guest.name, email: guest.email };
const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1d' });

res.json({ token });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// controllers/guestController.js
const db = require('../models');
const Guest = db.Guests;

// Получить профиль текущего гостя
exports.getProfile = async (req, res) => {
try {
const guest = await Guest.findByPk(req.user.id, {
attributes: ['guest_id', 'name', 'email', 'phone_number', 'created_at'],
});
if (!guest) {
return res.status(404).json({ message: 'Пользователь не найден' });
}
res.json(guest);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// Обновить профиль гостя
exports.updateProfile = async (req, res) => {
try {
const { name, email, phone_number, password } = req.body;


const guest = await Guest.findByPk(req.user.id);
if (!guest) {
  return res.status(404).json({ message: 'Пользователь не найден' });
}

if (name) guest.name = name;
if (email) guest.email = email;
if (phone_number) guest.phone_number = phone_number;
if (password) guest.password = password; // Хэшируется автоматически благодаря хуку

await guest.save();
res.json({ message: 'Профиль обновлен успешно' });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// controllers/paymentController.js
const db = require('../models');
const Payment = db.Payments;
const Reservation = db.Reservations;

// Создать платеж
exports.createPayment = async (req, res) => {
const { reservation_id, amount, payment_date } = req.body;

try {
const reservation = await Reservation.findOne({
where: { reservation_id, guest_id: req.user.id },
});


if (!reservation) {
  return res.status(404).json({ message: 'Бронирование не найдено' });
}

const payment = await Payment.create({
  reservation_id,
  amount,
  payment_date,
});

res.status(201).json({ message: 'Платеж успешно создан', payment });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// Получить все платежи текущего гостя
exports.getUserPayments = async (req, res) => {
try {
const reservations = await Reservation.findAll({
where: { guest_id: req.user.id },
include: [{ model: Payment }],
});


const payments = reservations.flatMap(reservation => reservation.Payments);

res.json(payments);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// controllers/reservationController.js
const db = require('../models');
const Reservation = db.Reservations;
const Room = db.Rooms;

// Создать бронирование
exports.createReservation = async (req, res) => {
const { room_id, check_in_date, check_out_date } = req.body;

if (new Date(check_in_date) >= new Date(check_out_date)) {
return res.status(400).json({ message: 'Дата выезда должна быть позже даты заезда' });
}

try {
const room = await Room.findByPk(room_id);
if (!room) {
return res.status(404).json({ message: 'Комната не найдена' });
}


if (room.status !== 'доступен') {
  return res.status(400).json({ message: 'Комната недоступна для бронирования' });
}

// Создаем бронирование
const reservation = await Reservation.create({
  guest_id: req.user.id,
  room_id,
  check_in_date,
  check_out_date,
  // total_cost будет рассчитана триггером в базе данных
});

// Обновляем статус комнаты
room.status = 'занят';
await room.save();

res.status(201).json({ message: 'Бронирование создано', reservation });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// Получить все бронирования текущего гостя
exports.getUserReservations = async (req, res) => {
try {
const reservations = await Reservation.findAll({
where: { guest_id: req.user.id },
include: [{ model: Room }],
});
res.json(reservations);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// Отменить бронирование
exports.cancelReservation = async (req, res) => {
try {
const reservation = await Reservation.findOne({
where: { reservation_id: req.params.id, guest_id: req.user.id },
});


if (!reservation) {
  return res.status(404).json({ message: 'Бронирование не найдено' });
}

const room = await Room.findByPk(reservation.room_id);
await reservation.destroy();

// Обновляем статус комнаты
if (room) {
  room.status = 'доступен';
  await room.save();
}

res.json({ message: 'Бронирование отменено' });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// controllers/reviewController.js
const db = require('../models');
const Review = db.Reviews;
const Room = db.Rooms;

// Создать отзыв
exports.createReview = async (req, res) => {
const { room_id, review_text, rating } = req.body;

if (!rating || rating < 1 || rating > 5) {
return res.status(400).json({ message: 'Рейтинг должен быть от 1 до 5' });
}

try {
const room = await Room.findByPk(room_id);
if (!room) {
return res.status(404).json({ message: 'Комната не найдена' });
}


const review = await Review.create({
  guest_id: req.user.id,
  room_id,
  review_text,
  rating,
});

res.status(201).json({ message: 'Отзыв создан', review });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// Получить все отзывы
exports.getAllReviews = async (req, res) => {
try {
const reviews = await Review.findAll({
include: [
{ model: db.Guests, attributes: ['name'] },
{ model: Room, attributes: ['room_number'] },
],
});
res.json(reviews);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// Получить отзывы по комнате
exports.getReviewsByRoom = async (req, res) => {
const { room_id } = req.params;

try {
const reviews = await Review.findAll({
where: { room_id },
include: [{ model: db.Guests, attributes: ['name'] }],
});
res.json(reviews);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// controllers/roomController.js
const db = require('../models');
const Room = db.Rooms;

// Получить все комнаты
exports.getAllRooms = async (req, res) => {
try {
const rooms = await Room.findAll();
res.json(rooms);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// Получить комнату по ID
exports.getRoomById = async (req, res) => {
try {
const room = await Room.findByPk(req.params.id);
if (!room) {
return res.status(404).json({ message: 'Комната не найдена' });
}
res.json(room);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
}
};

// middleware/authMiddleware.js
const jwt = require('jsonwebtoken');

module.exports = (req, res, next) => {
// Получить токен из заголовка Authorization
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Ожидаем формат "Bearer TOKEN"

if (!token) {
return res.status(401).json({ message: 'Доступ запрещен, токен не предоставлен' });
}

try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded; // Добавить информацию о пользователе в запрос
next();
} catch (error) {
console.error(error);
res.status(403).json({ message: 'Неверный токен' });
}
};

// models/guest.js
const bcrypt = require('bcryptjs');

module.exports = (sequelize, DataTypes) => {
const Guest = sequelize.define('Guest', {
guest_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
},
email: {
type: DataTypes.STRING(100),
unique: true,
allowNull: false,
validate: {
isEmail: true,
},
},
phone_number: {
type: DataTypes.STRING(15),
unique: true,
allowNull: false,
},
password: {
type: DataTypes.STRING(255),
allowNull: false,
},
created_at: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
}, {
tableName: 'Guests',
timestamps: false,
});

// Хук для хеширования пароля перед сохранением
Guest.beforeCreate(async (guest, options) => {
const salt = await bcrypt.genSalt(10);
guest.password = await bcrypt.hash(guest.password, salt);
});

// Метод для проверки пароля
Guest.prototype.validPassword = async function(password) {
return await bcrypt.compare(password, this.password);
};

return Guest;
};

// models/index.js
const { Sequelize } = require('sequelize');

const config = {
username: 'root',
password: '1369sukAnigghaD1ck',
database: 'hotel',
host: 'localhost',
port: 1369,
dialect: 'mysql',
dialectOptions: {
authPlugins: {
mysql_native_password: () => () => Buffer.from([0x00])
}
},
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
};

const sequelize = new Sequelize(
config.database,
config.username,
config.password,
{
host: config.host,
port: config.port,
dialect: config.dialect,
dialectOptions: config.dialectOptions,
pool: config.pool,
logging: false
}
);
const db = {};

db.Sequelize = Sequelize;
db.sequelize = sequelize;

// Импорт моделей
db.Guests = require('./guest')(sequelize, Sequelize);
db.Rooms = require('./room')(sequelize, Sequelize);
db.Reservations = require('./reservation')(sequelize, Sequelize);
db.Payments = require('./payment')(sequelize, Sequelize);
db.Reviews = require('./review')(sequelize, Sequelize);

// Определение ассоциаций

// Гость имеет много бронирований
db.Guests.hasMany(db.Reservations, { foreignKey: 'guest_id', onDelete: 'CASCADE' });
db.Reservations.belongsTo(db.Guests, { foreignKey: 'guest_id' });

// Комната имеет много бронирований
db.Rooms.hasMany(db.Reservations, { foreignKey: 'room_id', onDelete: 'CASCADE' });
db.Reservations.belongsTo(db.Rooms, { foreignKey: 'room_id' });

// Бронирование имеет много платежей
db.Reservations.hasMany(db.Payments, { foreignKey: 'reservation_id', onDelete: 'CASCADE' });
db.Payments.belongsTo(db.Reservations, { foreignKey: 'reservation_id' });

// Гость оставляет много отзывов
db.Guests.hasMany(db.Reviews, { foreignKey: 'guest_id', onDelete: 'SET NULL' });
db.Reviews.belongsTo(db.Guests, { foreignKey: 'guest_id' });

// Комната имеет много отзывов
db.Rooms.hasMany(db.Reviews, { foreignKey: 'room_id', onDelete: 'SET NULL' });
db.Reviews.belongsTo(db.Rooms, { foreignKey: 'room_id' });

module.exports = db;

// models/payment.js
module.exports = (sequelize, DataTypes) => {
const Payment = sequelize.define('Payment', {
payment_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
reservation_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
amount: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
},
payment_date: {
type: DataTypes.DATEONLY,
allowNull: false,
},
}, {
tableName: 'Payments',
timestamps: false,
});


return Payment;
};

// models/reservation.js
module.exports = (sequelize, DataTypes) => {
const Reservation = sequelize.define('Reservation', {
reservation_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
guest_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
room_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
check_in_date: {
type: DataTypes.DATEONLY,
allowNull: false,
},
check_out_date: {
type: DataTypes.DATEONLY,
allowNull: false,
},
total_cost: {
type: DataTypes.DECIMAL(10, 2),
allowNull: true,
},
}, {
tableName: 'Reservations',
timestamps: false,
});


return Reservation;
};

// models/review.js
module.exports = (sequelize, DataTypes) => {
const Review = sequelize.define('Review', {
review_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
guest_id: {
type: DataTypes.INTEGER,
allowNull: true,
},
room_id: {
type: DataTypes.INTEGER,
allowNull: true,
},
review_text: {
type: DataTypes.TEXT,
allowNull: false,
},
rating: {
type: DataTypes.TINYINT,
validate: {
min: 1,
max: 5,
},
},
review_date: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
}, {
tableName: 'Reviews',
timestamps: false,
});


return Review;
};

// models/room.js
module.exports = (sequelize, DataTypes) => {
const Room = sequelize.define('Room', {
room_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
room_number: {
type: DataTypes.STRING(10),
unique: true,
allowNull: false,
},
room_type: {
type: DataTypes.ENUM('стандартный', 'улучшенный', 'люкс'),
defaultValue: 'стандартный',
},
capacity: {
type: DataTypes.TINYINT,
allowNull: false,
},
price_per_night: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
},
status: {
type: DataTypes.ENUM('доступен', 'занят'),
defaultValue: 'доступен',
},
}, {
tableName: 'Rooms',
timestamps: false,
});


return Room;
};

// routes/auth.js
const express = require('express');
const router = express.Router();
const authController = require('../controllers/authController');

// Регистрация
router.post('/register', authController.register);

// Вход
router.post('/login', authController.login);

module.exports = router;

// routes/guests.js
const express = require('express');
const router = express.Router();
const guestController = require('../controllers/guestController');
const authMiddleware = require('../middleware/authMiddleware');

// Получить информацию о текущем госте
router.get('/profile', authMiddleware, guestController.getProfile);

// Обновить информацию гостя
router.put('/profile', authMiddleware, guestController.updateProfile);

module.exports = router;

// routes/payments.js
const express = require('express');
const router = express.Router();
const paymentController = require('../controllers/paymentController');
const authMiddleware = require('../middleware/authMiddleware');

// Создать платеж
router.post('/', authMiddleware, paymentController.createPayment);

// Получить все платежи текущего гостя
router.get('/', authMiddleware, paymentController.getUserPayments);

module.exports = router;

// routes/reservations.js
const express = require('express');
const router = express.Router();
const reservationController = require('../controllers/reservationController');
const authMiddleware = require('../middleware/authMiddleware');

// Создать бронирование
router.post('/', authMiddleware, reservationController.createReservation);

// Получить все бронирования текущего гостя
router.get('/', authMiddleware, reservationController.getUserReservations);

// Отменить бронирование
router.delete('/:id', authMiddleware, reservationController.cancelReservation);

module.exports = router;

// routes/reviews.js
const express = require('express');
const router = express.Router();
const reviewController = require('../controllers/reviewController');
const authMiddleware = require('../middleware/authMiddleware');

// Создать отзыв
router.post('/', authMiddleware, reviewController.createReview);

// Получить все отзывы
router.get('/', reviewController.getAllReviews);

// Получить отзывы по комнате
router.get('/room/:room_id', reviewController.getReviewsByRoom);

module.exports = router;

// routes/rooms.js
const express = require('express');
const router = express.Router();
const roomController = require('../controllers/roomController');
const authMiddleware = require('../middleware/authMiddleware');

// Получить все комнаты
router.get('/', roomController.getAllRooms);

// Получить конкретную комнату
router.get('/:id', roomController.getRoomById);

module.exports = router;

.env:
PORT=8080
JWT_SECRET = f44586c78f41b4a6eff99999e3291ce7d34034bf85c46bdac8826f593ee3f9814f84553ee6b96c5ea784901c338277d5c4430c3059e58b35d1002686f437a6d9

// server.js
const express = require('express');
const app = express();
const db = require('./models');
const dotenv = require('dotenv');
dotenv.config();

// Middleware
app.use(express.json());

// Роуты
const authRoutes = require('./routes/auth');
const guestRoutes = require('./routes/guests');
const roomRoutes = require('./routes/rooms');
const reservationRoutes = require('./routes/reservations');
const paymentRoutes = require('./routes/payments');
const reviewRoutes = require('./routes/reviews');

app.use('/api/auth', authRoutes);
app.use('/api/guests', guestRoutes);
app.use('/api/rooms', roomRoutes);
app.use('/api/reservations', reservationRoutes);
app.use('/api/payments', paymentRoutes);
app.use('/api/reviews', reviewRoutes);

// Тестовый маршрут
app.get('/', (req, res) => {
res.send('API для гостиничного приложения работает');
});

// Синхронизация базы данных и запуск сервера
const PORT = process.env.PORT || 8080;

db.sequelize.sync({ alter: true }) // Используйте { force: true } для сброса БД (разработке)
.then(() => {
console.log('База данных синхронизирована');
app.listen(PORT, () => {
console.log(Сервер запущен на порту ${PORT});
});
})
.catch(err => {
console.error('Ошибка синхронизации базы данных:', err);
});

База данных mysql:
CREATE DATABASE hotel;
USE hotel;

-- Таблица Guests (Гости)
CREATE TABLE Guests (
guest_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone_number VARCHAR(15) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Таблица Rooms (Номера)
CREATE TABLE Rooms (
room_id INT AUTO_INCREMENT PRIMARY KEY,
room_number VARCHAR(10) UNIQUE NOT NULL,
room_type ENUM('стандартный', 'улучшенный', 'люкс') DEFAULT 'стандартный',
capacity TINYINT NOT NULL,
price_per_night DECIMAL(10, 2) NOT NULL,
status ENUM('доступен', 'занят') DEFAULT 'доступен'
);

-- Таблица Reservations (Бронирования)
CREATE TABLE Reservations (
reservation_id INT AUTO_INCREMENT PRIMARY KEY,
guest_id INT,
room_id INT,
check_in_date DATE NOT NULL,
check_out_date DATE NOT NULL,
total_cost DECIMAL(10, 2),
FOREIGN KEY (guest_id) REFERENCES Guests(guest_id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE
);

-- Таблица Payments (Оплаты)
CREATE TABLE Payments (
payment_id INT AUTO_INCREMENT PRIMARY KEY,
reservation_id INT,
amount DECIMAL(10, 2) NOT NULL,
payment_date DATE NOT NULL,
FOREIGN KEY (reservation_id) REFERENCES Reservations(reservation_id) ON DELETE CASCADE
);

-- Таблица Reviews (Отзывы)
CREATE TABLE Reviews (
review_id INT AUTO_INCREMENT PRIMARY KEY,
guest_id INT,
room_id INT,
review_text TEXT NOT NULL,
rating TINYINT CHECK (rating BETWEEN 1 AND 5),
review_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (guest_id) REFERENCES Guests(guest_id) ON DELETE SET NULL,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE SET NULL
);

-- Триггер для расчета общей стоимости бронирования
DELIMITER //
CREATE TRIGGER calc_total_cost
BEFORE INSERT ON Reservations
FOR EACH ROW
BEGIN
DECLARE nights INT;
DECLARE room_price DECIMAL(10, 2);


SET nights = DATEDIFF(NEW.check_out_date, NEW.check_in_date);
SELECT price_per_night INTO room_price FROM Rooms WHERE room_id = NEW.room_id;

SET NEW.total_cost = nights * room_price;
END//
DELIMITER ;

-- Триггер для обновления статуса комнаты при бронировании
DELIMITER //
CREATE TRIGGER update_room_status
AFTER INSERT ON Reservations
FOR EACH ROW
BEGIN
UPDATE Rooms
SET status = 'занят'
WHERE room_id = NEW.room_id;
END//
DELIMITER ;

-- Триггер для освобождения комнаты при отмене бронирования
DELIMITER //
CREATE TRIGGER free_room_on_cancel
AFTER DELETE ON Reservations
FOR EACH ROW
BEGIN
UPDATE Rooms
SET status = 'доступен'
WHERE room_id = OLD.room_id;
END//
DELIMITER ;

я хочу сделать так чтобы в приложении на android, в котором все это будет использовано , можно было отдельно регистрироваться как гость и как администратор и для регистрации нужно будет заполнить все требуемые поля в базе данных а при входе просто указать email и пароль

также ты должен добавить все что надо в бэкенд часть так как я добавил таблицу для администраторов
CREATE TABLE Administrators (
admin_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(150) NOT NULL,
passport_number VARCHAR(20) UNIQUE NOT NULL,
address TEXT NOT NULL,
phone_number VARCHAR(15) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
registration_code VARCHAR(50) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

можно будет входить как простой клиент и как администратор отличия в том что клиент может только бронировать номера и это будет отображаться в таблице бронирований и оставлять отзывы, они будут в таблице отзывы
а администратор может просматривать все таблицы и редактировать все значения

напиши полные коды для всех компонентов которые нужно добавить для работы новой таблицы администраторов и обнови уже существующие если это нужно