Commit Graph

2 Commits

Author SHA1 Message Date
Дмитрий d61b552532 feat(смс-клиент): ДаДата даёт регион и оператора всем номерам, снимок дозревает
Этап 3 «Время и цена», Task 5. Строка листа 3.3 закрыта целиком.

Номер, про который мы ничего не знаем, перестал быть тупиком: теперь его судьбу решает
ДаДата — и решает ОДНИМ платным вопросом, потому что пояс и оператор приезжают в одном
ответе. Входов обогащения три, и все три ведут в одно и то же место.

Своя база: джоб обогащения контактов кладёт пояс рядом с оператором и догоняет номера,
загруженные до Этапа 3, — берёт контакт, у которого пусто хоть что-то одно. Без догона
такой номер после выката не получил бы СМС никогда.

Рассылка: новый EnrichClientSmsSnapshotRegionJob спрашивает про строки снимка без пояса и
пишет туда и пояс, и оператора. У номеров, вписанных руками, оператора не было вовсе —
это и есть закрытие старой жалобы В-69. Ставится из контроллера сразу после снимка и
только если такие строки есть; узнав регион, сам будит рассылку, чтобы созревшие номера
не ждали четверть часа зря. Будит ТОЛЬКО ждущую утра: у остановленной клиентом рассылки
статус другой, и повторный запуск затёр бы ей «остановлена» на «готово».

Авто-СМС: сделка без региона спрашивает ДаДату по номеру лида (В-100) — иначе заведённый
руками лид не получил бы СМС и после этой задачи. За номер платим один раз: узнанный пояс
несётся в самом отложенном задании, а «ответила, но пояса нет» помечается отметкой; заново
спрашиваем только если вопрос не состоялся — сбой сети или выбранный дневной лимит (В-104).

Дозревание: команда добора ставит окончательную причину строкам, которые ждут региона
дольше суток, и будит рассылку, чтобы та дописала причину в журнал и закончилась — деньги
размораживаются. В журнал пишет джоб отправки, он это и так умеет для всех пропущенных;
второго места, пишущего в журнал, не завёл (В-107).

Кого НЕ спрашиваем (В-105): строку, у которой пояс есть, а оператор пуст. Она и так уйдёт
универсальным каналом, вопрос был бы ради экономии на канале, а ДаДата — наши деньги:
рассылка на 20 000 номеров это до 12 000 ₽ при ещё не назначенном потолке расходов (В-90).
Платим только там, где без ответа сообщение не уйдёт вовсе.

Прогоны: СМС 217/217 (было 202, +15 новых), приём лидов 17/17, фронт 1663 зелёных,
phpstan 0, pint чисто. Миграций нет — колонки пояса завёл ещё Task 2.

Вырезанием проверено пятью вырезами: убрал запись пояса — покраснел «узнав регион, номер
уходит»; убрал суточный срок — покраснел «не узнали за сутки»; убрал вторую половину
условия пробуждения — покраснел денежный тест про заморозку; убрал вопрос ДаДате в
авто-СМС — покраснели три теста третьего входа; убрал догон старых номеров базы —
покраснел «догоняет уже загруженный номер».

Живьём на локальной базе (19:19 МСК, ДаДата заглушена, живой ключ не тронут): вписанные
руками номера узнали пояс и оператора, московский ушёл сразу, камчатский отложен до
10 утра его времени; номер, про который ДаДата молчит, через сутки получил причину и
рассылка стала «готово»; сделка без региона получила авто-СМС по ответу ДаДаты, а сделка,
про которую ДаДата молчит, ждёт дальше и в журнал не пишет. Стенд возвращён как был.

🪤 Урок В-106: повторный Http::fake() прежнюю заглушку не заменяет — отвечает первая, и
тест «а теперь ДаДата отвечает» молча проверяет старый ответ.

🟢 Запрет на выкат ветки СНЯТ (был из-за В-93): ни один источник больше не отправляет ноль.
Выкатывать всё равно рано — Этап 3 не закончен, цена считается по-старому.
2026-07-28 19:28:14 +03:00
Дмитрий 2460623ae0 feat(смс-клиент): ночной лид получает авто-СМС утром и ровно одно
Этап 3 «Время и цена», Task 4. Строка листа 3.6, дозакрыта 3.1.

Авто-СМС на новый лид больше не будит людей ночью. Джоб спрашивает про час у того же
SmsQuietHours, что и рассылка, и если у ПОЛУЧАТЕЛЯ окно закрыто — переставляет сам себя
на момент его открытия. В журнал при этом не пишет ничего: идемпотентность у авто-СМС —
по наличию любой записи по этому лиду, и запись «ждём утра» навсегда закрыла бы лиду
дорогу (класс поломки В-49). «Ровно одно» держит прежняя защита: три запуска подряд дают
одно сообщение.

Проверка стоит ПОСЛЕ отбора, а не сразу после идемпотентности, как предлагал план.
Сначала выясняется, уйдёт ли номер вообще (стоп-лист, дубль, нет маршрута), и только
потом — когда именно. Иначе номеру из стоп-листа вместо окончательной причины досталось
бы «ждём региона», и человек ждал бы отправки, которой не будет никогда (В-67).
Контрольный тест это стережёт: перенос проверки вперёд его роняет.

Москву при пустом регионе не подставляем. План велел «нет региона — считаем московским»,
но эту запись владелец отменил (В-85, журнал В-98): вставь я её дословно, камчатский лид,
пришедший в московский вечер, получил бы СМС в четыре утра. Сделка без региона сутки ждёт
уточнения — джоб переставляет себя раз в час и молчит, — потом получает одну честную
запись skipped_unknown_region. Подписи причины заведены в обоих словарях экрана сразу
(ловушка В-70), хотя журнала авто-СМС на портале пока нет вовсе — это существующая дыра,
названа отдельно (В-102).

Старым тестам авто-СМС проставлен регион в фикстурах и зафиксирован час: тот же ремонт,
что в В-93 — боевое правило не ослаблял.

Прогоны: СМС 202/202 (было 195, 7 новых тестов), приём лидов 17/17, фронт 1663 зелёных,
phpstan 0, pint чисто, типы — те же 8 чужих давних. Миграций нет.

Вырезанием проверено четырежды: убрал проверку окна — покраснели три теста; убрал суточный
срок — покраснел тест про честную причину; поставил проверку региона до стоп-листа —
покраснели «дверь открыта» и контрольный про стоп-лист; убрал подпись из словаря журнала —
покраснел фронтовый тест.

Живьём на локальной базе (18:15 в Москве, 03:15 на Камчатке): московский лид ушёл сразу,
камчатский отложен на 22:00 UTC — это десять утра следующего дня у него; свежая сделка без
региона отложена на час, двухдневная получила причину. Затем открыл окно камчатской и
запустил трижды — сообщение одно. Стенд возвращён как был.

🪤 Урок В-101: под тестовой очередью «переставить себя» исполняется немедленно — джоб зовёт
себя без конца и прогон виснет. В таких тестах обязателен Queue::fake().

⚠️ Ветку по-прежнему нельзя выкатывать до Task 5 (В-93).
2026-07-28 18:23:15 +03:00