Files
LMS/docs/08-groups-courses-plan.md

26 KiB
Raw Blame History

08. План реализации: группы, курсы, преподаватели

Статус: план согласован, этапы 0–5 выполнены. Проверка этапа 6 выполнена (тесты, upgrade миграций, security, deploy-runbook), но этап 6 закрыт не полностью: остаются права преподавателя на запись, видимость активов и CI — см. § 9.1 и 16-stage6-report.md. Основа: 07-groups-courses.md. Дата решений: 2026-10-23.

Документ фиксирует решения, принятые при разборе ТЗ, и порядок реализации. При противоречии с 07-groups-courses.md приоритет у раздела 1 этого плана; само ТЗ приводится в соответствие на этапе 0.


1. Зафиксированные решения

№ Вопрос Решение
1 Лабораторные вне курса Не нужны. Все лабораторные принадлежат курсу. Текущие лабы переносятся в курс «Промышленное программирование на языке C». Все текущие активные группы привязываются к этому курсу. labs.course_id сначала nullable, после переноса — NOT NULL. Перед миграцией проверяются дубли номеров.
2 Идентификатор курса в URL UUID остаётся первичным ключом. Добавляется целочисленный courses.course_no (BIGINT GENERATED ALWAYS AS IDENTITY, уникальный, неизменяемый). Курсовые материалы открываются по /learn/course/{course_no}/.... Все текущие материалы переносятся в курс C и доступны только студентам его групп, преподавателям курса и админу. Старые ссылки не поддерживаются. audience используется только для будущих глобальных материалов (course_id IS NULL).
3 Архив и вход accounts.status (ACTIVE/ARCHIVED) — для всех пользователей; students.status (ACTIVE/ACADEMIC_LEAVE/EXPELLED/GRADUATED/ARCHIVED) — академический статус. Вход запрещён для EXPELLED, GRADUATED, ARCHIVED и архивных аккаунтов. ACADEMIC_LEAVE — вход в режиме только чтения. Web перепроверяет статус с кэшем 30–60 секунд; сервисы проверяют доступ к курсу при каждом запросе.
4 Журнал повышения promotion_runs + promotion_run_items + membership_history. Dry-run сохраняется как план; применение идемпотентно; откат — по элементам журнала с проверкой конфликтов.
5 Объём Полный ТЗ, этапы 0–6, один релиз с внутренними merge gate.
6 Запуск повышения 31 августа в 00:00 МСК автоматически строится dry-run. Применение — только вручную админом после проверки плана. При пропуске даты dry-run строится при следующем старте.
7 Профиль студента курса Преподаватель своего курса может открыть профиль студента по клику на ФИО или отдельной кнопке: статистика по лабам, комментарии преподавателей, скачивание файлов любой попытки сдачи.
8 Резервное копирование Обязательный backup перед каждым деплоем/миграцией, ежедневный backup по расписанию, retention, проверка целостности и restore на staging. Без успешного backup деплой не продолжается.

2. Новое требование: профиль студента курса

2.1. Доступ

  • Преподаватель — только если он активен в course_teachers для этого курса (removed_at IS NULL), а студент входит в группу, привязанную к этому курсу (course_groups.removed_at IS NULL).
  • Админ — любой студент любого курса.
  • Студент — свой профиль и только опубликованные оценки/комментарии (опционально; в обязательный объём текущего требования не входит).
  • Аноним — доступа нет.
  • Проверка выполняется на сервисах, а не только в web-BFF. Чужой курс/студент должен давать 403 или 404, а не данные.

2.2. Состав профиля

Карточка студента:

  • ФИО, № студента, группа, статус, год набора;
  • контактные/служебные поля, если появятся позже.

Сводка по курсу:

  • количество лабораторных курса;
  • сколько сдано, сколько проверено;
  • средний балл;
  • количество защит и их статусы.

Таблица по каждой лабораторной курса:

  • номер и название лабы;
  • вариант и уровень (если назначены);
  • количество попыток сдачи;
  • дата последней попытки;
  • оценка;
  • статус публикации оценки;
  • статус защиты;
  • общий комментарий к оценке;
  • количество комментариев.

Комментарии преподавателей по каждой лабе:

  • общий комментарий оценки (lab_grades.general_comment);
  • отдельные комментарии (grade_comments) с текстом, тоном, автором и датой;
  • для старых комментариев без автора — «автор не указан»;
  • новые комментарии сохраняют author_account_id.

