docs: план, ADR и контракты групп и курсов
This commit is contained in:
292
docs/08-groups-courses-plan.md
Normal file
292
docs/08-groups-courses-plan.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# 08. План реализации: группы, курсы, преподаватели
|
||||
|
||||
**Статус:** план согласован, реализация по этапам 0–6, один релиз.
|
||||
**Основа:** [07-groups-courses.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. Ориентировочные миграции
|
||||
|
||||
Номера уточняются при реализации, чтобы не конфликтовать с текущими максимумами
|
||||
(identity V3, academic V5, progress V9).
|
||||
|
||||
| Сервис | Миграции | Содержание |
|
||||
|---|---|---|
|
||||
| identity | V4–V9 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status` |
|
||||
| academic | V6–V9 + post-bootstrap V10 | курсы, преподаватели, группы курса, `labs.course_id` (nullable), материалы курса/аудитории, частичные индексы, активы; `SET NOT NULL` — отдельный шаг после bootstrap |
|
||||
| progress | V10 (+ V11 при необходимости аудита) | `grade_comments.author_account_id`, `lab_grades.updated_by_account_id`, индекс `lab_grades(lab_id)`; аудит скачиваний — этап 6 |
|
||||
|
||||
---
|
||||
|
||||
## 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](10-stage0-report.md).
|
||||
- **Этап 1 — выполнен.** Роли, группы-когорты, статусы, история, импорт v2,
|
||||
админ-UI. Отчёт: [11-stage1-report.md](11-stage1-report.md).
|
||||
- **Этап 2 — следующий.** Курсы, преподаватели, привязка групп, лабораторные
|
||||
и материалы курса C, активы, копирование лаб.
|
||||
- Этапы 3–6 — по плану.
|
||||
|
||||
## 10. Definition of Done
|
||||
|
||||
- Все решения раздела 1 реализованы.
|
||||
- Текущие лабы и материалы работают в курсе C.
|
||||
- Преподаватель видит только свои курсы и профили своих студентов.
|
||||
- Профиль показывает статистику, комментарии и все попытки; файлы скачиваются.
|
||||
- Повышение, академотпуск, выпуск и архив работают по ТЗ.
|
||||
- Security-матрица и миграционный upgrade-тест зелёные.
|
||||
- Backup перед деплоем обязателен; restore проверен на staging.
|
||||
- Документация, deploy/runbook и smoke обновлены.
|
||||
Reference in New Issue
Block a user