- teacher/admin course pages, content, gradebook, profile, downloads - courseNo -> UUID только на границе REST API - обновлены e2e, шаблоны, тесты и docs - LDD-UI-002; baseline 59 claims / 385 тестов
16 KiB
16 KiB
13. Отчёт этапа 3: доступ и профиль студента курса
Историческая справка: в этом отчёте браузерные URL показаны с UUID курса. После перехода 2026-10-29 текущие URL используют
courseNo; REST API по-прежнему принимает UUID.
Дата: 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.ymlidentity, 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.