Все попытки сдачи по каждой лабе:

  • номер попытки, дата, список файлов;
  • скачивание любой попытки, а не только последней;
  • скачивание в рамках своего курса; чужой submissionId запрещён;
  • для преподавателя — ZIP попытки и, при необходимости, отдельные файлы;
  • для студента — только свои попытки.

2.3. API (предварительно)

  • GET /api/v1/courses/{courseId}/students/{studentId}/profile — агрегированный профиль студента курса.
  • GET /api/v1/courses/{courseId}/submissions/{submissionId}/download — ZIP попытки для преподавателя/админа.
  • GET /api/v1/courses/{courseId}/submissions/{submissionId}/files/{name} — отдельный файл попытки (если включаем отдельные файлы).
  • GET /api/v1/me/courses/{courseId}/profile — собственный профиль студента (только опубликованное), опционально; в обязательный объём не входит.

courseId в API — UUID. В браузерных URL используется course_no.

2.4. UI

  • В ведомости группы ФИО студента становится ссылкой на профиль.
  • В колонке действий появляется отдельная кнопка «Профиль».
  • Страница преподавателя: /teacher/courses/{courseNo}/students/{studentId}.
  • Админ открывает тот же профиль по /admin/courses/{courseNo}/students/{studentId} или через общий маршрут с проверкой роли.
  • Профиль не показывает чужие курсы и глобальные материалы.
  • Для архивных/выпущенных студентов профиль открывается в режиме истории, если студент был в курсе; иначе — 404.

2.5. Данные и миграции

  • grade_comments добавить author_account_id uuid NULL; индекс (student_id, lab_id) уже существует.
  • Для общего комментария оценки добавить lab_grades.updated_by_account_id (автор последнего изменения) и индекс lab_grades(lab_id).
  • Все попытки уже хранятся в submissions (student_id, lab_id, seq); отдельная таблица не нужна.
  • Для производительности: один агрегирующий запрос/эндпоинт, пакетная выборка комментариев, попыток, защит и публикаций по списку lab_id; без N+1.
  • Аудит скачиваний: этап 6 (журнал audit_events или отдельная таблица).

2.6. Ограничения

  • Преподаватель видит все лабы своего курса, включая черновики, но не может видеть лабы других курсов.
  • Студент видит только опубликованные оценки и комментарии.
  • Скачивание файлов не даёт доступа к чужим сдачам.
  • Профиль не заменяет ведомость: оценки по-прежнему выставляются в ведомости, профиль — просмотр и скачивание.

3. Этапы

Этап 0. Контракты и схема

  • Обновить 07-groups-courses.md и ADR под решения раздела 1.
  • Описать API профиля студента и формат ответа.
  • Миграции identity/academic/progress.
  • Preflight: группы, не подходящие под шаблон имени; дубли номеров лаб; материалы без курса; комментарии без автора.
  • Проверить миграции на пустой БД и на копии текущей.
  • Определить механизм labs.course_id NOT NULL: обычный Flyway-старт не может дождаться bootstrap-курса. Варианты — seed курса C в миграции с временно nullable владельцем либо отдельный post-bootstrap шаг. Решение — в ADR.

Выход: схема, контракты, отчёт о рисках; код бизнес-логики ещё не пишем.

Этап 1. Identity

  • Роли ADMIN/TEACHER/STUDENT.
  • Поля групп и парсер имени.
  • Статусы студентов и аккаунтов.
  • membership_history.
  • Импорт Excel v2 с построчным отчётом.
  • Админ-UI групп и пользователей.

Этап 2. Academic

  • courses, course_teachers, course_groups.
  • course_no и URL курса.
  • labs.course_id, перенос лаб в курс C, копирование лаб.
  • chapters.course_id/audience, перенос материалов в курс C.
  • Частичные уникальные индексы slug.
  • Активы: связь с главой/статьёй и наследуемая видимость.

Этап 3. Доступ и профиль (backend)

  • TEACHER в JWT и security-конфигах.
  • Внутренние access-эндпоинты academic/identity.
  • Проверки курса, группы, студента и публикации на сервисах.
  • Профиль студента курса: агрегирующий endpoint, все попытки, комментарии, скачивание файлов.
  • Negative-тесты: чужой курс, чужой студент, чужая сдача, аноним.

Этап 4. Lifecycle

  • Повышение: dry-run, журнал, применение, откат, выпуск.
  • Академический отпуск и возврат.
  • Отчисление, выпуск, архив.
  • Усиленное удаление и аудит.

