Logonok
5 subscribers

240 тысяч за 8 часов

240 тысяч за 8 часов

Сегодня расскажу как, используя правильные инструменты, можно снизить срок разработки веб-приложения на порядок, не потеряв ни в функциональности, ни в качестве.

Итак, есть клиент, которому необходимо веб-приложение для переноса своего бизнеса в онлайн среду. Цена вопроса за требуемый функционал (см. ниже) - 240 тысяч рублей. Примерное время разработки - 2 - 3 недели.

Обычное дело, когда требования от клиентов приходят в достаточно сумбурном виде и приходится дотошно выяснять многие неоднозначные моменты. Так было и на этот раз. По итогу сложилось следующее.

Требуемый функционал

Пекарня изготавливает различные изделия. Каждое изделие состоит из основного продукта и произвольного количества обязательных (и/или необязательных) ингредиентов.

Администратор приложения заводит новые продукты и ингредиенты, редактирует цены и запасы каждого из них, добавляет описания и изображения, ставит признак доступности. Опубликованные продукты доступны для заказа пользователям.

Анонимному пользователю доступны все предлагаемые товары. Он может просматривать их и выбирать. В момент создания заказа ему предлагается войти в приложение или зарегистрироваться.

Авторизованный пользователь выбирает нужные ему ингредиенты из списков доступных и делает заказ. Например, продукт пирог имеет два обязательных ингредиента - тесто и начинка. Заказывая пирог с дрожжевым тестом и яблочной начинкой, пользователь, тем самым, определяет конечную цену изделия, которая зависит от цены ингредиентов.

Заказ может состоять из нескольких разных изделий. Каждое изделие может быть заказано в разном количестве.

Продукты и ингредиенты имеют ограниченный запас, который должен учитываться при заказе. Один и тот же ингредиент может входить в состав разных изделий. Доступность и обязательность ингредиента для продукта определяется администратором приложения.

Заказ имеет несколько статусов. В черновом состоянии пользователь может изменить заказ или удалить его. В подтвержденном состоянии заказ может быть принят в работу. В процессе обработки заказа пользователь уже не может его удалить.

Сделанные заказы и их текущее состояние должны быть доступны в личном кабинете пользователя.

Берем 30 процентов предоплаты и можно приступать...

Начало работы

Для быстрого создания веб-приложения воспользуемся декларативным фреймворком Evado, который позволяет описать необходимые сущности и связи между ними через веб-интерфейс.

Скачиваем, устанавливаем и запускаем шаблон приложения (основы работы с фреймворком описаны в статье Создание веб-приложения без программирования). Далее переходим в модуль Студия для создания метаданных.

Создание сущностей приложения и связей между ними
Создание сущностей приложения и связей между ними
Создание сущностей приложения и связей между ними

Ингредиенты

Создаем класс Ингредиент. Добавляем строковые атрибуты Название, Описание, целочисленный атрибут Запас и вещественный Цена. Название ингредиента должно быть уникальным, поэтому оно может быть использовано как строковое представление объекта.

По умолчанию для представления объекта в строковом виде используется его системный идентификатор, но можно использовать вычисляемое выражение из Шаблона заголовка.

Создаем класс Список ингредиентов. Список определяет ингредиенты, которые взаимозаменяемы в изделии. Например, тесто может быть или дрожжевым или заварным или бисквитным. Один и тот же ингредиент может входить в разные списки.

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

Продукт

Создаем класс Продукт. Добавляем атрибуты Название, Описание, целочисленный атрибут Запас и вещественный Цена. Добавляем логический атрибут Опубликован для определения доступности для заказа. Добавляем целочисленный атрибут Порядковый номер для возможности произвольной сортировки объектов в списке.

Если добавить к атрибуту поведение "Автоматическое приращение", то для вновь создаваемых объектов порядковый номер будет присваиваться по умолчанию, с заданным шагом изменения.

Создадим представление для отображения продуктов в публичной части приложения. Укажем значение фильтра {"published": true}, которое покажет только опубликованные продукты, и порядок сортировки {"orderNumber": 1}. Добавим необходимые атрибуты.

Представления класса наследуют или переопределяет его свойства. Это позволяет отображать один и тот же объект в разном виде. Например, только с определенными атрибутами.

Заказчик

Создаем класс Заказчик. Добавляем атрибут Пользователь для ссылки на системного пользователя. Значение этого атрибута обязательное и уникальное, так как заказчик может быть связан только с одним пользователем. Добавляем атрибут Заказы - это обратная ссылка для получения всех заказов данного пользователя.

Обратные ссылки, в отличие от других атрибутов, не хранятся в самом объекте, а вычисляются по заданному отношению.

Элемент заказа

Создаем класс Элемент заказа, который описывает изделие, выбранное пользователем. Добавляем ссылочные атрибуты Заказ, Продукт и Ингредиенты, обязательный целочисленный атрибут Количество (по умолчанию - 1) и вещественный атрибут Цена.

Хотя цена в момент заказа вычисляется автоматически (см. ниже), она должна быть сохранена для того, чтобы ее изменения в продуктах не повлияли на уже созданные заказы.

Далее нужны валидаторы для проверки данных передаваемых пользователями. К атрибуту Количество добавим валидатор Число, в котором укажем минимальное и максимальное значение, кроме того разрешим только целые числа. Также добавим пользовательский валидатор, который будет проверять наличие необходимого запаса продукта и ингредиентов.

Пользовательский валидатор реализуется как отдельный JavaScript класс, наследуемый от базового валидатора, и может содержать любые необходимые проверки.

