Files
magistr/docs/DEVELOPMENT.md

8.5 KiB
Raw Permalink Blame History

🛠 Руководство для разработчиков

Локальный запуск

Предварительные требования

  • Docker и Docker Compose
  • Git
  • (Опционально) Java 17 + Maven 3.9+ для запуска backend вне Docker

Первый запуск

# Создать Docker-сеть
docker network create proxy

# Собрать и запустить
docker compose up -d --build

# Убедиться, что всё работает
docker compose logs -f

Приложение доступно: http://localhost:80

Пересборка после изменений

# Пересобрать только backend
docker compose up -d --build backend

# Пересобрать только frontend
docker compose up -d --build frontend

Полный сброс данных

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:

private static final Logger logger = LoggerFactory.getLogger(MyController.class);

// Информационные сообщения
logger.info("Запрос на получение всех занятий");

// Ошибки с полным стектрейсом
logger.error("Ошибка при сохранении: {}", e.getMessage(), e);

Валидация

  • Для сложных правил — отдельные классы-валидаторы (DayAndWeekValidator, TypeAndFormatLessonValidator)
  • Для простых — inline-проверки в контроллере с ResponseEntity.badRequest()

Импорты

// 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';

Лучшие практики

// ✅ Предпочитайте 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:

-- 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-сущность:

@Entity
@Table(name = "absences")
public class Absence {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // ...
}

2. Репозиторий

public interface AbsenceRepository extends JpaRepository<Absence, Long> {
    List<Absence> findByTeacherId(Long teacherId);
}

3. DTO (опционально)

public record AbsenceResponse(Long id, String teacherName, String reason) {}

4. Контроллер

@RestController
@RequestMapping("/api/absences")
public class AbsenceController {
    private final AbsenceRepository absenceRepository;

    public AbsenceController(AbsenceRepository absenceRepository) {
        this.absenceRepository = absenceRepository;
    }

    @GetMapping
    public List<Absence> getAll() {
        return absenceRepository.findAll();
    }
}

Работа с миграциями Flyway

Правила

  1. Никогда не изменяйте уже закоммиченные файлы миграций
  2. Имя файла: V{номер}__{описание}.sql (два подчёркивания!)
  3. Нумерация строго инкрементальная: V1, V2, V3, ...
  4. После добавления — перезапустите backend для применения

Применение

# Локально — сброс и повтор всех миграций
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