Files
LMS/docs/13-stage3-report.md

15 KiB
Raw Blame History

13. Отчёт этапа 3: доступ и профиль студента курса

Дата: 2026-10-27. Статус: этап 3 выполнен; этап 4 (повышение и жизненный цикл) — следующий.

1. Что сделано

Аккаунты преподавателей

  • Добавлен административный API /api/v1/teachers: список, создание, сброс пароля. Создаётся только аккаунт роли TEACHER.
  • Логин проверяется на длину (минимум 3 символа) и уникальность; пустой пароль заменяется сгенерированным. Новый пароль помечается как временный (mustChangePassword), показывается один раз.
  • В админке появилась страница /admin/teachers с созданием преподавателя и сбросом пароля; одноразовый пароль выводится во всплывающем уведомлении.

Курсовая область видимости

  • CourseAccessService в academic-service определяет доступные курсы по роли:
    • ADMIN — все курсы, включая архивные;
    • TEACHER — только активные курсы с активным назначением course_teachers (removed_at IS NULL);
    • STUDENT — только активные курсы активных групп, в которых студент состоит сейчас (группы запрашиваются у identity-service);
    • анонимный пользователь до сервиса не доходит — gateway требует JWT.
  • GET /api/v1/courses/mine возвращает курсы текущего пользователя.
  • GET /api/v1/courses/{id} проверяет доступ к курсу. Админские операции создания, переименования, архивирования, назначения преподавателей и привязки групп остались только для ADMIN.
  • Список лабораторных без courseId фильтруется по доступным курсам; с courseId доступ проверяется явно. Карточка лабораторной тоже проверяет доступ через её курс. Лабораторная без курса доступна только админу.
  • Недоступный курс/лабораторная дают 403, несуществующая — 404. Недоступность identity при проверке групп даёт 502, а не 403.

Внутренние API identity

Добавлены read-only эндпоинты для межсервисных вызовов:

Метод Путь Назначение
GET /api/v1/internal/students/{studentId}/context карточка студента: ФИО, №, статус, год набора, текущая группа
POST /api/v1/internal/accounts/lookup пакетные логины авторов комментариев и оценок
GET /api/v1/internal/groups/{groupId} группа и текущий состав без учётных данных

