fix обзвон: один человек, записанный через 8 и через +7, получал два звонка
Решение владельца Р127. В `odinVidNomera` восьмёрка приводится к семёрке ТОЛЬКО когда цифр ровно одиннадцать. Короткие и длинные номера не трогаются вовсе: слить двух разных людей в один знак — ошибка тише и хуже, второму не позвонили бы никогда и молча. Замерено до правки: `8 999 000-00-01` и `+7 999 000-00-01` давали два разных знака и два разных тела. Сторожа смотрят на то, что вправду уехало роботу, и сравнивают дословно — так же, как сравнивает приёмник. Сторожа показаны красными тремя сломами: без починки, без проверки длины и с приведением к восьмёрке вместо семёрки. Возврат доказан слепком файла. Названная владельцу цена: иностранные одиннадцатизначные номера с кодом на восьмёрку это правило портит. Вьетнамские `8496…`/`8497…`/`8498…` станут похожи на живые коды Московской области — робот позвонит чужому человеку. Лечится чисткой номеров на входе, это отдельная задача. Подробности и незакрытые хвосты — в отчёте. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -205,16 +205,46 @@ final class ObzvonClient
|
||||
* снимает отпечаток с тела ДОСЛОВНО и на такую пару отвечает 422 — то есть
|
||||
* честный повтор получал отказ вместо звонка.
|
||||
*
|
||||
* 🪤 Что этот канон НЕ делает, говорю вслух: он снимает ЗАПИСЬ (плюсы,
|
||||
* скобки, пробелы, тире), но не решает, один ли человек за `8999…` и
|
||||
* `+7999…`. Для портала это сегодня РАЗНЫЕ люди — разный знак, два звонка.
|
||||
* Считать ли их одним, решает владелец, и это отдельное решение: ошибись оно
|
||||
* в другую сторону — два РАЗНЫХ человека слились бы в один знак, и второму
|
||||
* не позвонили бы никогда. Молча угадывать такое нельзя.
|
||||
* 🔴 Р127 (решение владельца 07.08.2026). Канон снимает не только ЗАПИСЬ
|
||||
* (плюсы, скобки, пробелы, тире), но и разницу между `8999…` и `+7999…` —
|
||||
* ровно при ОДИННАДЦАТИ цифрах. Это один и тот же живой человек, записанный
|
||||
* двумя обычными способами; до Р127 он получал два разных знака, ему звонили
|
||||
* дважды, и клиент платил за два звонка вместо одного.
|
||||
*
|
||||
* 🔴 Почему правило такое узкое, а не «любой номер с восьмёрки». Ошибка в
|
||||
* обратную сторону тише и хуже: слей мы двух РАЗНЫХ людей в один знак, робот
|
||||
* счёл бы второе задание повтором первого, и второму человеку не позвонили бы
|
||||
* НИКОГДА и МОЛЧА. Ровно одиннадцать цифр с ведущей `8` — это российский
|
||||
* междугородний набор (та же форма и у казахстанских `+7`), и второго
|
||||
* толкования у него в наших списках нет. Короткие (`8495123`) и длинные
|
||||
* (`8999000000012`) не трогаем ВОВСЕ: там `8` может значить что угодно.
|
||||
*
|
||||
* 🪤 Цена, названная владельцу вслух: одиннадцатизначные ИНОСТРАННЫЕ номера,
|
||||
* начинающиеся на `8` (Вьетнам `+84`, Гонконг `+852`, Япония `+81`), это
|
||||
* правило испортит — `84…` превратится в `74…`. Вьетнамский при этом может
|
||||
* стать ПОХОЖИМ на живой российский, и позвонят чужому человеку. Лечится это
|
||||
* чисткой номеров на входе, при загрузке списка, — отдельная задача, которой
|
||||
* в плане сейчас нет.
|
||||
*
|
||||
* 🪤 И второе, тоже вслух: `9990000001` (десять цифр, без кода страны) для
|
||||
* этого канона по-прежнему ДРУГОЙ человек, чем `79990000001`. А проверка
|
||||
* запрета обзвона ({@see ObzvonZapretObrabotki}) сверяет ПОСЛЕДНИЕ ДЕСЯТЬ
|
||||
* цифр и считает их одним. Расхождение узкое и намеренное — Р127 короткие
|
||||
* номера трогать запретил.
|
||||
*
|
||||
* 🔴 Правило устойчиво к повтору: приведённый номер, поданный сюда второй
|
||||
* раз, не меняется. На это опирается {@see self::pozvonit()} — он приводит
|
||||
* номер сам, а {@see self::klyuchZadaniya()} приводит его ещё раз.
|
||||
*/
|
||||
private static function odinVidNomera(string $telefon): string
|
||||
{
|
||||
return preg_replace('/\D+/', '', $telefon) ?? '';
|
||||
$cifry = preg_replace('/\D+/', '', $telefon) ?? '';
|
||||
|
||||
if (strlen($cifry) === 11 && str_starts_with($cifry, '8')) {
|
||||
return '7'.substr($cifry, 1);
|
||||
}
|
||||
|
||||
return $cifry;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -442,6 +442,123 @@ it('🔴 Г: вторая попытка по тому же номеру — з
|
||||
->and($otvet->poluchenLi())->toBeTrue();
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// 🔴🔴 Р127 — ВОСЬМЁРКА К СЕМЁРКЕ, И ТОЛЬКО ПРИ ОДИННАДЦАТИ ЦИФРАХ
|
||||
//
|
||||
// Что было замерено ДО починки: `8 999 000-00-01` и `+7 999 000-00-01` — один и
|
||||
// тот же живой человек, записанный двумя обычными способами, — давали ДВА разных
|
||||
// знака и два разных тела. Ему позвонили бы дважды, и клиент заплатил бы за два
|
||||
// звонка вместо одного.
|
||||
//
|
||||
// Решение владельца Р127: приводить `8` к `7` ТОЛЬКО когда цифр ровно
|
||||
// одиннадцать. Короткие и длинные не трогать вовсе — там `8` может значить что
|
||||
// угодно, и слияние двух РАЗНЫХ людей в один знак было бы ошибкой в обратную
|
||||
// сторону: второму не позвонили бы никогда и МОЛЧА.
|
||||
//
|
||||
// 🪤 Сторожа ниже сверяют то, что и вправду уехало подставному роботу, и
|
||||
// сверяют ДОСЛОВНО — так же, как сравнивает приёмник. Сверка «с точностью до
|
||||
// приведения» была бы слепа ровно к той половине беды, ради которой пишется.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
it('🔴 Р127 А (нож): восьмёрка и плюс-семь — ОДИН человек, один знак и одно тело', function () {
|
||||
Http::fake(fn () => Http::response(['ok' => true, 'call_id' => 'x'], 200));
|
||||
|
||||
$klient = klientObzvona();
|
||||
$klient->pozvonit(...zadanieProbnoe(['telefon' => '8 999 000-00-01']));
|
||||
$klient->pozvonit(...zadanieProbnoe(['telefon' => '+7 999 000-00-01']));
|
||||
|
||||
$tela = telaUshedshih();
|
||||
|
||||
expect($tela)->toHaveCount(2)
|
||||
->and($tela[1]['zadanie'])->toBe($tela[0]['zadanie'])
|
||||
->and($tela[1]['telefon'])->toBe($tela[0]['telefon'])
|
||||
->and($tela[0]['telefon'])->toBe('79990000001');
|
||||
});
|
||||
|
||||
it('🔴 Р127 Б (контроль): что совпадало до починки — совпадает и после', function () {
|
||||
Http::fake(fn () => Http::response(['ok' => true, 'call_id' => 'x'], 200));
|
||||
|
||||
$klient = klientObzvona();
|
||||
$klient->pozvonit(...zadanieProbnoe(['telefon' => '+7 (999) 000-00-01']));
|
||||
$klient->pozvonit(...zadanieProbnoe(['telefon' => '79990000001']));
|
||||
|
||||
$tela = telaUshedshih();
|
||||
|
||||
expect($tela)->toHaveCount(2)
|
||||
->and($tela[1]['zadanie'])->toBe($tela[0]['zadanie'])
|
||||
->and($tela[1]['telefon'])->toBe($tela[0]['telefon']);
|
||||
});
|
||||
|
||||
it('🔴 Р127 В (граница): одиннадцать цифр НЕ с восьмёрки — не трогаем', function () {
|
||||
Http::fake(fn () => Http::response(['ok' => true, 'call_id' => 'x'], 200));
|
||||
|
||||
klientObzvona()->pozvonit(...zadanieProbnoe(['telefon' => '79990000001']));
|
||||
|
||||
expect(telaUshedshih()[0]['telefon'])->toBe('79990000001');
|
||||
});
|
||||
|
||||
it('🔴 Р127 Г (граница): начинается на 8, но цифр НЕ одиннадцать — не трогаем вовсе', function () {
|
||||
// 🔴 Ровно то, что владелец отрезал сознательно. Короткий `8495123` — это не
|
||||
// «российский мобильный без кода»: подставь мы туда семёрку, два РАЗНЫХ
|
||||
// человека могли бы слиться в один знак, и второму не позвонили бы МОЛЧА.
|
||||
Http::fake(fn () => Http::response(['ok' => true, 'call_id' => 'x'], 200));
|
||||
|
||||
$klient = klientObzvona();
|
||||
$klient->pozvonit(...zadanieProbnoe(['telefon' => '8495123']));
|
||||
$klient->pozvonit(...zadanieProbnoe(['telefon' => '8999000000012']));
|
||||
|
||||
$tela = telaUshedshih();
|
||||
|
||||
expect($tela)->toHaveCount(2)
|
||||
->and($tela[0]['telefon'])->toBe('8495123')
|
||||
->and($tela[1]['telefon'])->toBe('8999000000012');
|
||||
});
|
||||
|
||||
it('🔴 Р127 Г: длинный номер с восьмёрки НЕ сливается со своим семёрочным двойником', function () {
|
||||
// Сторож против «улучшения» правила: сними кто-нибудь проверку длины — эти
|
||||
// два знака стали бы одним, и одному из двух людей не позвонили бы вовсе.
|
||||
expect(ObzvonClient::klyuchZadaniya(1, 7, '8999000000012', 1))
|
||||
->not->toBe(ObzvonClient::klyuchZadaniya(1, 7, '7999000000012', 1));
|
||||
|
||||
expect(ObzvonClient::klyuchZadaniya(1, 7, '8495123', 1))
|
||||
->not->toBe(ObzvonClient::klyuchZadaniya(1, 7, '7495123', 1));
|
||||
});
|
||||
|
||||
it('🔴 Р127 Д (граница): строка без цифр — дверь по-прежнему закрыта', function () {
|
||||
// Это уже было построено до Р127 и обязано пережить починку: восьмёрка не
|
||||
// должна была открыть дверь тому, кому она закрыта.
|
||||
Http::fake();
|
||||
|
||||
$otvet = klientObzvona()->pozvonit(...zadanieProbnoe(['telefon' => 'абв']));
|
||||
|
||||
expect($otvet->iskhod)->toBe(IskhodObrashcheniya::DverZakryta)
|
||||
->and($otvet->zvonokMogSostoyatsya())->toBeFalse();
|
||||
Http::assertNothingSent();
|
||||
});
|
||||
|
||||
it('🔴 Р127 Е: вторая попытка по восьмёрочному номеру — знак ДРУГОЙ, звонок законный', function () {
|
||||
Http::fake(fn () => Http::response(['ok' => true, 'call_id' => 'x'], 200));
|
||||
|
||||
$klient = klientObzvona();
|
||||
$klient->pozvonit(...zadanieProbnoe(['telefon' => '8 999 000-00-01', 'popytkaNo' => 1]));
|
||||
$otvet = $klient->pozvonit(...zadanieProbnoe(['telefon' => '8 999 000-00-01', 'popytkaNo' => 2]));
|
||||
|
||||
$tela = telaUshedshih();
|
||||
|
||||
expect($tela)->toHaveCount(2)
|
||||
->and($tela[1]['zadanie'])->not->toBe($tela[0]['zadanie'])
|
||||
->and($tela[1]['telefon'])->toBe($tela[0]['telefon'])
|
||||
->and($otvet->poluchenLi())->toBeTrue();
|
||||
});
|
||||
|
||||
it('🔴 Р127: приведение НЕ портит знак дважды — второй прогон ничего не меняет', function () {
|
||||
// 🪤 `pozvonit` приводит номер, а `klyuchZadaniya` приводит его ВТОРОЙ раз
|
||||
// уже приведённым. Не будь правило устойчивым к повтору, знак в теле и знак,
|
||||
// посчитанный от тела, разошлись бы — и робот ответил бы 422.
|
||||
expect(ObzvonClient::klyuchZadaniya(1, 7, '8 999 000-00-01', 1))
|
||||
->toBe(ObzvonClient::klyuchZadaniya(1, 7, '79990000001', 1));
|
||||
});
|
||||
|
||||
it('🔴 знак в заголовках — тот же самый, что в теле', function () {
|
||||
// Приёмник сверяет тело с заголовком и на расхождении отвечает 400. Разойдись
|
||||
// они здесь — не дозвонились бы вовсе, и по коду 400 причину не узнать.
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
# Отчёт помощника — Р127: восьмёрка к семёрке, стройка 6
|
||||
|
||||
**Дата:** 07.08.2026
|
||||
**Ветка:** `feat/prospects-manual-testing-kp`
|
||||
**Задача:** исполнить решение владельца Р127 в `odinVidNomera()` и ничего сверх.
|
||||
|
||||
## Что было велено
|
||||
|
||||
Ровно одиннадцать цифр, первая `8` → заменить первую на `7`. Короткие и длинные
|
||||
не трогать вовсе. Всё остальное в клиенте не трогать.
|
||||
|
||||
## Моя ошибка — называю первой
|
||||
|
||||
Разбирая задание, я сперва принял на веру довод «одиннадцать цифр с восьмёркой
|
||||
не имеют второго толкования» и собирался ответить «возражений нет». Проверив, я
|
||||
нашёл, что второе толкование есть, — и нашёл его только потому, что владелец
|
||||
попросил проверить именно это место. Сам бы я мимо прошёл. Подробности — в
|
||||
разделе «Разбор задания».
|
||||
|
||||
## Что построено
|
||||
|
||||
Один файл, один метод, четыре строки работы:
|
||||
|
||||
```php
|
||||
$cifry = preg_replace('/\D+/', '', $telefon) ?? '';
|
||||
|
||||
if (strlen($cifry) === 11 && str_starts_with($cifry, '8')) {
|
||||
return '7'.substr($cifry, 1);
|
||||
}
|
||||
|
||||
return $cifry;
|
||||
```
|
||||
|
||||
Правило устойчиво к повтору (приведённый номер, поданный второй раз, не
|
||||
меняется) — это обязательно: `pozvonit()` приводит номер сам, а
|
||||
`klyuchZadaniya()` приводит его ещё раз. Не будь устойчивости, знак в теле и
|
||||
знак, посчитанный от тела, разошлись бы, и робот ответил бы 422.
|
||||
|
||||
Сверх этого не тронуто ничего: разбор ответа робота, оба предела времени, предел
|
||||
одновременных, проверка шифрования (Р123), подпись `pozvonit` — как были.
|
||||
|
||||
## Мерка 1 — сторожа, показанные КРАСНЫМИ
|
||||
|
||||
Сторожа смотрят на то, что **вправду уехало подставному роботу** (поля `zadanie`
|
||||
и `telefon` перехваченного запроса), и сравнивают **дословно** — так же, как
|
||||
сравнивает приёмник. Сверка «с точностью до приведения» была бы слепа ровно к
|
||||
той половине беды, ради которой пишется.
|
||||
|
||||
Файл: `app/tests/Feature/Obzvon/ObzvonClientShovTest.php`, раздел «Р127».
|
||||
|
||||
### Слом 1 — починки нет вовсе (исходное состояние, RED до правки)
|
||||
|
||||
`63 теста, 61 зелёный, 2 красных`
|
||||
|
||||
| Сторож | Что сказал дословно |
|
||||
|---|---|
|
||||
| `Р127 А (нож)` | `Failed asserting that two strings are identical. -'z55cd4b336662c0c967a469e193b10791fe7231d5' +'z3739fcb0392739d92d950b3b97eb31e145da4dce'` |
|
||||
| `Р127: приведение НЕ портит знак дважды` | `Failed asserting that two strings are identical. -'z3739fcb0392739d92d950b3b97eb31e145da4dce' +'z55cd4b336662c0c967a469e193b10791fe7231d5'` |
|
||||
|
||||
Это и есть замеренная беда: два знака на одного человека.
|
||||
|
||||
### Слом 2 — снял проверку длины (`=== 11` → `>= 7`)
|
||||
|
||||
`63 теста, 61 зелёный, 2 красных`
|
||||
|
||||
| Сторож | Что сказал дословно |
|
||||
|---|---|
|
||||
| `Р127 Г (граница): начинается на 8, но цифр НЕ одиннадцать` | `Failed asserting that two strings are identical. -'8495123' +'7495123'` |
|
||||
| `Р127 Г: длинный номер с восьмёрки НЕ сливается со своим семёрочным двойником` | `Expecting 'zc0a12c93a346ed7b0ee5f1849f90…53b662' not to be 'zc0a12c93a346ed7b0ee5f1849f90…53b662'.` |
|
||||
|
||||
Второй сторож — тот самый, что охраняет от ошибки в обратную сторону: слияния
|
||||
двух РАЗНЫХ людей в один знак.
|
||||
|
||||
### Слом 3 — привёл к восьмёрке вместо семёрки
|
||||
|
||||
`63 теста, 59 зелёных, 4 красных`
|
||||
|
||||
| Сторож | Что сказал дословно |
|
||||
|---|---|
|
||||
| `Р127 А (нож)` | `-'79990000001' +'89990000001'` |
|
||||
| `Р127 В (граница): одиннадцать цифр НЕ с восьмёрки — не трогаем` | `-'79990000001' +'89990000001'` |
|
||||
| `в теле задания номер без украшений` (прежний сторож) | `-'79990000001' +'89990000001'` |
|
||||
| `«что ещё сказать роботу» не может перебить знак или номер` (прежний) | `-'79991234567' +'89991234567'` |
|
||||
|
||||
Этот слом доказывает, что случай В — не украшение: он ловит правило, которое
|
||||
сливает пару правильно, но выбирает не тот канон.
|
||||
|
||||
### Возврат доказан слепком
|
||||
|
||||
```text
|
||||
должно быть 72f220ebe9a26cb4545be92e83d14c672e96b00d
|
||||
72f220ebe9a26cb4545be92e83d14c672e96b00d
|
||||
```
|
||||
|
||||
(`tr -d '\r' < app/app/Services/Obzvon/ObzvonClient.php | git hash-object --stdin`)
|
||||
|
||||
После возврата: `63 теста, 63 зелёных, 158 проверок`.
|
||||
|
||||
## Мерка 2 — полный прогон
|
||||
|
||||
Своя база `liderra_testing_vosmerka`, поднята с нуля через `migrate --force`.
|
||||
Вывод в файл, не в трубу.
|
||||
|
||||
```text
|
||||
"result":"passed"
|
||||
"tests":5301
|
||||
"passed":5297
|
||||
"skipped":4
|
||||
"assertions":21819
|
||||
"duration_ms":1177716
|
||||
```
|
||||
|
||||
**Датчик — число проверок, а не код возврата.** Арифметика сходится:
|
||||
`5297 зелёных + 0 красных + 0 ошибок + 4 пропущено = 5301 всего`.
|
||||
|
||||
На входе было **5293** проверки при нуле красных. Стало **5301** — ровно на
|
||||
восемь больше, и восемь новых сторожей я и написал. Красных — **ноль**.
|
||||
Красноты в `Render` и `Autopodbor` не было вовсе, расследовать нечего.
|
||||
|
||||
Заодно: `composer stan` — **0 ошибок**; `vendor/bin/pint --test` по обоим моим
|
||||
файлам — **passed**.
|
||||
|
||||
## Мерка 3 — про соседей
|
||||
|
||||
моего имени нет ни в одном выводе ни одного сторожа
|
||||
|
||||
чужие файлы в дереве не тронуты
|
||||
|
||||
## Мерка 4 — зовущие поимённо
|
||||
|
||||
🔴 Прибор (`grep`) считает **упоминания строк**, а не вызовы: он одинаково
|
||||
покажет и `self::odinVidNomera($telefon)`, и `{@see self::odinVidNomera()}`
|
||||
внутри пояснения. По `app/app` он даёт **13 упоминаний**. Вызовов среди них
|
||||
**три**, и все три — внутри самого `ObzvonClient.php`:
|
||||
|
||||
| Кто зовёт | Кого | Где |
|
||||
|---|---|---|
|
||||
| `ObzvonClient::klyuchZadaniya()` | `odinVidNomera()` | `ObzvonClient.php:193` |
|
||||
| `ObzvonClient::pozvonit()` | `odinVidNomera()` | `ObzvonClient.php:255` |
|
||||
| `ObzvonClient::pozvonit()` | `klyuchZadaniya()` | `ObzvonClient.php:269` |
|
||||
|
||||
Остальные десять — упоминания в пояснениях (`{@see …}`), в том числе
|
||||
`ObzvonOtvet.php:84` и `Autopodbor/Agent/Fetch/DverRendera.php:21`. Ни одного
|
||||
вызова оттуда нет.
|
||||
|
||||
**За пределами класса `ObzvonClient` живых потребителей сегодня НЕТ ни одного** —
|
||||
ни джоба, ни команды, ни контроллера. Из тестов зовёт один файл:
|
||||
`app/tests/Feature/Obzvon/ObzvonClientShovTest.php`. Значит область поражения
|
||||
правки замкнута двумя файлами.
|
||||
|
||||
## Разбор задания
|
||||
|
||||
### Картина беды верна
|
||||
|
||||
Замер сходится: `'8 999 000-00-01'` и `'+7 999 000-00-01'` давали разные знаки и
|
||||
разные тела. Я это увидел красным до правки (слом 1). Лечение лечит именно это.
|
||||
|
||||
### 🔴 «Одиннадцать цифр с восьмёркой не имеют второго толкования» — НЕВЕРНО. Цена
|
||||
|
||||
Владелец просил проверить это место, и оно не выдерживает проверки. Второе
|
||||
толкование есть: **иностранные номера, у которых код страны начинается на `8` и
|
||||
всего цифр ровно одиннадцать.**
|
||||
|
||||
- **Вьетнам `+84`** — код 2 цифры + 9 цифр номера = **ровно 11**.
|
||||
- **Гонконг `+852`**, **Макао `+853`**, **Камбоджа `+855`**, **Лаос `+856`** —
|
||||
3 + 8 = **11**.
|
||||
- **Япония `+81`**, **Корея `+82`** — 2 + 9 = **11** (городские).
|
||||
|
||||
Большинство из них после подмены дают номер, которого в России не существует
|
||||
(`+852…` → `+7 52…`, а кода `52x` у нас нет) — такой звонок просто не состоится,
|
||||
это ошибка громкая и неопасная.
|
||||
|
||||
🔴 **А вот Вьетнам опасен по-настоящему.** Вьетнамские мобильные Viettel
|
||||
`096/097/098` в международной записи выглядят `8496…`, `8497…`, `8498…`.
|
||||
Р127 превратит их в `7496…`, `7497…`, `7498…` — а `496` и `498` это **живые коды
|
||||
Московской области**. То есть иностранный номер молча станет похож на настоящий
|
||||
российский, и робот позвонит **чужому человеку**, который в списке не значился.
|
||||
|
||||
Это меняет род ошибки: **было** «номер не тот, звонок не состоится», **стало**
|
||||
«звонок состоится, но не тому». Второе тише и хуже — тот самый род, ради
|
||||
которого Р127 и делали узким.
|
||||
|
||||
**Насколько это вероятно у нас.** Списки клиента — российские, иностранный номер
|
||||
в них попадёт разве что опечаткой или мусором в выгрузке. Оценивать вероятность
|
||||
я не берусь, это не мой замер.
|
||||
|
||||
🔴 **И сразу поправлю сам себя, иначе цена названа нечестно.** Эта беда **не
|
||||
новая и не моей правкой внесена**: у портала уже есть общий нормализатор
|
||||
`App\Support\PhoneNormalizer::normalize()`, и он делает **ровно то же самое**:
|
||||
|
||||
```php
|
||||
if (strlen($digits) === 11 && ($digits[0] === '8' || $digits[0] === '7')) {
|
||||
$digits = '7'.substr($digits, 1);
|
||||
}
|
||||
```
|
||||
|
||||
Им пользуются **двадцать файлов** — считано тем же прибором и с той же оговоркой:
|
||||
это файлы, где имя встречается, а не число вызовов. Среди них загрузка баз СМС и телеграма, загрузка
|
||||
номеров рекламной кампании, реквизиты, проекты, автоподбор — и, что важнее
|
||||
всего, **сам обзвон** в `ObzvonZamorozka` при проверке номера менеджера. То есть
|
||||
вьетнамский номер уже сегодня превращается в подмосковный на входе в любой
|
||||
канал. Р127 не добавляет опасности — он **прекращает расхождение** обзвона с
|
||||
остальным порталом. Это сильный довод ЗА Р127, и владельцу его стоит знать.
|
||||
|
||||
**Чем лечится по-хорошему.** Отдельной чисткой на входе в обзвон, которая умеет
|
||||
сказать «это не российский номер» и отбить его с внятной причиной. Владелец эту
|
||||
задачу уже отрезал как отдельную.
|
||||
|
||||
**Чем НЕ лечится.** Сузить правило по второй цифре (пускать только `3/4/8/9` —
|
||||
первые цифры живых российских кодов) кажется дешёвым ходом, но Вьетнам он **не
|
||||
отсекает**: у `84…` вторая цифра как раз `4`. Так что полумеры тут нет.
|
||||
|
||||
**Решение Р127 я не отменяю и правку сделал.** Цена названа, чтобы владелец знал
|
||||
её и решил сам.
|
||||
|
||||
### Что Р127 делает правильно и стоит сказать вслух
|
||||
|
||||
Казахстан тоже живёт на коде `+7` с междугородним `8` (`8 701 …` = 11 цифр).
|
||||
Р127 приводит казахстанские номера верно, а не ломает.
|
||||
|
||||
### Второе место того же класса, о котором в задании не сказано
|
||||
|
||||
🔴 **В обзвоне сейчас живут ТРИ разных канона телефона, и `odinVidNomera` —
|
||||
третий, самодельный.**
|
||||
|
||||
| Канон | Что делает | Где |
|
||||
|---|---|---|
|
||||
| `PhoneNormalizer::normalize` | 11 цифр с `8`/`7` → `+7…`; **10 цифр → `+7…`**; всё прочее → `null`, то есть **отбить** | общий для портала, в обзвоне — `ObzvonZamorozka` |
|
||||
| `ObzvonErasureAdapter::hvostCifr` | **последние 10 цифр**, короче — как есть | `ObzvonZapretObrabotki` — проверка запрета звонить |
|
||||
| `ObzvonClient::odinVidNomera` | после Р127: 11 цифр с `8` → `7…`; всё прочее — **как есть** | знак и тело задания |
|
||||
|
||||
После Р127 все три сходятся на главном случае — это и есть польза правки. Но два
|
||||
расхождения остаются, и оба того же класса, что и починенная беда:
|
||||
|
||||
1. **Десятизначный номер.** Пару `9990000001` и `79990000001` общий
|
||||
нормализатор и проверка запрета считают **одним человеком**, а знак задания —
|
||||
**двумя**. Значит на этой паре «позвонят дважды» **остаётся**.
|
||||
2. **Мусор вместо номера.** `PhoneNormalizer` такую строку **отбивает** словом
|
||||
«не читается как телефон». `odinVidNomera` пропускает её дальше, если в ней
|
||||
есть хоть одна цифра, — и робот получит задание на несуществующий номер.
|
||||
|
||||
Чинить я это не стал и чинить молча не буду: Р127 короткие номера трогать
|
||||
запретил прямым словом, а замена самодельного канона на общий — это уже другая
|
||||
правка, которая меняет поведение двери и объём отбоя. Записываю как незакрытое.
|
||||
|
||||
## Что осталось незакрытым
|
||||
|
||||
1. **Иностранные одиннадцатизначные номера с `8`** — цена Р127, описана выше.
|
||||
Вьетнамский может стать похожим на подмосковный, и позвонят чужому. Беда не
|
||||
новая: общий `PhoneNormalizer` ведёт себя так же уже сегодня во всех каналах.
|
||||
2. **Пара «десять цифр / одиннадцать цифр»** — два знака на одного человека
|
||||
остаются. Запрет обзвона и общий нормализатор считают их одним, знак задания
|
||||
— нет.
|
||||
3. **Мусорная строка с одной цифрой** проходит в задание. Общий нормализатор
|
||||
такую отбивает, `odinVidNomera` — нет.
|
||||
4. **Три разных канона телефона в одном модуле** — сведение их к общему
|
||||
`PhoneNormalizer` не делалось: это шире Р127.
|
||||
5. **Что робот и вправду читает знак** — как и раньше, здесь не доказывается.
|
||||
Замыкает приёмник З-0.6.
|
||||
6. **Отдельной чистки номеров на входе в обзвон нет** — отрезано владельцем как
|
||||
отдельная задача. Пункты 1–3 закрылись бы там.
|
||||
</content>
|
||||
Reference in New Issue
Block a user