turns-00053.parquet:5440
3bc4e9cd3ac67d01c43df01d
turn 1/1o1-mini-2024-09-12EnglishKyrgyzstan9493 words
degenerate_repetitionAbsentFinal dense release
USER
-- Создание базы данных
CREATE DATABASE IF NOT EXISTS hotel;
USE hotel;
-- Таблица Administrators
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(30) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
login VARCHAR(30) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Таблица Receptionists
CREATE TABLE Receptionists (
receptionist_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,
login VARCHAR(20) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
admin_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (admin_id) REFERENCES Administrators(admin_id) ON DELETE CASCADE
);
-- Таблица Rooms
CREATE TABLE Rooms (
room_id INT AUTO_INCREMENT PRIMARY KEY,
room_number INT UNIQUE NOT NULL,
status ENUM('Доступен', 'Недоступен', 'Требует уборки') DEFAULT 'Доступен',
price DECIMAL(10,2) NOT NULL,
bed_type ENUM('Одноместный', 'Двухместный', 'Трехместный', 'Четырехместный') NOT NULL,
room_type ENUM('Стандарт', 'Люкс', 'Делюкс') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Таблица Customers
CREATE TABLE Customers (
customer_id INT AUTO_INCREMENT PRIMARY KEY,
full_name VARCHAR(100) NOT NULL,
phone VARCHAR(15) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE,
gender ENUM('Мужской', 'Женский'),
country VARCHAR(50),
address TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
receptionist_id INT,
FOREIGN KEY (receptionist_id) REFERENCES Receptionists(receptionist_id) ON DELETE SET NULL
);
-- Таблица Reservations
CREATE TABLE Reservations (
reservation_id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
room_id INT NOT NULL,
check_in DATE NOT NULL,
check_out DATE NOT NULL,
status ENUM('Забронирован', 'Заселён', 'Выселен', 'Отменён') DEFAULT 'Забронирован',
total_amount DECIMAL(10,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
receptionist_id INT,
FOREIGN KEY (customer_id) REFERENCES Customers(customer_id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE,
FOREIGN KEY (receptionist_id) REFERENCES Receptionists(receptionist_id) ON DELETE RESTRICT
);
-- Таблица Payments
CREATE TABLE Payments (
payment_id INT AUTO_INCREMENT PRIMARY KEY,
reservation_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
payment_date DATE NOT NULL,
payment_method ENUM('Наличные', 'Кредитная карта', 'Дебетовая карта', 'Онлайн перевод') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (reservation_id) REFERENCES Reservations(reservation_id) ON DELETE CASCADE
);
-- Таблица Archived_Reservations
CREATE TABLE IF NOT EXISTS Archived_Reservations (
archive_id INT AUTO_INCREMENT PRIMARY KEY,
reservation_id INT NOT NULL,
customer_id INT NOT NULL,
room_id INT NOT NULL,
check_in DATE NOT NULL,
check_out DATE NOT NULL,
status ENUM('Заселён', 'Выселен', 'Отменён') NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (customer_id) REFERENCES Customers(customer_id) ON DELETE CASCADE,
FOREIGN KEY (room_id) REFERENCES Rooms(room_id) ON DELETE CASCADE
);
// src/h/DashboardAdmin.java
package h;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class DashboardAdmin extends JFrame {
private int adminId; // Идентификатор администратора
public DashboardAdmin(int adminId) {
this.adminId = adminId;
setTitle("Панель Администратора");
setSize(900, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// Создание вкладок
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Ресепшионисты", new ReceptionistsPanel());
tabbedPane.addTab("Номера", new RoomsPanel());
tabbedPane.addTab("Архивы", new ArchivesPanel());
tabbedPane.addTab("Финансы", new FinancePanel());
add(tabbedPane);
setVisible(true);
}
// Метод для хеширования пароля
private String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for(byte b : hashBytes){
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
// Панель управления ресепшионистами
class ReceptionistsPanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JButton addButton, editButton, deleteButton;
public ReceptionistsPanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"ID", "Полное имя", "Паспорт", "Адрес", "Телефон", "Email", "Логин"});
table = new JTable(tableModel);
loadReceptionists();
add(new JScrollPane(table), BorderLayout.CENTER);
// Кнопки управления
JPanel buttonPanel = new JPanel();
addButton = new JButton("Добавить");
editButton = new JButton("Редактировать");
deleteButton = new JButton("Удалить");
buttonPanel.add(addButton);
buttonPanel.add(editButton);
buttonPanel.add(deleteButton);
add(buttonPanel, BorderLayout.SOUTH);
// Обработчики событий
addButton.addActionListener(e -> openReceptionistForm(null));
editButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int receptionistId = (int)tableModel.getValueAt(selectedRow, 0);
openReceptionistForm(receptionistId);
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите ресепшиониста для редактирования.");
}
});
deleteButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int receptionistId = (int)tableModel.getValueAt(selectedRow, 0);
int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить этого ресепшиониста?", "Подтверждение", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
deleteReceptionist(receptionistId);
loadReceptionists();
}
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите ресепшиониста для удаления.");
}
});
}
private void loadReceptionists() {
tableModel.setRowCount(0);
String query = "SELECT receptionist_id, full_name, passport_number, address, phone_number, email, login FROM Receptionists";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("receptionist_id"),
rs.getString("full_name"),
rs.getString("passport_number"),
rs.getString("address"),
rs.getString("phone_number"),
rs.getString("email"),
rs.getString("login")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке ресепшионистов.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void openReceptionistForm(Integer receptionistId) {
new ReceptionistForm(receptionistId, this);
}
private void deleteReceptionist(int receptionistId) {
String query = "DELETE FROM Receptionists WHERE receptionist_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, receptionistId);
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Ресепшионист успешно удален.");
} catch (SQLException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при удалении ресепшиониста.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
// Форма добавления/редактирования ресепшиониста
class ReceptionistForm extends JDialog implements ActionListener {
private JTextField nameField, passportField, addressField, phoneField, emailField, loginField;
private JPasswordField passwordField;
private JButton saveButton, cancelButton;
private Integer receptionistId; // Null для добавления, не null для редактирования
private ReceptionistsPanel parentPanel;
public ReceptionistForm(Integer receptionistId, ReceptionistsPanel parentPanel) {
this.receptionistId = receptionistId;
this.parentPanel = parentPanel;
setTitle(receptionistId == null ? "Добавить Ресепшиониста" : "Редактировать Ресепшиониста");
setSize(400, 500);
setLocationRelativeTo(null);
setModal(true);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(5,5,5,5);
// Полное имя
gbc.gridx = 0;
gbc.gridy = 0;
add(new JLabel("Полное имя:"), gbc);
nameField = new JTextField(20);
gbc.gridx = 1;
add(nameField, gbc);
// Паспорт
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Номер паспорта:"), gbc);
passportField = new JTextField(20);
gbc.gridx = 1;
add(passportField, gbc);
// Адрес
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Адрес:"), gbc);
addressField = new JTextField(20);
gbc.gridx = 1;
add(addressField, gbc);
// Телефон
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Телефон:"), gbc);
phoneField = new JTextField(20);
gbc.gridx = 1;
add(phoneField, gbc);
// Email
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Email:"), gbc);
emailField = new JTextField(20);
gbc.gridx = 1;
add(emailField, gbc);
// Логин
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Логин:"), gbc);
loginField = new JTextField(20);
gbc.gridx = 1;
add(loginField, gbc);
// Пароль
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Пароль:"), gbc);
passwordField = new JPasswordField(20);
gbc.gridx = 1;
add(passwordField, gbc);
// Кнопки
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
JPanel buttonPanel = new JPanel();
saveButton = new JButton("Сохранить");
cancelButton = new JButton("Отмена");
buttonPanel.add(saveButton);
buttonPanel.add(cancelButton);
add(buttonPanel, gbc);
// Загрузка данных для редактирования
if (receptionistId != null) {
loadReceptionistData();
}
// Обработчики
saveButton.addActionListener(this);
cancelButton.addActionListener(e -> dispose());
setVisible(true);
}
private void loadReceptionistData() {
String query = "SELECT * FROM Receptionists WHERE receptionist_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, receptionistId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
nameField.setText(rs.getString("full_name"));
passportField.setText(rs.getString("passport_number"));
addressField.setText(rs.getString("address"));
phoneField.setText(rs.getString("phone_number"));
emailField.setText(rs.getString("email"));
loginField.setText(rs.getString("login"));
// Пароль не загружается для безопасности
}
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных ресепшиониста.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == saveButton) {
String fullName = nameField.getText().trim();
String passport = passportField.getText().trim();
String address = addressField.getText().trim();
String phone = phoneField.getText().trim();
String email = emailField.getText().trim();
String login = loginField.getText().trim();
String password = new String(passwordField.getPassword()).trim();
// Валидация
if (fullName.isEmpty() || passport.isEmpty() || address.isEmpty() ||
phone.isEmpty() || email.isEmpty() || login.isEmpty() ||
(receptionistId == null && password.isEmpty())) {
JOptionPane.showMessageDialog(this, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
try (ConnectMySQL connection = new ConnectMySQL()) {
Connection conn = connection.getConnection();
PreparedStatement pstmt;
if (receptionistId == null) {
// Добавление нового ресепшиониста
String query = "INSERT INTO Receptionists (full_name, passport_number, address, phone_number, email, login, password, admin_id, created_at, updated_at) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
pstmt = conn.prepareStatement(query);
pstmt.setString(1, fullName);
pstmt.setString(2, passport);
pstmt.setString(3, address);
pstmt.setString(4, phone);
pstmt.setString(5, email);
pstmt.setString(6, login);
pstmt.setString(7, hashPassword(password));
pstmt.setInt(8, adminId); // текущий администратор
} else {
// Обновление существующего ресепшиониста
String query = "UPDATE Receptionists SET full_name = ?, passport_number = ?, address = ?, phone_number = ?, email = ?, login = ?" +
(password.isEmpty() ? "" : ", password = ?") +
", updated_at = NOW() WHERE receptionist_id = ?";
pstmt = conn.prepareStatement(query);
pstmt.setString(1, fullName);
pstmt.setString(2, passport);
pstmt.setString(3, address);
pstmt.setString(4, phone);
pstmt.setString(5, email);
pstmt.setString(6, login);
int paramIndex = 7;
if (!password.isEmpty()) {
pstmt.setString(paramIndex++, hashPassword(password));
}
pstmt.setInt(paramIndex, receptionistId);
}
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Данные успешно сохранены.");
parentPanel.loadReceptionists();
dispose();
} catch (SQLIntegrityConstraintViolationException ex) {
JOptionPane.showMessageDialog(this, "Дублирование логина, паспорта или телефона.", "Ошибка", JOptionPane.ERROR_MESSAGE);
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при сохранении данных ресепшиониста.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
}
// Панель управления номерами
class RoomsPanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JButton addButton, editButton, deleteButton;
public RoomsPanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"ID", "Номер", "Статус", "Цена", "Тип кровати", "Тип номера"});
table = new JTable(tableModel);
loadRooms();
add(new JScrollPane(table), BorderLayout.CENTER);
// Кнопки управления
JPanel buttonPanel = new JPanel();
addButton = new JButton("Добавить");
editButton = new JButton("Редактировать");
deleteButton = new JButton("Удалить");
buttonPanel.add(addButton);
buttonPanel.add(editButton);
buttonPanel.add(deleteButton);
add(buttonPanel, BorderLayout.SOUTH);
// Обработчики событий
addButton.addActionListener(e -> openRoomForm(null));
editButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int roomId = (int)tableModel.getValueAt(selectedRow, 0);
openRoomForm(roomId);
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для редактирования.");
}
});
deleteButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int roomId = (int)tableModel.getValueAt(selectedRow, 0);
int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить этот номер?", "Подтверждение", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
deleteRoom(roomId);
loadRooms();
}
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите номер для удаления.");
}
});
}
private void loadRooms() {
tableModel.setRowCount(0);
String query = "SELECT room_id, room_number, status, price, bed_type, room_type FROM Rooms";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("room_id"),
rs.getInt("room_number"),
rs.getString("status"),
rs.getBigDecimal("price"),
rs.getString("bed_type"),
rs.getString("room_type")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке номеров.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void openRoomForm(Integer roomId) {
new RoomForm(roomId, this);
}
private void deleteRoom(int roomId) {
String query = "DELETE FROM Rooms WHERE room_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, roomId);
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Номер успешно удален.");
} catch (SQLException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при удалении номера.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
// Форма добавления/редактирования номера
class RoomForm extends JDialog implements ActionListener {
private JTextField roomNumberField, priceField;
private JComboBox<String> statusComboBox, bedTypeComboBox, roomTypeComboBox;
private JButton saveButton, cancelButton;
private Integer roomId;
private RoomsPanel parentPanel;
public RoomForm(Integer roomId, RoomsPanel parentPanel) {
this.roomId = roomId;
this.parentPanel = parentPanel;
setTitle(roomId == null ? "Добавить Номер" : "Редактировать Номер");
setSize(400, 400);
setLocationRelativeTo(null);
setModal(true);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(5,5,5,5);
// Номер комнаты
gbc.gridx = 0;
gbc.gridy = 0;
add(new JLabel("Номер комнаты:"), gbc);
roomNumberField = new JTextField(20);
gbc.gridx = 1;
add(roomNumberField, gbc);
// Статус
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Статус:"), gbc);
statusComboBox = new JComboBox<>(new String[] {"Доступен", "Недоступен"});
gbc.gridx = 1;
add(statusComboBox, gbc);
// Цена
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Цена:"), gbc);
priceField = new JTextField(20);
gbc.gridx = 1;
add(priceField, gbc);
// Тип кровати
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Тип кровати:"), gbc);
bedTypeComboBox = new JComboBox<>(new String[] {"Одноместный", "Двухместный", "Трехместный", "Четырехместный"});
gbc.gridx = 1;
add(bedTypeComboBox, gbc);
// Тип номера
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Тип номера:"), gbc);
roomTypeComboBox = new JComboBox<>(new String[] {"Стандарт", "Люкс", "Делюкс"});
gbc.gridx = 1;
add(roomTypeComboBox, gbc);
// Кнопки
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
JPanel buttonPanel = new JPanel();
saveButton = new JButton("Сохранить");
cancelButton = new JButton("Отмена");
buttonPanel.add(saveButton);
buttonPanel.add(cancelButton);
add(buttonPanel, gbc);
// Загрузка данных для редактирования
if (roomId != null) {
loadRoomData();
}
// Обработчики
saveButton.addActionListener(this);
cancelButton.addActionListener(e -> dispose());
setVisible(true);
}
private void loadRoomData() {
String query = "SELECT * FROM Rooms WHERE room_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, roomId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
roomNumberField.setText(String.valueOf(rs.getInt("room_number")));
statusComboBox.setSelectedItem(rs.getString("status"));
priceField.setText(rs.getBigDecimal("price").toString());
bedTypeComboBox.setSelectedItem(rs.getString("bed_type"));
roomTypeComboBox.setSelectedItem(rs.getString("room_type"));
}
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных номера.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == saveButton) {
String roomNumberStr = roomNumberField.getText().trim();
String status = (String)statusComboBox.getSelectedItem();
String priceStr = priceField.getText().trim();
String bedType = (String)bedTypeComboBox.getSelectedItem();
String roomType = (String)roomTypeComboBox.getSelectedItem();
// Валидация
if (roomNumberStr.isEmpty() || status == null || priceStr.isEmpty() || bedType == null || roomType == null) {
JOptionPane.showMessageDialog(this, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
int roomNumber;
double price;
try {
roomNumber = Integer.parseInt(roomNumberStr);
price = Double.parseDouble(priceStr);
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(this, "Номер комнаты и цена должны быть числовыми значениями.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
try (ConnectMySQL connection = new ConnectMySQL()) {
Connection conn = connection.getConnection();
PreparedStatement pstmt;
if (roomId == null) {
// Добавление нового номера
String query = "INSERT INTO Rooms (room_number, status, price, bed_type, room_type, created_at, updated_at) " +
"VALUES (?, ?, ?, ?, ?, NOW(), NOW())";
pstmt = conn.prepareStatement(query);
pstmt.setInt(1, roomNumber);
pstmt.setString(2, status);
pstmt.setDouble(3, price);
pstmt.setString(4, bedType);
pstmt.setString(5, roomType);
} else {
// Обновление существующего номера
String query = "UPDATE Rooms SET room_number = ?, status = ?, price = ?, bed_type = ?, room_type = ?, updated_at = NOW() WHERE room_id = ?";
pstmt = conn.prepareStatement(query);
pstmt.setInt(1, roomNumber);
pstmt.setString(2, status);
pstmt.setDouble(3, price);
pstmt.setString(4, bedType);
pstmt.setString(5, roomType);
pstmt.setInt(6, roomId);
}
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Данные успешно сохранены.");
parentPanel.loadRooms();
dispose();
} catch (SQLIntegrityConstraintViolationException ex) {
JOptionPane.showMessageDialog(this, "Номер комнаты должен быть уникальным.", "Ошибка", JOptionPane.ERROR_MESSAGE);
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при сохранении данных номера.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
}
// Панель архивов резервирований
class ArchivesPanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
public ArchivesPanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"Архив ID", "Бронирование ID", "Клиент", "Номер", "Дата заезда", "Дата выезда", "Статус", "Сумма"});
table = new JTable(tableModel);
loadArchivedReservations();
add(new JScrollPane(table), BorderLayout.CENTER);
}
private void loadArchivedReservations() {
tableModel.setRowCount(0);
String query = "SELECT ar.archive_id, ar.reservation_id, c.full_name, r.room_number, ar.check_in, ar.check_out, ar.status, ar.total_amount " +
"FROM Archived_Reservations ar " +
"JOIN Customers c ON ar.customer_id = c.customer_id " +
"JOIN Rooms r ON ar.room_id = r.room_id";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("archive_id"),
rs.getInt("reservation_id"),
rs.getString("full_name"),
rs.getInt("room_number"),
rs.getDate("check_in"),
rs.getDate("check_out"),
rs.getString("status"),
rs.getBigDecimal("total_amount")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке архивных резервирований.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
// Панель финансов
class FinancePanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JLabel totalRevenueLabel;
public FinancePanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"Платеж ID", "Бронирование ID", "Клиент", "Номер", "Дата оплаты", "Сумма", "Метод оплаты"});
table = new JTable(tableModel);
loadPayments();
add(new JScrollPane(table), BorderLayout.CENTER);
// Панель для отображения общего дохода
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
totalRevenueLabel = new JLabel("Общий доход: 0.00");
bottomPanel.add(totalRevenueLabel);
add(bottomPanel, BorderLayout.SOUTH);
calculateTotalRevenue();
}
private void loadPayments() {
tableModel.setRowCount(0);
String query = "SELECT p.payment_id, p.reservation_id, c.full_name, r.room_number, " +
"p.payment_date, p.amount, p.payment_method " +
"FROM Payments p " +
"LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
"LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
"LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
"LEFT JOIN Rooms r ON (res.room_id = r.room_id OR ar.room_id = r.room_id)";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("payment_id"),
rs.getInt("reservation_id"),
rs.getString("full_name"),
rs.getInt("room_number"),
rs.getDate("payment_date"),
rs.getBigDecimal("amount"),
rs.getString("payment_method")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке платежей.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void calculateTotalRevenue() {
String query = "SELECT SUM(amount) AS total_revenue FROM Payments";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
double totalRevenue = rs.getDouble("total_revenue");
totalRevenueLabel.setText("Общий доход: " + String.format("%.2f", totalRevenue));
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при расчете общего дохода.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
}
// src/h/DashboardReceptionist.java
package h;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import org.jdatepicker.impl.*;
import java.util.Properties;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class DashboardReceptionist extends JFrame {
private int receptionistId; // Идентификатор ресепшиониста
public DashboardReceptionist(int receptionistId) {
this.receptionistId = receptionistId;
setTitle("Панель Ресепшиониста");
setSize(1000, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// Создание вкладок
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Клиенты", new CustomersPanel());
tabbedPane.addTab("Номера", new RoomsViewPanel());
tabbedPane.addTab("Бронирования", new ReservationsPanel());
tabbedPane.addTab("Платежи", new PaymentsPanel());
tabbedPane.addTab("Архив", new ArchivePanel());
add(tabbedPane);
setVisible(true);
}
// Метод для хеширования пароля
private String hashPassword(String password) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
byte[] hashBytes = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for(byte b : hashBytes){
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
// Панель управления клиентами
class CustomersPanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JButton addButton, editButton, deleteButton, refreshButton;
public CustomersPanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"ID", "Полное имя", "Телефон", "Email", "Пол", "Страна", "Адрес"});
table = new JTable(tableModel);
loadCustomers();
add(new JScrollPane(table), BorderLayout.CENTER);
// Кнопки управления
JPanel buttonPanel = new JPanel();
addButton = new JButton("Добавить");
editButton = new JButton("Редактировать");
deleteButton = new JButton("Удалить");
refreshButton = new JButton("Обновить");
buttonPanel.add(addButton);
buttonPanel.add(editButton);
buttonPanel.add(deleteButton);
buttonPanel.add(refreshButton);
add(buttonPanel, BorderLayout.SOUTH);
// Обработчики событий
addButton.addActionListener(e -> openCustomerForm(null));
editButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int customerId = (int)tableModel.getValueAt(selectedRow, 0);
openCustomerForm(customerId);
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите клиента для редактирования.");
}
});
deleteButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int customerId = (int)tableModel.getValueAt(selectedRow, 0);
int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите удалить этого клиента?", "Подтверждение", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
deleteCustomer(customerId);
loadCustomers();
}
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите клиента для удаления.");
}
});
refreshButton.addActionListener(e -> loadCustomers());
}
private void loadCustomers() {
tableModel.setRowCount(0);
String query = "SELECT customer_id, full_name, phone, email, gender, country, address FROM Customers";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("customer_id"),
rs.getString("full_name"),
rs.getString("phone"),
rs.getString("email"),
rs.getString("gender"),
rs.getString("country"),
rs.getString("address")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке клиентов.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void openCustomerForm(Integer customerId) {
new CustomerForm(customerId, this);
}
private void deleteCustomer(int customerId) {
String query = "DELETE FROM Customers WHERE customer_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, customerId);
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Клиент успешно удален.");
} catch (SQLException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при удалении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
// Форма добавления/редактирования клиента
class CustomerForm extends JDialog implements ActionListener {
private JTextField nameField, phoneField, emailField, countryField, addressField;
private JComboBox<String> genderComboBox;
private JButton saveButton, cancelButton;
private Integer customerId; // Null для добавления, не null для редактирования
private CustomersPanel parentPanel;
public CustomerForm(Integer customerId, CustomersPanel parentPanel) {
this.customerId = customerId;
this.parentPanel = parentPanel;
setTitle(customerId == null ? "Добавить Клиента" : "Редактировать Клиента");
setSize(500, 500);
setLocationRelativeTo(null);
setModal(true);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(10,10,10,10);
// Полное имя
gbc.gridx = 0;
gbc.gridy = 0;
add(new JLabel("Полное имя:"), gbc);
nameField = new JTextField(20);
gbc.gridx = 1;
add(nameField, gbc);
// Телефон
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Телефон:"), gbc);
phoneField = new JTextField(20);
gbc.gridx = 1;
add(phoneField, gbc);
// Email
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Email:"), gbc);
emailField = new JTextField(20);
gbc.gridx = 1;
add(emailField, gbc);
// Пол
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Пол:"), gbc);
genderComboBox = new JComboBox<>(new String[] {"Мужской", "Женский"});
gbc.gridx = 1;
add(genderComboBox, gbc);
// Страна
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Страна:"), gbc);
countryField = new JTextField(20);
gbc.gridx = 1;
add(countryField, gbc);
// Адрес
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Адрес:"), gbc);
addressField = new JTextField(20);
gbc.gridx = 1;
add(addressField, gbc);
// Кнопки
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
JPanel buttonPanel = new JPanel();
saveButton = new JButton("Сохранить");
cancelButton = new JButton("Отмена");
buttonPanel.add(saveButton);
buttonPanel.add(cancelButton);
add(buttonPanel, gbc);
// Загрузка данных для редактирования
if (customerId != null) {
loadCustomerData();
}
// Обработчики
saveButton.addActionListener(this);
cancelButton.addActionListener(e -> dispose());
setVisible(true);
}
private void loadCustomerData() {
String query = "SELECT * FROM Customers WHERE customer_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, customerId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
nameField.setText(rs.getString("full_name"));
phoneField.setText(rs.getString("phone"));
emailField.setText(rs.getString("email"));
genderComboBox.setSelectedItem(rs.getString("gender"));
countryField.setText(rs.getString("country"));
addressField.setText(rs.getString("address"));
}
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == saveButton) {
String fullName = nameField.getText().trim();
String phone = phoneField.getText().trim();
String email = emailField.getText().trim();
String gender = (String)genderComboBox.getSelectedItem();
String country = countryField.getText().trim();
String address = addressField.getText().trim();
// Валидация
if (fullName.isEmpty() || phone.isEmpty() || email.isEmpty() || gender == null || country.isEmpty() || address.isEmpty()) {
JOptionPane.showMessageDialog(this, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
try (ConnectMySQL connection = new ConnectMySQL()) {
Connection conn = connection.getConnection();
PreparedStatement pstmt;
if (customerId == null) {
// Добавление нового клиента
String query = "INSERT INTO Customers (full_name, phone, email, gender, country, address, created_at, updated_at, receptionist_id) " +
"VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW(), ?)";
pstmt = conn.prepareStatement(query);
pstmt.setString(1, fullName);
pstmt.setString(2, phone);
pstmt.setString(3, email);
pstmt.setString(4, gender);
pstmt.setString(5, country);
pstmt.setString(6, address);
pstmt.setInt(7, receptionistId);
} else {
// Обновление существующего клиента
String query = "UPDATE Customers SET full_name = ?, phone = ?, email = ?, gender = ?, country = ?, address = ?, updated_at = NOW() WHERE customer_id = ?";
pstmt = conn.prepareStatement(query);
pstmt.setString(1, fullName);
pstmt.setString(2, phone);
pstmt.setString(3, email);
pstmt.setString(4, gender);
pstmt.setString(5, country);
pstmt.setString(6, address);
pstmt.setInt(7, customerId);
}
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Данные успешно сохранены.");
parentPanel.loadCustomers();
dispose();
} catch (SQLIntegrityConstraintViolationException ex) {
JOptionPane.showMessageDialog(this, "Дублирование телефона или email.", "Ошибка", JOptionPane.ERROR_MESSAGE);
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при сохранении данных клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
}
// Панель просмотра номеров
class RoomsViewPanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JButton refreshButton;
public RoomsViewPanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"Номер ID", "Номер комнаты", "Статус", "Цена", "Тип кровати", "Тип номера"});
table = new JTable(tableModel);
loadRooms();
add(new JScrollPane(table), BorderLayout.CENTER);
// Кнопка обновления
JPanel buttonPanel = new JPanel();
refreshButton = new JButton("Обновить");
buttonPanel.add(refreshButton);
add(buttonPanel, BorderLayout.SOUTH);
refreshButton.addActionListener(e -> loadRooms());
}
private void loadRooms() {
tableModel.setRowCount(0);
String query = "SELECT room_id, room_number, status, price, bed_type, room_type FROM Rooms";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("room_id"),
rs.getInt("room_number"),
rs.getString("status"),
rs.getBigDecimal("price"),
rs.getString("bed_type"),
rs.getString("room_type")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке номеров.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
// Панель управления бронированиями
class ReservationsPanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JButton addButton, editButton, refreshButton, checkInButton, checkOutButton;
public ReservationsPanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"ID", "Клиент", "Номер", "Дата заезда", "Дата выезда", "Статус", "Сумма"});
table = new JTable(tableModel);
loadReservations();
add(new JScrollPane(table), BorderLayout.CENTER);
// Кнопки управления
JPanel buttonPanel = new JPanel();
addButton = new JButton("Добавить");
editButton = new JButton("Редактировать");
checkInButton = new JButton("Заселить");
checkOutButton = new JButton("Выселить");
refreshButton = new JButton("Обновить");
buttonPanel.add(addButton);
buttonPanel.add(editButton);
buttonPanel.add(checkInButton);
buttonPanel.add(checkOutButton);
buttonPanel.add(refreshButton);
add(buttonPanel, BorderLayout.SOUTH);
// Обработчики событий
addButton.addActionListener(e -> openReservationForm(null));
editButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
openReservationForm(reservationId);
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для редактирования.");
}
});
checkInButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
checkIn(reservationId);
loadReservations();
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для заселения.");
}
});
checkOutButton.addActionListener(e -> {
int selectedRow = table.getSelectedRow();
if (selectedRow >= 0) {
int reservationId = (int)tableModel.getValueAt(selectedRow, 0);
checkOut(reservationId);
loadReservations();
} else {
JOptionPane.showMessageDialog(this, "Пожалуйста, выберите бронирование для выселения.");
}
});
refreshButton.addActionListener(e -> loadReservations());
}
private void loadReservations() {
tableModel.setRowCount(0);
String query = "SELECT res.reservation_id, c.full_name, r.room_number, res.check_in, res.check_out, res.status, res.total_amount " +
"FROM Reservations res " +
"JOIN Customers c ON res.customer_id = c.customer_id " +
"JOIN Rooms r ON res.room_id = r.room_id";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("reservation_id"),
rs.getString("full_name"),
rs.getInt("room_number"),
rs.getDate("check_in"),
rs.getDate("check_out"),
rs.getString("status"),
rs.getBigDecimal("total_amount")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке бронирований.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void openReservationForm(Integer reservationId) {
new ReservationForm(reservationId, this, receptionistId);
}
private void deleteReservation(int reservationId) {
String query = "DELETE FROM Reservations WHERE reservation_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, reservationId);
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Бронирование успешно удалено.");
} catch (SQLException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при удалении бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void checkIn(int reservationId) {
// Проверка и обновление статуса бронирования
String updateQuery = "UPDATE Reservations SET status = 'Заселён', updated_at = NOW() WHERE reservation_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(updateQuery)) {
pstmt.setInt(1, reservationId);
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Клиент успешно заселен.");
} catch (SQLException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при заселении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void checkOut(int reservationId) {
String selectQuery = "SELECT * FROM Reservations WHERE reservation_id = ?";
String updateQuery = "UPDATE Reservations SET status = 'Выселен', updated_at = NOW() WHERE reservation_id = ?";
String insertArchiveQuery = "INSERT INTO Archived_Reservations (reservation_id, customer_id, room_id, check_in, check_out, status, total_amount, created_at, updated_at) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
String deleteQuery = "DELETE FROM Reservations WHERE reservation_id = ?";
Connection conn = null;
PreparedStatement selectStmt = null;
PreparedStatement updateStmt = null;
PreparedStatement insertArchiveStmt = null;
PreparedStatement deleteStmt = null;
try {
ConnectMySQL connection = new ConnectMySQL();
conn = connection.getConnection();
conn.setAutoCommit(false); // Начало транзакции
// Получение данных бронирования
selectStmt = conn.prepareStatement(selectQuery);
selectStmt.setInt(1, reservationId);
ResultSet rs = selectStmt.executeQuery();
if (!rs.next()) {
JOptionPane.showMessageDialog(this, "Бронирование не найдено.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
int customerId = rs.getInt("customer_id");
int roomId = rs.getInt("room_id");
Date checkIn = rs.getDate("check_in");
Date checkOut = rs.getDate("check_out");
String status = rs.getString("status");
double totalAmount = rs.getDouble("total_amount");
Timestamp createdAt = rs.getTimestamp("created_at");
Timestamp updatedAt = rs.getTimestamp("updated_at");
// Обновление статуса бронирования
updateStmt = conn.prepareStatement(updateQuery);
updateStmt.setInt(1, reservationId);
updateStmt.executeUpdate();
// Вставка в архив
insertArchiveStmt = conn.prepareStatement(insertArchiveQuery);
insertArchiveStmt.setInt(1, reservationId);
insertArchiveStmt.setInt(2, customerId);
insertArchiveStmt.setInt(3, roomId);
insertArchiveStmt.setDate(4, checkIn);
insertArchiveStmt.setDate(5, checkOut);
insertArchiveStmt.setString(6, "Выселен");
insertArchiveStmt.setDouble(7, totalAmount);
insertArchiveStmt.setTimestamp(8, createdAt);
insertArchiveStmt.setTimestamp(9, updatedAt);
insertArchiveStmt.executeUpdate();
// Удаление из текущих бронирований
deleteStmt = conn.prepareStatement(deleteQuery);
deleteStmt.setInt(1, reservationId);
deleteStmt.executeUpdate();
// Обновление статуса номера на 'Доступен'
String updateRoomQuery = "UPDATE Rooms SET status = 'Доступен' WHERE room_id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(updateRoomQuery)) {
pstmt.setInt(1, roomId);
pstmt.executeUpdate();
}
conn.commit(); // Коммит транзакции
JOptionPane.showMessageDialog(this, "Клиент успешно выселен.");
} catch (SQLException e) {
e.printStackTrace();
if (conn != null) {
try {
conn.rollback(); // Откат транзакции при ошибке
} catch (SQLException ex) {
ex.printStackTrace();
}
}
JOptionPane.showMessageDialog(this, "Ошибка при выселении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
} finally {
try {
if (selectStmt != null) selectStmt.close();
if (updateStmt != null) updateStmt.close();
if (insertArchiveStmt != null) insertArchiveStmt.close();
if (deleteStmt != null) deleteStmt.close();
if (conn != null) conn.setAutoCommit(true);
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
// Форма добавления/редактирования бронирования
class ReservationForm extends JDialog implements ActionListener {
private JComboBox<String> customerComboBox, roomComboBox;
private JDatePickerImpl checkInDatePicker, checkOutDatePicker;
private JButton saveButton, cancelButton;
private Integer reservationId; // Null для добавления, не null для редактирования
private ReservationsPanel parentPanel;
private int receptionistId;
public ReservationForm(Integer reservationId, ReservationsPanel parentPanel, int receptionistId) {
this.reservationId = reservationId;
this.parentPanel = parentPanel;
this.receptionistId = receptionistId;
setTitle(reservationId == null ? "Добавить Бронирование" : "Редактировать Бронирование");
setSize(500, 400);
setLocationRelativeTo(null);
setModal(true);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(10,10,10,10);
// Клиент
gbc.gridx = 0;
gbc.gridy = 0;
add(new JLabel("Клиент:"), gbc);
customerComboBox = new JComboBox<>();
loadCustomers();
gbc.gridx = 1;
add(customerComboBox, gbc);
// Номер
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Номер:"), gbc);
roomComboBox = new JComboBox<>();
loadAvailableRooms();
gbc.gridx = 1;
add(roomComboBox, gbc);
// Дата заезда
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Дата заезда:"), gbc);
checkInDatePicker = createDatePicker();
gbc.gridx = 1;
add(checkInDatePicker, gbc);
// Дата выезда
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Дата выезда:"), gbc);
checkOutDatePicker = createDatePicker();
gbc.gridx = 1;
add(checkOutDatePicker, gbc);
// Кнопки
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
JPanel buttonPanel = new JPanel();
saveButton = new JButton("Сохранить");
cancelButton = new JButton("Отмена");
buttonPanel.add(saveButton);
buttonPanel.add(cancelButton);
add(buttonPanel, gbc);
// Загрузка данных для редактирования
if (reservationId != null) {
loadReservationData();
}
// Обработчики
saveButton.addActionListener(this);
cancelButton.addActionListener(e -> dispose());
setVisible(true);
}
private void loadCustomers() {
String query = "SELECT customer_id, full_name FROM Customers";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
customerComboBox.addItem(rs.getInt("customer_id") + " - " + rs.getString("full_name"));
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке клиентов.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void loadAvailableRooms() {
// Упрощенный запрос для загрузки всех доступных номеров
String query = "SELECT room_id, room_number FROM Rooms WHERE status = 'Доступен'";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
roomComboBox.addItem(rs.getInt("room_id") + " - " + rs.getInt("room_number"));
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке номеров.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private JDatePickerImpl createDatePicker() {
UtilDateModel model = new UtilDateModel();
Properties p = new Properties();
p.put("text.today", "Сегодня");
p.put("text.month", "Месяц");
p.put("text.year", "Год");
JDatePanelImpl datePanel = new JDatePanelImpl(model, p);
return new JDatePickerImpl(datePanel, new DateLabelFormatter());
}
private void loadReservationData() {
String query = "SELECT * FROM Reservations WHERE reservation_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, reservationId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
customerComboBox.setSelectedItem(rs.getInt("customer_id") + " - " + getCustomerName(rs.getInt("customer_id")));
roomComboBox.setSelectedItem(rs.getInt("room_id") + " - " + getRoomNumber(rs.getInt("room_id")));
checkInDatePicker.getModel().setDate(rs.getDate("check_in").toLocalDate().getYear(), rs.getDate("check_in").toLocalDate().getMonthValue() - 1, rs.getDate("check_in").toLocalDate().getDayOfMonth());
checkInDatePicker.getModel().setSelected(true);
checkOutDatePicker.getModel().setDate(rs.getDate("check_out").toLocalDate().getYear(), rs.getDate("check_out").toLocalDate().getMonthValue() - 1, rs.getDate("check_out").toLocalDate().getDayOfMonth());
checkOutDatePicker.getModel().setSelected(true);
}
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке данных бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private String getCustomerName(int customerId) {
String name = "";
String query = "SELECT full_name FROM Customers WHERE customer_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, customerId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
name = rs.getString("full_name");
}
}
} catch (Exception e) {
e.printStackTrace();
}
return name;
}
private int getRoomNumber(int roomId) {
int roomNumber = -1;
String query = "SELECT room_number FROM Rooms WHERE room_id = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, roomId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
roomNumber = rs.getInt("room_number");
}
}
} catch (Exception e) {
e.printStackTrace();
}
return roomNumber;
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == saveButton) {
String customerStr = (String)customerComboBox.getSelectedItem();
String roomStr = (String)roomComboBox.getSelectedItem();
java.util.Date checkInDate = (java.util.Date)checkInDatePicker.getModel().getValue();
java.util.Date checkOutDate = (java.util.Date)checkOutDatePicker.getModel().getValue();
if (customerStr == null || roomStr == null || checkInDate == null || checkOutDate == null) {
JOptionPane.showMessageDialog(this, "Пожалуйста, заполните все поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
int customerId = Integer.parseInt(customerStr.split(" - ")[0]);
int roomId = Integer.parseInt(roomStr.split(" - ")[0]);
java.sql.Date checkInSqlDate = new java.sql.Date(checkInDate.getTime());
java.sql.Date checkOutSqlDate = new java.sql.Date(checkOutDate.getTime());
// Валидация дат
if (!validateDates(checkInSqlDate, checkOutSqlDate)) {
JOptionPane.showMessageDialog(this, "Дата выезда должна быть позже даты заезда.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
// Проверка доступности номера
if (!isRoomAvailable(roomId, checkInSqlDate, checkOutSqlDate, reservationId)) {
JOptionPane.showMessageDialog(this, "Номер уже забронирован на указанные даты.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
// Вычисление общей суммы
double totalAmount = calculateTotalAmount(roomId, checkInSqlDate, checkOutSqlDate);
try (ConnectMySQL connection = new ConnectMySQL()) {
Connection conn = connection.getConnection();
PreparedStatement pstmt;
if (reservationId == null) {
// Добавление нового бронирования
String query = "INSERT INTO Reservations (customer_id, room_id, check_in, check_out, status, total_amount, created_at, updated_at, receptionist_id) " +
"VALUES (?, ?, ?, ?, 'Забронирован', ?, NOW(), NOW(), ?)";
pstmt = conn.prepareStatement(query);
pstmt.setInt(1, customerId);
pstmt.setInt(2, roomId);
pstmt.setDate(3, checkInSqlDate);
pstmt.setDate(4, checkOutSqlDate);
pstmt.setDouble(5, totalAmount);
pstmt.setInt(6, receptionistId);
} else {
// Обновление существующего бронирования
String query = "UPDATE Reservations SET customer_id = ?, room_id = ?, check_in = ?, check_out = ?, status = 'Забронирован', total_amount = ?, updated_at = NOW(), receptionist_id = ? WHERE reservation_id = ?";
pstmt = conn.prepareStatement(query);
pstmt.setInt(1, customerId);
pstmt.setInt(2, roomId);
pstmt.setDate(3, checkInSqlDate);
pstmt.setDate(4, checkOutSqlDate);
pstmt.setDouble(5, totalAmount);
pstmt.setInt(6, receptionistId);
pstmt.setInt(7, reservationId);
}
pstmt.executeUpdate();
JOptionPane.showMessageDialog(this, "Бронирование успешно сохранено.");
parentPanel.loadReservations();
dispose();
} catch (SQLException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при сохранении бронирования.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
private boolean validateDates(java.sql.Date checkIn, java.sql.Date checkOut) {
return checkOut.after(checkIn);
}
private boolean isRoomAvailable(int roomId, java.sql.Date checkIn, java.sql.Date checkOut, Integer currentReservationId) {
String query = "SELECT COUNT(*) AS count FROM Reservations WHERE room_id = ? AND status NOT IN ('Отменён', 'Выселен') AND (" +
"(check_in <= ? AND check_out > ?) OR " +
"(check_in < ? AND check_out >= ?) OR " +
"(check_in >= ? AND check_out <= ?))";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, roomId);
pstmt.setDate(2, checkOut);
pstmt.setDate(3, checkIn);
pstmt.setDate(4, checkIn);
pstmt.setDate(5, checkOut);
pstmt.setDate(6, checkIn);
pstmt.setDate(7, checkOut);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
int count = rs.getInt("count");
if (currentReservationId != null && count > 0) {
// Исключение текущего бронирования из проверки
String excludeQuery = "SELECT COUNT(*) AS exclude_count FROM Reservations WHERE reservation_id = ? AND " +
"room_id = ? AND status NOT IN ('Отменён', 'Выселен') AND (" +
"(check_in <= ? AND check_out > ?) OR " +
"(check_in < ? AND check_out >= ?) OR " +
"(check_in >= ? AND check_out <= ?))";
try (PreparedStatement pstmtExclude = connection.getConnection().prepareStatement(excludeQuery)) {
pstmtExclude.setInt(1, currentReservationId);
pstmtExclude.setInt(2, roomId);
pstmtExclude.setDate(3, checkOut);
pstmtExclude.setDate(4, checkIn);
pstmtExclude.setDate(5, checkIn);
pstmtExclude.setDate(6, checkOut);
pstmtExclude.setDate(7, checkIn);
pstmtExclude.setDate(8, checkOut);
try (ResultSet rsExclude = pstmtExclude.executeQuery()) {
if (rsExclude.next()) {
int excludeCount = rsExclude.getInt("exclude_count");
if (excludeCount > 0) {
count -= excludeCount;
}
}
}
}
}
return count == 0;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private double calculateTotalAmount(int roomId, java.sql.Date checkIn, java.sql.Date checkOut) {
String query = "SELECT price FROM Rooms WHERE room_id = ?";
double price = 0.0;
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setInt(1, roomId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
price = rs.getDouble("price");
}
}
long diffInMillies = checkOut.getTime() - checkIn.getTime();
long days = diffInMillies / (1000 * 60 * 60 * 24);
return price * days;
} catch (Exception e) {
e.printStackTrace();
}
return price;
}
}
// Панель управления платежами
class PaymentsPanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JButton refreshButton;
private JLabel totalRevenueLabel;
public PaymentsPanel() {
setLayout(new BorderLayout());
// Панель для отображения общего дохода
JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
totalRevenueLabel = new JLabel("Общий доход: 0.00");
topPanel.add(totalRevenueLabel);
add(topPanel, BorderLayout.NORTH); // Добавление верхней панели
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"ID Платежа", "Бронирование ID", "Клиент", "Номер", "Дата оплаты", "Сумма", "Метод оплаты"});
table = new JTable(tableModel);
loadPayments();
add(new JScrollPane(table), BorderLayout.CENTER);
// Кнопки управления
JPanel buttonPanel = new JPanel();
refreshButton = new JButton("Обновить");
buttonPanel.add(refreshButton);
add(buttonPanel, BorderLayout.SOUTH);
// Обработчики событий
refreshButton.addActionListener(e -> loadPayments());
}
private void loadPayments() {
tableModel.setRowCount(0);
String query = "SELECT p.payment_id, p.reservation_id, c.full_name, r.room_number, " +
"p.payment_date, p.amount, p.payment_method " +
"FROM Payments p " +
"LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
"LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
"LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
"LEFT JOIN Rooms r ON (res.room_id = r.room_id OR ar.room_id = r.room_id)";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("payment_id"),
rs.getInt("reservation_id"),
rs.getString("full_name"),
rs.getInt("room_number"),
rs.getDate("payment_date"),
rs.getBigDecimal("amount"),
rs.getString("payment_method")
});
}
// Вычисление общего дохода
calculateTotalRevenue();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке платежей.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void calculateTotalRevenue() {
String query = "SELECT SUM(amount) AS total_revenue FROM Payments";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
double totalRevenue = rs.getDouble("total_revenue");
totalRevenueLabel.setText("Общий доход: " + String.format("%.2f", totalRevenue));
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при расчете общего дохода.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
// Панель архивов бронирований
class ArchivePanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JButton refreshButton;
public ArchivePanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"Архив ID", "Бронирование ID", "Клиент", "Номер", "Дата заезда", "Дата выезда", "Статус", "Сумма"});
table = new JTable(tableModel);
loadArchivedReservations();
add(new JScrollPane(table), BorderLayout.CENTER);
// Кнопка обновления
JPanel buttonPanel = new JPanel();
refreshButton = new JButton("Обновить");
buttonPanel.add(refreshButton);
add(buttonPanel, BorderLayout.SOUTH);
refreshButton.addActionListener(e -> loadArchivedReservations());
}
private void loadArchivedReservations() {
tableModel.setRowCount(0);
String query = "SELECT ar.archive_id, ar.reservation_id, c.full_name, r.room_number, ar.check_in, ar.check_out, ar.status, ar.total_amount " +
"FROM Archived_Reservations ar " +
"JOIN Customers c ON ar.customer_id = c.customer_id " +
"JOIN Rooms r ON ar.room_id = r.room_id";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("archive_id"),
rs.getInt("reservation_id"),
rs.getString("full_name"),
rs.getInt("room_number"),
rs.getDate("check_in"),
rs.getDate("check_out"),
rs.getString("status"),
rs.getBigDecimal("total_amount")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке архивных бронирований.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
// Класс для форматирования даты в JDatePicker
class DateLabelFormatter extends JFormattedTextField.AbstractFormatter {
private String datePattern = "yyyy-MM-dd";
private SimpleDateFormat dateFormatter = new SimpleDateFormat(datePattern);
@Override
public Object stringToValue(String text) throws ParseException {
if (text == null || text.isEmpty()) {
return null;
}
return dateFormatter.parse(text);
}
@Override
public String valueToString(Object value) throws ParseException {
if (value != null) {
if (value instanceof java.util.Date) {
java.util.Date date = (java.util.Date) value;
return dateFormatter.format(date);
} else if (value instanceof java.util.Calendar) {
java.util.Calendar cal = (java.util.Calendar) value;
return dateFormatter.format(cal.getTime());
}
}
return "";
}
}
}
// src/h/ConnectMySQL.java
package h;
import java.sql.*;
public class ConnectMySQL implements AutoCloseable {
private static final String URL = "jdbc:mysql://localhost:1369/hotel?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASSWORD = "1369sukAnigghaD1ck";
private Connection connection;
public ConnectMySQL() throws SQLException {
// Класс загрузки драйвера MySQL JDBC теперь не нужен
this.connection = DriverManager.getConnection(URL, USER, PASSWORD);
}
public Connection getConnection() {
return connection;
}
@Override
public void close() throws SQLException {
if (connection != null && !connection.isClosed()) {
connection.close();
}
}
}
// src/h/Login.java
package h;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.sql.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Login extends JFrame implements ActionListener {
private JLabel usernameLabel, passwordLabel;
private JTextField usernameField;
private JPasswordField passwordField;
private JButton loginButton, cancelButton;
private JCheckBox showPasswordCheck;
private JButton signupLink;
public Login() {
setTitle("Авторизация");
setBounds(700, 300, 400, 300);
setLayout(null);
// Метка и поле ввода для логина
usernameLabel = new JLabel("Имя пользователя:");
usernameLabel.setBounds(50, 50, 120, 30);
add(usernameLabel);
usernameField = new JTextField();
usernameField.setBounds(180, 50, 150, 30);
add(usernameField);
// Метка и поле ввода для пароля
passwordLabel = new JLabel("Пароль:");
passwordLabel.setBounds(50, 100, 120, 30);
add(passwordLabel);
passwordField = new JPasswordField();
passwordField.setBounds(180, 100, 150, 30);
add(passwordField);
// Чекбокс для показа пароля
showPasswordCheck = new JCheckBox("Показать пароль");
showPasswordCheck.setBounds(180, 140, 150, 25);
showPasswordCheck.setBackground(Color.WHITE);
showPasswordCheck.addActionListener(this);
add(showPasswordCheck);
// Кнопка входа
loginButton = new JButton("Войти");
loginButton.setBounds(50, 180, 120, 30);
loginButton.setForeground(Color.BLACK);
loginButton.setBackground(Color.BLACK);
loginButton.addActionListener(this);
add(loginButton);
// Кнопка отмены
cancelButton = new JButton("Выход");
cancelButton.setBounds(210, 180, 120, 30);
cancelButton.setForeground(Color.BLACK);
cancelButton.setBackground(Color.BLACK);
cancelButton.addActionListener(this);
add(cancelButton);
// Ссылка на регистрацию
signupLink = new JButton("<HTML><U>Нет аккаунта? Зарегистрироваться</U></HTML>");
signupLink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
signupLink.setBorderPainted(false);
signupLink.setForeground(Color.BLUE.darker());
signupLink.setBackground(Color.WHITE);
signupLink.setFocusPainted(false);
signupLink.setBounds(100, 220, 200, 25);
signupLink.addActionListener(this);
add(signupLink);
getContentPane().setBackground(Color.WHITE);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
// Метод для хеширования пароля
private String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : hash) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
// Обработка событий
public void actionPerformed(ActionEvent ae) {
// Обработка чекбокса показа пароля
if (ae.getSource() == showPasswordCheck) {
if (showPasswordCheck.isSelected()) {
passwordField.setEchoChar((char) 0);
} else {
passwordField.setEchoChar('•');
}
}
// Обработка кнопки "Войти"
if (ae.getSource() == loginButton) {
authenticateUser();
}
// Обработка кнопки "Отмена"
if (ae.getSource() == cancelButton) {
int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите выйти?", "Подтверждение", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
// Обработка ссылки на регистрацию
if (ae.getSource() == signupLink) {
new SignUp().setVisible(true);
this.dispose();
}
}
// Метод аутентификации пользователя
private void authenticateUser() {
String username = usernameField.getText().trim();
String password = new String(passwordField.getPassword()).trim();
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "Пожалуйста, введите имя пользователя и пароль.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
String hashedPassword = hashPassword(password);
if (hashedPassword == null) {
JOptionPane.showMessageDialog(this, "Ошибка хеширования пароля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
// Попытка авторизоваться как Администратор
Pr user = authenticateInRole("Administrators", "admin_id", "login", "password");
if (user != null) {
JOptionPane.showMessageDialog(this, "Добро пожаловать, Администратор!");
new DashboardAdmin(user.getId()).setVisible(true);
this.dispose();
return;
}
// Попытка авторизоваться как Ресепшионист
user = authenticateInRole("Receptionists", "receptionist_id", "login", "password");
if (user != null) {
JOptionPane.showMessageDialog(this, "Добро пожаловать, Ресепшионист!");
new DashboardReceptionist(user.getId()).setVisible(true);
this.dispose();
return;
}
// Если пользователь не найден
JOptionPane.showMessageDialog(this, "Неверные имя пользователя или пароль.", "Ошибка", JOptionPane.ERROR_MESSAGE);
usernameField.setText("");
passwordField.setText("");
}
/**
* Метод для аутентификации пользователя в заданной роли.
*
* @param tableName Название таблицы (Administrators или Receptionists)
* @param idColumn Название столбца ID (admin_id или receptionist_id)
* @param loginColumn Название столбца с логином
* @param passwordColumn Название столбца с паролем
* @return Объект Pr с информацией о пользователе или null, если не найден
*/
private Pr authenticateInRole(String tableName, String idColumn, String loginColumn, String passwordColumn) {
String query = "SELECT * FROM " + tableName + " WHERE " + loginColumn + " = ? AND " + passwordColumn + " = ?";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query)) {
pstmt.setString(1, usernameField.getText().trim());
pstmt.setString(2, hashPassword(new String(passwordField.getPassword()).trim()));
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
int id = rs.getInt(idColumn);
String fullName = rs.getString("full_name");
String role = tableName.equals("Administrators") ? "Administrator" : "Receptionist";
return new Pr(id, fullName, role);
}
}
} catch (SQLException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
return null;
}
public static void main(String[] args) {
// Установка системного вида для современного оформления
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ignored) {}
new Login();
}
}
// src/h/Pr.java
package h;
public class Pr {
private int id;
private String fullName;
private String role;
public Pr(int id, String fullName, String role) {
this.id = id;
this.fullName = fullName;
this.role = role;
}
public int getId() {
return id;
}
public String getFullName() {
return fullName;
}
public String getRole() {
return role;
}
}
// src/h/SignUp.java
package h;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.sql.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class SignUp extends JFrame implements ActionListener {
private JTextField nameField, passportField, addressField, phoneField, emailField, loginField;
private JPasswordField passwordField;
private JCheckBox showPasswordCheck;
private JButton signupButton, cancelButton;
private JButton loginLink;
public SignUp() {
setTitle("Регистрация Администратора");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 500);
setLocationRelativeTo(null);
JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(5,5,5,5);
// Полное имя
JLabel nameLabel = new JLabel("Полное имя:");
gbc.gridx = 0;
gbc.gridy = 0;
panel.add(nameLabel, gbc);
nameField = new JTextField(20);
gbc.gridx = 1;
panel.add(nameField, gbc);
// Номер паспорта
JLabel passportLabel = new JLabel("Номер паспорта:");
gbc.gridx = 0;
gbc.gridy++;
panel.add(passportLabel, gbc);
passportField = new JTextField(20);
gbc.gridx = 1;
panel.add(passportField, gbc);
// Адрес
JLabel addressLabel = new JLabel("Адрес:");
gbc.gridx = 0;
gbc.gridy++;
panel.add(addressLabel, gbc);
addressField = new JTextField(20);
gbc.gridx = 1;
panel.add(addressField, gbc);
// Телефон
JLabel phoneLabel = new JLabel("Телефон:");
gbc.gridx = 0;
gbc.gridy++;
panel.add(phoneLabel, gbc);
phoneField = new JTextField(20);
gbc.gridx = 1;
panel.add(phoneField, gbc);
// Email
JLabel emailLabel = new JLabel("Email:");
gbc.gridx = 0;
gbc.gridy++;
panel.add(emailLabel, gbc);
emailField = new JTextField(20);
gbc.gridx = 1;
panel.add(emailField, gbc);
// Логин
JLabel loginLabel = new JLabel("Логин:");
gbc.gridx = 0;
gbc.gridy++;
panel.add(loginLabel, gbc);
loginField = new JTextField(20);
gbc.gridx = 1;
panel.add(loginField, gbc);
// Пароль
JLabel passwordLabel = new JLabel("Пароль:");
gbc.gridx = 0;
gbc.gridy++;
panel.add(passwordLabel, gbc);
passwordField = new JPasswordField(20);
gbc.gridx = 1;
panel.add(passwordField, gbc);
// Чекбокс для показа пароля
showPasswordCheck = new JCheckBox("Показать пароль");
gbc.gridx = 1;
gbc.gridy++;
panel.add(showPasswordCheck, gbc);
showPasswordCheck.addActionListener(this);
// Кнопки регистрации и отмены
signupButton = new JButton("Зарегистрироваться");
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
gbc.insets = new Insets(20,5,5,5);
panel.add(signupButton, gbc);
signupButton.addActionListener(this);
cancelButton = new JButton("Отмена");
gbc.gridy++;
panel.add(cancelButton, gbc);
cancelButton.addActionListener(this);
// Ссылка на окно входа
loginLink = new JButton("<HTML><U>Уже есть аккаунт? Войти</U></HTML>");
loginLink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
loginLink.setBorderPainted(false);
loginLink.setForeground(Color.BLUE.darker());
loginLink.setBackground(Color.WHITE);
loginLink.setFocusPainted(false);
loginLink.addActionListener(this);
gbc.gridy++;
panel.add(loginLink, gbc);
add(panel);
setVisible(true);
}
// Метод для хеширования пароля
private String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for(byte b : hashBytes){
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
// Обработка событий
@Override
public void actionPerformed(ActionEvent ae) {
Object source = ae.getSource();
if (source == showPasswordCheck) {
if (showPasswordCheck.isSelected()) {
passwordField.setEchoChar((char)0);
} else {
passwordField.setEchoChar('•');
}
}
if (source == signupButton) {
String fullName = nameField.getText().trim();
String passportNumber = passportField.getText().trim();
String address = addressField.getText().trim();
String phone = phoneField.getText().trim();
String email = emailField.getText().trim();
String login = loginField.getText().trim();
String password = new String(passwordField.getPassword()).trim();
// Валидация
if (fullName.isEmpty() || passportNumber.isEmpty() || address.isEmpty() ||
phone.isEmpty() || email.isEmpty() || login.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "Пожалуйста, заполните все обязательные поля.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
try {
ConnectMySQL connection = new ConnectMySQL();
Connection conn = connection.getConnection();
String query = "INSERT INTO Administrators (full_name, passport_number, address, phone_number, email, login, password, created_at, updated_at) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, fullName);
pstmt.setString(2, passportNumber);
pstmt.setString(3, address);
pstmt.setString(4, phone);
pstmt.setString(5, email);
pstmt.setString(6, login);
pstmt.setString(7, hashPassword(password));
int rowsAffected = pstmt.executeUpdate();
if (rowsAffected > 0) {
JOptionPane.showMessageDialog(this, "Аккаунт администратора успешно создан!");
new Login().setVisible(true);
this.dispose();
} else {
JOptionPane.showMessageDialog(this, "Ошибка при создании аккаунта. Пожалуйста, попробуйте снова.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
conn.close();
} catch (SQLIntegrityConstraintViolationException e) {
JOptionPane.showMessageDialog(this, "Такой логин, номер паспорта, телефон или email уже существуют!", "Ошибка", JOptionPane.ERROR_MESSAGE);
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка подключения к базе данных.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
if (source == cancelButton) {
int confirm = JOptionPane.showConfirmDialog(this, "Вы уверены, что хотите отменить регистрацию?", "Подтверждение", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
new Login().setVisible(true);
this.dispose();
}
}
if (source == loginLink) {
new Login().setVisible(true);
this.dispose();
}
}
public static void main(String[] args) {
// Установка системного вида для современного оформления
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ignored) {}
new SignUp();
}
}
у меня идея что после того как клиента выселит ресепшионист или автоматически выселится то его запись удаляется из вкладки бронирований и переходит во вкладку архивов и информация об оплате при этом должна также сохранится во вкладке платежей на панелях и администратора и ресепшионистаASSISTANT
Ваше предложение по перемещению записей о бронированиях в архив после выселения клиента и сохранению информации об оплате — отличная идея для улучшения управления данными в системе гостиничного бизнеса. Давайте подробно рассмотрим, как можно реализовать эту функциональность, обеспечив сохранность данных и корректное отображение информации как на панелях администратора, так и ресепшиониста.
## Основные Шаги Реализации
1. **Изменение Схемы Базы Данных**
2. **Обновление Логики Приложения при Выселении Клиента**
3. **Обновление Панелей Администратора и Ресепшиониста для Учета Архивных Данных**
4. **Дополнительные Рекомендации по Улучшению**
### 1. Изменение Схемы Базы Данных
Ваша текущая схема базы данных имеет следующую структуру для таблицы `Payments`:
```sql
CREATE TABLE Payments (
payment_id INT AUTO_INCREMENT PRIMARY KEY,
reservation_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
payment_date DATE NOT NULL,
payment_method ENUM('Наличные', 'Кредитная карта', 'Дебетовая карта', 'Онлайн перевод') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (reservation_id) REFERENCES Reservations(reservation_id) ON DELETE CASCADE
);
```
**Проблема:** Использование `ON DELETE CASCADE` приводит к удалению записей о платежах при удалении бронирования, что нарушает целостность данных, поскольку информация об оплате должна сохраняться.
**Решение:** Нам необходимо изменить поведение внешнего ключа, чтобы платежи сохранялись даже после удаления бронирования. Для этого предложены два подхода:
#### a. Изменение Поведения Внешнего Ключа
1. **Сделать `reservation_id` в `Payments` `NULL` допускающим:**
```sql
ALTER TABLE Payments MODIFY reservation_id INT NULL;
```
2. **Изменить внешнee ключ на `ON DELETE SET NULL`:**
```sql
ALTER TABLE Payments DROP FOREIGN KEY [имя_констрейнта];
ALTER TABLE Payments ADD FOREIGN KEY (reservation_id) REFERENCES Reservations(reservation_id) ON DELETE SET NULL;
```
> **Примечание:** Вам нужно узнать имя текущего внешнего ключа `FOREIGN KEY` в вашей базе данных, чтобы правильно его удалить.
#### b. Создание Таблицы Архивных Платежей
1. **Создать новую таблицу `Archived_Payments`:**
```sql
CREATE TABLE IF NOT EXISTS Archived_Payments (
payment_id INT AUTO_INCREMENT PRIMARY KEY,
reservation_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
payment_date DATE NOT NULL,
payment_method ENUM('Наличные', 'Кредитная карта', 'Дебетовая карта', 'Онлайн перевод') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (reservation_id) REFERENCES Archived_Reservations(reservation_id) ON DELETE CASCADE
);
```
2. **Пересмотр логики приложения для копирования платежей в архив:**
При выселении клиента необходимо копировать связанные платежи из `Payments` в `Archived_Payments` перед удалением записи о бронировании.
### 2. Обновление Логики Приложения при Выселении Клиента
В вашем `DashboardReceptionist.java` уже реализован метод `checkOut`, который выполняет выселение клиента. Мы дополнительно внедрим в этот метод копирование платежей в архив.
#### Обновленный Метод `checkOut`
```java
private void checkOut(int reservationId) {
String selectQuery = "SELECT * FROM Reservations WHERE reservation_id = ?";
String updateReservationQuery = "UPDATE Reservations SET status = 'Выселен', updated_at = NOW() WHERE reservation_id = ?";
String insertArchiveReservationQuery = "INSERT INTO Archived_Reservations (reservation_id, customer_id, room_id, check_in, check_out, status, total_amount, created_at, updated_at) " +
"VALUES (?, ?, ?, ?, ?, 'Выселен', ?, ?, ?)";
String deleteReservationQuery = "DELETE FROM Reservations WHERE reservation_id = ?";
String selectPaymentsQuery = "SELECT * FROM Payments WHERE reservation_id = ?";
String insertArchivePaymentsQuery = "INSERT INTO Archived_Payments (reservation_id, amount, payment_date, payment_method, created_at) " +
"VALUES (?, ?, ?, ?, ?)";
String deletePaymentsQuery = "DELETE FROM Payments WHERE reservation_id = ?";
String updateRoomStatusQuery = "UPDATE Rooms SET status = 'Доступен' WHERE room_id = ?";
Connection conn = null;
PreparedStatement selectStmt = null, updateStmt = null, insertArchiveReservationStmt = null;
PreparedStatement selectPaymentsStmt = null, insertArchivePaymentsStmt = null, deletePaymentsStmt = null;
PreparedStatement deleteReservationStmt = null;
PreparedStatement updateRoomStmt = null;
try {
ConnectMySQL connection = new ConnectMySQL();
conn = connection.getConnection();
conn.setAutoCommit(false); // Начало транзакции
// Получение данных бронирования
selectStmt = conn.prepareStatement(selectQuery);
selectStmt.setInt(1, reservationId);
try (ResultSet rs = selectStmt.executeQuery()) {
if (!rs.next()) {
JOptionPane.showMessageDialog(this, "Бронирование не найдено.", "Ошибка", JOptionPane.ERROR_MESSAGE);
return;
}
int customerId = rs.getInt("customer_id");
int roomId = rs.getInt("room_id");
Date checkIn = rs.getDate("check_in");
Date checkOut = rs.getDate("check_out");
String status = rs.getString("status");
double totalAmount = rs.getDouble("total_amount");
Timestamp createdAt = rs.getTimestamp("created_at");
Timestamp updatedAt = rs.getTimestamp("updated_at");
// Обновление статуса бронирования
updateStmt = conn.prepareStatement(updateReservationQuery);
updateStmt.setInt(1, reservationId);
updateStmt.executeUpdate();
// Вставка в архив бронирования
insertArchiveReservationStmt = conn.prepareStatement(insertArchiveReservationQuery);
insertArchiveReservationStmt.setInt(1, reservationId);
insertArchiveReservationStmt.setInt(2, customerId);
insertArchiveReservationStmt.setInt(3, roomId);
insertArchiveReservationStmt.setDate(4, checkIn);
insertArchiveReservationStmt.setDate(5, checkOut);
insertArchiveReservationStmt.setDouble(6, totalAmount);
insertArchiveReservationStmt.setTimestamp(7, createdAt);
insertArchiveReservationStmt.setTimestamp(8, updatedAt);
insertArchiveReservationStmt.executeUpdate();
// Копирование платежей в Archived_Payments
selectPaymentsStmt = conn.prepareStatement(selectPaymentsQuery);
selectPaymentsStmt.setInt(1, reservationId);
try (ResultSet rsPayments = selectPaymentsStmt.executeQuery()) {
while (rsPayments.next()) {
insertArchivePaymentsStmt = conn.prepareStatement(insertArchivePaymentsQuery);
insertArchivePaymentsStmt.setInt(1, rsPayments.getInt("reservation_id"));
insertArchivePaymentsStmt.setDouble(2, rsPayments.getDouble("amount"));
insertArchivePaymentsStmt.setDate(3, rsPayments.getDate("payment_date"));
insertArchivePaymentsStmt.setString(4, rsPayments.getString("payment_method"));
insertArchivePaymentsStmt.setTimestamp(5, rsPayments.getTimestamp("created_at"));
insertArchivePaymentsStmt.executeUpdate();
insertArchivePaymentsStmt.close(); // Закрытие после использования
}
}
// Удаление платежей из Payments
deletePaymentsStmt = conn.prepareStatement(deletePaymentsQuery);
deletePaymentsStmt.setInt(1, reservationId);
deletePaymentsStmt.executeUpdate();
// Удаление бронирования из Reservations
deleteReservationStmt = conn.prepareStatement(deleteReservationQuery);
deleteReservationStmt.setInt(1, reservationId);
deleteReservationStmt.executeUpdate();
// Обновление статуса номера на 'Доступен'
updateRoomStmt = conn.prepareStatement(updateRoomStatusQuery);
updateRoomStmt.setInt(1, roomId);
updateRoomStmt.executeUpdate();
conn.commit(); // Коммит транзакции
JOptionPane.showMessageDialog(this, "Клиент успешно выселен и архивирован.");
} catch (SQLException e) {
e.printStackTrace();
if (conn != null) {
try {
conn.rollback(); // Откат транзакции при ошибке
} catch (SQLException ex) {
ex.printStackTrace();
}
}
JOptionPane.showMessageDialog(this, "Ошибка при выселении клиента.", "Ошибка", JOptionPane.ERROR_MESSAGE);
} finally {
try {
if (selectStmt != null) selectStmt.close();
if (updateStmt != null) updateStmt.close();
if (insertArchiveReservationStmt != null) insertArchiveReservationStmt.close();
if (selectPaymentsStmt != null) selectPaymentsStmt.close();
if (insertArchivePaymentsStmt != null) insertArchivePaymentsStmt.close();
if (deletePaymentsStmt != null) deletePaymentsStmt.close();
if (deleteReservationStmt != null) deleteReservationStmt.close();
if (updateRoomStmt != null) updateRoomStmt.close();
if (conn != null) {
conn.setAutoCommit(true);
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
```
**Пояснения:**
1. **Транзакция:** Все операции выполняются в рамках транзакции, чтобы обеспечить целостность данных. Если какая-либо операция завершится неудачно, все изменения будут откатаны.
2. **Копирование Платежей:** Перед удалением бронирования из таблицы `Reservations`, связанные платежи копируются в таблицу `Archived_Payments`.
3. **Удаление Платежей:** После копирования платежей, они удаляются из основной таблицы `Payments`.
4. **Обновление Статуса Номера:** После выселения номера статус обновляется на 'Доступен'.
### 3. Обновление Панелей Администратора и Ресепшиониста для Учета Архивных Данных
Теперь, когда платежи копируются в `Archived_Payments`, необходимо обновить панели администратора и ресепшиониста, чтобы они отображали как текущие, так и архивные платежи.
#### a. Обновление `FinancePanel` (Панель Администратора)
```java
// Панель финансов
class FinancePanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JLabel totalRevenueLabel;
public FinancePanel() {
setLayout(new BorderLayout());
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"Платеж ID", "Бронирование ID", "Клиент", "Номер", "Дата оплаты", "Сумма", "Метод оплаты"});
table = new JTable(tableModel);
loadPayments();
add(new JScrollPane(table), BorderLayout.CENTER);
// Панель для отображения общего дохода
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
totalRevenueLabel = new JLabel("Общий доход: 0.00");
bottomPanel.add(totalRevenueLabel);
add(bottomPanel, BorderLayout.SOUTH);
calculateTotalRevenue();
}
private void loadPayments() {
tableModel.setRowCount(0);
String query = "SELECT p.payment_id, p.reservation_id, c.full_name, r.room_number, " +
"p.payment_date, p.amount, p.payment_method " +
"FROM Payments p " +
"LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
"LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
"LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
"LEFT JOIN Rooms r ON (res.room_id = r.room_id OR ar.room_id = r.room_id)";
String archiveQuery = "SELECT ap.payment_id, ap.reservation_id, c.full_name, r.room_number, " +
"ap.payment_date, ap.amount, ap.payment_method " +
"FROM Archived_Payments ap " +
"JOIN Archived_Reservations ar ON ap.reservation_id = ar.reservation_id " +
"JOIN Customers c ON ar.customer_id = c.customer_id " +
"JOIN Rooms r ON ar.room_id = r.room_id";
String finalQuery = query + " UNION ALL " + archiveQuery;
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(finalQuery);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("payment_id"),
rs.getInt("reservation_id"),
rs.getString("full_name"),
rs.getInt("room_number"),
rs.getDate("payment_date"),
rs.getBigDecimal("amount"),
rs.getString("payment_method")
});
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке платежей.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void calculateTotalRevenue() {
String query = "SELECT SUM(amount) AS total_revenue FROM (" +
"SELECT amount FROM Payments " +
"UNION ALL " +
"SELECT amount FROM Archived_Payments" +
") AS combined_payments";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
double totalRevenue = rs.getDouble("total_revenue");
totalRevenueLabel.setText("Общий доход: " + String.format("%.2f", totalRevenue));
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при расчете общего дохода.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
```
**Пояснения:**
1. **Объединение Таблиц Платежей:** Используется `UNION ALL` для объединения записей из `Payments` и `Archived_Payments`, что позволяет отображать все платежи в одном представлении.
2. **Расчет Общего Дохода:** Общий доход рассчитывается путем суммирования всех платежей из объединенной выборки.
#### b. Обновление `PaymentsPanel` (Панель Ресепшиониста)
Аналогично панели администратора, необходимо обновить панель ресепшиониста для отображения архивных платежей:
```java
// Панель управления платежами
class PaymentsPanel extends JPanel {
private JTable table;
private DefaultTableModel tableModel;
private JButton refreshButton;
private JLabel totalRevenueLabel;
public PaymentsPanel() {
setLayout(new BorderLayout());
// Панель для отображения общего дохода
JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
totalRevenueLabel = new JLabel("Общий доход: 0.00");
topPanel.add(totalRevenueLabel);
add(topPanel, BorderLayout.NORTH); // Добавление верхней панели
// Создание модели таблицы
tableModel = new DefaultTableModel();
tableModel.setColumnIdentifiers(new String[] {"ID Платежа", "Бронирование ID", "Клиент", "Номер", "Дата оплаты", "Сумма", "Метод оплаты"});
table = new JTable(tableModel);
loadPayments();
add(new JScrollPane(table), BorderLayout.CENTER);
// Кнопки управления
JPanel buttonPanel = new JPanel();
refreshButton = new JButton("Обновить");
buttonPanel.add(refreshButton);
add(buttonPanel, BorderLayout.SOUTH);
// Обработчики событий
refreshButton.addActionListener(e -> loadPayments());
}
private void loadPayments() {
tableModel.setRowCount(0);
String query = "SELECT p.payment_id, p.reservation_id, c.full_name, r.room_number, " +
"p.payment_date, p.amount, p.payment_method " +
"FROM Payments p " +
"LEFT JOIN Reservations res ON p.reservation_id = res.reservation_id " +
"LEFT JOIN Archived_Reservations ar ON p.reservation_id = ar.reservation_id " +
"LEFT JOIN Customers c ON (res.customer_id = c.customer_id OR ar.customer_id = c.customer_id) " +
"LEFT JOIN Rooms r ON (res.room_id = r.room_id OR ar.room_id = r.room_id)";
String archiveQuery = "SELECT ap.payment_id, ap.reservation_id, c.full_name, r.room_number, " +
"ap.payment_date, ap.amount, ap.payment_method " +
"FROM Archived_Payments ap " +
"JOIN Archived_Reservations ar ON ap.reservation_id = ar.reservation_id " +
"JOIN Customers c ON ar.customer_id = c.customer_id " +
"JOIN Rooms r ON ar.room_id = r.room_id";
String finalQuery = query + " UNION ALL " + archiveQuery;
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(finalQuery);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
tableModel.addRow(new Object[] {
rs.getInt("payment_id"),
rs.getInt("reservation_id"),
rs.getString("full_name"),
rs.getInt("room_number"),
rs.getDate("payment_date"),
rs.getBigDecimal("amount"),
rs.getString("payment_method")
});
}
// Вычисление общего дохода
calculateTotalRevenue();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при загрузке платежей.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
private void calculateTotalRevenue() {
String query = "SELECT SUM(amount) AS total_revenue FROM (" +
"SELECT amount FROM Payments " +
"UNION ALL " +
"SELECT amount FROM Archived_Payments" +
") AS combined_payments";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
double totalRevenue = rs.getDouble("total_revenue");
totalRevenueLabel.setText("Общий доход: " + String.format("%.2f", totalRevenue));
}
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Ошибка при расчете общего дохода.", "Ошибка", JOptionPane.ERROR_MESSAGE);
}
}
}
```
### 4. Дополнительные Рекомендации по Улучшению
1. **Автоматическое Выселение Клиентов:**
- **Задача:** Автоматически проверять и выселять клиентов, чей срок пребывания истек.
- **Реализация:**
- Используйте ScheduledExecutorService или внешний планировщик задач (например, cron) для периодической проверки базы данных.
- При обнаружении бронирования с `check_out` датой раньше текущей даты и статусом не "Выселен" или "Отменён", вызывайте метод `checkOut`.
Пример использования `ScheduledExecutorService`:
```java
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.sql.*;
public class AutoCheckoutService {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void startService() {
Runnable task = () -> {
String query = "SELECT reservation_id FROM Reservations WHERE check_out < CURDATE() AND status NOT IN ('Выселен', 'Отменён')";
try (ConnectMySQL connection = new ConnectMySQL();
PreparedStatement pstmt = connection.getConnection().prepareStatement(query);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
int reservationId = rs.getInt("reservation_id");
// Вызов метода выселения
// Предполагается, что метод checkOut доступен здесь или через соответствующий класс
// Например:
// new ReservationsPanel().checkOut(reservationId);
}
} catch (SQLException e) {
e.printStackTrace();
}
};
// Планирование задачи на ежедневное выполнение в полночь
scheduler.scheduleAtFixedRate(task, 0, 1, TimeUnit.DAYS);
}
public void stopService() {
scheduler.shutdown();
}
}
```
> **Примечание:** Реализация метода `checkOut` может потребовать рефакторинга, чтобы он был доступен из сервиса автоматического выселения (например, сделать его `static` или использовать подходящий дизайн-паттерн).
2. **Обработка Исключений и Логирование:**
- Убедитесь, что все потенциальные точки сбоя обрабатываются корректно с информативными сообщениями пользователю.
- Реализуйте систему логирования для упрощения отладки и мониторинга.
3. **Улучшение Пользовательского Интерфейса:**
- Добавьте подтверждающие диалоговые окна при выполнении критических операций (например, при высокеликлении).
- Используйте визуальные индикаторы статусов (например, цветной код для различных статусов номеров).
4. **Безопасность:**
- Убедитесь, что все вводимые данные проверяются на корректность и защищены от SQL-инъекций (использование `PreparedStatement` уже помогает в этом).
- Храните пароли в зашифрованном виде (что уже реализовано с использованием SHA-256).
5. **Оптимизация Запросов:**
- Используйте индексы для часто используемых полей, таких как `reservation_id`, `customer_id`, и `room_id`, для повышения производительности запросов.
## Заключение
Реализовав предложенные изменения, вы сможете эффективно управлять записями о бронированиях и платежах, обеспечивая их сохранность и доступность для отображения на соответствующих панелях как администратора, так и ресепшиониста. Кроме того, дополнительные рекомендации помогут улучшить общую надежность и удобство использования вашей системы.
Если возникнут дополнительные вопросы или потребуется помощь с реализацией конкретных элементов, не стесняйтесь обращаться!