# 🛠 Руководство для разработчиков ## Локальный запуск ### Предварительные требования - Docker и Docker Compose - Git - (Опционально) Java 17 + Maven 3.9+ для запуска backend вне Docker ### Первый запуск ```bash # Создать Docker-сеть docker network create proxy # Собрать и запустить docker compose up -d --build # Убедиться, что всё работает docker compose logs -f ``` Приложение доступно: **http://localhost:80** ### Пересборка после изменений ```bash # Пересобрать только backend docker compose up -d --build backend # Пересобрать только frontend docker compose up -d --build frontend ``` ### Полный сброс данных ```bash docker compose down -v # Удаляет БД docker compose up -d # Пересоздаёт с нуля ``` --- ## Соглашения о коде ### Java (Backend) #### Именование | Категория | Стиль | Пример | |-----------|-------|--------| | Классы | PascalCase | `LessonsController`, `LessonResponse` | | Методы и переменные | camelCase | `getAllLessons()`, `teacherId` | | Константы | UPPER_SNAKE_CASE | `ROLE_REDIRECTS` | | Пакеты | lowercase | `com.magistr.app.controller` | #### Архитектурные правила - **Constructor Injection** — все зависимости через конструктор (не `@Autowired` на поля) - **Controller → Repository** — контроллеры работают напрямую с репозиториями (без слоя service) - **Префикс `/api/`** — все REST-эндпоинты - **`ResponseEntity`** — все мутирующие методы возвращают `ResponseEntity` с HTTP-статусом - **Сообщения на русском** — все ошибки и уведомления на русском языке #### Логирование Используйте SLF4J: ```java private static final Logger logger = LoggerFactory.getLogger(MyController.class); // Информационные сообщения logger.info("Запрос на получение всех занятий"); // Ошибки с полным стектрейсом logger.error("Ошибка при сохранении: {}", e.getMessage(), e); ``` #### Валидация - Для сложных правил — отдельные классы-валидаторы (`DayAndWeekValidator`, `TypeAndFormatLessonValidator`) - Для простых — inline-проверки в контроллере с `ResponseEntity.badRequest()` #### Импорты ```java // 1. Static imports import static org.junit.Assert.*; // 2. Java/Jakarta import java.util.*; import jakarta.persistence.*; // 3. External libraries import org.springframework.web.bind.annotation.*; import com.fasterxml.jackson.databind.ObjectMapper; // 4. Internal packages (wildcard для того же модуля) import com.magistr.app.model.*; import com.magistr.app.repository.*; ``` #### Форматирование - **Отступы:** 4 пробела - **Скобки:** K&R style (открывающая на той же строке) - **Длина строки:** до 120 символов - **Фигурные скобки** обязательны для `if`/`for`/`while` ### JavaScript (Frontend) #### Именование | Категория | Стиль | Пример | |-----------|-------|--------| | Файлы | kebab-case | `main.js`, `schedule-view.js` | | Функции и переменные | camelCase | `loadUsers()`, `pageTitle` | | Константы | UPPER_SNAKE_CASE | `API_BASE_URL` | #### Модули - ES6 Modules с `import`/`export` - **Всегда указывать расширение:** `import { api } from './api.js';` #### Лучшие практики ```javascript // ✅ Предпочитайте const const token = localStorage.getItem('token'); // ✅ Async/await вместо .then() async function loadData() { try { const data = await api.get('/api/users'); } catch (e) { console.error('Ошибка:', e.message); } } // ✅ Template literals const msg = `Найдено ${items.length} записей`; // ✅ Деструктуризация const { id, name, role } = user; ``` #### Форматирование - **Отступы:** 4 пробела - **Кавычки:** одинарные `'` - **Точки с запятой:** обязательны --- ## Создание нового эндпоинта (пошагово) ### 1. Модель (если нужна новая таблица) Создайте Flyway миграцию `V{N}__{description}.sql`: ```sql -- backend/src/main/resources/db/migration/V3__add_absences.sql CREATE TABLE IF NOT EXISTS absences ( id BIGSERIAL PRIMARY KEY, teacher_id BIGINT NOT NULL REFERENCES users(id), reason VARCHAR(255) NOT NULL, start_date DATE NOT NULL, end_date DATE NOT NULL ); ``` Создайте JPA-сущность: ```java @Entity @Table(name = "absences") public class Absence { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // ... } ``` ### 2. Репозиторий ```java public interface AbsenceRepository extends JpaRepository { List findByTeacherId(Long teacherId); } ``` ### 3. DTO (опционально) ```java public record AbsenceResponse(Long id, String teacherName, String reason) {} ``` ### 4. Контроллер ```java @RestController @RequestMapping("/api/absences") public class AbsenceController { private final AbsenceRepository absenceRepository; public AbsenceController(AbsenceRepository absenceRepository) { this.absenceRepository = absenceRepository; } @GetMapping public List getAll() { return absenceRepository.findAll(); } } ``` --- ## Работа с миграциями Flyway ### Правила 1. **Никогда** не изменяйте уже закоммиченные файлы миграций 2. Имя файла: `V{номер}__{описание}.sql` (два подчёркивания!) 3. Нумерация строго инкрементальная: `V1`, `V2`, `V3`, ... 4. После добавления — перезапустите backend для применения ### Применение ```bash # Локально — сброс и повтор всех миграций docker compose down -v && docker compose up -d # Продакшн — применить к существующим тенантам kubectl rollout restart deployment backend -n magistr ``` --- ## Структура пакетов (Backend) ``` com.magistr.app/ ├── Application.java # Точка входа ├── config/ │ ├── AppConfig.java # Бины (BCryptPasswordEncoder) │ ├── DataInitializer.java # Инициализация данных │ └── tenant/ # Мультитенантность │ ├── TenantConfig.java # POJO конфигурации тенанта │ ├── TenantContext.java # ThreadLocal текущего тенанта │ ├── TenantInterceptor.java # Определение тенанта из Host │ ├── TenantRoutingDataSource.java # Маршрутизация к БД │ ├── TenantDataSourceConfig.java # Spring-конфигурация │ ├── TenantConfigWatcher.java # Периодическая синхронизация │ └── ConfigMapUpdater.java # Обновление K8s ConfigMap ├── controller/ # REST-контроллеры │ ├── AuthController.java │ ├── LessonsController.java │ ├── ClassroomController.java │ ├── DatabaseController.java │ ├── UserController.java │ ├── GroupController.java │ ├── SubjectController.java │ ├── EquipmentController.java │ ├── EducationFormController.java │ └── TeacherSubjectController.java ├── dto/ # Data Transfer Objects ├── model/ # JPA-сущности ├── repository/ # Spring Data JPA └── utils/ # Валидаторы ├── DayAndWeekValidator.java └── TypeAndFormatLessonValidator.java ```