Этап 5. UX

  • Кабинет преподавателя.
  • Статус защиты в ведомости: «готова» / «кончилось время» (timeout), с разным цветом и подсказкой; сейчас в матрице всегда показывается «готова».
  • Страница профиля студента: ссылка с ФИО и отдельная кнопка.
  • Студенческие страницы курсов.
  • Админ-страницы курсов, повышения, отпусков, архива, импорта и копирования лаб.
  • Публичные глобальные материалы ALL.

Этап 6. Hardening

  • Полная security-матрица.
  • Upgrade-тест миграций.
  • Индексы и пагинация.
  • Аудит скачиваний и чувствительных действий.
  • Проверка backup: создание, retention, verify, restore на staging.
  • Документация, deploy/runbook, smoke.

4. Ориентировочные миграции

Номера уточняются при реализации, чтобы не конфликтовать с максимумами master (identity V3, academic V5, progress V12). Максимумы этой ветки: identity V9, academic V11, progress V13 (+V14 при необходимости аудита).

Сервис Миграции Содержание
identity V4–V9 роли, поля групп, статусы студентов, membership_history, promotion_runs/items, accounts.status
academic V6–V11 + post-bootstrap step курсы, преподаватели, группы курса, labs.course_id (nullable), материалы курса/аудитории, частичные индексы, активы; V10 — уникальный индекс lower(title) по курсам; V11 — backfill audience='ALL' для глобальных глав; SET NOT NULL — post-bootstrap шаг вне Flyway
progress V13 (+ V14 при необходимости аудита) grade_comments.author_account_id, lab_grades.updated_by_account_id, индекс lab_grades(lab_id); аудит скачиваний — этап 6

Переход со старой feature-миграции V10 на V13

Старая progress V10__profile_authors.sql — временная feature-миграция, её нет в master.

  • Production / master: есть только V11 и V12, записи V10 нет — V13 применяется обычным порядком, ничего делать не нужно.
  • Dev/feature-БД, где V10__profile_authors.sql уже применена и имеет success=true: удалить только эту запись истории, ничего не удаляя из схемы; V13 идемпотентна и сама создаст недостающие колонки и индекс. Используйте flyway repair или точечно: DELETE FROM progress.flyway_schema_history WHERE version = '10' AND script = 'V10__profile_authors.sql' AND success;.
  • Staging-БД, которые ненадолго запускали master-коммит bfdae41 (out-of-order
    • V10), тоже содержат запись V10 — для них действует тот же порядок.
  • Если V10 осталась с success=false: сначала убедитесь, что её частичные изменения откатились на уровне объектов, затем flyway repair или удаление неуспешной строки; V13 досоздаст недостающее.
  • Перед очисткой и деплоем проверьте состояние через deploy/preflight/progress-v10-v13.sql.
  • Переход forward-only: после V13 откат на сборку без V13 не пройдёт валидацию Flyway. Не оставляйте out-of-order включённым.
  • Нельзя переставлять V11/V12.

5. Агенты

