Перейти к основному содержимому
Мобилка41ГЛАВА 41Магазин за один конкурсный деньПройдено: 15 из 15срезами, не слоями

Магазин за один конкурсный день

Прочитано0%
Квизы0/4
Уровень 1 · Новичок

Зачем это нужно​

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

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

Что вообще требуется​

В спецификации задания ровно четырнадцать операций по шести коллекциям. Вот они целиком — этот список стоит держать перед глазами:

№Метод и путьЧто этоТокен
1POST /collections/users/recordsРегистрациянет
2POST /collections/users/auth-with-passwordВходнет
3GET /collections/users/records/{id}Профильда
4PATCH /collections/users/records/{id}Изменение профиляда
5GET /collections/news/recordsАкции и новостида
6GET /collections/products/recordsКаталог, он же поиск через filterда
7GET /collections/products/records/{id}Карточка товарада
8POST /collections/cart/recordsДобавить в корзинуда
9PATCH /collections/cart/records/{id}Изменить количествода
10POST /collections/orders/recordsОформить заказда
11GET /collections/project/recordsСписок проектовда
12POST /collections/project/recordsСоздать проект с файломда
13GET /collections/_authOrigins/recordsСписок сессийда
14DELETE /collections/_authOrigins/records/{id}Удалить запись сессиида
Интересный факт

Поиск — не отдельный запрос, а тот же шестой с параметром filter. А удаления из корзины в списке нет, хотя приложению оно необходимо: иначе товар не убрать, а после заказа корзина останется полной. PocketBase его поддерживает — просто в спецификацию оно не попало.

Столбец «токен» стоит прочитать внимательно: без токена работают только два запроса — регистрация и вход. Всё остальное требует заголовка Authorization, и это определяет порядок работ.

Порядок, который окупается​

ПОРЯДОК, КОТОРЫЙ ОКУПАЕТСЯ1каркас сетиклиент, DTO, один запрос20 мин2входтокен, хранилище, интерцептор40 мин3каталогсписок и карточка60 мин4корзинаи заказ60 миндальше по остатку времени: акции · профиль · проекты · отказы · офлайнвход раньше каталога — потому что без токена половина запросов вернёт 404красивый экран без данных не стоит ничего: критерии смотрят на работу, а не на вёрстку
Четыре шага, после каждого из которых приложение работает

Шаг 1. Каркас сети, ~20 минут. Зависимости, разрешение INTERNET в манифесте, Retrofit.Builder с конвертером, один-единственный метод — каталог. Запустить, убедиться, что список товаров приходит и разбирается. Пока нет ни экранов, ни архитектуры.

Шаг 2. Вход, ~40 минут. Регистрация, вход, сохранение токена в DataStore, интерцептор, который его вешает. Проверить, что профиль читается.

Шаг 3. Каталог, ~час. Экран списка, карточка товара, поиск. Здесь появляются состояния: загрузка, содержимое, пусто, ошибка.

Шаг 4. Корзина и заказ, ~час. Добавление, количество, сборка корзины с ценами, оформление.

Дальше — по остатку времени: акции с баннерами, профиль и его редактирование, проекты с загрузкой файла, обработка отказов и поведение без сети.

Важно

Вход делается раньше каталога, хотя каталог интереснее. Причина простая: без токена двенадцать запросов из четырнадцати отвечают 404. Начав с каталога, вы полдня будете смотреть на «не найдено» и искать ошибку в коде, которой там нет.

Срезами, а не слоями​

Самая дорогая ошибка конкурсного дня — делать по слоям: сначала все DTO, потом все запросы, потом все экраны, потом связать. Звучит аккуратно и почти всегда заканчивается тем, что к вечеру не работает ничего.

ДВА СПОСОБА ПОТРАТИТЬ ДЕНЬпо слоям: к обеду ничего не работаетвсе DTOвсе запросывсе экранысвязатьпо срезам: после каждого шага что-то работает целикомкаталог+ вход+ корзина+ заказесли день кончится раньше плана, во втором случае есть что показать, а в первом нетэксперт оценивает работающее поведение, а не количество написанных классов
Одна и та же работа, разложенная двумя способами

Правильно — вертикальными срезами: один запрос доводится до экрана и работает целиком, потом следующий. После каждого среза приложение можно показать. Если день кончится раньше плана — а он кончится, — у вас будет работающая часть, а не набор незаконченных слоёв.

