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
99 lines
7.8 KiB
Markdown
99 lines
7.8 KiB
Markdown
# Полное удаление сотрудника
|
||
|
||
Администратор может полностью удалить аккаунт сотрудника через существующий
|
||
маршрут управления пользователями:
|
||
|
||
```http
|
||
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
|
||
|
||
```bash
|
||
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 к рабочей БД:
|
||
|
||
```bash
|
||
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 эта
|
||
доработка не вносит.
|