docs: этап 5 — курсовые материалы

This commit is contained in:
2026-10-27 19:01:37 +03:00
parent 0b5d5d0ca5
commit 60a318539c
5 changed files with 52 additions and 3 deletions

View File

@@ -280,6 +280,15 @@ CREATE UNIQUE INDEX courses_title_lower_uidx ON courses (lower(title));
Сервис дополнительно проверяет название без учёта регистра и возвращает `409`
на дубликат; индекс закрывает гонку между проверкой и вставкой.
**V11 — аудитория глобальных глав (реальный файл `V11__global_audience_all.sql`)**
```sql
-- Глобальные главы доступны публично; курсовые остаются с audience = NULL.
UPDATE academic.chapters SET audience = 'ALL' WHERE course_id IS NULL AND audience IS NULL;
```
Backfill нужен для глав, созданных до появления правила: публичное дерево
показывает только глобальные главы с `audience = 'ALL'`. Новые глобальные главы
создаются и импортируются сразу с `audience = 'ALL'`.
**Post-bootstrap шаг academic (вне Flyway) — курс C: завершение привязки лаб**
```sql
-- Выполняется сервисом CourseBootstrapService после переноса данных, когда все

View File

@@ -202,12 +202,12 @@
Номера уточняются при реализации, чтобы не конфликтовать с максимумами master
(identity V3, **academic V5**, progress V12). Максимумы этой ветки:
identity V9, **academic V10**, progress V13 (+V14 при необходимости аудита).
identity V9, **academic V11**, progress V13 (+V14 при необходимости аудита).
| Сервис | Миграции | Содержание |
|---|---|---|
| identity | V4–V9 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status` |
| academic | V6–V10 + post-bootstrap step | курсы, преподаватели, группы курса, `labs.course_id` (nullable), материалы курса/аудитории, частичные индексы, активы; `V10` — уникальный индекс `lower(title)` по курсам; `SET NOT NULL` — post-bootstrap шаг вне Flyway |
| 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

View File

@@ -11,9 +11,10 @@
- Миграции:
- identity `V4–V9`: роли, группы-когорты, статусы студентов, повышение,
история членства, статусы аккаунтов;
- academic `V6–V10`: курсы, преподаватели, группы курса, `labs.course_id`,
- academic `V6–V11`: курсы, преподаватели, группы курса, `labs.course_id`,
материалы курса/аудитории, индексы активов; `V10` (уникальный индекс
`lower(title)` по курсам) добавлена на этапе 2, после этого отчёта;
`V11` backfill-ом проставляет `audience='ALL'` глобальным главам;
- progress `V13`: автор комментария и последнего изменения оценки.
- Preflight-скрипты:
- `deploy/preflight/groups-courses.sql` — до миграций;

View File

@@ -73,6 +73,7 @@
- `V8__chapters_course.sql` — `chapters.course_id/audience`, частичные индексы slug.
- `V9__article_assets_indexes.sql` — индекс `article_assets(article_id)`.
- `V10__course_title_unique.sql` — уникальный индекс `lower(title)` по курсам.
- `V11__global_audience_all.sql` — backfill `audience='ALL'` для глобальных глав.
## 2. API

View File

@@ -0,0 +1,38 @@
# Этап 5. Материалы курса
**Статус:** реализовано в academic-service и web (без изменения identity/progress).
## Что изменилось
- Публичные `/api/v1/content/tree` и `/api/v1/content/{chapter}/{section}/{article}`
отдают только **глобальные** главы (`course_id IS NULL`) с аудиторией `ALL`.
Курсовые главы через публичный контракт больше не доступны.
- Материалы курса открываются по `/api/v1/courses/{courseId}/content/...` и требуют
аутентификации. Доступ проверяется через `CourseAccessService`:
админ — любой курс, преподаватель — только назначенный, студент — только курс
активной группы.
- Администратор и преподаватель курса видят скрытые главы/разделы/статьи;
студент — только опубликованные.
- В браузере материалы курса открываются по `/learn/course/{course_no}` и
`/learn/course/{course_no}/{chapter}/{section}/{article}`. Анонимный пользователь
перенаправляется на `/login`, чужой курс даёт `403`.
- Ссылки на материалы курса добавлены в кабинеты студента (карточки лабораторных),
преподавателя и администратора.
- Админский просмотр и авторство глобальных материалов (`treeAll`, `/api/v1/content/all/...`,
`/learn` для админа) сохранены.
- Новые глобальные главы создаются и импортируются с `audience = "ALL"`.
Миграция `V11__global_audience_all.sql` backfill-ом проставляет `ALL` старым
глобальным главам; курсовые главы остаются с `audience = NULL`.
## Остающийся разрыв: активы
Публичный прокси активов `/api/v1/content/assets/{name}` и прокси web
`/api/v1/content/assets/{name}` **не изменялись**. Активы по-прежнему не привязаны
к главе/статье и не наследуют курсовую видимость. Для этапа 6 нужно:
- связать `article_assets` со статьёй/главой и проверять доступ по токену;
- отдавать картинки курсовых статей только авторизованным пользователям курса;
- предусмотреть отдельный безопасный путь для публичных `ALL`-материалов.
В рамках этапа 5 это осознанно не реализуется, чтобы не ломать существующие
публичные ссылки на изображения.