Проект по реализации системы управления безопасностью для авиакомпаний (SMS). Данная система является частью комплекса по обеспечению безопасности полетов. Обычно SMS включает в себя следующие компоненты:
- Система регистрации событий на рейсах. База данных, в которой регистрируются события на рейсах авиакомпаний. Собирает события от самых простых (закончился кофе для пассажиров) до самых сложных (отказ системы);
- Блок статистики. Отображает детальную статистику по отказам в разрезе месяцев и категорий событий. Представляет эти данные в удобных видах (например графически);
- Блок анализа общей безопасности полетов в АК. Обычно представляет собой информацию о текущей обстановке с безопасностью полетов в АК. У каждого события есть тип, у типов есть факторы влияния на безопасность (к примеру у катастрофической ситуации фактор влияния больше, чем у усложнений условий полета). На основе общего количества событий того или иного типа и факторов этих типов и делается вывод об общей безопасности полетов в авиакомпании.
В данном проекте реализуется первый из этих трех компонентов. Это система, в которой регистрируются события на рейсах. Реализовать остальные компоненты в дополнение к ней не составит особого труда. Более подробную информацию о SMS, ее разработке и внедрении, вы можете найти здесь.
Данный проект является тестовым, но, в принципе, готов для использования на авиапредприятиях. Дело в том, что когда-то, когда я только начинал заниматься веб-разработкой, мы реализовали такую систему в авиакомпании, в которой я работал, но сделали это без фреймворков, на чистом PHP, JS и AJAX. Много лет спустя, я решил переписать этот проект на Laravel Framework, в качестве портфолио и платформы для собственного развития.
Система состоит из нескольких компонентов, которые будут описаны ниже:
В системе используется стандартная система авторизации с логином и паролем. В качестве логина используется email пользователя. Регистрация новых пользователей осуществляется администраторами через панель управления. Пароли пользователей не хранятся в открытом виде, а отправляются им на почту. Если пользователь хочет сменить пароль на собственный, он всегда это может сделать через форму восстановления пароля.
В системе существует три роли пользователей:
- Пользователь. Может добавлять события. Идея в том, что обычные, рядовые пользователи отдела занимаются первичной регистрацией событий. В списке событий видит только события, которые были созданы им;
- Менеджер событий. Имеет доступ к редактированию событий. В списке событий видят все события на предприятии. После создания события, получают уведомление об этом и обрабатывают события. Назначают ответственные подразделения, менеджеры событий которых в последствии и занимаются событием. Могут удалять события, но физически они останутся в базе данных (подробнее об этом ниже);
- Администраторы. Имеют полный доступ к приложению. Могут править справочники, добавлять и удалять пользователей. Могут окончательно удалить событие из системы (подробнее об этом ниже).
Основная идея, чтобы сотрудники внутри отдела тесно работали над созданием и обработкой событий.
Анонимные события могут добавляться без авторизации. Для перехода на форму добавления анонимного события, нужно пройти по ссылке "Добавить событие анонимно", на странице авторизации. Анонимные события не появляются в общем списке, пока их не одобрит менеджер событий.
При создании события (не анонимно) можно поставить связь события с рейсом. В приложении есть таблица рейсов авиакомпании. Загрузку рейсов туда можно осуществлять через REST API (подробнее об этом в секции развертывания приложения). После загрузки, при создании события когда выбирается дата, будет отправлен фоновый запрос к таблице с поиском рейсов на указанную дату. После связи с рейсом, информация о нем будет подгружаться при просмотре события. Кроме этого, у событий связанных с рейсом, появляется возможность фильтрации по параметрам рейса (бортовой номер ВС, КВС).
Событий может быть много (в нашей системе накопилось 22928). Поэтому в приложении есть возможность фильтровать список событий. Фильтры доступны по следующим параметрам:
- Начальная и конечная дата события;
- Номер борта у связанного рейса;
- КВС связанного рейса;
- Где произошло событие;
- Статус события;
- Ответственные за событие подразделения;
- Пользователь, создавший событие;
- К чему относится событие;
- Наличие / отсутствие прикрепленных файлов.
Фильтры, которые отображаются как чекбоксы (например номера бортов), работают по следующему принципу:
- Если отмечены все галочки, фильтрация по данному свойству не производится;
- Если отмечены некоторые галочки, будут показаны только события у которых в связанном рейсе есть борта из отмеченных;
- Если не отмечено ни одной галочки, то фильтрация будет по принципу пустого значения. То есть в данном случае будут показаны все события, у которых нет номеров борта в связанном рейсе.
Иногда встречается частный случай. Например нужно показать все события, у которых в связанных рейсах есть бортовой номер. В этом случае нужно отметить все галочки, кроме "Все". Тогда все существующие значения бортов, будут записаны в фильтр.
Еще одно важное замечание. Фильтрация по полям вроде пользователя, создавшего событие или ответственных подразделений, производится только из существующих значений. То есть в списке пользователей, которых можно отметить, будут только те, кто создавал события в системе, а не все пользователи, а среди ответственных подразделений, будут только те, которые числятся ответственными в событиях.
Кроме фильтров, существует поиск события по его номеру.
В системе реализован способ "мягкого удаления" событий. То есть при удалении, само событие физически остается в базе данных, просто пропадает из списка событий и при попытке его запросить будет выдан статус 404. Удалить событие из базы данных окончательно может только администратор системы. При удалении события удаляются все мероприятия по нему, ответственные подразделения и прикрепленные файлы. Если событие было удалено ошибочно, администратор системы может его восстановить.
Для контроля за действиями пользователей, пишется лог их действий. По-умолчанию лог пишется по пути storage/logs/user_actions.log. Лог имеет ежедневный формат (то есть на каждый день создается свой файл). Приложение хранит логи за 14 дней (это, как и название сохраняемого файла, можно изменить). Логируются следующие события:
- Авторизация пользователя;
- Неудачная попытка авторизации;
- Выход пользователя из приложения;
- Сброс пароля пользователем;
- Добавление события;
- Добавление анонимного события;
- Одобрение / отклонение анонимного события;
- Редактирование события;
- Удаление события;
- Восстановление события;
- Уничтожение события;
- Загрузка рейса / рейсов через API;
- Редактирование рейса через API;
- Удаление рейса через API;
- Добавление / изменение пользователя;
- Сброс пароля пользователя через панель администрирования;
- Авторизация под другим пользователем.
Для загрузки рейсов в систему присутствует API. API реализовано по стандартному подходу Laravel (подробнее - тут). API предоставляет полный спектр операций с рейсами. Их можно добавлять/редактировать/запрашивать/удалять.
Общее описание полей объекта "Рейс":
- id - ID рейса в БД;
- departure_datetime - дата и время вылета (форматы будут приведены ниже);
- arrival_datetime - дата и время прилета;
- number - номер рейса;
- board - бортовой номер ВС;
- aircraft_code - код ВС;
- departure_airport - аэропорт вылета;
- arrival_airport - аэропорт прилета;
- captain - КВС;
- extra_captain - второй КВС;
- created_at - дата создания рейса в БД;
- updated_at - дата обновления рейса в БД.
Для работы с API, необходимо указать три HTTP-заголовка:
- Accept: application/json;
- Content-Type: application/json;
- Authorization: Bearer <токен доступа>.
Про токен доступа будет разъяснено ниже.
Для загрузки рейсов есть два метода. В обоих методах используется объект рейса. В первом методе можно загрузить один рейс, во втором - массив рейсов. Для массовой загрузки рейсов рекомендуется использовать второй метод.
Тип | URL |
---|---|
POST | api/flights |
Пример JSON для отправки:
{
"departure_datetime": "2020-03-02 02:00:00",
"arrival_datetime": "2020-03-02 04:00:00",
"number": "ARLN 16",
"board": "VP-BJX",
"aircraft_code": "735",
"departure_airport": "Москва (Жуковский)",
"arrival_airport": "Сочи (Адлер)",
"captain": "Евгеньев Евгений Евгеньевич",
"extra_captain": "Иванов Иван Иванович"
}
Тип | URL |
---|---|
POST | api/flights/load |
Пример JSON для отправки:
{
"flights": [
{
"departure_datetime": "2020-02-02 17:40:00",
"arrival_datetime": "2020-02-02 19:20:00",
"number": "ARLN 17",
"board": "VQ-BFZ",
"aircraft_code": "738",
"departure_airport": "Калининград",
"arrival_airport": "Москва (Домодедово)",
"captain": "Иванов Иван Иванович"
},
{
"departure_datetime": "2020-02-02 05:00:00",
"arrival_datetime": "2020-02-02 07:00:00",
"number": "ARLN 18",
"board": "VQ-BPG",
"aircraft_code": "738",
"departure_airport": "Москва (Домодедово)",
"arrival_airport": "Мурманск",
"captain": "Иванов Иван Иванович"
},
{
"departure_datetime": "2020-02-02 23:45:00",
"arrival_datetime": "2020-02-03 10:30:00",
"number": "ARLN 19",
"board": "VQ-BLA",
"aircraft_code": "772",
"departure_airport": "Пунта-Кана",
"arrival_airport": "Москва (Домодедово)",
"captain": "Иванов Иван Иванович",
"extra_captain": "Петров Петр Петрович"
}
]
}
В ответ вернется объект с количеством добавленных рейсов (по сути должен совпасть с количеством переданных).
Тип | URL |
---|---|
GET | api/flights |
Список рейсов содержит список объектов типа "Рейс", описанного выше. Также важно учитывать, что для экономии ресурсов, список отображается в постраничном виде. На странице выводится по 15 записей.
Пример возвращаемого JSON (для удобства список рейсов сокращен до 3):
{
"data": [
{
"id": 1,
"departure_datetime": "2020-01-22T05:00:00.000000Z",
"arrival_datetime": "2020-01-22T07:00:00.000000Z",
"number": "ARLN 1",
"board": "VP-BNU",
"aircraft_code": "738",
"departure_airport": "Москва (Домодедово)",
"arrival_airport": "Оренбург (Центральный)",
"captain": "Петров Петр Петрович",
"extra_captain": "Иванов Иван Иванович",
"created_at": "2020-02-05T14:33:28.000000Z",
"updated_at": "2020-02-05T14:33:28.000000Z"
},
{
"id": 2,
"departure_datetime": "2020-01-22T06:00:00.000000Z",
"arrival_datetime": "2020-01-22T07:30:00.000000Z",
"number": "ARLN 2",
"board": "VP-BNU",
"aircraft_code": "738",
"departure_airport": "Оренбург (Центральный)",
"arrival_airport": "Москва (Домодедово)",
"captain": "Петров Петр Петрович",
"extra_captain": null,
"created_at": "2020-02-05T14:33:28.000000Z",
"updated_at": "2020-02-05T14:33:28.000000Z"
},
{
"id": 3,
"departure_datetime": "2020-01-22T07:00:00.000000Z",
"arrival_datetime": "2020-01-22T08:00:00.000000Z",
"number": "ARLN 3",
"board": "VP-BVV",
"aircraft_code": "772",
"departure_airport": "Москва (Шереметьево)",
"arrival_airport": "Оренбург (Центральный)",
"captain": "Петров Петр Петрович",
"extra_captain": "Сидоров Сидр Сидорович",
"created_at": "2020-02-05T14:33:28.000000Z",
"updated_at": "2020-02-05T14:33:28.000000Z"
}
],
"links": {
"first": "http://<адрес приложения>/api/flights?page=1",
"last": "http://<адрес приложения>/api/flights?page=43",
"prev": null,
"next": "http://<адрес приложения>/api/flights?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 43,
"path": "http://<адрес приложения>/api/flights",
"per_page": 15,
"to": 15,
"total": 645
}
}
Тип | URL |
---|---|
GET | api/flights/<id рейсв> |
Пример возвращаемого JSON:
{
"data": {
"id": 1,
"departure_datetime": "2020-01-22 10:00:00",
"arrival_datetime": "2020-01-22 12:00:00",
"number": "ARLN 1",
"board": "VP-BNU",
"aircraft_code": "738",
"departure_airport": "Москва (Домодедово)",
"arrival_airport": "Оренбург (Центральный)",
"captain": "Петров Петр Петрович",
"extra_captain": "Иванов Иван Иванович",
"created_at": "2020-02-05 19:33:28",
"updated_at": "2020-02-05 19:33:28"
}
}
Тип | URL |
---|---|
PUT | api/flights/<id рейсв> |
Редактировать рейс можно частично. Если нужно изменить, к примеру, только дату вылета, можно указать только её, не обязательно снова полностью передавать информацию о рейсе. Если нужно обновить весь рейс, то JSON будет аналогичен созданию рейса.
Пример JSON для отправки:
{
"extra_captain": "Евгеньев Евгений Евгеньевич"
}
В ответ вернется JSON с измененным объектом типа "Рейс".
Тип | URL |
---|---|
DELETE | api/flights/<id рейсв> |
В ответ вернется сообщение об успешном удалении рейса.
Теперь немного о технической части приложения. Система реализована на Laravel Framework. У этого фреймворка есть определенные требования к окружению.
Для подготовки среды развертывания, нужно выполнить следующие шаги:
- Установить рекомендуемое ПО для запуска Laravel (там же можно найти рекомендации по настройке веб-сервера для отображения "красивых" адресов);
- Установить СУБД. Laravel поддерживает несколько СУБД (полный список - тут), можно выбрать любую;
- Установить движок очередей. Многие функции приложения выполняются отложенно (отправка писем или сообщений в лог) с помощью движка очередей. Laravel поддерживает множество движков очередей (в моем проекте использовался Redis). Полная информация о поддерживаемых движках - тут;
- Настроить отправку почты. Уведомления в приложении отправляются через email, поэтому необходим почтовый сервер для отправки писем;
- Установить git и Composer.
Для развертывания проекта нужно выполнить следующие шаги:
- Клонировать проект из git-репозитория (либо загрузить его) -
git clone https://github.com/Nikita-C47/sms.git
; - Перейти в корневую директорию проекта;
- Установить зависимости (фреймворк и вспомогательные библиотеки) с помощью Composer -
composer install
; - Заполнить файл конфигурации приложения
.env
(если при установке он не создался автоматически, можно сохранить файл.env.example
под этим именем). В нем необходимо заполнить следующие переменные (подробнее о заполнении файлов такого типа написано здесь):APP_NAME
- название приложения. Отоброжается, например, в заголовке вкладок со страницами приложенияжAPP_ENV
- окружение приложения. При развертывание на рабочем сервере, нужно указатьproduction
. В завимости от окружения, в приложении варьируются некоторые параметры (об этом ниже);APP_DEBUG
- включение или отключение отладки. При включенной отладке все ошибки отображаются в браузере. На рабочем сервере должна быть отключена;APP_URL
- хост, на котором разворачивается приложение (используется, например, при генерации ссылок);DB_CONNECTION
- используемое подключение к СУБД (список поддерживаемых СУБД можно найти выше), там же описано как заполнять данный параметр и все параметры, связанные с БД;DB_HOST
- хост СУБД;DB_PORT
- порт СУБД;DB_DATABASE
- база данных приложения;DB_USERNAME
- имя пользователя для подключения к СУБД (пользователь должен иметь полные права на базу данных);DB_PASSWORD
- пароль пользователя для подключения к СУБД;QUEUE_CONNECTION
- используемый движок очередей. О поддерживаемых движках и заполнении этого параметра также можно прочитать выше. Обращаю внимание, что при использовании Redis, нужно будет заполнить секцию с его настройками. В частности переменныеREDIS_HOST
,REDIS_PASSWORD
иREDIS_PORT
;MAIL_DRIVER
- используемый драйвер для почты. Поддерживается как обычный SMTP, так и сторонние сервисы;MAIL_HOST
- хост сервера отправки электронной почты;MAIL_PORT
- порт сервера отправки электронной почты;MAIL_USERNAME
- имя пользователя для сервера отправки электронной почты;MAIL_PASSWORD
- пароль пользователя для сервера отправки электронной почты;MAIL_ENCRYPTION
- используемое шифрование электронной почты;MAIL_FROM_ADDRESS
- адрес, от которого ведется отправка сообщений;MAIL_FROM_NAME
- имя, от которого ведется отправка.
- Сгенерировать ключ шифрования приложения -
php artisan key:generate
; - Добавить ссылку на публичное хранилище файлов (необходимо для возможности скачивать файлы,
прикрепленные к событиям) -
php artisan storage:link
; - Запустить миграцию схемы БД приложения:
php artisan migrate
; - Запустить воркер для очередей (подробнее о том как это сделать - здесь. Там же можно найти конфигурацию для Supervisor, например);
- Сгенерировать администратора для приложения. Он необходим для первичного входа и настройки приложения.
Сделать это можно с помощью команды
php artisan admin:assign <email пользователя>
. Желательно указать реальный email пользователя. Администратор необходим чтобы получить доступ к функциям администрирования приложения, в том числе заполнению справочников и созданию пользователей; - Заполнить данные для справочников приложения. Это могут сделать администраторы, войдя в приложение. Необходимо заполнить типы событий, категории событий, мероприятия, к которым относятся события и подразделения компании (если нужны тестовые данные - смотрите ниже).
Есть несколько дополнительных шагов для конфигурирования приложения. Они обеспечивают его более тонкую настройку:
- Заполнение приложения тестовыми данными (необходимо только при разработке и на тестовых серверах). В
приложении есть три seeder-а (расположены в папке database/seeds):
- DictionariesSeeder.php - заполняет приложение данными справочников. Указаны стандартные типы событий, категории событий, подразделения и мероприятия, к которым относятся события. Срабатывает на всех окружениях;
- FlightsSeeder.php - заполняет рейсы. Берет дату на две недели раньше текущей и заполняет каждый день рейсами из определенного списка. Срабатывает только на не-production окружениях;
- UsersSeeder.php - заполняет пользователей. Данный seeder должен запускаться после заполнения справочников, так как использует таблицу с подразделениями. Для каждого подразделения генерируется 10 пользователей - 4 обычных пользователя, 4 менеджера событий и 2 администратора. На не-production средах у всех пользователей устанавливаются пароли qwerty123, для простоты тестирования. Срабатывает только на не-production окружениях.
- Настройка дополнительных параметров. Файлы конфигурации Laravel расположены в папке config. Среди
наиболее важных настроек можно выделить:
- Временная зона приложения. Файл
app.php
, параметр -timezone
. Необходимо указать значение, поддерживаемое php. Список значений - тут; - Логин пользователя для API. Если вы хотите изменить логин пользователя, который будет иметь
доступ к API приложения, это можно сделать в файле
auth.php
с помощью параметраapi_user
; - Путь к файлу для записи пользовательских действий. Конфигурируется в файле
logging.php
. В нем вы найдете массивchannels
, где есть элементuser_actions
. В параметреpath
можно указать путь, по которому будет сохраняться лог пользовательских действий, а в параметреdays
- количество дней, за которые хранятся записи лога.
- Временная зона приложения. Файл
- Генерация пользователя для API. Если вы планируете использовать API для загрузки рейсов в систему,
необходимо сгенерировать пользователя для API. Это можно сделать с помощью команды
php artisan api:generate
. Будет создан пользователь для API и токен доступа для клиентского приложения будет в ответе на выполнение команды. Если Вы по какой-то причине его не записали - не беда, при повторном выполнении команды, будет сгенерирован и возвращен новый токен.
Сам Laravel распространяется по лицензии MIT, как и многие компоненты, входящие в этот проект (например Vue.js). Также огромная благодарность создателям шаблона для панели администрирования AdminCAST, на котором реализован интерфейс приложения. На использование проекта не налагается никаких ограничений, Вы можете модифицировать и изменять его по своему усмотрению. Моей целью в нем было только саморазвитие и еще один проект в портфолио. По всем вопросам, связанным с проектом, обращайтесь на мой контактный email - nikita_c47@outlook.com.