Совет

Практическое следствие: не пишите все четырнадцать методов интерфейса сразу. Опечатка в аннотации одного метода роняет создание всего клиента — Retrofit проверяет интерфейс целиком в момент create(). Четырнадцать методов — четырнадцать мест, где искать; один метод — одно.

Отвечено вопросов: 0 из 3
Сколько запросов спецификации работают без токена?
Почему не стоит описывать все методы интерфейса сразу?
В чём смысл работы вертикальными срезами?
Отвечено верно: 0 из 3

Спринт как рабочий день​

По правилам чемпионата день — это спринт, и у него есть формальная часть, за которую тоже дают баллы:

  • утром ветка sprint-X от main;
  • вечером pull request в main — и он должен быть закрыт, а не просто создан;
  • проект лежит файлами, не архивом;
  • коммиты по ходу дня, а не один в конце.
Важно

«Pull request создан» и «pull request закрыт» — разные вещи, и второе проверяется отдельно. Запрос, оставленный открытым, засчитывается не полностью. Это чистая формальность, которая не требует ни строчки кода, и терять на ней баллы особенно обидно.

Про коммиты стоит сказать отдельно. Один коммит «готово» в конце дня хуже десяти по ходу — и не только по критерию. Когда в шесть вечера что-то ломается, история из десяти коммитов позволяет откатиться на последнее работающее состояние. История из одного — нет.

Приёмочный прогон​

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

ПРИЁМОЧНЫЙ ПРОГОН ПЕРЕД СДАЧЕЙ1—4регистрация · вход · профиль · изменениеusers5—8акции · каталог · поиск · карточкаnews, products9—11корзина · количество · заказcart, orders12—14проект с файлом · список · сессииproject, _authOriginsПройдено: 15 из 15прогон занимает секунды и ловит то, что глазами по экранам не поймать
Отчёт приёмочного прогона: все операции спецификации за несколько секунд

Вот настоящий вывод такого прогона против стенда:

ПРИЁМОЧНЫЙ ПРОГОН: 14 операций спецификации

1. ✓ Регистрация создан 5o0rfb82cab1lxx
2. ✓ Авторизация токен 224 символов
3. ✓ Информация о пользователе профиль 5o0rfb82cab1lxx
4. ✓ Изменение профиля имя = Пробный
5. ✓ Акции и новости 3 шт., у первой файл banner_y2zhtudxtd.png
6. ✓ Список продуктов 12 товаров, страниц 3
7. ✓ Поиск (тот же путь, filter) 2 найдено
8. ✓ Описание продукта Nike Air Max 270
9. ✓ Создание корзины строка l79q3gp9zhnf01u, count 1
10. ✓ Изменение корзины count = 3
11. ✓ Создание заказа заказ bsvg1v9qupuletd
12. ✓ Создание проекта файл check_e79930sqp5.png
13. ✓ Список проектов 4 шт.
14. ✓ Получение id токена сессий 1, первая ggzj4zb99hl1z2m
15. ✓ Удаление записи сессии 204; токен после этого ПО-ПРЕЖНЕМУ РАБОТАЕТ

Пройдено: 15 из 15

Устроен он просто: одна функция-обёртка, которая ловит исключение и печатает строку.

private var no = 0
private var passed = 0

private suspend fun check(name: String, block: suspend () -> String) {
no++
val r = runCatching { block() }
if (r.isSuccess) {
passed++
println(" %2d. ✓ %-34s %s".format(no, name, r.getOrNull()))
} else {
println(" %2d. ✗ %-34s %s".format(no, name, r.exceptionOrNull()))
}
}

Каждая проверка возвращает строку с тем, что важно увидеть: не «ок», а настоящее значение из ответа. count = 3 в одиннадцатой строке — это доказательство, что изменение количества не просто вернуло 200, а действительно изменило.

Совет

Прогон окупается со второго запуска. После любой правки он за несколько секунд отвечает на вопрос «я ничего не сломал?» — а руками этот вопрос стоит десяти минут кликанья по экранам. И он же служит списком задач: каждый ✗ — незакрытый запрос.

Отвечено вопросов: 0 из 3
Что нужно сделать с pull request в конце спринта?
Почему приёмочная проверка возвращает значение, а не просто «ок»?
Зачем коммитить по ходу дня, а не одним коммитом в конце?
Отвечено верно: 0 из 3

