Files
state-corp-backend/docs/employee-deletion-api-ru.md
Aleksandr Meshchryakov e99616ed6d
All checks were successful
State Corp Backend CI/CD / Quality gate (push) Successful in 2m42s
State Corp Backend CI/CD / Build linux/amd64 images once (push) Successful in 5m29s
State Corp Backend CI/CD / Refresh and release internal main (push) Has been skipped
State Corp Backend CI/CD / Release customer main (push) Has been skipped
State Corp Backend CI/CD / Release dev (push) Successful in 36s
feat(users): add transactional employee deletion and token revocation
2026-09-13 22:55:31 +02:00

7.8 KiB
Raw Permalink Blame History

Полное удаление сотрудника

Администратор может полностью удалить аккаунт сотрудника через существующий маршрут управления пользователями:

DELETE /api/v1/users/admin/users/{user_id}/
Authorization: Bearer <access-token>

Тело запроса не требуется. Успех — 204 No Content без JSON-тела. Сотрудник исчезает из списка, поиска и карточки; повторный DELETE возвращает 404. Деактивация и обратная активация остаются отдельными операциями.

Права и ошибки

Право удаления имеет существующий активный пользователь с is_staff=True, как и для остальных административных endpoint'ов. Проверка повторяется внутри транзакции после получения блокировок: ранее аутентифицированный, но уже удалённый или лишённый прав администратор не может завершить изменение.

HTTP Код ошибки Значение
400 self_delete_forbidden Нельзя удалить самого себя
401 Стандартная ошибка аутентификации Нет действующего access token
403 Стандартная ошибка прав / permission_denied Требуется активный администратор
404 not_found Сотрудник отсутствует, в том числе после удаления
409 last_active_admin Нельзя оставить систему без активного администратора
409 user_delete_conflict Защищённая связь препятствует удалению

Ошибки используют стандартный envelope проекта: success: false, data: null, errors: [{code, message}], meta. Запрет удаления себя имеет приоритет над проверкой последнего администратора. Существующие связи не блокируют удаление; 409 для защищённой связи сохраняет безопасное поведение при расширении модели.

DELETE, создание аккаунта администратором, смена роли/активности и соответствующие операции Django admin используют одни блокировки пользователей. Пакетное действие Django admin атомарно: если выбран сам исполнитель, вся операция отклоняется. Сохранение собственных учётных данных и смена пароля также блокируют актуальную строку User, чтобы начавшийся до DELETE запрос не создал аккаунт заново.

Данные после удаления

  • Физически удаляются User, Profile, связи с группами/permissions, записи OutstandingToken и связанные BlacklistedToken. Сами справочники прав остаются.
  • Отчёты ReportUpload, загрузки реестров RegisterUpload, ExchangePackageImport сохраняются; их ссылки на автора обнуляются штатным SET_NULL. Сохраняются загруженные файлы, предметные данные и цепочки импортов.
  • BackgroundJob сохраняются, user_id очищается явно, поскольку это integer, а не внешний ключ. Выполняющиеся задачи не отменяются.
  • Аватар удаляется из storage после commit, если его не использует другой профиль. При ошибке storage удаление аккаунта остаётся успешным, ошибка регистрируется в серверном журнале для последующей очистки файла.
  • Технический Django admin LogEntry сохраняет штатный CASCADE для записей, исполнителем которых был удалённый пользователь; предметные журналы выше не удаляются. Содержимое исторических документов не переписывается.
  • Старый access перестаёт проходить JWTAuthentication. Refresh endpoint дополнительно проверяет существование и активность пользователя и возвращает 401 для удалённого/неактивного аккаунта. Повторный refresh активного пользователя поддерживается как раньше. Записей восстановления пароля в текущей модели нет.

Изменений схемы БД и миграций не требуется. Откат версии приложения не восстанавливает уже удалённые аккаунты.

Передача frontend-разработчику (Глебу)

  1. Добавить отдельное действие «Удалить» в меню сотрудника; для текущего пользователя скрыть или отключить его.
  2. Перед запросом показать имя выбранного сотрудника и подтверждение полного необратимого удаления аккаунта. Сообщить, что отчёты и история импортов останутся.
  3. Вызвать DELETE; на время запроса блокировать повторное действие. После 204 закрыть диалог и обновить список/счётчик; если последняя строка страницы удалена, перейти на предыдущую существующую страницу.
  4. Обработать стандартный error envelope, включая 400/403/404/409. При 404 обновить список. Не пытаться читать JSON из успешного ответа 204.
  5. Обновить клиент из OpenAPI и проверить удаление обычного, неактивного и другого административного аккаунта, запрет удаления себя и потерю прав во время запроса.

Проверка backend

PYTHONPATH=src uv run --no-sync pytest tests/apps/user

Восемь тестов test_management_concurrency.py пропускаются SQLite и требуют PostgreSQL. Для них использовать отдельную пустую тестовую БД и явно задавать все TEST_POSTGRES_HOST, TEST_POSTGRES_PORT, TEST_POSTGRES_USER, TEST_POSTGRES_PASSWORD, TEST_POSTGRES_DB, чтобы исключить fallback к рабочей БД:

scripts/run-tests-prod.sh ../tests/apps/user

Регрессии обмена и подготовленных выгрузок находятся в tests/apps/exchange/test_api.py, tests/apps/external_data/test_source_record_export.py и tests/apps/external_data/test_export_tasks.py. Изменений upload/export API эта доработка не вносит.