Эндпоинты защищены заголовком X-Internal-Token; сравнение токена — constant-time. Gateway не маршрутизирует /api/v1/internal/**, поэтому снаружи эти пути недоступны.

Кабинет преподавателя

  • /teacher — список назначенных курсов с количеством групп и лабораторных.
  • /teacher/courses/{id} — карточка курса: привязанные группы и лабораторные в режиме просмотра. Редактирование курса и лабораторных остаётся у админа.
  • /teacher/courses/{courseId}/groups/{groupId} — состав группы, привязанной к курсу, со ссылками на профиль студента.
  • Доступ к /teacher/** разрешён роли TEACHER и админу; студент перенаправляется в свой кабинет, аноним — на вход. После входа преподаватель попадает на /teacher.

Профиль студента курса

  • Progress-service собирает агрегированный профиль одним запросом: GET /api/v1/courses/{courseId}/students/{studentId}/profile.
  • В ответе: курс, карточка студента, сводка и полный список лабораторных курса.
  • Для каждой лабораторной: вариант и уровень, число попыток, дата последней сдачи, оценка и её публикация, общий комментарий, комментарии преподавателей, защита (статус, код, задание, остаток времени), все попытки с файлами и ссылкой на скачивание.
  • Скачивание любой попытки: GET /api/v1/courses/{courseId}/submissions/{submissionId}/download (ZIP). Проверяется, что сдача относится к лабораторной этого курса; чужой submissionId и чужой курс отклоняются.
  • BFF-страницы профиля:
    • /teacher/courses/{courseId}/students/{studentId};
    • /admin/courses/{courseId}/students/{studentId}.
  • Авторство: миграция V13__profile_authors.sql добавляет grade_comments.author_account_id и lab_grades.updated_by_account_id; новые комментарии и оценки сохраняют автора. Старые записи показываются без автора. Индекс lab_grades(lab_id) ускоряет выборку по списку лаб.
  • В ведомости группы ФИО студента и кнопка «Профиль» ведут на профиль, если группа открыта из карточки курса. На странице группы преподавателя те же ссылки доступны из состава группы.

2. Правила доступа

Объект ADMIN TEACHER STUDENT
Курс любой, включая архивный только активный назначенный только активный курс текущей группы
Лабораторные курса все все курса, включая черновики все лабораторные доступного курса
Состав группы курса любая привязанная группа группа должна быть активной привязкой курса доступ к курсу по своей группе
Профиль студента любой студент любого курса студент должен состоять в активной группе курса только свой профиль
Оценки и комментарии все все, включая неопубликованные только опубликованные
Скачивание попытки любая попытка курса только студент из активной группы курса отдельный download-эндпоинт недоступен
Внутренние API identity по сервисному токену по сервисному токену по сервисному токену

Дополнительно:

  • Преподаватель чужого курса получает 403; студент чужого профиля — 403.
  • Студент видит только опубликованные оценки и комментарии. Черновики и неопубликованные комментарии скрыты.
  • Профиль не даёт доступа к другим курсам студента.
  • Автор комментария/оценки показывается логином; старые записи без автора — «—». Логины подтягиваются одним пакетным запросом.

3. Токен и конфигурация

  • Общий секрет: LMS_INTERNAL_TOKEN.
  • Где задаётся: lms.internal.token в application.yml identity, academic и progress. Локально допускается значение по умолчанию dev-internal-token; в production оно обязательно заменяется.
  • docker-compose.prod.yml передаёт токен в identity, academic и progress; .env.prod.example содержит заготовку без секрета.
  • Заголовок: X-Internal-Token. Пустой или неверный токен — 403.
  • Gateway не публикует внутренние пути; вызовы идут напрямую между сервисами по внутренней сети Docker.

4. UI

  • /admin/teachers — создание преподавателя, список, сброс пароля, отображение временного пароля один раз.
  • /teacher — «Мои курсы».
  • /teacher/courses/{id} — группы и лабораторные курса, переход к составу группы.
  • /teacher/courses/{courseId}/groups/{groupId} — студенты группы, ФИО и кнопка «Открыть» ведут в профиль.
  • /teacher/courses/{courseId}/students/{studentId} и /admin/courses/{courseId}/students/{studentId} — единый шаблон профиля с разными маршрутами скачивания и возврата.
  • В навигации: «Преподаватели» у админа, «Мои курсы» у преподавателя.
  • Профиль показывает сводку (лабораторных, сдано, оценено, средний балл, защиты), каждую лабораторную, комментарии, защиты и все попытки.

5. Тесты

Добавлены и расширены наборы:

  • identity: InternalControllerIntegrationTest (12) — контекст студента, пакетные логины, состав группы, неверный/пустой токен, неизвестные id; TeacherIntegrationTest (6) — создание, генерация пароля, список, сброс пароля, запрет для не-админа.
  • academic: CourseAccessServiceTest (16) — роли, чужие/архивные курсы, снятые назначения, студенты без групп, mine, доступ к лабораторным; CourseRosterServiceTest (6) — состав группы, чужая группа/преподаватель, недоступность identity.
  • progress: StudentProfileIntegrationTest (5) — неопубликованные данные для преподавателя, только опубликованное для студента, чужой курс/студент, скачивание чужой сдачи.
  • web: StudentProfileTemplateTest, ProfileDtoJsonTest, TeacherGroupTemplateTest, TeacherInterceptorTest, CourseGroupRosterDtoTest — контракты, шаблоны, роли и ссылки.

По результатам последнего прогона (2026-10-27) перечисленные наборы прошли с 0 failures / 0 errors.

6. Известные пробелы (исторические, на момент этапа 3)

Список ниже сохранён как история; актуальный статус — в разделе 7 и 16-stage6-report.md.

  • Контент курса. URL материалов вида /learn/course/{course_no}/..., проверка chapters.course_id/audience, наследуемая видимость активов и выдача файлов по курсу ещё не реализованы. Схема подготовлена на этапе 2.
  • Права преподавателя. Кабинет преподавателя — просмотр: курс, группы, состав, лабораторные и профили. Редактирование лабораторных, оценок и раздачи вариантов из кабинета не добавлялось.
  • Названия групп в кабинете. На странице курса преподавателя группа показывается UUID: academic отдаёт groupId, а имя группы не входит в контракт карточки курса. Нужен batch-запрос имён или расширение DTO.
  • Собственный профиль студента. API допускает STUDENT только для своего профиля, но отдельной студенческой BFF-страницы профиля нет; текущие страницы профиля — преподавательская и админская.
  • Аудит скачиваний. Скачивание попыток пока не пишется в аудит; это этап 6.

7. Обновление после этапа 6

  • Права преподавателя на запись закрыты: преподаватель своего курса управляет лабораторными/уровнями/вариантами, выставляет оценки и комментарии, публикует их, раздаёт варианты, назначает/запускает защиты, привязывает группы и авторит материалы курса. Ведомость переиспользует /admin/groups/{groupId}?courseId=... без админских контролов; сервисы проверяют курс и отклоняют чужие лабы/студентов/группы.
  • Имена групп в кабинете преподавателя разрешены: страница курса показывает название, при недоступности — id.
  • Контент курса и активы закрыты на этапе 5: URL /learn/course/{course_no}/..., проверка chapters.course_id/audience, наследуемая видимость активов; на этапе 6 добавлено авторство материалов преподавателем (/teacher/courses/{id}/content), глобальные материалы остаются admin-only.
  • Аудит скачиваний реализован: identity.audit_events, внутренний POST /api/v1/internal/audit и админский /admin/audit.