
Кольцова А.А. Разработка прототипа системы бронирования велопрокатной организации
.pdf101
5.1 Описание организации информационной базы
5.1.1 Описание входящей информации
Реестр входящей информации представлен в таблице 12.
Таблица 12 – Реестр входящей информации
№ |
Наименование |
Обрабатывает |
Откуда |
Формат |
Описание |
|
|
|
поступает |
|
|
1 |
Данные о |
Система |
Пользователи |
Электронный |
Включает |
|
пользователе |
бронирования |
|
|
информацию о |
|
|
|
|
|
пользователе, такие |
|
|
|
|
|
как имя, email, |
|
|
|
|
|
телефон. Эти |
|
|
|
|
|
данные |
|
|
|
|
|
используются для |
|
|
|
|
|
регистрации и |
|
|
|
|
|
авторизации. |
2 |
Учетные |
Система |
Пользователи |
Электронный |
Включает |
|
данные |
бронирования |
|
|
информацию для |
|
пользователя |
|
|
|
авторизации, такие |
|
|
|
|
|
как логин и пароль. |
3 |
Данные о |
Система |
Пользователи |
Электронный |
Включает |
|
бронировании |
бронирования |
|
|
информацию о |
|
|
|
|
|
бронировании, |
|
|
|
|
|
такие как даты, |
|
|
|
|
|
время и выбранный |
|
|
|
|
|
велосипед. |
4 |
Данные на |
Система |
Менеджеры |
Электронный |
Включает |
|
обновление |
бронирования |
по продажам |
|
контактные данные |
|
информации о |
|
|
|
клиента, историю |
|
клиенте |
|
|
|
бронирований, |
|
|
|
|
|
отзывы. |
5 |
Данные на |
Система |
Менеджеры |
Электронный |
Включает |
|
обновление |
бронирования |
по продажам |
|
информацию о |
|
каталога |
|
|
|
велосипедах, такие |
|
|
|
|
|
как модель, |
|
|
|
|
|
доступность, |
|
|
|
|
|
техническое |
|
|
|
|
|
состояние. |
6 |
Данные для |
Система |
Менеджеры |
Электронный |
Включает данные о |
|
формирования |
бронирования |
по продажам |
|
бронированиях, |
|
отчета о |
|
|
|
такие как даты, |
|
бронировании |
|
|
|
время, модели |
|
|
|
|
|
велосипедов и |
|
|
|
|
|
клиенты. |
|
5.1.2 Описание исходящей информации |
|
|
||
|
Реестр исходящей информации представлен в таблице 13. |
|
102
Таблица 13 – Реестр исходящей информации
№ |
Наименование |
Обрабатывает |
Кому |
Формат |
Описание |
|
|
|
отправляется |
|
|
1 |
Подтверждение |
Система |
Пользователи |
Электронный |
Документ, |
|
регистрации |
бронирования |
|
|
подтверждающий |
|
|
|
|
|
успешную |
|
|
|
|
|
регистрацию |
|
|
|
|
|
пользователя в |
|
|
|
|
|
системе. |
2 |
Уведомление |
Система |
Пользователи |
Электронный |
Уведомление, |
|
об авторизации |
бронирования |
|
|
отправляемое |
|
|
|
|
|
после успешной |
|
|
|
|
|
авторизации в |
|
|
|
|
|
системе. Содержит |
|
|
|
|
|
информацию о |
|
|
|
|
|
времени и дате |
|
|
|
|
|
входа. |
3 |
Подтверждение |
Система |
Пользователи |
Электронный |
Документ, |
|
бронирования |
бронирования |
|
|
подтверждающий |
|
|
|
|
|
успешное |
|
|
|
|
|
бронирование |
|
|
|
|
|
велосипеда. |
4 |
Отчет о |
Система |
Менеджеры |
Электронный |
Отчет, |
|
клиентах |
бронирования |
по продажам |
|
содержащий |
|
|
|
|
|
информацию о |
|
|
|
|
|
клиентах, такие |
|
|
|
|
|
как контактные |
|
|
|
|
|
данные и история |
|
|
|
|
|
бронирований. |
5 |
Каталог |
Система |
Пользователи |
Электронный |
Список всех |
|
|
бронирования |
|
|
доступных |
|
|
|
|
|
велосипедов и |
|
|
|
|
|
услуг с |
|
|
|
|
|
описаниями, |
|
|
|
|
|
фотографиями и |
|
|
|
|
|
информацией о |
|
|
|
|
|
наличии. |
6 |
Отчет о |
Система |
Менеджеры |
Электронный |
Документ, |
|
бронировании |
бронирования |
по продажам |
|
представляющий |
|
|
|
|
|
информацию о |
|
|
|
|
|
предстоящих |
|
|
|
|
|
бронированиях. |

