Контур вакансий позволяет искать открытые роли в публичных проектах, отправлять
и отзывать собственный отклик, а руководителю проекта — рассматривать кандидатов.
Модели Vacancy и VacancyResponse и legacy API сохранены без миграций.
GET /vacancies/ возвращает только активные вакансии проектов, у которых
draft=false и is_public=true. Возраст вакансии выдачу не ограничивает.
Поддерживаются параметры:
search— поиск без учёта регистра по роли, специализации, описанию и названию проекта;required_experience— один или несколько вариантов опыта;work_format— один или несколько форматов работы;work_schedule— один или несколько вариантов графика;salary_minиsalary_max— границы указанного вознаграждения;project_id— проект;limitиoffset— пагинация.
Фильтр is_active=false не позволяет получить закрытые вакансии через публичный
список. Стабильная сортировка выполняется по дате создания и идентификатору в
обратном порядке.
GET /vacancies/<vacancy_id>/ открывает активную вакансию публичного
опубликованного проекта. Закрытую вакансию могут получить только руководитель её
проекта, staff и superuser. Для остальных закрытая или связанная с недоступным
проектом запись скрывается через 404.
POST /vacancies/<vacancy_id>/responses/ требует авторизации и принимает:
{
"why_me": "Почему я хочу присоединиться к проекту",
"accompanying_file": "https://cdn.example.test/cv.pdf"
}Пользователь всегда определяется из сессии. Значение из payload не используется.
Сопроводительный UserFile должен принадлежать текущему пользователю.
Отклик запрещён, если:
- вакансия закрыта или её проект не является публичным и опубликованным;
- пользователь уже является руководителем или участником проекта;
- пользователь уже откликался на эту вакансию.
Проверка и создание выполняются в одной транзакции под блокировкой вакансии, поэтому параллельные запросы не создают два отклика. Уведомление руководителю отправляется только после успешного завершения транзакции.
GET /vacancies/responses/self возвращает пагинированный список только текущего
пользователя вместе с безопасными данными вакансии и проекта.
Статус определяется полем is_approved:
null— на рассмотрении;true— принят;false— отклонён.
Пока значение равно null, владелец может обновить сообщение или файл через
PATCH /vacancies/responses/<response_id>/ и отозвать отклик через
DELETE /vacancies/responses/<response_id>/. null в поле
accompanying_file удаляет связь с файлом. Обработанный отклик остаётся в
истории и больше не редактируется и не удаляется.
Detail отклика доступен только его владельцу, руководителю соответствующего
проекта, staff и superuser. Для постороннего пользователя используется безопасный
404.
GET /vacancies/<vacancy_id>/responses/ доступен руководителю проекта, staff и
superuser. Остальные авторизованные пользователи получают 403, неизвестная
вакансия — 404.
Карточка кандидата построена по явному публичному allow-list и содержит только:
- идентификатор;
- имя и фамилию;
- аватар;
- специализацию;
- публичные навыки;
- краткое описание профиля.
Email, телефон, дата рождения, административные признаки и другие закрытые поля не возвращаются. Сопроводительный файл сериализуется без данных его владельца и доступен только кандидату и пользователям с правом управления вакансией.
Решение принимается через:
POST /vacancies/responses/<response_id>/accept/;POST /vacancies/responses/<response_id>/decline/.
При принятии одной атомарной операцией:
- отклик получает
is_approved=true; - кандидат добавляется в
Collaboratorс ролью из вакансии; - вакансия закрывается;
- остальные ожидающие отклики получают
is_approved=false; - после commit отправляются уведомления принятому и отклонённым кандидатам.
Повторная обработка уже принятого или отклонённого отклика возвращает 400 и не
создаёт дубликат участника.
POST /vacancies/<vacancy_id>/close/закрывает вакансию и отклоняет ожидающие отклики;POST /vacancies/<vacancy_id>/reopen/возобновляет вакансию только в публичном опубликованном проекте;DELETE /vacancies/<vacancy_id>/удаляет вакансию только при полном отсутствии откликов.
Эти операции доступны руководителю проекта, staff и superuser. История откликов не удаляется вместе с вакансией. Проверка удаления и само удаление выполняются под блокировкой одной транзакции.
Существующие модели, миграции, CRUD вакансий, workspace detail проекта, служебные записи ленты и legacy endpoints не переименованы. Контур не добавляет чат с кандидатом, собеседования, рейтинг кандидатов или внешние интеграции.