{ "openapi": "3.0.4", "info": { "title": "UniVerse API", "description": "REST API веб-платформы UniVerse.\n\nАутентификация: JWT Bearer (получить через `POST /api/v1/auth/login/microsoft` или `POST /api/v1/auth/login/dev` в Development).", "contact": { "name": "UniVerse Dev" }, "version": "v1" }, "paths": { "/api/v1/achievements": { "get": { "tags": [ "Achievements" ], "summary": "Получить список всех достижений.", "description": "Возвращает определения достижений (без информации о получении конкретным пользователем).\n Для достижений конкретного пользователя используйте GET /api/v1/users/{id}/achievements.\n\n**Required:** any authenticated user", "responses": { "200": { "description": "Список достижений.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/AchievementDto" } } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "post": { "tags": [ "Achievements" ], "summary": "Создать новое достижение.", "description": "Только Admin. Достижения автоматически присваиваются студентам при выполнении условий.\n\n**Required roles:** Admin", "requestBody": { "description": "Название, описание, иконка, награда в XP/монетах и условие получения.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAchievementRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/CreateAchievementRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/CreateAchievementRequest" } } } }, "responses": { "201": { "description": "Достижение создано.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AchievementDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/achievements/{id}": { "get": { "tags": [ "Achievements" ], "summary": "Получить достижение по ID.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "id", "in": "path", "description": "ID достижения.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Данные достижения.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AchievementDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Достижение не найдено.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Achievements" ], "summary": "Обновить достижение по ID.", "description": "Только Admin.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID достижения.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "Обновляемые поля достижения.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateAchievementRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateAchievementRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateAchievementRequest" } } } }, "responses": { "200": { "description": "Обновлённые данные достижения.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AchievementDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Достижение не найдено.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "delete": { "tags": [ "Achievements" ], "summary": "Удалить достижение по ID.", "description": "Только Admin. Удаление не отзывает достижение у уже получивших его пользователей.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID достижения.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Достижение удалено." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Достижение не найдено.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/auth/login/microsoft": { "post": { "tags": [ "Auth" ], "summary": "Вход через Microsoft Entra ID (SPA/PKCE flow).", "description": "Фронтенд самостоятельно обрабатывает редирект к Microsoft и передаёт сюда\nполученный authorization code. В ответ возвращается пара JWT-токенов;\nrefresh token устанавливается в HttpOnly cookie.", "requestBody": { "description": "Authorization code и redirect URI из Microsoft OAuth2.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginMicrosoftRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/LoginMicrosoftRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/LoginMicrosoftRequest" } } } }, "responses": { "200": { "description": "Успешный вход — возвращает access token и данные пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } }, "400": { "description": "Неверный или просроченный authorization code.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } } }, "get": { "tags": [ "Auth" ], "summary": "Инициация server-driven входа через Microsoft (редирект-flow).", "description": "Браузер переходит на этот URL; backend строит Microsoft authorize URL с CSRF state\nи редиректит пользователя на `login.microsoftonline.com`.\nПосле успешного входа Microsoft редиректит на `GET /api/v1/auth/callback/microsoft`.", "parameters": [ { "name": "returnUrl", "in": "query", "description": "URL для редиректа после успешного входа (опционально).", "schema": { "type": "string" } } ], "responses": { "302": { "description": "Редирект на Microsoft authorize endpoint." }, "500": { "description": "Microsoft authentication не настроен (AzureAd:TenantId/ClientId отсутствуют)." } } } }, "/api/v1/auth/callback/microsoft": { "get": { "tags": [ "Auth" ], "summary": "OAuth2 callback — обмен code на токены (server-driven flow).", "description": "Microsoft редиректит браузер сюда после успешного входа.\nBackend валидирует CSRF state, обменивает code на токены,\nустанавливает refresh token cookie и редиректит на `returnUrl` с access token в URL-фрагменте.", "parameters": [ { "name": "code", "in": "query", "description": "Authorization code от Microsoft.", "schema": { "type": "string" } }, { "name": "state", "in": "query", "description": "CSRF state для верификации.", "schema": { "type": "string" } }, { "name": "error", "in": "query", "description": "Код ошибки от Microsoft (если вход не удался).", "schema": { "type": "string" } }, { "name": "error_description", "in": "query", "description": "Описание ошибки от Microsoft.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Если returnUrl не задан — возвращает JSON с токенами (удобно для тестирования).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } }, "302": { "description": "Успешный вход — редирект на returnUrl с токеном в URL-фрагменте." }, "400": { "description": "Отсутствует authorization code.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "401": { "description": "Ошибка от Microsoft или невалидный CSRF state.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } } } }, "/api/v1/auth/login/dev": { "post": { "tags": [ "Auth" ], "summary": "Dev-only вход без OAuth (только в Development-окружении).", "description": "Создаёт или находит пользователя по email без реального OAuth flow.\nВозвращает 404 в Production и Staging.", "requestBody": { "description": "Email, отображаемое имя и роль тестового пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DevLoginRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/DevLoginRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/DevLoginRequest" } } } }, "responses": { "200": { "description": "Успешный вход.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } }, "404": { "description": "Endpoint недоступен вне Development.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } } } }, "/api/v1/auth/refresh": { "post": { "tags": [ "Auth" ], "summary": "Обновление access token по refresh token из HttpOnly cookie.", "description": "Refresh token читается из HttpOnly cookie `refreshToken` (устанавливается при входе).\nВозвращает новую пару токенов и обновляет cookie.", "responses": { "200": { "description": "Новая пара токенов.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } }, "401": { "description": "Refresh token отсутствует, просрочен или отозван.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Аккаунт деактивирован или refresh token недействителен.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } } } }, "/api/v1/auth/logout": { "post": { "tags": [ "Auth" ], "summary": "Выход из системы — отзыв refresh token.", "description": "Инвалидирует текущий refresh token в БД и удаляет cookie.\nПосле этого вызова access token остаётся валидным до истечения его TTL (30 минут).\n\n**Required:** any authenticated user", "responses": { "204": { "description": "Выход выполнен успешно." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/auth/me": { "get": { "tags": [ "Auth" ], "summary": "Получение профиля текущего авторизованного пользователя.", "description": "**Required:** any authenticated user", "responses": { "200": { "description": "Данные текущего пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CurrentUserDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Forbidden", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден в БД (рассинхронизация токена).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/courses": { "get": { "tags": [ "Courses" ], "summary": "Получить список курсов с фильтрацией и пагинацией.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "TagId", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "Search", "in": "query", "schema": { "type": "string" } }, { "name": "IsSynced", "in": "query", "schema": { "type": "boolean" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список курсов (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "post": { "tags": [ "Courses" ], "summary": "Создать новый курс.", "description": "Только Admin.\n\n**Required roles:** Admin", "requestBody": { "description": "Название и описание курса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCourseRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/CreateCourseRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/CreateCourseRequest" } } } }, "responses": { "201": { "description": "Курс создан.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/courses/{id}": { "get": { "tags": [ "Courses" ], "summary": "Получить курс по ID (включая теги).", "description": "**Required:** any authenticated user", "parameters": [ { "name": "id", "in": "path", "description": "ID курса.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Данные курса с тегами.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Курс не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Courses" ], "summary": "Обновить курс по ID.", "description": "Только Admin.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID курса.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "Новое название и/или описание.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateCourseRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateCourseRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateCourseRequest" } } } }, "responses": { "200": { "description": "Обновлённые данные курса.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CourseDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Курс не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "delete": { "tags": [ "Courses" ], "summary": "Удалить курс по ID.", "description": "Только Admin. Удаление курса каскадно удаляет связанные лекции.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID курса.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Курс удалён." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Курс не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/courses/{id}/tags": { "post": { "tags": [ "Courses" ], "summary": "Привязать тег к курсу.", "description": "Только Admin. Тег должен существовать в системе.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID курса.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "ID тега.", "content": { "application/json": { "schema": { "type": "integer", "format": "int32" } }, "text/json": { "schema": { "type": "integer", "format": "int32" } }, "application/*+json": { "schema": { "type": "integer", "format": "int32" } } } }, "responses": { "204": { "description": "Тег привязан." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Курс или тег не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "409": { "description": "Тег уже привязан к курсу.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/courses/{id}/tags/{tagId}": { "delete": { "tags": [ "Courses" ], "summary": "Отвязать тег от курса.", "description": "Только Admin.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID курса.", "required": true, "schema": { "type": "integer", "format": "int32" } }, { "name": "tagId", "in": "path", "description": "ID тега.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Тег отвязан." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Курс или тег не найден, либо связь не существует.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/lectures": { "get": { "tags": [ "Lectures" ], "summary": "Получить каталог лекций с фильтрацией и пагинацией.", "description": "Включает флаг `isEnrolled` — записан ли текущий пользователь на лекцию.\n\n**Required:** any authenticated user", "parameters": [ { "name": "DateFrom", "in": "query", "schema": { "type": "string", "format": "date" } }, { "name": "DateTo", "in": "query", "schema": { "type": "string", "format": "date" } }, { "name": "CourseId", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "TeacherId", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "Format", "in": "query", "schema": { "$ref": "#/components/schemas/LectureFormat" } }, { "name": "IsOpen", "in": "query", "schema": { "type": "boolean" } }, { "name": "TagId", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "Search", "in": "query", "schema": { "type": "string" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список лекций (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LectureDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "post": { "tags": [ "Lectures" ], "summary": "Создать новую лекцию.", "description": "Только Admin. Курс задаётся при создании и не может быть изменён.\n\n**Required roles:** Admin", "requestBody": { "description": "Данные лекции: курс, преподаватель, локация, время, формат, вместимость.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateLectureRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/CreateLectureRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/CreateLectureRequest" } } } }, "responses": { "201": { "description": "Лекция создана.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LectureDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/lectures/{id}": { "get": { "tags": [ "Lectures" ], "summary": "Получить детальную карточку лекции по ID.", "description": "Включает флаг `isEnrolled` — записан ли текущий пользователь на эту лекцию.\n\n**Required:** any authenticated user", "parameters": [ { "name": "id", "in": "path", "description": "ID лекции.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Детальные данные лекции.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LectureDetailDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Lectures" ], "summary": "Обновить лекцию по ID.", "description": "Admin или Teacher. CourseId изменить нельзя.\n\n**Required roles:** Admin, Teacher", "parameters": [ { "name": "id", "in": "path", "description": "ID лекции.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "Обновляемые поля: преподаватель, локация, время, формат, описание.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateLectureRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateLectureRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateLectureRequest" } } } }, "responses": { "200": { "description": "Обновлённые данные лекции.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LectureDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin или Teacher.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "delete": { "tags": [ "Lectures" ], "summary": "Удалить лекцию по ID.", "description": "Только Admin. Каскадно удаляет записи и отзывы.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID лекции.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Лекция удалена." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/lectures/{id}/enroll": { "post": { "tags": [ "Lectures" ], "summary": "Записаться на лекцию.", "description": "Только Student. Проверяет наличие свободных мест и отсутствие повторной записи.\nПосле посещения начисляются монеты через gamification.\n\n**Required roles:** Student", "parameters": [ { "name": "id", "in": "path", "description": "ID лекции.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Запись выполнена." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Student.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "409": { "description": "Студент уже записан или мест нет.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "delete": { "tags": [ "Lectures" ], "summary": "Отменить запись на лекцию.", "description": "Только Student. Отменить можно только свою запись.\n\n**Required roles:** Student", "parameters": [ { "name": "id", "in": "path", "description": "ID лекции.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Запись отменена." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Student.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция или запись не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/lectures/{id}/attendance/{userId}": { "patch": { "tags": [ "Lectures" ], "summary": "Отметить посещение студента на лекции.", "description": "Admin или Teacher. При отметке `attended=true` начисляются монеты за посещение\nчерез gamification service.\n\n**Required roles:** Admin, Teacher", "parameters": [ { "name": "id", "in": "path", "description": "ID лекции.", "required": true, "schema": { "type": "integer", "format": "int32" } }, { "name": "userId", "in": "path", "description": "ID студента.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "true — посетил, false — не посетил.", "content": { "application/json": { "schema": { "type": "boolean" } }, "text/json": { "schema": { "type": "boolean" } }, "application/*+json": { "schema": { "type": "boolean" } } } }, "responses": { "204": { "description": "Посещение отмечено." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin или Teacher.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция или запись студента не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/lectures/{id}/enrollments": { "get": { "tags": [ "Lectures" ], "summary": "Получить список записавшихся студентов на лекцию.", "description": "Только Admin или Teacher. Включает флаг посещения (`attended`).\n\n**Required roles:** Admin, Teacher", "parameters": [ { "name": "id", "in": "path", "description": "ID лекции.", "required": true, "schema": { "type": "integer", "format": "int32" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список записей (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EnrollmentDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin или Teacher.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/lectures/{id}/reviews": { "get": { "tags": [ "Lectures" ], "summary": "Получить отзывы к лекции.", "description": "Только Admin или Teacher.\n\n**Required roles:** Admin, Teacher", "parameters": [ { "name": "id", "in": "path", "description": "ID лекции.", "required": true, "schema": { "type": "integer", "format": "int32" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список отзывов (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin или Teacher.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/locations": { "get": { "tags": [ "Locations" ], "summary": "Получить список всех локаций.", "description": "**Required:** any authenticated user", "responses": { "200": { "description": "Список локаций.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/LocationDto" } } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "post": { "tags": [ "Locations" ], "summary": "Создать новую локацию.", "description": "Только Admin. Локации также создаются автоматически при синхронизации с Modeus.\n\n**Required roles:** Admin", "requestBody": { "description": "Название, корпус, аудитория и/или адрес.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateLocationRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/CreateLocationRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/CreateLocationRequest" } } } }, "responses": { "201": { "description": "Локация создана.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LocationDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/locations/{id}": { "get": { "tags": [ "Locations" ], "summary": "Получить локацию по ID.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "id", "in": "path", "description": "ID локации.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Данные локации.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LocationDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Локация не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Locations" ], "summary": "Обновить локацию по ID.", "description": "Только Admin.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID локации.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "Обновляемые поля: название, корпус, аудитория, адрес.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateLocationRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateLocationRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateLocationRequest" } } } }, "responses": { "200": { "description": "Обновлённые данные локации.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LocationDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Локация не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "delete": { "tags": [ "Locations" ], "summary": "Удалить локацию по ID.", "description": "Только Admin. При удалении локации у связанных лекций поле `locationId` становится null.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID локации.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Локация удалена." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Локация не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/notifications": { "get": { "tags": [ "Notifications" ], "summary": "Получить уведомления текущего пользователя.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список уведомлений.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserNotificationDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/notifications/read-all": { "patch": { "tags": [ "Notifications" ], "summary": "Отметить все уведомления текущего пользователя как прочитанные.", "description": "**Required:** any authenticated user", "responses": { "204": { "description": "Уведомления отмечены прочитанными." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/notifications/send": { "post": { "tags": [ "Notifications" ], "summary": "Отправить уведомление немедленно.", "description": "Канал задаётся строкой, например `email`. Новые провайдеры добавляются через `INotificationProvider`.\n\n**Required roles:** Admin", "requestBody": { "description": "Канал, получатель, тема и текст уведомления.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SendNotificationRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/SendNotificationRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/SendNotificationRequest" } } } }, "responses": { "202": { "description": "Уведомление принято к отправке." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/notifications/schedule": { "post": { "tags": [ "Notifications" ], "summary": "Запланировать отложенную отправку уведомления через Quartz.NET.", "description": "**Required roles:** Admin", "requestBody": { "description": "Уведомление и момент отправки.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduleNotificationRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/ScheduleNotificationRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/ScheduleNotificationRequest" } } } }, "responses": { "202": { "description": "Уведомление поставлено в очередь Quartz.NET.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScheduledNotificationResponse" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/reviews": { "post": { "tags": [ "Reviews" ], "summary": "Создать отзыв к лекции.", "description": "Только Student. После создания отзыв отправляется на LLM-анализ\n(статус `Pending`). LLM оценивает содержательность и начисляет монеты\nскрытно от пользователя.\n\n**Required roles:** Student", "requestBody": { "description": "ID лекции, оценка (Like/Neutral/Dislike) и текст отзыва.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateReviewRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/CreateReviewRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/CreateReviewRequest" } } } }, "responses": { "201": { "description": "Отзыв создан и поставлен в очередь на LLM-анализ.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Student.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Лекция не найдена.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "409": { "description": "Студент уже оставил отзыв к этой лекции.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "get": { "tags": [ "Reviews" ], "summary": "Получить список всех отзывов.", "description": "Только Admin. Возвращает все отзывы независимо от LLM-статуса.\n\n**Required roles:** Admin", "parameters": [ { "name": "LlmStatus", "in": "query", "schema": { "$ref": "#/components/schemas/ReviewLlmStatus" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список всех отзывов (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/reviews/llm-prompt": { "get": { "tags": [ "Reviews" ], "summary": "Получить текущий промпт LLM-анализа отзывов.", "description": "Только Admin. Если настройка ещё не сохранена, возвращает базовый промпт.\n\n**Required roles:** Admin", "responses": { "200": { "description": "Текущий шаблон промпта.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewPromptDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Reviews" ], "summary": "Обновить промпт LLM-анализа отзывов.", "description": "Только Admin. Промпт применяется к следующим анализам и ручным повторам.\n\n**Required roles:** Admin", "requestBody": { "description": "Новый шаблон с плейсхолдерами {lectureContext} и {reviewText}.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateReviewPromptRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateReviewPromptRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateReviewPromptRequest" } } } }, "responses": { "200": { "description": "Сохранённый шаблон промпта.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewPromptDto" } } } }, "400": { "description": "Промпт пустой или не содержит обязательные плейсхолдеры.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/reviews/{id}": { "get": { "tags": [ "Reviews" ], "summary": "Получить отзыв по ID.", "description": "Только Admin или Teacher.\n\n**Required roles:** Admin, Teacher", "parameters": [ { "name": "id", "in": "path", "description": "ID отзыва.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Данные отзыва (включая LLM-статус и сентимент).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin или Teacher.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Отзыв не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Reviews" ], "summary": "Обновить отзыв.", "description": "Разрешено любому авторизованному пользователю, но сервис проверяет владельца.\nИзменение текста сбрасывает LLM-статус в `Pending` (повторный анализ).\n\n**Required:** any authenticated user", "parameters": [ { "name": "id", "in": "path", "description": "ID отзыва.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "Новая оценка и/или текст.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateReviewRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateReviewRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateReviewRequest" } } } }, "responses": { "200": { "description": "Обновлённые данные отзыва.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Отзыв принадлежит другому пользователю.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Отзыв не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "delete": { "tags": [ "Reviews" ], "summary": "Удалить отзыв.", "description": "Владелец может удалить свой отзыв. Admin может удалить любой.\n\n**Required:** any authenticated user", "parameters": [ { "name": "id", "in": "path", "description": "ID отзыва.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Отзыв удалён." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Нет прав на удаление (не владелец и не Admin).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Отзыв не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/reviews/{id}/reanalyze": { "post": { "tags": [ "Reviews" ], "summary": "Запустить повторный LLM-анализ отзыва.", "description": "Только Admin. Сбрасывает статус отзыва на `Pending` и отправляет его\nна повторную обработку.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID отзыва.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Повторный анализ запланирован." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Отзыв не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/sync/schedule": { "post": { "tags": [ "Sync" ], "summary": "Запустить синхронизацию расписания лекций из Modeus.", "description": "Только Admin. Выполняет upsert лекций и связанных курсов на основе данных\nиз внешнего API `schedule.rdcenter.ru`. Поддерживает фильтрацию по периоду,\nразмеру выборки, аудиториям, участникам, реализациям курсов/циклов,\nспециальностям, годам набора, профилям, учебным планам и типам занятий.\n\n**Required roles:** Admin", "requestBody": { "description": "Параметры поиска событий во внешнем сервисе расписания.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SyncScheduleRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/SyncScheduleRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/SyncScheduleRequest" } } } }, "responses": { "200": { "description": "Результат синхронизации: кол-во созданных, обновлённых и пропущенных записей.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SyncResultDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/sync/status": { "get": { "tags": [ "Sync" ], "summary": "Получить статус последней синхронизации.", "description": "Только Admin. Возвращает время и результат последней успешной синхронизации.\n\n**Required roles:** Admin", "responses": { "200": { "description": "Статус синхронизации.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SyncStatusDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/sync/rooms": { "post": { "tags": [ "Sync" ], "summary": "Синхронизировать аудитории (локации) из Modeus.", "description": "Только Admin. Импортирует аудитории из `schedule.rdcenter.ru` и создаёт\nсоответствующие записи в таблице locations.\n\n**Required roles:** Admin", "responses": { "200": { "description": "Результат синхронизации аудиторий.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SyncResultDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/sync/employees": { "post": { "tags": [ "Sync" ], "summary": "Поиск преподавателей в Modeus по ФИО.", "description": "Только Admin. Ищет преподавателей через внешнее API и возвращает список\nдля ручного импорта. Найденные преподаватели не создаются автоматически.\n\n**Required roles:** Admin", "parameters": [ { "name": "fullname", "in": "query", "description": "Полное имя или часть имени преподавателя для поиска.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Список найденных преподавателей.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/EmployeeDto" } } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/tags": { "get": { "tags": [ "Tags" ], "summary": "Получить список тегов с опциональной фильтрацией по типу и родителю.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "type", "in": "query", "description": "Тип тега: Institute, Faculty, Subject, Organization, Topic, Other.", "schema": { "$ref": "#/components/schemas/TagType" } }, { "name": "parentId", "in": "query", "description": "ID родительского тега (фильтрация дочерних).", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список тегов.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/TagDto" } } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "post": { "tags": [ "Tags" ], "summary": "Создать новый тег.", "description": "Только Admin.\n\n**Required roles:** Admin", "requestBody": { "description": "Название, тип и опциональный родительский тег.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateTagRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/CreateTagRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/CreateTagRequest" } } } }, "responses": { "201": { "description": "Тег создан.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TagDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/tags/{id}": { "get": { "tags": [ "Tags" ], "summary": "Получить тег по ID.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "id", "in": "path", "description": "ID тега.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Данные тега.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TagDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Тег не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Tags" ], "summary": "Обновить тег по ID.", "description": "Только Admin.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID тега.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "Новое название, тип и/или родительский тег.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateTagRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateTagRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateTagRequest" } } } }, "responses": { "200": { "description": "Обновлённые данные тега.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TagDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Тег не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "delete": { "tags": [ "Tags" ], "summary": "Удалить тег по ID.", "description": "Только Admin. Удаление тега каскадно удаляет привязки к курсам (`course_tags`).\nДочерние теги остаются, но их `parentId` становится null.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID тега.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "204": { "description": "Тег удалён." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Тег не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/tags/tree": { "get": { "tags": [ "Tags" ], "summary": "Получить иерархическое дерево всех тегов.", "description": "Возвращает корневые теги с вложенными дочерними тегами.\nПолезно для построения фильтрующих UI-компонентов.\n\n**Required:** any authenticated user", "responses": { "200": { "description": "Иерархический список тегов.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/TagTreeDto" } } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/me": { "get": { "tags": [ "Users" ], "summary": "Получить профиль текущего пользователя.", "description": "**Required:** any authenticated user", "responses": { "200": { "description": "Данные текущего пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CurrentUserDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Users" ], "summary": "Обновить профиль текущего пользователя (displayName, avatarUrl).", "description": "**Required:** any authenticated user", "requestBody": { "description": "Обновляемые поля профиля.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateUserRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateUserRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateUserRequest" } } } }, "responses": { "200": { "description": "Обновлённые данные текущего пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CurrentUserDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/me/stats": { "get": { "tags": [ "Users" ], "summary": "Получить статистику текущего пользователя.", "description": "**Required:** any authenticated user", "responses": { "200": { "description": "Статистика текущего пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserStatsDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/me/enrollments": { "get": { "tags": [ "Users" ], "summary": "Получить список записей текущего пользователя на лекции.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список записей (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LectureDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/me/reviews": { "get": { "tags": [ "Users" ], "summary": "Получить отзывы текущего пользователя.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список отзывов (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/me/achievements": { "get": { "tags": [ "Users" ], "summary": "Получить достижения текущего пользователя.", "description": "**Required:** any authenticated user", "responses": { "200": { "description": "Список полученных достижений.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserAchievementDto" } } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/me/transactions": { "get": { "tags": [ "Users" ], "summary": "Получить историю транзакций монет текущего пользователя.", "description": "**Required:** any authenticated user", "parameters": [ { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "История транзакций (пагинированная).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CoinTransactionDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/{id}": { "get": { "tags": [ "Users" ], "summary": "Получить профиль пользователя по ID.", "description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Данные пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] }, "put": { "tags": [ "Users" ], "summary": "Обновить профиль пользователя (displayName, avatarUrl).", "description": "Только Admin. Для текущего пользователя используйте PUT /api/v1/users/me.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "Обновляемые поля профиля.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateUserRequest" } }, "text/json": { "schema": { "$ref": "#/components/schemas/UpdateUserRequest" } }, "application/*+json": { "schema": { "$ref": "#/components/schemas/UpdateUserRequest" } } } }, "responses": { "200": { "description": "Обновлённые данные пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/{id}/stats": { "get": { "tags": [ "Users" ], "summary": "Получить статистику пользователя (XP, монеты, уровень, посещения).", "description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/stats.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Статистика пользователя.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserStatsDto" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/{id}/enrollments": { "get": { "tags": [ "Users" ], "summary": "Получить список записей пользователя на лекции.", "description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/enrollments.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список записей (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LectureDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/{id}/reviews": { "get": { "tags": [ "Users" ], "summary": "Получить отзывы пользователя.", "description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/reviews.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список отзывов (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReviewDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/{id}/achievements": { "get": { "tags": [ "Users" ], "summary": "Получить достижения пользователя.", "description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/achievements.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список полученных достижений.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserAchievementDto" } } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/{id}/transactions": { "get": { "tags": [ "Users" ], "summary": "Получить историю транзакций монет пользователя.", "description": "Только Admin. Для текущего пользователя используйте GET /api/v1/users/me/transactions.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "История транзакций (пагинированная).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CoinTransactionDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users": { "get": { "tags": [ "Users" ], "summary": "Получить список всех пользователей с фильтрацией и пагинацией.", "description": "Только Admin.\n\n**Required roles:** Admin", "parameters": [ { "name": "Search", "in": "query", "schema": { "type": "string" } }, { "name": "Role", "in": "query", "schema": { "$ref": "#/components/schemas/UserRole" } }, { "name": "IsActive", "in": "query", "schema": { "type": "boolean" } }, { "name": "Page", "in": "query", "schema": { "type": "integer", "format": "int32" } }, { "name": "PageSize", "in": "query", "schema": { "type": "integer", "format": "int32" } } ], "responses": { "200": { "description": "Список пользователей (пагинированный).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserDtoPagedResult" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/{id}/role": { "patch": { "tags": [ "Users" ], "summary": "Изменить набор ролей пользователя.", "description": "Только Admin. Доступные роли: Student, Teacher, Admin.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "Новый набор ролей пользователя.", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserRole" } } }, "text/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserRole" } } }, "application/*+json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/UserRole" } } } } }, "responses": { "204": { "description": "Роли успешно изменены." }, "400": { "description": "Bad Request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } }, "/api/v1/users/{id}/active": { "patch": { "tags": [ "Users" ], "summary": "Активировать или деактивировать аккаунт пользователя.", "description": "Только Admin. Деактивированный пользователь не может войти в систему.\n\n**Required roles:** Admin", "parameters": [ { "name": "id", "in": "path", "description": "ID пользователя.", "required": true, "schema": { "type": "integer", "format": "int32" } } ], "requestBody": { "description": "true — активировать, false — деактивировать.", "content": { "application/json": { "schema": { "type": "boolean" } }, "text/json": { "schema": { "type": "boolean" } }, "application/*+json": { "schema": { "type": "boolean" } } } }, "responses": { "204": { "description": "Статус успешно изменён." }, "401": { "description": "Требуется аутентификация.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "403": { "description": "Требуется роль Admin.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } }, "404": { "description": "Пользователь не найден.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } } } }, "security": [ { "Bearer": [ ] } ] } } }, "components": { "schemas": { "AchievementDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "name": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "iconUrl": { "type": "string", "nullable": true }, "xpReward": { "type": "integer", "format": "int32" }, "coinReward": { "type": "integer", "format": "int32" }, "condition": { "type": "string", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "AuthResponse": { "type": "object", "properties": { "accessToken": { "type": "string", "nullable": true }, "expiresAt": { "type": "string", "format": "date-time" }, "user": { "$ref": "#/components/schemas/UserAuthDto" } }, "additionalProperties": false }, "CoinTransactionDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "amount": { "type": "integer", "format": "int32" }, "type": { "$ref": "#/components/schemas/CoinTransactionType" }, "reviewId": { "type": "integer", "format": "int32", "nullable": true }, "achievementId": { "type": "integer", "format": "int32", "nullable": true }, "description": { "type": "string", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "CoinTransactionDtoPagedResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/CoinTransactionDto" }, "nullable": true }, "totalCount": { "type": "integer", "format": "int32" }, "page": { "type": "integer", "format": "int32" }, "pageSize": { "type": "integer", "format": "int32" }, "totalPages": { "type": "integer", "format": "int32" } }, "additionalProperties": false }, "CoinTransactionType": { "enum": [ "ReviewReward", "AchievementReward", "AttendanceReward", "AdminAdjustment" ], "type": "string" }, "CourseDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "name": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "isSynced": { "type": "boolean" }, "tags": { "type": "array", "items": { "$ref": "#/components/schemas/TagDto" }, "nullable": true }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "CourseDtoPagedResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/CourseDto" }, "nullable": true }, "totalCount": { "type": "integer", "format": "int32" }, "page": { "type": "integer", "format": "int32" }, "pageSize": { "type": "integer", "format": "int32" }, "totalPages": { "type": "integer", "format": "int32" } }, "additionalProperties": false }, "CreateAchievementRequest": { "type": "object", "properties": { "name": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "iconUrl": { "type": "string", "nullable": true }, "xpReward": { "type": "integer", "format": "int32" }, "coinReward": { "type": "integer", "format": "int32" }, "condition": { "type": "string", "nullable": true } }, "additionalProperties": false }, "CreateCourseRequest": { "type": "object", "properties": { "name": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true } }, "additionalProperties": false }, "CreateLectureRequest": { "type": "object", "properties": { "courseId": { "type": "integer", "format": "int32" }, "teacherId": { "type": "integer", "format": "int32", "nullable": true }, "locationId": { "type": "integer", "format": "int32", "nullable": true }, "title": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "format": { "$ref": "#/components/schemas/LectureFormat" }, "startsAt": { "type": "string", "format": "date-time" }, "endsAt": { "type": "string", "format": "date-time" }, "isOpen": { "type": "boolean" }, "maxEnrollments": { "type": "integer", "format": "int32" }, "onlineUrl": { "type": "string", "nullable": true } }, "additionalProperties": false }, "CreateLocationRequest": { "type": "object", "properties": { "name": { "type": "string", "nullable": true }, "building": { "type": "string", "nullable": true }, "room": { "type": "string", "nullable": true }, "address": { "type": "string", "nullable": true } }, "additionalProperties": false }, "CreateReviewRequest": { "type": "object", "properties": { "lectureId": { "type": "integer", "format": "int32" }, "rating": { "$ref": "#/components/schemas/ReviewRating" }, "text": { "type": "string", "nullable": true } }, "additionalProperties": false }, "CreateTagRequest": { "type": "object", "properties": { "name": { "type": "string", "nullable": true }, "type": { "$ref": "#/components/schemas/TagType" }, "parentId": { "type": "integer", "format": "int32", "nullable": true } }, "additionalProperties": false }, "CurrentUserDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "email": { "type": "string", "nullable": true }, "displayName": { "type": "string", "nullable": true }, "avatarUrl": { "type": "string", "nullable": true }, "roles": { "type": "array", "items": { "$ref": "#/components/schemas/UserRole" }, "nullable": true }, "xp": { "type": "integer", "format": "int32" }, "coins": { "type": "integer", "format": "int32" }, "level": { "type": "integer", "format": "int32" }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "DevLoginRequest": { "type": "object", "properties": { "email": { "type": "string", "nullable": true }, "displayName": { "type": "string", "nullable": true }, "roles": { "type": "array", "items": { "$ref": "#/components/schemas/UserRole" }, "nullable": true } }, "additionalProperties": false }, "EmployeeDto": { "type": "object", "properties": { "id": { "type": "string", "nullable": true }, "fullName": { "type": "string", "nullable": true }, "department": { "type": "string", "nullable": true } }, "additionalProperties": false }, "EnrollmentDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "userId": { "type": "integer", "format": "int32" }, "userName": { "type": "string", "nullable": true }, "userEmail": { "type": "string", "nullable": true }, "attended": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "EnrollmentDtoPagedResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/EnrollmentDto" }, "nullable": true }, "totalCount": { "type": "integer", "format": "int32" }, "page": { "type": "integer", "format": "int32" }, "pageSize": { "type": "integer", "format": "int32" }, "totalPages": { "type": "integer", "format": "int32" } }, "additionalProperties": false }, "EnrollmentSlotRuleDto": { "type": "object", "properties": { "level": { "type": "integer", "format": "int32" }, "slots": { "type": "integer", "format": "int32" } }, "additionalProperties": false }, "LectureDetailDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "courseId": { "type": "integer", "format": "int32" }, "courseName": { "type": "string", "nullable": true }, "teacherId": { "type": "integer", "format": "int32", "nullable": true }, "teacherName": { "type": "string", "nullable": true }, "locationId": { "type": "integer", "format": "int32", "nullable": true }, "locationName": { "type": "string", "nullable": true }, "title": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "format": { "$ref": "#/components/schemas/LectureFormat" }, "startsAt": { "type": "string", "format": "date-time" }, "endsAt": { "type": "string", "format": "date-time" }, "isOpen": { "type": "boolean" }, "maxEnrollments": { "type": "integer", "format": "int32" }, "enrollmentsCount": { "type": "integer", "format": "int32" }, "onlineUrl": { "type": "string", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" }, "isEnrolled": { "type": "boolean" } }, "additionalProperties": false }, "LectureDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "courseId": { "type": "integer", "format": "int32" }, "courseName": { "type": "string", "nullable": true }, "teacherId": { "type": "integer", "format": "int32", "nullable": true }, "teacherName": { "type": "string", "nullable": true }, "locationId": { "type": "integer", "format": "int32", "nullable": true }, "locationName": { "type": "string", "nullable": true }, "title": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "format": { "$ref": "#/components/schemas/LectureFormat" }, "startsAt": { "type": "string", "format": "date-time" }, "endsAt": { "type": "string", "format": "date-time" }, "isOpen": { "type": "boolean" }, "maxEnrollments": { "type": "integer", "format": "int32" }, "enrollmentsCount": { "type": "integer", "format": "int32" }, "onlineUrl": { "type": "string", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" }, "isEnrolled": { "type": "boolean" } }, "additionalProperties": false }, "LectureDtoPagedResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/LectureDto" }, "nullable": true }, "totalCount": { "type": "integer", "format": "int32" }, "page": { "type": "integer", "format": "int32" }, "pageSize": { "type": "integer", "format": "int32" }, "totalPages": { "type": "integer", "format": "int32" } }, "additionalProperties": false }, "LectureFormat": { "enum": [ "Online", "Offline" ], "type": "string" }, "LocationDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "name": { "type": "string", "nullable": true }, "building": { "type": "string", "nullable": true }, "room": { "type": "string", "nullable": true }, "address": { "type": "string", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "LoginMicrosoftRequest": { "type": "object", "properties": { "authorizationCode": { "type": "string", "nullable": true }, "redirectUri": { "type": "string", "nullable": true } }, "additionalProperties": false }, "ProblemDetails": { "type": "object", "properties": { "type": { "type": "string", "nullable": true }, "title": { "type": "string", "nullable": true }, "status": { "type": "integer", "format": "int32", "nullable": true }, "detail": { "type": "string", "nullable": true }, "instance": { "type": "string", "nullable": true } }, "additionalProperties": { } }, "ReviewDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "lectureId": { "type": "integer", "format": "int32" }, "lectureTitle": { "type": "string", "nullable": true }, "userId": { "type": "integer", "format": "int32" }, "userName": { "type": "string", "nullable": true }, "rating": { "$ref": "#/components/schemas/ReviewRating" }, "text": { "type": "string", "nullable": true }, "llmStatus": { "$ref": "#/components/schemas/ReviewLlmStatus" }, "sentiment": { "$ref": "#/components/schemas/ReviewSentiment" }, "qualityScore": { "type": "number", "format": "double", "nullable": true }, "isInformative": { "type": "boolean", "nullable": true }, "llmTags": { "type": "array", "items": { "type": "string" }, "nullable": true }, "llmRawOutput": { "type": "string", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "ReviewDtoPagedResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ReviewDto" }, "nullable": true }, "totalCount": { "type": "integer", "format": "int32" }, "page": { "type": "integer", "format": "int32" }, "pageSize": { "type": "integer", "format": "int32" }, "totalPages": { "type": "integer", "format": "int32" } }, "additionalProperties": false }, "ReviewLlmStatus": { "enum": [ "Pending", "Analyzed", "Rejected" ], "type": "string" }, "ReviewPromptDto": { "type": "object", "properties": { "prompt": { "type": "string", "nullable": true }, "updatedAt": { "type": "string", "format": "date-time", "nullable": true } }, "additionalProperties": false }, "ReviewRating": { "enum": [ "Like", "Neutral", "Dislike" ], "type": "string" }, "ReviewSentiment": { "enum": [ "Positive", "Neutral", "Negative" ], "type": "string" }, "ScheduleNotificationRequest": { "type": "object", "properties": { "channel": { "type": "string", "nullable": true }, "recipient": { "type": "string", "nullable": true }, "subject": { "type": "string", "nullable": true }, "body": { "type": "string", "nullable": true }, "sendAt": { "type": "string", "format": "date-time" }, "recipientName": { "type": "string", "nullable": true }, "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "nullable": true } }, "additionalProperties": false }, "ScheduledNotificationResponse": { "type": "object", "properties": { "jobId": { "type": "string", "nullable": true }, "sendAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "SendNotificationRequest": { "type": "object", "properties": { "channel": { "type": "string", "nullable": true }, "recipient": { "type": "string", "nullable": true }, "subject": { "type": "string", "nullable": true }, "body": { "type": "string", "nullable": true }, "recipientName": { "type": "string", "nullable": true }, "metadata": { "type": "object", "additionalProperties": { "type": "string" }, "nullable": true } }, "additionalProperties": false }, "SyncResultDto": { "type": "object", "properties": { "created": { "type": "integer", "format": "int32" }, "updated": { "type": "integer", "format": "int32" }, "skipped": { "type": "integer", "format": "int32" }, "error": { "type": "string", "nullable": true }, "details": { "type": "array", "items": { "type": "string" }, "nullable": true } }, "additionalProperties": false }, "SyncScheduleRequest": { "type": "object", "properties": { "specialtyCode": { "type": "array", "items": { "type": "string" }, "nullable": true }, "timeMin": { "type": "string", "format": "date-time", "nullable": true }, "timeMax": { "type": "string", "format": "date-time", "nullable": true }, "typeId": { "type": "array", "items": { "type": "string" }, "nullable": true }, "size": { "type": "integer", "format": "int32", "nullable": true }, "roomId": { "type": "array", "items": { "type": "string" }, "nullable": true }, "attendeePersonId": { "type": "array", "items": { "type": "string" }, "nullable": true }, "courseUnitRealizationId": { "type": "array", "items": { "type": "string" }, "nullable": true }, "cycleRealizationId": { "type": "array", "items": { "type": "string" }, "nullable": true }, "learningStartYear": { "type": "array", "items": { "type": "integer", "format": "int32" }, "nullable": true }, "profileName": { "type": "array", "items": { "type": "string" }, "nullable": true }, "curriculumId": { "type": "array", "items": { "type": "string" }, "nullable": true } }, "additionalProperties": false }, "SyncStatusDto": { "type": "object", "properties": { "lastSyncAt": { "type": "string", "format": "date-time", "nullable": true }, "status": { "type": "string", "nullable": true }, "lastResult": { "$ref": "#/components/schemas/SyncResultDto" } }, "additionalProperties": false }, "TagDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "name": { "type": "string", "nullable": true }, "type": { "$ref": "#/components/schemas/TagType" }, "parentId": { "type": "integer", "format": "int32", "nullable": true }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "TagTreeDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "name": { "type": "string", "nullable": true }, "type": { "$ref": "#/components/schemas/TagType" }, "children": { "type": "array", "items": { "$ref": "#/components/schemas/TagTreeDto" }, "nullable": true } }, "additionalProperties": false }, "TagType": { "enum": [ "Institute", "Faculty", "Subject", "Organization", "Topic", "Other" ], "type": "string" }, "UpdateAchievementRequest": { "type": "object", "properties": { "name": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "iconUrl": { "type": "string", "nullable": true }, "xpReward": { "type": "integer", "format": "int32" }, "coinReward": { "type": "integer", "format": "int32" }, "condition": { "type": "string", "nullable": true } }, "additionalProperties": false }, "UpdateCourseRequest": { "type": "object", "properties": { "name": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true } }, "additionalProperties": false }, "UpdateLectureRequest": { "type": "object", "properties": { "teacherId": { "type": "integer", "format": "int32", "nullable": true }, "locationId": { "type": "integer", "format": "int32", "nullable": true }, "title": { "type": "string", "nullable": true }, "description": { "type": "string", "nullable": true }, "format": { "$ref": "#/components/schemas/LectureFormat" }, "startsAt": { "type": "string", "format": "date-time" }, "endsAt": { "type": "string", "format": "date-time" }, "isOpen": { "type": "boolean" }, "maxEnrollments": { "type": "integer", "format": "int32" }, "onlineUrl": { "type": "string", "nullable": true } }, "additionalProperties": false }, "UpdateLocationRequest": { "type": "object", "properties": { "name": { "type": "string", "nullable": true }, "building": { "type": "string", "nullable": true }, "room": { "type": "string", "nullable": true }, "address": { "type": "string", "nullable": true } }, "additionalProperties": false }, "UpdateReviewPromptRequest": { "type": "object", "properties": { "prompt": { "type": "string", "nullable": true } }, "additionalProperties": false }, "UpdateReviewRequest": { "type": "object", "properties": { "rating": { "$ref": "#/components/schemas/ReviewRating" }, "text": { "type": "string", "nullable": true } }, "additionalProperties": false }, "UpdateTagRequest": { "type": "object", "properties": { "name": { "type": "string", "nullable": true }, "type": { "$ref": "#/components/schemas/TagType" }, "parentId": { "type": "integer", "format": "int32", "nullable": true } }, "additionalProperties": false }, "UpdateUserRequest": { "type": "object", "properties": { "displayName": { "type": "string", "nullable": true }, "avatarUrl": { "type": "string", "nullable": true } }, "additionalProperties": false }, "UserAchievementDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "achievement": { "$ref": "#/components/schemas/AchievementDto" }, "awardedAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "UserAuthDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "email": { "type": "string", "nullable": true }, "displayName": { "type": "string", "nullable": true }, "roles": { "type": "array", "items": { "$ref": "#/components/schemas/UserRole" }, "nullable": true } }, "additionalProperties": false }, "UserDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "email": { "type": "string", "nullable": true }, "displayName": { "type": "string", "nullable": true }, "avatarUrl": { "type": "string", "nullable": true }, "roles": { "type": "array", "items": { "$ref": "#/components/schemas/UserRole" }, "nullable": true }, "isActive": { "type": "boolean" }, "xp": { "type": "integer", "format": "int32" }, "coins": { "type": "integer", "format": "int32" }, "level": { "type": "integer", "format": "int32" }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "UserDtoPagedResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/UserDto" }, "nullable": true }, "totalCount": { "type": "integer", "format": "int32" }, "page": { "type": "integer", "format": "int32" }, "pageSize": { "type": "integer", "format": "int32" }, "totalPages": { "type": "integer", "format": "int32" } }, "additionalProperties": false }, "UserNotificationDto": { "type": "object", "properties": { "id": { "type": "integer", "format": "int32" }, "type": { "type": "string", "nullable": true }, "title": { "type": "string", "nullable": true }, "body": { "type": "string", "nullable": true }, "isRead": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false }, "UserNotificationDtoPagedResult": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/UserNotificationDto" }, "nullable": true }, "totalCount": { "type": "integer", "format": "int32" }, "page": { "type": "integer", "format": "int32" }, "pageSize": { "type": "integer", "format": "int32" }, "totalPages": { "type": "integer", "format": "int32" } }, "additionalProperties": false }, "UserRole": { "enum": [ "Student", "Teacher", "Admin" ], "type": "string" }, "UserStatsDto": { "type": "object", "properties": { "totalLectures": { "type": "integer", "format": "int32" }, "attendedLectures": { "type": "integer", "format": "int32" }, "totalReviews": { "type": "integer", "format": "int32" }, "xp": { "type": "integer", "format": "int32" }, "coins": { "type": "integer", "format": "int32" }, "level": { "type": "integer", "format": "int32" }, "achievementsCount": { "type": "integer", "format": "int32" }, "currentLevelXp": { "type": "integer", "format": "int32" }, "nextLevelXp": { "type": "integer", "format": "int32", "nullable": true }, "activeEnrollments": { "type": "integer", "format": "int32" }, "enrollmentSlotLimit": { "type": "integer", "format": "int32" }, "enrollmentSlotRules": { "type": "array", "items": { "$ref": "#/components/schemas/EnrollmentSlotRuleDto" }, "nullable": true } }, "additionalProperties": false } }, "securitySchemes": { "Bearer": { "type": "http", "description": "Введите JWT access token, полученный из `/api/v1/auth/login/microsoft`.\n\nПример: `eyJhbGci...`", "scheme": "bearer", "bearerFormat": "JWT" } } }, "tags": [ { "name": "Achievements" }, { "name": "Auth" }, { "name": "Courses" }, { "name": "Lectures" }, { "name": "Locations" }, { "name": "Notifications" }, { "name": "Reviews" }, { "name": "Sync" }, { "name": "Tags" }, { "name": "Users" } ] }