Двадцать вещей, на которых теряют баллы​

Всё собрано из предыдущих глав — здесь коротким списком, чтобы пробежать глазами перед сдачей.

Сеть и разбор

  1. Забыли <uses-permission android:name="android.permission.INTERNET" />.
  2. Базовый адрес без завершающего слэша.
  3. Тип ответа списка описан как List<…> вместо PageResponse<…>.
  4. Нет ignoreUnknownKeys — разбор падает на служебных полях PocketBase.
  5. Одиночную запись пытаются разобрать как конверт.

Авторизация

  1. Поле входа названо email вместо identity.
  2. Токен хранится в переменной и не переживает перезапуск.
  3. Заголовок вешается вручную и в каком-то методе забыт.
  4. Написана ветка if (code == 401), которой не бывает: здесь 400 и 404.

Формы

  1. Под полем показан верхний message вместо разбора по полю из data.
  2. passwordConfirm проверили только на клиенте и не отправили.

Каталог и корзина

  1. Конец страниц определяют по пустому списку, а не по page >= totalPages.
  2. Пустой результат поиска показан как ошибка, а не как «ничего не найдено».
  3. Дубли в корзине: повторное нажатие создаёт вторую строку.
  4. Количество уходит в ноль и минус.
  5. Товары для корзины запрашиваются по одному — задача N+1.
  6. Сумма считается по строке approximateCost вместо числового price.

Файлы и отказы

  1. Имя файла подставлено как адрес картинки.
  2. В самолётном режиме приложение падает или показывает пустой экран.
  3. При выходе не очищены локальные данные — следующий видит чужую корзину.
Отвечено вопросов: 0 из 3
Список показывает товары, но подгрузка следующей страницы не срабатывает. Что проверить первым?
Сумма корзины получается странной, хотя товары верные. Наиболее вероятная причина?
После выхода и входа другим пользователем видна прежняя корзина. Что забыли?
Отвечено верно: 0 из 3

За полчаса до сдачи​

Короткий список того, что делается в самом конце и занимает минуты:

  • прогнать приёмочную проверку — все ли запросы живы;
  • пройти по всем экранам в самолётном режиме;
  • выйти из аккаунта и войти другим пользователем — не видно ли чужого;
  • перезапустить приложение — остался ли вход, на месте ли корзина;
  • проверить, что проект лежит файлами и собирается из чистой копии;
  • закрыть pull request.
Интересный факт

Сборка из чистой копии — отдельная ловушка. Проект, который собирается у вас, может не собраться у эксперта, если что-то важное не попало в репозиторий: например, файл с адресом сервера, исключённый в .gitignore вместе с остальными настройками. Проверяется это одной командой — клонировать свой же репозиторий в другую папку и собрать.

Куда углубиться​

  • Шаблон проекта, заготовленный заранее: зависимости, клиент, DataStore, базовые состояния экрана. На чемпионате разрешено, и это экономит первый час.
  • MockWebServer — прогон запросов без интернета, полезно, когда сервер площадки лежит.
  • Генерация клиента из спецификации: почему стоит писать руками, а не доверять генератору — в этой спецификации, например, есть места с нарушенными отступами.
  • Замер времени: сколько на самом деле уходит на каждый шаг, если засечь.

Сводная проверка по всей главе: 8 вопросов, 5:00 на всё, без подсказок по ходу. Вопросы показываются по одному, ответ изменить нельзя. Если время выйдет — экзамен завершится с тем, что успел ответить. Пересдавать можно сколько угодно раз.

Куда дальше: проверенные ресурсы​

Видео по теме

ВидеоThe Only Retrofit Tutorial You’ll Ever Need! (with Jetpack Compose)Android Knowledge
ВидеоGit - Pull request на практике / GitHubТимофей Коваленко
ВидеоPull request на GitHub — инструкция для новичков | ХекслетХекслет - нормальные it-курсы
ВидеоRetrofit Tutorial — How to Upload Multiple Files to ServerFuture Studio
ВидеоИнди-разработка Android-приложений. Боли, идеи и заработокAndroid Broadcast. Все об Андроид разработке

Комментарии

Обсуждение живёт в GitHub Discussions и грузится с чужого домена — поэтому только по нажатию.