Монитор госзакупок, который не подаёт и не подписывает
Сервис следит за электронным магазином малых государственных закупок: находит новые закупки в узкой нише, подтягивает публичные оферты, разбирает проект контракта и готовит подачу. Двенадцать экранов, двадцать шесть маршрутов, восемь фоновых задач.
Оферту он не подаёт и электронной подписью не подписывает. Это не недоделка, и об этом ниже.
Проверка источника до разработки
Начальная идея была шире: считать, как часто конкретный поставщик выходит на закупки, и по этому оценивать активность конкурентов. От неё пришлось отказаться на второй день.
Состав участников процедуры публично не публикуется: ни списка поданных оферт, ни протоколов, ни проигравших заявок, и ни в одном статусе, включая завершённые. Я проверил это на пяти карточках в разных состояниях, чтобы не делать вывод по одной неудачной. Считается только то, где поставщик выиграл, потому что победитель попадает в реестр контрактов.
Несколько часов проверки против нескольких недель разработки интерфейса поверх данных, которых не существует.
Одна очередь оказалась на одну меньше, чем нужно
Задач восемь, и сначала они крутились в общей очереди под одним замком. Сломалось это об обход каталогов магазинов: одиннадцать разделов с полуторасекундной паузой между запросами, то есть десятки минут работы. Всё это время замок держал остальных, включая опрос оферт по горящим лотам, а горящий лот это тот, до дедлайна которого осталось меньше часа.
Сейчас очереди две, в разных потоках. Быстрая ходит на портал закупок и держит ленту свежей, медленная обходит каталоги и прайс-фиды дистрибьюторов. Замка тоже два, и это не симметрия ради симметрии: портальный нужен, чтобы несколько задач не били площадку залпом, а магазинный существует отдельно потому, что обход каталогов ходит своей сессией и очереди запросов к порталу не касается вовсе.
Периоды после разделения выглядят так:
| Задача | Период |
|---|---|
| Поиск новых закупок в нише | 10 минут |
| Оферты по лотам, где решено участвовать | 3 минуты |
| Оферты по остальным отобранным | 15 минут |
| Скачать и разобрать проект контракта | 10 минут |
| Сверить модели справочника с позициями лота | 20 минут |
| Обойти каталоги магазинов | сутки |
| Подтвердить, что наша подача прошла | 5 минут |
| Архив завершённых и резервная копия | сутки |
Три минуты и час до дедлайна лежат в конфиге рядом и правятся в настройках, без правки кода. Подходящий период выясняется в работе, а не при проектировании, и это оказалось нужнее, чем я думал, когда закладывал экран настроек.
Каждая задача пишет результат в отдельную таблицу, и падение одной не мешает остальным. Выключение ждёт потоки не дольше десяти секунд: обход каталогов может идти полчаса, держать из-за него остановку незачем, недоделанное повторится на следующем старте.
Где сервис останавливается
Он доводит работу до момента подачи. Оферту отправляет человек, подписывает тоже человек.
Последний шаг самый рутинный, и соблазн его закрыть автоматикой очевиден. Но подача оферты это юридически значимое действие, и ошибка здесь даёт не неудобство, а подписанный контракт, который придётся исполнять. Разница с кодом простая: плохой код переписывают, подписанный контракт исполняют.
Отказ от расчёта маржи
Сначала в сервисе был расчёт цены: прайсы поставщиков, наценка, себестоимость лота, маржа. Потом я от этого отказался. Заказчик здесь я сам, так что и решение, и переделка были мои.
Сравнение стало не про деньги, а про соответствие техническому заданию. Вместо оценки совпадения числом от нуля до ста появились счётчики по требованиям: сколько параметров лучше требуемого, сколько ровно, сколько не проходит, по скольким нет данных. Сводка теперь звучит как «модель, проходящая по ТЗ, найдена для N позиций из M», и сравнение характеристик строгое: требование закупки против характеристики модели, без округления в свою пользу.
«87 из 100» выглядит убедительно и не отвечает на вопрос, можно ли подавать. «Два параметра лучше, три ровно, один не проходит» отвечает.
Заодно поменялось, что считать критичным. Раньше туда входила отрицательная маржа, теперь осталось три вещи: меньше часа до дедлайна, непройденное обязательное требование, ошибка фоновой задачи.
Резервные копии
Служба делает копию базы раз в сутки, сжимает и прореживает: семь последних суточных, по одной на неделю в пределах месяца, по одной на месяц.
Копия рядом с базой не спасает от потери сервера, поэтому есть вторая, наружу, и перед отправкой она проверяется: распаковать, прогнать проверку целостности, пересчитать записи. Копия, которая не открывается, хуже отсутствия копии, потому что создаёт уверенность.
Отдельно записано, что наружу не уезжает и потеряется вместе с сервером: файл с паролями и секретами. Такой список оказался полезнее самой схемы резервирования.
В тексте нет адресов запросов, параметров и особенностей поведения источника, а также названий компаний, по которым велась аналитика. Первое это рабочее преимущество, второе чужие данные.