163 lines
12 KiB
Markdown
163 lines
12 KiB
Markdown
# LMS
|
||
|
||
Система для проведения практических занятий на основе микросервисной архитектуры (Kotlin + Spring Boot + PostgreSQL 18).
|
||
|
||
## Документация
|
||
|
||
UML- и архитектурные диаграммы (Mermaid) — в каталоге [`docs/`](docs/README.md):
|
||
|
||
- [Архитектура](docs/01-architecture.md) — контекст, компоненты, модули, аутентификация;
|
||
- [Развёртывание](docs/02-deployment.md) — Docker, nginx, тома, обновление;
|
||
- [Модель данных и классов](docs/03-domain-model.md) — диаграммы классов по сервисам и ER-модель;
|
||
- [Диаграммы последовательностей](docs/04-sequences.md) — вход, импорт ЛР, раздача вариантов, сдача, защита, оценки, материалы;
|
||
- [Диаграммы состояний](docs/05-states.md) — защита, публикация оценок и материалов, сдача, пароль.
|
||
|
||
## Модули
|
||
|
||
- `libs/common-http` — общий формат ошибок и обработчик исключений.
|
||
- `libs/security-jwt` — выпуск/проверка JWT (RSA) и `PasswordEncoder`.
|
||
- `libs/markdown` — серверный рендер markdown со спойлерами и санитизацией.
|
||
- `libs/test-support` — общая тестовая инфраструктура (Testcontainers, `postgres:18`).
|
||
- `services/identity-service` — аккаунты, студенты, группы, зачисления (схема `identity`).
|
||
- `services/academic-service` — курсы, статьи, лабораторные, уровни, варианты (схема `academic`).
|
||
- `services/progress-service` — уровни студентов, распределение вариантов, работы, оценки (схема `progress`).
|
||
- `services/gateway` — Spring Cloud Gateway, маршрутизация и проверка JWT.
|
||
- `web` — серверный веб-интерфейс (Thymeleaf + htmx), BFF.
|
||
|
||
## Требования
|
||
|
||
- JDK 21 (runtime) + Docker (для PostgreSQL 18). Gradle сам скачает toolchain 21 для сборки.
|
||
- **Не запускать сервисы на JAVA_HOME=Java 25**: на 25 ломается logback внутри
|
||
fat-jar (NoClassDefFoundError `ThrowableProxy`), что роняет потоки Tomcat.
|
||
Скрипт `run.sh` использует JDK 21 автоматически.
|
||
|
||
## Запуск
|
||
|
||
```bash
|
||
./gradlew bootJar -x test # собрать все сервисы
|
||
./run.sh start # поднять БД + все 5 сервисов на JDK 21
|
||
./run.sh status # проверить здоровье
|
||
./run.sh logs web # посмотреть лог сервиса
|
||
./run.sh stop | restart
|
||
```
|
||
|
||
Можно и без скрипта: `docker compose up -d postgres`, затем
|
||
`/usr/lib/jvm/java-21-openjdk/bin/java -jar <jar>` для каждого сервиса,
|
||
или `./gradlew :services:identity-service:bootRun` и т.д. по одному.
|
||
|
||
Порты по умолчанию: gateway `:8080`, identity `:8081`, academic `:8082`,
|
||
progress `:8083`, web `:8090`. Интерфейс: `http://localhost:8090`.
|
||
|
||
## Качество кода и тесты
|
||
|
||
```bash
|
||
./gradlew ktlintCheck detekt test # линтеры (строгий режим) + тесты
|
||
./gradlew ktlintFormat # автоформат Kotlin
|
||
```
|
||
|
||
- `ktlint` отвечает за формат, `detekt` — за качество (сложность, вложенность, ошибки).
|
||
Оба подключены во всех Kotlin-модулях конвенцией `lms.lint` и **ломают сборку** при замечаниях.
|
||
- Конфигурация: `.editorconfig`, `config/detekt/detekt.yml`.
|
||
- Gradle daemon фиксируется на JDK 21 (`gradle/gradle-daemon-jvm.properties`): detekt 1.23
|
||
не работает на JDK 25.
|
||
- Тестовая инфраструктура — `libs:test-support` (JUnit 5, MockK, Testcontainers `postgres:18`).
|
||
|
||
## Запуск в Docker (для сервера)
|
||
|
||
На сервере Java не нужна: всё собирается и работает в контейнерах (multi-stage
|
||
`Dockerfile`, сборка на JDK 21, запуск на JRE 21).
|
||
|
||
```bash
|
||
cp .env.prod.example .env.prod # заполнить LMS_DB_PASSWORD
|
||
./deploy/deploy.sh up # backup + сборка + запуск (postgres, 5 сервисов)
|
||
./deploy/deploy.sh logs web
|
||
./deploy/deploy.sh ps
|
||
./deploy/deploy.sh down
|
||
```
|
||
|
||
Перед обновлением `deploy.sh up` обязательно делает backup БД и файлов.
|
||
Ежедневное расписание, retention и восстановление — в [`deploy/BACKUP.md`](deploy/BACKUP.md).
|
||
|
||
Наружу публикуется только web на `127.0.0.1:8090`; reverse proxy — nginx
|
||
(конфиг и полная инструкция по деплою на `lms.our-place.ru` в
|
||
[`deploy/DEPLOY.md`](deploy/DEPLOY.md) и `deploy/nginx/lms.our-place.ru.conf`).
|
||
|
||
## Наполнение данными
|
||
|
||
Порядок ввода данных (всё через админку): `/setup` → администратор; импорт групп
|
||
(`.xls`, логин = студенческий номер, временный пароль со сменой при первом входе);
|
||
импорт лабораторной ZIP'ом (`lab.yaml` + `README.md` + `variants/*.md`); «Распределить»
|
||
— случайная раздача по кругам в рамках уровня; оценки/защита — на странице группы;
|
||
учебные материалы — статьи или ZIP (`chapters/…`, доступны гостям).
|
||
|
||
Подробный пошаговый порядок — в [`deploy/IMPORT.md`](deploy/IMPORT.md).
|
||
|
||
## Текущий функционал
|
||
|
||
Открыть `http://localhost:8090`. При первом запуске создаётся аккаунт
|
||
администратора (`/setup`); далее — вход по логину и паролю.
|
||
|
||
- **Доступ**: JWT (RSA, ключи identity генерирует при первом старте и хранит в БД),
|
||
проверка токена на gateway, роли ADMIN/STUDENT, CSRF на всех POST-формах,
|
||
session fixation-защита; вход студента с обязательной сменой пароля.
|
||
- **Группы и студенты**: создание групп, импорт `.xls/.xlsx/.csv`, ручное
|
||
добавление студента (генерация пароля) и «Добавить существующего» (перевод из
|
||
другой группы), «Отчислить из группы», выгрузка логинов XLSX/CSV, сброс пароля.
|
||
- **Лабораторные**: импорт ZIP (`lab.yaml` + `README.md` + `variants/*.md` +
|
||
`assets/`); создание через UI с произвольным числом уровней; единая форма
|
||
настроек с одной кнопкой «Сохранить всё»; статусы draft/published
|
||
(клик по бейджу меняет); даты в московском времени.
|
||
- **Распределение вариантов** (только на странице лабы): «Распределить» с
|
||
выбором групп; раздача случайная и равномерная — студенты выбранных групп
|
||
идут общей очередью, вариант берётся со своего уровня, пока в уровне есть
|
||
невыданные — без повторов, затем выдаются наименее занятые; ручные
|
||
закрепления сохраняются всегда. На странице лабы — список назначений по
|
||
группам (уровень/вариант каждому), массовые операции чекбоксами, индикаторы
|
||
«выдано/всего». Матрица на странице группы — просмотр (вариант, сдача) +
|
||
оценки/защита, управление раздачей оттуда убрано.
|
||
- **Индивидуальные задания (защита)**: на странице группы — назначение/изменение
|
||
задания, импорт CSV (2 или 3 колонки, с кавычками и `\n`), выдача кода,
|
||
«доп. задание: начать заново» после завершения защиты.
|
||
- **Материалы** (`/learn`, публичная читалка): главы → разделы → статьи в
|
||
markdown (код, спойлеры, картинки; санитизация OWASP); импорт ZIP
|
||
(`chapters/N-глава/N-раздел/N-статья.md` + `assets/`); создание/переименование/
|
||
удаление глав, разделов, статей; перенос статей между разделами.
|
||
- **Студент**: дашборд успеваемости (сдано, средний балл, лучшая/слабая оценка),
|
||
карточки опубликованных лаб, страница лабы: условие, свой вариант, сдача
|
||
файлов (zip/.c, переотправка до дедлайна, история, скачивание последней),
|
||
оценка и комментарии отдельным блоком, защита по одноразовому коду (30 мин).
|
||
- **Публикация оценок**: в группе по каждой лабе — кнопка «опубликовать»; студент
|
||
видит оценку и комментарии только после публикации (публикуется вся группа).
|
||
- **Комментарии к оценкам**: общий (итоговый) комментарий по лабораторной + комментарии
|
||
с тональностью (positive/negative/neutral); на странице студента — ненумерованными списками
|
||
в трёх колонках, хранятся в БД, автоподсказки при вводе ранжируются по частоте использования.
|
||
- **Архив сдач группы**: одна кнопка «Скачать все работы» — ZIP со всеми
|
||
последними решениями группы, разложенными по лабам и студентам.
|
||
- **Материалы** `/learn` доступны без входа; аккордеон глав: свёрнуты все,
|
||
открыта последняя.
|
||
- UI: светлая/тёмная тема (сохраняется в localStorage), модальные окна вместо
|
||
отдельных страниц, аккордеоны глав/разделов в `/learn`, фавиконка,
|
||
адаптивная вёрстка без inline-стилей.
|
||
|
||
## Примечания
|
||
|
||
- **JDK 21 обязателен для runtime**. В связке Spring Boot 4.0.x + fat-jar logback
|
||
не находит `ThrowableProxy`, и это падает при любом исключении в потоке ответа.
|
||
Если после деплоя сервис вдруг «висит» на страницах (а `/actuator/health` даёт 200) —
|
||
лечится перезапуском (`./run.sh restart`).
|
||
- Даты хранятся в UTC (instant), а в UI конвертируются в Europe/Moscow
|
||
(`DateUtils`); формы datetime-local отправляются в московском времени.
|
||
- Капча/rate-limit на входе отсутствует — для учебного стендома оставлено;
|
||
пароли студентов показываются один раз при выдаче.
|
||
- `lms.submissions.dir` (default ./data/submissions) и `lms.content.assets-dir`
|
||
держите на постоянном томе при деплое.
|
||
|
||
## Сборка и тесты
|
||
|
||
```bash
|
||
./gradlew build # сборка всех модулей
|
||
./gradlew check # тесты и проверки
|
||
```
|
||
|
||
Переменные окружения для БД: `LMS_DB_URL`, `LMS_DB_USER`, `LMS_DB_PASSWORD`.
|