8.5 KiB
8.5 KiB
🛠 Руководство для разработчиков
Локальный запуск
Предварительные требования
- 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
Правила
- Никогда не изменяйте уже закоммиченные файлы миграций
- Имя файла:
V{номер}__{описание}.sql(два подчёркивания!) - Нумерация строго инкрементальная:
V1,V2,V3, ... - После добавления — перезапустите 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