103
Приложение Д
Текст программы
Введение
Настоящий документ содержит описание текстов программ, входящих в состав информационной системы. В нем представлены коды основных модулей системы, а также краткие пояснения к ним.
Структура файлов
1.Файл header.php
Описание: Этот файл содержит HTML-код заголовка всех страниц. Он включает стили и скрипты, а также выводит навигационное меню, которое зависит от состояния сессии пользователя. В нем также реализована проверка куки для отображения ссылки на
бронирование.
<!DOCTYPE html> <html lang="ru"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>RIDE-RENT</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.css">
<link rel="stylesheet" href="css/styles.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://unpkg.com/imask"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-masker/1.1.1/vanilla-masker.min.js"></script> <script src="js/script.js"></script>
</head>
<body>
<header>
<h1 class="animate__animated">RIDE-RENT</h1> <nav>
<a href="index.php" class="animate__animated">Главная</a> <a href="catalog.php" class="animate__animated">Каталог</a> <?php
if (session_status() == PHP_SESSION_NONE) { session_start();
}
if (isset($_SESSION['user_id'])): ?>
<?php if ($_SESSION['role_id'] == 1): ?>
<a href="admin_profile.php" class="animate__animated">Админ-панель</a> <?php elseif ($_SESSION['role_id'] == 3): ?>
<a href="manager_profile.php" class="animate__animated">Панель менеджера</a> <?php else: ?>
<a href="profile.php" class="animate__animated">Личный кабинет</a> <?php endif; ?>
<a href="logout.php" class="animate__animated">Выход</a> <?php else: ?>
<a href="registration.php" class="animate__animated">Регистрация</a> <a href="login.php" class="animate__animated">Вход</a>
<?php endif; ?> </nav>
<a href="booking.php" id="booking-link" class="bron" style="display: none;">Перейти к бронированию</a> </header>
<script>

104
//Функция для получения значения куки function getCookie(name) {
let matches = document.cookie.match(new RegExp(
"(?:^|; )" + name.replace(/([.$?*|{}()[]\/+^])/g, '\\$1') + "=([^;]*)"
));
return matches ? decodeURIComponent(matches[1]) : undefined;
}
//Проверка наличия куки и отображение кнопки document.addEventListener('DOMContentLoaded', function() {
let bookings = getCookie('bookings'); if (bookings) {
document.getElementById('booking-link').style.display = 'block';
}
});
</script>
2.Файл footer.php
Описание: Файл содержит HTML-код нижнего колонтитула, который включается на
все страницы. В нем размещена информация о компании и ссылки на социальные сети.
<footer>
<p>Мы предоставляем качественные велосипеды для проката. Наша цель - обеспечить лучший опыт для наших клиентов.</p>
<div class="social-icons">
<a href="https://t.me/ko1tsovaa" target="_blank"> <img src="images/tele.svg" alt="Email">
</a>
<a href="https://wa.me/89189034094" target="_blank"> <img src="images/whatsapp1.svg" alt="Email">
</a>
<a href="https://vk.com/ko1tsovaa" target="_blank"> <img src="images/vk1.svg" alt="Email">
</a>
</div>
</footer>
3.Файл login.php
Описание: Этот файл отображает форму для входа пользователей в систему. Он также
показывает оповещения об ошибках или успехах входа.
<?php
include 'header.php'; include 'db.php';
// Показ оповещений
if (isset($_GET['error'])) {
echo '<div class="alert alert-error">' . htmlspecialchars($_GET['error']) . '</div>';
}
if (isset($_GET['success'])) {
echo '<div class="alert alert-success">' . htmlspecialchars($_GET['success']) . '</div>';
}
?>
<div class="login-container"> <h1>Вход</h1>
<form action="login_process.php" method="POST" class="form"> <label for="email">Электронная почта:</label>
<input type="email" id="email" name="email" required>
<label for="password">Пароль:</label>
<input type="password" id="password" name="password" required>

