7.5 KiB
7.5 KiB
Техническое задание: Эндпоинт получения попыток по цепочке
Цель
Создать новый API эндпоинт для получения списка попыток (submissions) участников в рамках конкретной цепочки заданий. Это упростит работу админ-панели и уменьшит объём передаваемых данных.
Текущая проблема
Сейчас для отображения попыток по цепочке фронтенд должен:
- Загрузить список цепочек (
GET /challenge/chains/admin) - Загрузить общую статистику (
GET /challenge/stats/v2) - Для каждого участника отдельно загрузить его submissions (
GET /challenge/user/:userId/submissions) - На клиенте фильтровать submissions по taskIds из выбранной цепочки
Это создаёт избыточные запросы и усложняет логику на фронтенде.
Новый эндпоинт
GET /challenge/chain/:chainId/submissions
Возвращает все попытки всех участников для заданий из указанной цепочки.
Параметры URL
| Параметр | Тип | Обязательный | Описание |
|---|---|---|---|
chainId |
string | Да | ID цепочки заданий |
Query параметры (опциональные)
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
userId |
string | - | Фильтр по конкретному пользователю |
status |
string | - | Фильтр по статусу: pending, in_progress, accepted, needs_revision |
limit |
number | 100 | Лимит записей |
offset |
number | 0 | Смещение для пагинации |
Формат ответа
interface ChainSubmissionsResponse {
success: boolean;
body: {
chain: {
id: string;
name: string;
tasks: Array<{
id: string;
title: string;
}>;
};
participants: Array<{
userId: string;
nickname: string;
completedTasks: number;
totalTasks: number;
progressPercent: number;
}>;
submissions: Array<{
id: string;
user: {
id: string;
nickname: string;
};
task: {
id: string;
title: string;
};
status: 'pending' | 'in_progress' | 'accepted' | 'needs_revision';
attemptNumber: number;
submittedAt: string; // ISO date
checkedAt?: string; // ISO date
feedback?: string;
}>;
pagination: {
total: number;
limit: number;
offset: number;
};
};
}
Пример запроса
GET /api/challenge/chain/607f1f77bcf86cd799439021/submissions?status=needs_revision&limit=50
Пример ответа
{
"success": true,
"body": {
"chain": {
"id": "607f1f77bcf86cd799439021",
"name": "Основы JavaScript",
"tasks": [
{ "id": "507f1f77bcf86cd799439011", "title": "Реализовать сортировку массива" },
{ "id": "507f1f77bcf86cd799439015", "title": "Валидация формы" }
]
},
"participants": [
{
"userId": "user_123",
"nickname": "alex_dev",
"completedTasks": 1,
"totalTasks": 2,
"progressPercent": 50
},
{
"userId": "user_456",
"nickname": "maria_coder",
"completedTasks": 2,
"totalTasks": 2,
"progressPercent": 100
}
],
"submissions": [
{
"id": "sub_001",
"user": {
"id": "user_123",
"nickname": "alex_dev"
},
"task": {
"id": "507f1f77bcf86cd799439011",
"title": "Реализовать сортировку массива"
},
"status": "needs_revision",
"attemptNumber": 2,
"submittedAt": "2024-12-10T14:30:00.000Z",
"checkedAt": "2024-12-10T14:30:45.000Z",
"feedback": "Алгоритм работает неверно для отрицательных чисел"
}
],
"pagination": {
"total": 15,
"limit": 50,
"offset": 0
}
}
}
Логика на бэкенде
Алгоритм
- Получить цепочку по
chainId - Если цепочка не найдена — вернуть 404
- Получить список
taskIdsиз цепочки - Найти все submissions где
task._idвходит вtaskIds - Применить фильтры (
userId,status) если указаны - Вычислить прогресс по каждому участнику:
- Найти уникальных пользователей из submissions
- Для каждого посчитать
completedTasks(количество уникальных tasks со статусомaccepted) - Рассчитать
progressPercent = (completedTasks / totalTasks) * 100
- Применить пагинацию к submissions
- Вернуть результат
Индексы MongoDB (рекомендуется)
// Для быстрой выборки submissions по task
db.submissions.createIndex({ "task": 1, "submittedAt": -1 })
// Составной индекс для фильтрации
db.submissions.createIndex({ "task": 1, "status": 1, "submittedAt": -1 })
Права доступа
Эндпоинт должен быть доступен только пользователям с ролями:
challenge-adminchallenge-teacher
Коды ошибок
| Код | Описание |
|---|---|
| 200 | Успешный ответ |
| 400 | Некорректные параметры запроса |
| 401 | Не авторизован |
| 403 | Недостаточно прав |
| 404 | Цепочка не найдена |
| 500 | Внутренняя ошибка сервера |
Изменения на фронтенде после реализации
После добавления эндпоинта в src/__data__/api/api.ts нужно добавить:
// В endpoints builder
getChainSubmissions: builder.query<ChainSubmissionsResponse, {
chainId: string;
userId?: string;
status?: SubmissionStatus;
limit?: number;
offset?: number;
}>({
query: ({ chainId, userId, status, limit, offset }) => ({
url: `/challenge/chain/${chainId}/submissions`,
params: { userId, status, limit, offset },
}),
transformResponse: (response: { body: ChainSubmissionsResponse }) => response.body,
providesTags: ['Submission'],
}),
Это позволит упростить SubmissionsPage.tsx:
- Один запрос вместо нескольких
- Убрать клиентскую фильтрацию по taskIds
- Получать готовый прогресс участников
Приоритет
Средний — текущая реализация работает, но создаёт избыточную нагрузку при большом количестве участников.
Оценка трудозатрат
~4-6 часов (включая тесты)