Commit Graph

2 Commits

Author SHA1 Message Date
Дмитрий 76e4862f95 fix(реклама): удалённый черновик уносил запертые деньги, отказы говорили по-машинному
Приёмка Яндекса, восемь бесплатных карточек. Две проверены на живых деньгах
боевой кампании #13, шесть — сторожами на тестовой базе.

Нашлась настоящая дыра. Если запуск оборвался на полпути, деньги остаются
заперты нарочно: в Директе кампания уже завелась и может крутить показы,
клиент дожмёт «Запустить». Но выглядит она обычным черновиком, а черновик
разрешено удалить. Кампании после этого нет, а запись о заморозке ссылается
на её номер — возврат ищут по кампании и не находят. Деньги клиента заперты
навсегда, молча, без единой ошибки в журнале. Теперь такой черновик удалить
нельзя, и портал объясняет, что сделать вместо этого.

Второе: портал отказывал на языке разработчика — «Поле file должно быть
файлом одного из типов», «не может быть больше 10240 Кбайт». Клиент не знает,
что такое поле file, и не считает мегабайты в килобайтах. Четыре отказа по
файлам переписаны по образцу, который в этом же модуле уже был: «Нужен ровно
300×250. Вы загрузили 100×100.»

Сторожа приёмки:
- чужая кампания отдаёт 404 на всех двадцати одном пути, ни одного 403;
- сколько раз ни жми «Запустить» — заморозка одна;
- отклонённую в обход «Исправить» не запустить, денег не трогает;
- черновик с запертыми деньгами не уносит их;
- списание посреди запуска другой кампании — касса сходится;
- отказы по файлам сказаны человеческим языком.

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

Прогоны: 413 тестов рекламы зелёные, статанализ 0.

Заодно форматирование теста минимума площадки — форматтер перенёс длинное имя
класса в шапку уже после записи 80987996e.
2026-08-05 07:40:27 +03:00
Дмитрий 80987996ed fix(реклама): мастер не врёт про показы, остановка без денег платит за показанное
Три починки, каждая сперва увидена красной.

1. Мастер обещал показы как факт: «~8 150». Живой замер 04.08 по кабинету:
   чужая кампания с бюджетом 13 000 руб за четверо суток набрала 457 показов
   и потратила 138 руб — деньги не кончились, кончились люди. Число из мастера
   это потолок, до которого кампания почти наверняка не дотянет. Стало
   «не больше 8 150» плюс объяснение: упрётся в список, а не в деньги;
   за несостоявшиеся показы деньги вернутся. Поправлено на шаге частоты
   и в сводке перед отправкой.

2. Остановка «нет денег» возвращала заморозку, НЕ заплатив за уже показанное.
   Списание делает часовая задача, а она берёт только кампании со статусом
   «крутится» — остановленную пропускала навсегда. Показы последнего часа
   уходили клиенту даром, а Яндексу за них платили мы. Та же дыра, что чинили
   в паузе, только через другую дверь. Теперь: сперва заплати, потом отпускай;
   не узнал число показов — не отпускай вовсе.

3. Остановка ходит в Директ по два раза на каждую кампанию, и делала это
   ВНУТРИ денежной транзакции — замок строки висел всё время сетевых запросов.
   Вынесено наружу: денежная операция закрывается, и только потом остановка.

Плюс минимум площадки в мастере. Директ не берёт кампанию дешевле 300 руб
за каждый календарный день и отвечает по-английски на последнем шаге, когда
клиент уже пятнадцать часов собирал аудиторию. Теперь мастер предупреждает
заранее и по-русски. Формула вынесена в YandexMinimumSpend и одна на портал:
ею пользуются и запуск, и мастер — две копии однажды разошлись бы.

Сторожа: 7 новых на бэкенде и фронте, каждый принят красным.
Прогоны: реклама 458 тестов зелёные, Larastan 0, vue-tsc чисто.
2026-08-05 04:26:27 +03:00