Агент Зона Границы
lms-orchestrator контракты, ADR, merge, review не пишет массово код
lms-db миграции и тесты миграций только db/migration/** и фикстуры
lms-identity identity-service только identity и его тесты
lms-academic academic-service только academic и его тесты
lms-progress progress-service, профиль, сдачи только progress и его тесты
lms-web web, UI профиля, кабинеты только web/**
lms-security libs/security-jwt, gateway, security-review без бизнес-логики
lms-qa матрица ролей, upgrade, smoke production-код не правит
lms-docs docs, deploy/runbook только документация

6. Skills

  • lms-context — инварианты и карта модулей.
  • lms-migrations — Flyway, двухфазные миграции, preflight, legacy-БД.
  • lms-authz — ролевая матрица, internal access, negative-тесты.
  • lms-identity-domain — группы, статусы, история, импорт, повышение.
  • lms-academic-domain — курсы, лабы, материалы, аудитории, активы.
  • lms-web-ui — Thymeleaf/Bootstrap/JS, формы, навигация.
  • lms-testing — Testcontainers, MockMvc с JWT, фикстуры.
  • lms-release — lint/detekt/test, bootJar, run.sh, smoke.

7. Контроль качества

  1. Миграции на пустой БД и на копии текущей; preflight-отчёт.
  2. Security-матрица: аноним, студент свой/чужой, преподаватель свой/чужой, админ, архивный/выпущенный.
  3. Профиль: чужой курс/студент/сдача → 403/404; все попытки скачиваются; комментарии с автором; нет N+1.
  4. ./gradlew ktlintCheck detekt test после каждого этапа.
  5. UI-smoke для четырёх ролей, старые и новые URL, публичные/приватные активы.
  6. Аудит скачиваний и чувствительных действий.
  7. Backup: обязательный перед деплоем, ежедневный, с проверкой restore.
  8. Документация и runbook миграций.

8. Риски и открытые вопросы

  • Большой объём одного релиза. Снижаем контрактами, merge gate и независимым QA.
  • Межсервисная авторизация. Самый сложный участок; ADR до кода.
  • Перенос данных. Нужны preflight и копия БД; группы с нестандартными именами и дубли номеров лаб могут заблокировать NOT NULL.
  • Активы. Сейчас публичны и не связаны со статьями; нужна миграция связей.
  • JWT 30 дней. Средний вариант блокировки оставляет остаточный риск для прямого API; фиксируем в документации.
  • Комментарии без автора. Старые записи останутся с «автор не указан».
  • Архивные студенты. Открывать ли профиль после отвязки группы — уточнить на этапе 3; по умолчанию история курса доступна, если студент был в курсе.

9. Статус реализации

  • Этап 0 — выполнен. Схема, contract API профиля, preflight, ADR, проверка на копии БД. Отчёт: 10-stage0-report.md.
  • Этап 1 — выполнен. Роли, группы-когорты, статусы, история, импорт v2, админ-UI. Отчёт: 11-stage1-report.md.
  • Этап 2 — выполнен. Курсы и course_no, история преподавателей и групп, bootstrap курса C, привязка лабораторных к курсу, копирование лаб, админ-UI курсов и лабораторных. Отчёт: 12-stage2-report.md.
  • Этап 3 — выполнен. Аккаунты преподавателей, курсовая область видимости, внутренние API identity с LMS_INTERNAL_TOKEN, кабинет и состав групп преподавателя, профиль студента курса и скачивание всех попыток. Отчёт: 13-stage3-report.md.
  • Этап 4 — выполнен. Повышение (dry-run/apply/rollback, планировщик 31 августа МСК и догон), жизненный цикл студента, архив группы, усиленное подтверждение удаления, блокировка входа для архивных и выпущенных групп. Отчёт: 14-stage4-report.md.
  • Этап 5 — выполнен. Студенческие страницы курсов, материалы по /learn/course/{course_no}/... и аудитории, публичные материалы ALL, скрытые материалы для преподавателя, перепроверка статуса web-сессии. Отчёт: 15-stage5-course-materials.md.
  • Этап 6 — проверка выполнена, закрыт не полностью. Проверены полный прогон тестов/линтеров, upgrade миграций с master-схемы, security-матрица, gateway и deploy-runbook. Отчёт: 16-stage6-report.md. Не закрыты: права преподавателя на запись, видимость активов, аудит скачиваний и CI. Остальные пункты hardening (индексы, backup/restore, документация, smoke) описаны в отчёте и чек-листе.

9.1. Известные пробелы после этапа 6

  • Активы публичны. /api/v1/content/assets/{name} не проверяет курс и статью; нужна проверка article_assets → article → section → chapter и visibility-aware маршруты.
  • Преподаватель — в основном read-only. Авторство лабораторных/материалов, распределение, оценки, комментарии и защита остаются admin-only в UI/API; ТЗ ожидает преподавательский self-service.
  • Имена групп в кабинете преподавателя. На странице курса показывается UUID группы, batch-разрешения имён нет.
  • Аудит. Есть история членства и журнал повышения, но нет audit-log скачиваний, просмотров профилей, назначений и публикаций.
  • Импорт. Старый формат не поддерживается; ведущие нули номера группы нормализуются (01 → 1).
  • JWT 30 дней. Web перепроверяет статус каждые 45 секунд, но прямой API с уже выданным токеном живёт до истечения.
  • CI отсутствует. Гейт — локальный ./gradlew test ktlintCheck detekt; автоматического pipeline и upgrade-теста в CI нет.

10. Definition of Done

  • Все решения раздела 1 реализованы.
  • Текущие лабы и материалы работают в курсе C.
  • Преподаватель видит только свои курсы и профили своих студентов.
  • Профиль показывает статистику, комментарии и все попытки; файлы скачиваются.
  • Повышение, академотпуск, выпуск и архив работают по ТЗ.
  • Security-матрица и миграционный upgrade-тест зелёные.
  • Backup перед деплоем обязателен; restore проверен на staging.
  • Документация, deploy/runbook и smoke обновлены.