Task #6130
openПользователи: пакетная загрузка из Excel с ролями и выгрузкой паролей
0%
Description
Страница для суперпользователя: загрузка пользователей пачкой из Excel через существующий эндпоинт создания пользователя, с возвратом файла сгенерированных паролей.
Столбцы файла: № | Фамилия | Имя | Отчество | Организация | Роль | Телефон | Почта
Реализовано (6a8a048), маршрут /users/batch:
-
users/common/roles.ts— справочник ролей (Исполнитель, Заявитель, ЦИК, ultra) и нормализация значения из файла с поддержкой синонимов. Раньше роли были зашиты инлайном вselectформы редактирования. -
users/batch/passwords.ts— генерация паролей черезcrypto.getRandomValues, алфавит без похожих символов (0/O, 1/l/I), гарантированно есть строчная, заглавная и цифра. -
users/batch/UsersBatchUploadPage.tsx— скачивание шаблона (с отдельным листом-справочником организаций), предпросмотр с построчной валидацией, последовательное создание с прогрессом «N из M», экран результата и выгрузка файла с паролями.
Валидация до отправки: обязательные Фамилия / Имя / Почта, формат почты, роль строго из справочника, организация должна находиться в справочнике по названию (сопоставление без учёта регистра, кавычек и лишних пробелов). Отдельно показываются дубли почты внутри файла. Некорректные строки можно удалить из предпросмотра, загружаются только валидные.
Доступ: страница и кнопка входа на неё видны только при role === "ultra".
Связь с ролями. Валидация роли закрывает тот же класс проблем, из-за которого появлялись пользователи с ролью user без прав: значение вне справочника не пройдёт импорт.
На согласование / доработку:
- Пароли отдаются файлом в открытом виде — на экране результата есть предупреждение сохранить его в защищённом месте и удалить. Повторно получить пароли нельзя, только сбросить.
- Эндпоинт создания пользователя вызывается по одному в цикле, отката нет: если часть строк упала, созданные остаются созданными. В файле результата у каждой строки проставлен статус и текст ошибки.
- Письма с доступами не отправляются — рассылка на стороне оператора.
Updated by Redmine Admin about 5 hours ago
Доработано (9bbc58f) по двум замечаниям.
1. Не распознавались столбцы «Телефон» и «Почта».
Проверил разбор на файле с точно такой шапкой, как в задаче (№ Фамилия Имя Отчество Организация Роль Телефон Почта) — она разбирается корректно. Значит, в реальном файле заголовки этих двух столбцов записаны иначе, а сопоставление было слишком строгим: делался только toLowerCase().trim(), поэтому мимо карты проходили «Телефон:», «Эл. почта», «Тел.», «Мобильный телефон», а также неразрывный пробел и zero-width символы, которые появляются при копировании шапки из веба. Столбец при этом игнорировался молча — данные просто терялись.
Разбор шапки вынесен в users/batch/headers.ts: жёсткая нормализация (удаление zero-width и BOM, приведение юникодных пробелов к обычному, снятие хвостовых : . *, регистр), расширенный список точных заголовков и запасной поиск по вхождению ключевого слова. Проверено на 22 вариантах написания.
Главное — нераспознанные столбцы больше не игнорируются молча: на экране показывается список пропущенных столбцов, а если не найден обязательный (Фамилия / Имя / Роль / Почта) — отдельное сообщение об ошибке с подсказкой проверить, что шапка находится в первой строке листа.
2. Телефон — только цифры, формат 7XXXXXXXXXX.
Из файла принимается любое написание (+7 (999) 123-45-67, 8 999 123-45-67, 9991234567), на сервер уходит 79991234567. Если номер не приводится к 11 цифрам — строка помечается ошибкой и не загружается. В предпросмотре показывается уже нормализованное значение, то есть ровно то, что уйдёт на сервер. Шаблон и пример в нём обновлены.
Замечание на будущее. Тем же слабым местом обладает импорт волонтеров (ImportVolunteersModal) — там сопоставление заголовков тоже сделано через toLowerCase().trim(), и незнакомый столбец так же молча пропускается. Пока не трогал, чтобы не менять поведение работающего импорта; при желании нормализацию можно переиспользовать оттуда же.