diff --git a/docs/07-groups-courses.md b/docs/07-groups-courses.md index 069e59e..a691a45 100644 --- a/docs/07-groups-courses.md +++ b/docs/07-groups-courses.md @@ -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 после переноса данных, когда все diff --git a/docs/08-groups-courses-plan.md b/docs/08-groups-courses-plan.md index b3fd3eb..c3ba65f 100644 --- a/docs/08-groups-courses-plan.md +++ b/docs/08-groups-courses-plan.md @@ -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 diff --git a/docs/10-stage0-report.md b/docs/10-stage0-report.md index 6eeaa2b..b36e625 100644 --- a/docs/10-stage0-report.md +++ b/docs/10-stage0-report.md @@ -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` — до миграций; diff --git a/docs/12-stage2-report.md b/docs/12-stage2-report.md index f4cac54..3be4dee 100644 --- a/docs/12-stage2-report.md +++ b/docs/12-stage2-report.md @@ -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 diff --git a/docs/15-stage5-course-materials.md b/docs/15-stage5-course-materials.md new file mode 100644 index 0000000..fd9d70b --- /dev/null +++ b/docs/15-stage5-course-materials.md @@ -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 это осознанно не реализуется, чтобы не ломать существующие +публичные ссылки на изображения.