Этап 5, Task 4. ДЕНЕЖНАЯ задача — решения владельца В-198 и В-199.
Что появилось:
- через трое суток «ещё в пути» становится «не доставлено». Довод не только
продуктовый: оператор помнит судьбу ровно трое суток, после этого ответа не
будет вовсе — закрыть сообщение обязаны мы, иначе итог рассылки никогда не
станет окончательным и досыл не будет знать, кого досылать;
- за «не доставлено» и за «оператор не отправил» деньги возвращаются клиенту на
кошелёк. МТС за недоставленное с нас не берёт (В-7), значит эти рубли лежали
у нас ни за что;
- у кошелька появился метод refund() — ОТДЕЛЬНО от пополнения, и это не
украшение: у topup() нет ключа события, а команда по расписанию по своей
природе повторяется. На topup() второй заход вернул бы деньги ДВАЖДЫ.
ИДЕМПОТЕНТНОСТЬ ДВОЙНАЯ, и проверено, что нужны обе опоры:
- ключ события sms:refund:{рассылка}:{номер} + уникальный ключ в базе;
- отметка refunded_at на сообщении, ставится в ТОЙ ЖЕ транзакции.
Вырез только ключа тест НЕ красит (спасает отметка). Вырез только отметки тоже.
Сняв ОБЕ, тест краснеет числом 117.00 вместо 108.50 — деньги вернулись дважды.
Так и должно быть: одна опора страхует другую.
Вырезы (все вернуты): убрать «оператор не отправил» из возвращаемых -> 1 красный;
убрать закрытие по сроку -> 3 красных; возвращать за ЛЮБУЮ судьбу -> 2 красных
(значит тесты «за доставленное не возвращаем» и «за в пути не возвращаем» —
настоящие приборы, а не зелень вхолостую).
ЖИВОЙ ДЕНЕЖНЫЙ ПРОГОН (sandbox выключен — в песочнице деньги не двигаются вовсе,
и прогон доказал бы НОЛЬ): баланс 100.00 -> 108.50 ₽, движение типа refund ровно
одно, отметка у недоставленного стоит, у доставленного нет. ВТОРОЙ заход той же
команды: баланс тот же, движений по-прежнему одно. Стенд возвращён в исходное.
Прогоны: модуль 343/343 (13 пачек, все с первой попытки), рекламный кошелёк 5/5
(я трогал общий AdWalletService), phpstan ровно 2 чужие давние, pint чисто.
Этап 5, Task 3. Появилась команда client-sms:poll-delivery — каждые десять минут
спрашивает у канала, что стало с отправленными сообщениями, и проставляет судьбу
в журнал.
Устройство:
- умение рассказать судьбу — способность КАНАЛА: канал без разъёма просто
пропускается. Когда у Т2/Мегафона/Билайна появятся кабинеты, команда не
изменится ни строчкой;
- отчёт ПРАВИТ существующую строку, новых не пишет (В-204): сторож зависших
меряет движение рассылки по последней записи журнала, и новые строки делали бы
зависшую рассылку «живой» — деньги остались бы замороженными;
- спрашиваем только реально отправленные этим каналом, с номером сообщения,
не старше трёх суток и с неокончательной судьбой. Оператор помнит судьбу трое
суток — спрашивать про старое бессмысленно;
- незнакомое слово оператора судьбу НЕ меняет: пишем слово в журнал и оставляем
графу как была. Угадать значило бы соврать про деньги.
ЖИВОЙ ПРОГОН ПОД БОЕВОЙ РОЛЬЮ, тройкой (политика srv_bypass воспроизведена тем же
текстом, что в db/03, — на стенде их нет ни одной):
А) право UPDATE есть + политика есть -> «уточнено: 1», судьба delivered;
Б) права UPDATE нет -> команда ПАДАЕТ «нет доступа к таблице» (видно);
В) права есть, политики нет -> «успех», «уточнено: 0», судьба пустая (НЕ видно).
Подтверждает В-181: у двух опор разная цена отказа, и опаснее вторая. Стенд
вернулся в исходное, политик srv_bypass не осталось.
ПРО ПРИБОРЫ — два теста оказались НЕ приборами, починены, а не подогнаны:
- «незнакомое слово не меняет судьбу» не отличал «графу не тронули» от «записали
пусто» — теперь у сообщения есть стартовая судьба, и проверяется, что она цела;
- «пробное не спрашиваем» краснел бы не от того: пробное отсекает фильтр по
КАНАЛУ, а не по статусу. Переименован по тому, что реально охраняет, и под две
оставшиеся защиты заведены свои тесты.
После починки каждый из четырёх вырезов добавляет ровно один красный тест.
Прогоны: модуль 336/336 (13 пачек, все с первой попытки), phpstan ровно 2 чужие
давние, pint чисто. Команда в расписании — проверено schedule:list.