К атрибуту Продукт добавим валидатор Условие для проверки того, что выбранный продукт доступен для заказа (опубликован).

К атрибуту Ингредиенты добавим пользовательский валидатор для проверки того, что выбранные ингредиенты соответствуют тем спискам ингредиентов, которые определены для продукта.

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

Поведение определяет дополнительный функционал в момент различных событий жизненного цикла объекта - создание, изменение, удаление и других.

Заказ

Создаем класс Заказ. Добавляем ссылочный атрибут Заказчик для определения создателя. Добавляем обратную ссылку Элементы для определения элементов, относящихся к заказу.

Добавляем вычисляемый атрибут Цена. В отличие от элемента заказа, цена всего заказа - это всегда сумма цен его элементов. Хотя в дальнейшем, возможно, все-таки потребуется хранить сумму в заказе. Например для скидок, купонов и тому подобного, влияющего на итоговую цену.

Далее добавляем несколько служебных атрибутов - Состояние, Дата создания и Дата редактирования.

Служебные атрибуты сохраняются в объекте вне зависимости от наличия метаданных. Их кодовые имена начинаются со знака подчеркивания. Служебные атрибуты доступны для пользователей только для чтения.

Добавляем пользовательский валидатор для класса, который при создании заказа проверит отсутствие у пользователя других заказов в состояниях Черновик или Новый.

Бизнес-процесс

Состояния необходимы для различного функционала объектов одного класса. Например, заказ в состоянии Черновик может быть отредактирован пользователем или удален. А заказ в состоянии Приготовление доступен только для чтения.

Добавляем четыре состояния заказа - Черновик, Новый, Приготовление и Готов. Для черновика поставим флажок По умолчанию, чтобы назначать это состояние всем создаваемым заказам. Для трех других поставим флажок Только чтение - данные заказа не могут изменяться.

Переходы определяют возможные изменения состояний объекта. Они могут быть ограничены условиями и/или безопасностью системы. Создаем переходы:

  • Подтвердить сформированный заказ (Черновик -> Новый)
  • Вернуться к редактированию заказа (Новый -> Черновик)
  • Начать готовить (Новый -> Приготовление)
  • Завершить заказ (Приготовление -> Готов)

Метаданные готовы. Экспортируем их в хранилище приложения (metadata/app) через кнопку на верхней панели.

Права доступа

Во фреймворке Evado права доступа реализованы на основе ролей и разрешений (RBAC). Пользователь может иметь одну или несколько ролей, а каждая роль может иметь разрешения, определяющие доступ к тому или иному ресурсу.

Переходим в модуль Администрирование. Для приложения определим три роли - администратор, пользователь и гость. Администратор имеет полный доступ ко всему функционалу. Пользователь может создавать и редактировать свои заказы. И, наконец, гость может только просматривать продукты.

Роли могут назначаться пользователям как вручную, так и в автоматическом режиме. Например, по умолчанию роль Гость присваивается всем анонимным посетителям, а роль Пользователь всем авторизованным посетителям, у которых отсутствуют другие роли.

На странице Безопасность -> Разрешения метаданных создадим нужные разрешения и назначим их соответствующим ролям.

Права доступа реализовано на основе ролей и разрешений
Права доступа реализовано на основе ролей и разрешений
Права доступа реализовано на основе ролей и разрешений

Применение разрешения может быть ограничено наложенными на него правилами, которые вычисляются в момент запроса разрешения. Например, доступ к собственному заказу определяется через разрешение читать класс Заказ и правило проверяющее, что текущий пользователь является создателем заказа. Правило реализуется как отдельный JavaScript класс.

Если обычные разрешения дают право на использование ресурса (например, доступ к модулю), то разрешения метаданных могут носить и обратный смысл - запрещать действие с выбранными данными.

После внесенных изменений перезагружаем безопасность системы кнопкой на верхней панели.

Наполнение данными

Модуль Офис предоставляет готовый интерфейс для работы администратора или менеджера приложения по заведению данных. Переходим в Офис и создаем продукты и ингредиенты. Заполняем списки ингредиентов, привязываем их к продуктам, выставляем цены и запасы. Теперь приложение готово к эксплуатации.

Наполнение приложения данными
Наполнение приложения данными
Наполнение приложения данными

Для изменения шаблонов отображения, контроллеров или клиентских ресурсов достаточно переопределить данный модуль в проекте, унаследовав класс текущей реализации. Эта возможность работает и с другими модулями фреймворка.

Пользовательский интерфейс

Фреймворк Evado предоставляет клиентский API, с помощью которого можно получить доступ как к данным, так и к метаданным, через AJAX-запросы. Таким образом, пользовательский интерфейс может быть реализован полностью независимо от основного приложения (даже располагаться на другом сервере).

В данном случае дизайн был заказан на стороне, а пользовательский интерфейс выполнен отдельным модулем Фронт. Подробно останавливаться на его работе не буду, потому что реализация может быть сделана любыми средствами.

Пример оформления интерфейса в стандартном дизайне Bootstrap 5
Пример оформления интерфейса в стандартном дизайне Bootstrap 5
Пример оформления интерфейса в стандартном дизайне Bootstrap 5

Заключение

Итак, за 8 часов был полностью готов функционал указанный в требованиях. Через 3 дня дизайнер нарисовал пользовательский интерфейс. Затем еще пару дней ушло на его доработку.

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

  • онлайн-оплата
  • система скидок/купонов
  • комментирование и оценка

P. S. В демо-приложении на github вместо дизайнерского интерфейса применена стандартная тема Bootstrap 5, но функционал сохранен.