bc573a99eb
Доработки экрана «Моя база» клиентской СМС-рассылки по просьбе владельца: - Кривые номера больше не выбрасываются молча: uploadContacts/uploadContactsFile возвращают rejected + rejected_samples, экран показывает «Добавлено: N, не распознали: M (например: …) — проверьте формат». - Таблица базы — только «Номер» и «Оператор» (колонку «Имя» убрал). - Загрузка базы Excel-файлом: сервис ClientSmsPhoneFileReader (phpspreadsheet) читает первую колонку, пропускает заголовок, номера-как-числа не уходят в научную нотацию; endpoint POST /api/sms/contacts/file; на экране — выбор файла и кнопка «Загрузить файл». - «Скачать пример файла»: ClientSmsBaseExampleWriter + GET /api/sms/contacts/example — готовый .xlsx с колонкой «Телефон» и образцами номеров. - Оператор через ДаДату: EnrichClientSmsContactsOperatorJob обогащает добавленные номера (DaDataPhoneClient.provider) в фоне, под tenant-контекстом (RLS), с защитой дневного бюджета (DaDataBudgetGuard); сбой ДаДаты не роняет прогон, оператор остаётся пустым; уже известного оператора не перезапрашиваем. Тесты: бэкенд ClientSms 130/130, фронт СМС-спеки 63/63 — зелёные; pint чист; портал пересобран. Синтетические номера 7999… в тестах. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
76 lines
2.6 KiB
PHP
76 lines
2.6 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use App\Services\ClientSms\ClientSmsPhoneFileReader;
|
||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
|
||
|
||
/**
|
||
* Клиент загружает свою базу номеров Excel-файлом. Читаем только первую колонку;
|
||
* заголовок («Телефон») пропускаем; номер, записанный как число, не должен
|
||
* превратиться в научную нотацию.
|
||
*/
|
||
|
||
/**
|
||
* Собирает временный .xlsx: значения в колонку A. $numericRows — индексы (с 0),
|
||
* которые записать как ЧИСЛО (иначе — как строку).
|
||
*
|
||
* @param list<string> $colA
|
||
* @param list<int> $numericRows
|
||
*/
|
||
function makeXlsx(array $colA, array $numericRows = []): string
|
||
{
|
||
$ss = new Spreadsheet;
|
||
$sheet = $ss->getActiveSheet();
|
||
foreach (array_values($colA) as $i => $v) {
|
||
$ref = 'A'.($i + 1);
|
||
if (in_array($i, $numericRows, true)) {
|
||
$sheet->setCellValueExplicit($ref, $v, DataType::TYPE_NUMERIC);
|
||
} else {
|
||
$sheet->setCellValueExplicit($ref, $v, DataType::TYPE_STRING);
|
||
}
|
||
}
|
||
$path = tempnam(sys_get_temp_dir(), 'xlsxtest').'.xlsx';
|
||
(new XlsxWriter($ss))->save($path);
|
||
|
||
return $path;
|
||
}
|
||
|
||
it('читает номера из первой колонки и пропускает заголовок', function () {
|
||
$path = makeXlsx(['Телефон', '+7 913 519-12-64', '89135191264']);
|
||
|
||
$rows = (new ClientSmsPhoneFileReader)->read($path);
|
||
|
||
expect($rows)->toBe(['+7 913 519-12-64', '89135191264']);
|
||
@unlink($path);
|
||
});
|
||
|
||
it('без заголовка читает все строки', function () {
|
||
$path = makeXlsx(['79001234567', '79007654321']);
|
||
|
||
$rows = (new ClientSmsPhoneFileReader)->read($path);
|
||
|
||
expect($rows)->toBe(['79001234567', '79007654321']);
|
||
@unlink($path);
|
||
});
|
||
|
||
it('номер, записанный как число, не уходит в научную нотацию', function () {
|
||
$path = makeXlsx(['79135191264'], numericRows: [0]);
|
||
|
||
$rows = (new ClientSmsPhoneFileReader)->read($path);
|
||
|
||
expect($rows)->toBe(['79135191264']);
|
||
@unlink($path);
|
||
});
|
||
|
||
it('пропускает пустые ячейки', function () {
|
||
$path = makeXlsx(['79001234567', '', ' ', '79007654321']);
|
||
|
||
$rows = (new ClientSmsPhoneFileReader)->read($path);
|
||
|
||
expect($rows)->toBe(['79001234567', '79007654321']);
|
||
@unlink($path);
|
||
});
|