105
<button type="submit">Войти</button> </form>
</div>
<?php include 'footer.php'; ?> </body>
</html>
4.Файл db.php
Описание: Файл содержит код для подключения к базе данных MySQL и установки кодировки. Он используется для подключения к базе данных во всех остальных файлах.
<?php
$servername = "localhost"; $username = "root"; $password = "usbw"; $dbname = "bron";
// Создание подключения
$conn = new mysqli($servername, $username, $password, $dbname);
//Проверка подключения if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
//Устанавливаем кодировку
$conn->set_charset("utf8"); ?>
5.Файл catalog.php
Описание: Этот файл отвечает за отображение каталога велосипедов. В нем также реализована фильтрация велосипедов по различным параметрам и отображение списка доступных велосипедов из базы данных.
<?php include 'header.php'; ?> <?php include 'db.php'; ?>
<button id="toggle-filters">Показать/Скрыть фильтры</button> <div id="filters" class="catalog-filters" style="display: none;">
<form method="GET" action="catalog.php" class="none"> <label for="frameSize">Размер рамы:</label>
<select id="frameSize" name="frameSize"> <option value="">Любой</option> <?php
$frameSizes = $conn->query("SELECT * FROM FrameSizes"); while ($frameSize = $frameSizes->fetch_assoc()) {
echo '<option value="' . $frameSize['frame_size_id'] . '">' . htmlspecialchars($frameSize['frame_size'], ENT_QUOTES, 'UTF-8') . '</option>';
}
?>
</select>
<label for="ageLimit">Возрастное ограничение:</label> <select id="ageLimit" name="ageLimit">
<option value="">Любое</option> <?php
$ageLimits = $conn->query("SELECT * FROM AgeLimits"); while ($ageLimit = $ageLimits->fetch_assoc()) {
echo '<option value="' . $ageLimit['age_limit_id'] . '">' . htmlspecialchars($ageLimit['age_limit'], ENT_QUOTES, 'UTF- 8') . '</option>';
}
?>
</select>
<label for="availability">Статус:</label> <select id="availability" name="availability">
<option value="">Любой</option>

106
<?php
$availabilities = $conn->query("SELECT * FROM Availability"); while ($availability = $availabilities->fetch_assoc()) {
echo '<option value="' . $availability['availability_id'] . '">' . htmlspecialchars($availability['status'], ENT_QUOTES, 'UTF-8') . '</option>';
}
?>
</select>
<label for="category">Категория:</label> <select id="category" name="category">
<option value="">Любая</option> <?php
$categories = $conn->query("SELECT * FROM Categories"); while ($category = $categories->fetch_assoc()) {
echo '<option value="' . $category['category_id'] . '">' . htmlspecialchars($category['category_name'], ENT_QUOTES, 'UTF-8') . '</option>';
}
?>
</select>
<button type="submit">Фильтровать</button> </form>
</div>
<div class="catalog"> <?php
$conditions = [];
if (!empty($_GET['frameSize'])) {
$conditions[] = "B.frame_size_id = " . (int)$_GET['frameSize'];
}
if (!empty($_GET['ageLimit'])) {
$conditions[] = "B.age_limit_id = " . (int)$_GET['ageLimit'];
}
if (!empty($_GET['availability'])) {
$conditions[] = "B.availability_id = " . (int)$_GET['availability'];
}
if (!empty($_GET['category'])) {
$conditions[] = "B.category_id = " . (int)$_GET['category'];
}
$whereClause = !empty($conditions) ? 'WHERE ' . implode(' AND ', $conditions) : '';
$sql = "SELECT B.bike_id, B.bike_model, B.serial_number, B.photo, A.status as Availability, FS.frame_size as FrameSize, AL.age_limit as AgeLimit, C.category_name
FROM Bikes B
LEFT JOIN FrameSizes FS ON B.frame_size_id = FS.frame_size_id LEFT JOIN AgeLimits AL ON B.age_limit_id = AL.age_limit_id LEFT JOIN Availability A ON B.availability_id = A.availability_id LEFT JOIN Categories C ON B.category_id = C.category_id $whereClause";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo '<div class="catalog-item" data-aos="fade-right">'; if ($row['photo']) {
echo '<img src="data:image/jpeg;base64,' . base64_encode($row['photo']) . '" alt="Фото велосипеда">'; } else {
echo '<img src="images/placeholder.png" alt="Изображение отсутствует">';
}
echo '<h3>'.htmlspecialchars($row['bike_model'], ENT_QUOTES, 'UTF-8').'</h3>';
echo '<p>Категория: '.htmlspecialchars($row['category_name'], ENT_QUOTES, 'UTF-8').'</p>'; echo '<p>Статус: '.htmlspecialchars($row['Availability'], ENT_QUOTES, 'UTF-8').'</p>';
echo '<p>Размер рамы: '.htmlspecialchars($row['FrameSize'], ENT_QUOTES, 'UTF-8').'</p>';
echo '<p>Возрастное ограничение: '.htmlspecialchars($row['AgeLimit'], ENT_QUOTES, 'UTF-8').'+</p>'; echo '<p>Серийный номер: '.htmlspecialchars($row['serial_number'], ENT_QUOTES, 'UTF-8').'</p>'; echo '<button class="booking-button" data-bike-id="' . $row['bike_id'] . '">Бронировать</button>';
echo '</div>';
}
} else {
echo '<p>Нет доступных велосипедов.</p>';
}

