← Все проекты уровня 11
Уровень 11 · Поисковые сервисыВариант B

Поиск вакансий

Поисковый API вакансий с однозначными фильтрами зарплаты, навыков, города и удалённой работы.

Техническое задание

Данные и правила зарплаты

Реализуйте REST-сервис поиска вакансий для соискателей. Запись вакансии содержит идентификатор, работодателя, должность, описание, город, формат занятости, признак удалённой работы, список нормализованных навыков, диапазон зарплаты, дату публикации, дату закрытия и состояние публикации. Зарплата хранится в целых копейках RUB, API принимает salary_min и salary_max в целых копейках RUB и поле salary_period со значениями month или year. Обе границы преобразуются в копейки за месяц; year делится на 12 с округлением вниз. Пустая граница хранится как NULL и никогда не считается нулём. Описания и названия обрабатываются с Unicode-регистронезависимым поиском.

Режим навыков принимает только AND или OR. AND требует все выбранные каноническим ID навыков, OR — хотя бы один; максимум 10 ID, повторы удаляются, совпадение только точное по ID. Город сравнивается по city_id. remote=true включает удалённые вакансии вне зависимости от city_id; remote=false требует совпадения city_id. Зарплата хранится в RUB копейках с периодом month или year; годовая сумма нормализуется в месячную делением на 12 с округлением вниз. Фильтр min_salary_monthly требует salary_max_monthly >= min; max_salary_monthly требует salary_min_monthly <= max. Неизвестная граница не проходит фильтр зарплаты. Закрытые, снятые и просроченные вакансии исключаются до сортировки, подсчёта и фасетных значений. Срок действия оценивается по времени базы в UTC, а не по часам клиента. Порядок релевантности и даты публикации завершается идентификатором, чтобы равные позиции не прыгали между страницами. Ограничьте размер страницы, число навыков и длину текста; сортировки задаются перечислением разрешённых значений.

GET /api/v1/vacancies/search?q=&city_id=&remote=&skills=&skill_mode=&min_salary_monthly=&max_salary_monthly=&sort=&cursor= ищет; GET /api/v1/vacancies/{id} скрывает неактивную запись с 404. Admin POST/PUT/DELETE /api/v1/admin/vacancies создают, меняют и снимают публикацию. Локальный seed создаёт vacancy_admin; его фиксированный токен передаётся только в Authorization header. Сортировки relevance, published_desc, salary_desc завершаются ID; размер страницы 20, курсор по ключу охватывает максимум 5000 результатов. Вакансия истекает через 30 дней от published_at. Повторная публикация задаёт новое published_at и срок. Неверные skill_mode, сортировка, валюта, период, диапазон, city и skills дают 400. Используйте Go, PostgreSQL, SQL без ORM и миграции с индексами по состоянию, сроку, городу, skill ID и полнотекстовым данным. Все SQL-вызовы используют request context с deadline 3 секунды. Изменение публикации и событие в outbox фиксируются одной транзакцией; индексатор применяет до 200 событий в секунду и сохраняет контрольную позицию после каждой партии. Повторное событие обрабатывается идемпотентно по vacancy ID и revision. Очередь ограничена 100 000; при заполнении admin изменение получает 503 и не подтверждается. Поиск возвращает index_lag_seconds, приемочный предел — 60 секунд.

Структура программы

HTTP-слой валидирует параметры; предметная логика определяет сравнение зарплаты, навыков и активность вакансии; SQL-пакет обслуживает поиск, отдельный цикл применяет outbox. main.go только загружает конфигурацию, связывает зависимости и управляет запуском. Пакеты без циклов, общих utils и интерфейсов без потребителя. README показывает зависимости; тесты ядра без HTTP/БД, интеграционные отдельно.

Критерии приёмки выполняются офлайн: фикстуры содержат месячную и годовую зарплату, неизвестные суммы, разные города, навыки с разным регистром, просроченные и закрытые публикации, а также равные даты. Проверки подтверждают точную семантику AND/OR, пересчёт зарплаты только при допустимом периоде, исключение неактивных записей из результатов и подсчёта, фильтр удалённой работы, стабильную пагинацию, повтор событий и восстановление индексатора. Измерьте фактический p95 на 10 000 вакансиях, сохранив среду, команду, конфигурацию PostgreSQL и набор запросов; результат является замером этого стенда, не общим SLA. Критерии готовности: перезапуск обработчика outbox не теряет обновления, очередь ограничена, задержка индекса не выше 60 секунд в тестовом сценарии.

Критерии готовности

Ожидаемый результат

Проверяемый результат

  • README показывает границы и направление зависимостей; main.go только связывает конфигурацию и запуск; модульные тесты ядра обходятся без HTTP и БД, интеграционные тесты отделены.
  • Публичные маршруты поиска и просмотра возвращают только опубликованные и неистёкшие вакансии.
  • Зарплата нормализована в RUB за месяц; неизвестный диапазон не проходит денежный фильтр.
  • AND/OR сопоставляют выбранные навыки по ID; город и удалённость следуют правилам API.
  • Повторная публикация задаёт новый 30-дневный срок, сортировка завершается стабильным ID.
  • Набор 10 000 вакансий сопровождается командой и фактическим замером p95 на указанной среде.
  • Тесты проверяют перезапуск индексатора, ошибки валидации, salary period и исключение закрытых записей.
  • С чистой PostgreSQL применяются миграции; SQL timeout и безопасные ошибки задокументированы.
  • Отчёт содержит фиксированный набор поисковых запросов и измеренный lag после обновления записи.
  • Ответы поиска и фасет содержат только активные записи, даже если индекс отстаёт.