107
$conn->close(); ?>
</div>
<?php include 'footer.php'; ?> <script>
function addToBooking(bikeId) {
let bookings = getCookie('bookings');
bookings = bookings ? JSON.parse(bookings) : []; if (!bookings.includes(bikeId)) {
bookings.push(bikeId);
setCookie('bookings', JSON.stringify(bookings), 7); location.reload();
} else {
alert('Велосипед уже добавлен в бронирование');
}
}
function getCookie(name) {
let matches = document.cookie.match(new RegExp(
"(?:^|; )" + name.replace(/([.$?*|{}()[]\/+^])/g, '\\$1') + "=([^;]*)"
));
return matches ? decodeURIComponent(matches[1]) : undefined;
}
function setCookie(name, value, days) { let expires = "";
if (days) {
let date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); expires = "; expires=" + date.toUTCString();
} else {
expires = "; expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
document.addEventListener('DOMContentLoaded', function() { let bookings = getCookie('bookings');
bookings = bookings ? JSON.parse(bookings) : []; document.querySelectorAll('.booking-button').forEach(function(button) {
let bikeId = parseInt(button.getAttribute('data-bike-id')); if (bookings.includes(bikeId)) {
button.style.display = 'none'; } else {
button.addEventListener('click', function() { addToBooking(bikeId); button.style.display = 'none';
});
}
});
if (bookings.length === 0) { setCookie('bookings', '', -1);
}
});
</script>
</body>
</html>
6. Файл catalog_edit.php
Описание: Файл для администраторов и менеджеров, который позволяет добавлять,
редактировать и удалять велосипеды из каталога. Он включает форму для загрузки изображений и обработки данных велосипедов.
<?php
include 'header_adm.php';

108
include 'db.php';
//Проверка роли пользователя if ($_SESSION['role_id'] != 3) { header("Location: login.php");
exit();
}
//Обработка загрузки изображения function handleFileUpload($file, &$error) {
$upload_ok = 1;
$image_file_type = strtolower(pathinfo($file["name"], PATHINFO_EXTENSION));
//Проверка, является ли файл изображением $check = getimagesize($file["tmp_name"]);
if ($check === false) {
$error = "Файл не является изображением."; $upload_ok = 0;
}
//Ограничение размера файла (5MB)
if ($file["size"] > 5000000) {
$error = "Извините, ваш файл слишком большой."; $upload_ok = 0;
}
// Разрешенные форматы файлов
if ($image_file_type != "jpg" && $image_file_type != "png" && $image_file_type != "jpeg" && $image_file_type != "gif") { $error = "Извините, только JPG, JPEG, PNG и GIF файлы разрешены.";
$upload_ok = 0;
}
// Проверка на ошибки if ($upload_ok == 0) {
return false; } else {
return file_get_contents($file["tmp_name"]);
}
}
// Добавление велосипеда
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'add') { $bike_model = $conn->real_escape_string($_POST['bike_model']);
$frame_size_id = (int)$_POST['frame_size_id']; $age_limit_id = (int)$_POST['age_limit_id'];
$serial_number = $conn->real_escape_string($_POST['serial_number']); $availability_id = (int)$_POST['availability_id'];
$category_id = (int)$_POST['category_id']; $photo = null;
if (isset($_FILES['photo']) && $_FILES['photo']['error'] == UPLOAD_ERR_OK) { $upload_error = "";
$photo = handleFileUpload($_FILES['photo'], $upload_error); if (!$photo) {
header("Location: catalog_edit.php?error=" . $upload_error); exit();
}
}
$stmt = $conn->prepare("INSERT INTO bikes (bike_model, frame_size_id, age_limit_id, serial_number, availability_id, category_id, photo) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("siisiss", $bike_model, $frame_size_id, $age_limit_id, $serial_number, $availability_id, $category_id, $photo);
if ($stmt->execute()) {
header("Location: catalog_edit.php?message=Велосипед успешно добавлен."); exit();
} else {
header("Location: catalog_edit.php?error=Ошибка добавления велосипеда: " . $stmt->error); exit();

109
}
}
// Удаление велосипеда
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'delete' && isset($_POST['bike_id'])) {
$bike_id = (int)$_POST['bike_id'];
$delete_query = "DELETE FROM bikes WHERE bike_id = $bike_id";
if ($conn->query($delete_query) === TRUE) {
header("Location: catalog_edit.php?message=Велосипед успешно удален."); exit();
} else {
header("Location: catalog_edit.php?error=Ошибка удаления велосипеда: " . $conn->error); exit();
}
}
// Обновление информации о велосипеде
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['bike_id']) && !isset($_POST['action'])) { $bike_id = (int)$_POST['bike_id'];
$bike_model = $conn->real_escape_string($_POST['bike_model']); $frame_size_id = (int)$_POST['frame_size_id'];
$age_limit_id = (int)$_POST['age_limit_id'];
$serial_number = $conn->real_escape_string($_POST['serial_number']); $availability_id = (int)$_POST['availability_id'];
$category_id = (int)$_POST['category_id']; $photo = null;
if (isset($_FILES['photo']) && $_FILES['photo']['error'] == UPLOAD_ERR_OK) { $upload_error = "";
$photo = handleFileUpload($_FILES['photo'], $upload_error); if (!$photo) {
header("Location: catalog_edit.php?error=" . $upload_error); exit();
}
}
if ($photo) {
$update_query = "UPDATE bikes SET bike_model = ?,
frame_size_id = ?, age_limit_id = ?, serial_number = ?, availability_id = ?, category_id = ?, photo = ?
WHERE bike_id = ?";
$stmt = $conn->prepare($update_query);
$stmt->bind_param("siisissi", $bike_model, $frame_size_id, $age_limit_id, $serial_number, $availability_id, $category_id, $photo, $bike_id);
} else {
$update_query = "UPDATE bikes SET bike_model = ?,
frame_size_id = ?, age_limit_id = ?, serial_number = ?, availability_id = ?, category_id = ? WHERE bike_id = ?";
$stmt = $conn->prepare($update_query);
$stmt->bind_param("siisisi", $bike_model, $frame_size_id, $age_limit_id, $serial_number, $availability_id, $category_id, $bike_id);
}
if ($stmt->execute()) {
header("Location: catalog_edit.php?message=Информация о велосипеде успешно обновлена."); exit();
} else {
header("Location: catalog_edit.php?error=Ошибка обновления информации: " . $stmt->error);

110
exit();
}
}
// Получение списка велосипедов $bikes_query = "SELECT * FROM bikes"; $bikes_result = $conn->query($bikes_query);
// Получение списка размеров рам $frame_sizes_query = "SELECT * FROM framesizes";
$frame_sizes_result = $conn->query($frame_sizes_query);
// Получение списка возрастных ограничений $age_limits_query = "SELECT * FROM agelimits"; $age_limits_result = $conn->query($age_limits_query);
// Получение списка доступности $availability_query = "SELECT * FROM availability";
$availability_result = $conn->query($availability_query);
// Получение списка категорий
$categories_query = "SELECT * FROM categories"; $categories_result = $conn->query($categories_query);
$conn->close(); ?>
<!DOCTYPE html> <html lang="ru"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Управление каталогом</title>
<link rel="stylesheet" href="css/styles.css"> <script>
function filterTable() {
var input, filter, table, tr, td, i, j, txtValue;
input = document.getElementById("searchInput"); filter = input.value.toUpperCase();
table = document.getElementById("bikesTable"); tr = table.getElementsByTagName("tr");
for (i = 1; i < tr.length; i++) {
tr[i].style.display = "none"; // Hide all rows initially td = tr[i].getElementsByTagName("td");
for (j = 0; j < td.length; j++) { if (td[j]) {
txtValue = td[j].textContent || td[j].innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) { tr[i].style.display = ""; // Show the row if match is found break;
}
}
}
}
}
</script>
</head>
<body>
<div class="manager-container">
<h2>Управление каталогом велосипедов</h2>
<?php
if (isset($_GET['message'])) {
echo '<div class="alert alert-success">' . htmlspecialchars($_GET['message'], ENT_QUOTES, 'UTF-8') . '</div>';
}
if (isset($_GET['error'])) {
echo '<div class="alert alert-error">' . htmlspecialchars($_GET['error'], ENT_QUOTES, 'UTF-8') . '</div>';
}
?>
<h3>Добавить велосипед</h3>