Многопоточный Spring Boot сервис для управления документами с поддержкой workflow (Draft → Submitted → Approved) и фоновыми воркерами.
- Java 21+ (
java -version) - Docker (для PostgreSQL)
- Gradle 8.x
docker-compose upPostgreSQL будет доступен на порту 5432:
- Database:
documents - Username:
postgres - Password:
123
Через Gradle:
# Запуск document-service (порт 80)
gradle documentServiceRun
# Запуск генератора тестовых данных
gradle generatorRunЧерез IntelliJ IDEA:
- Gradle →
document-root→Tasks→application→documentServiceRun
curl -X POST http://localhost:80/api/v1/documents \
-H "Content-Type: application/json" \
-d '{"author": "Иванов И.И.", "title": "Тестовый документ"}'document/
├── document-service/ # Основной REST API сервис
│ ├── controller/ # REST endpoints
│ ├── service/ # Бизнес-логика
│ ├── dao/ # Data Access Layer (JPA + native queries)
│ ├── entity/ # JPA сущности
│ ├── dto/ # DTO records
│ ├── mapper/ # MapStruct мапперы
│ ├── worker/ # Фоновые воркеры (Submit/Approve)
│ ├── config/ # Конфигурация (Scheduler, Async)
│ └── aop/ # AOP логирование
├── generator/ # Утилита генерации тестовых данных
└── docker-compose.yml # PostgreSQL контейнер
| Компонент | Версия |
|---|---|
| Java | 21 LTS (виртуальные потоки) |
| Spring Boot | 4.0.2 |
| Spring Data JPA | ✓ |
| Hibernate | 7.x |
| PostgreSQL | 17 |
| Liquibase | ✓ |
| Lombok | ✓ |
| MapStruct | 1.5.5.Final |
| Gradle | 8.x |
Base URL: http://localhost:80/api/v1
| Метод | Endpoint | Описание |
|---|---|---|
| POST | /documents |
Создание документа |
| GET | /documents/{id} |
Получить документ с историей |
| GET | /documents |
Список с пагинацией и фильтрацией по IDs |
| GET | /documents/search |
Поиск по статусу, автору, дате |
| POST | /documents/submit |
Пакетная отправка на submit |
| POST | /documents/approve |
Пакетное одобрение |
| POST | /documents/approve/concurrent-spam |
Стресс-тест параллельного approve |
| POST | /documents/create/batch |
Пакетное создание документов |
Создание документа:
POST /documents
{
"author": "Иванов И.И.",
"title": "Договор поставки"
}Поиск документов:
GET /documents/search?status=APPROVED&author=GENERATOR&dateFrom=2025-01-01&dateTo=2026-12-31&page=0&size=20&sort=created_at,desc
Пакетный submit:
POST /documents/submit
{
"ids": [1, 2, 3],
"comment": "На согласование",
"initiator": "USER123"
}Полный цикл workflow через Postman:
- Импортируйте
POSTMAN_COLLECTION.json - Создайте документ → "Новый документ"
- Установите статус DRAFT в БД:
UPDATE document SET status = 'DRAFT' WHERE id = 1; - Отправьте на submit → "сабмит"
- Одобрите → "апрув"
- Проверьте историю → "Посмотреть по айди"
document — основная таблица:
id— первичный ключnumber— уникальный номер (sequence starting from 777)author,title— метаданныеstatus— DRAFT/SUBMITTED/APPROVEDcreated_at,modified_at— временные метки
document_history — аудит операций:
document_id— FK на documentoperation— CREATE/SUBMIT/APPROVEcreated_by,comment— кто и зачем
approve_registry — реестр одобрений (уникальный constraint на document_id)
-- Уникальность по автору+названию
CREATE UNIQUE INDEX idx_document_author_title ON document (author, title);
-- Для поиска по дате
CREATE INDEX idx_document_created_at ON document (created_at);
-- Композитный индекс для search endpoint
CREATE INDEX idx_document_status_author_created_at
ON document (status, author, created_at DESC);Воркеры работают по cron и автоматически обрабатывают документы:
| Воркер | Cron | Описание |
|---|---|---|
| SubmitWorker | */50 * * * * * |
Каждые 50 сек: DRAFT → SUBMITTED |
| ApproveWorker | */58 * * * * * |
Каждые 58 сек: SUBMITTED → APPROVED |
Отключение воркеров для тестирования:
worker:
submit:
cron: "-"
approve:
cron: "-"gradle testВсе тесты используют H2 базу данных в памяти и Liquibase для инициализации схемы.
Импортируйте POSTMAN_COLLECTION.json для удобного тестирования API.
- Файл логов:
spam_logsв корне проекта - Формат:
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n - Ротация: gzip архивы с датой (spam_logs.2026-02-23.0.gz)
server:
port: 80
context-path: /api/v1
spring:
threads:
virtual:
enabled: true # Виртуальные потоки Java 21
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
jpa:
properties:
hibernate:
jdbc.batch_size: 100 # Оптимизация batch операций
worker:
batch-size: 50
submit:
cron: "*/50 * * * * *"
approve:
cron: "*/58 * * * * *"generator:
count: 100 # Количество документов
batch-size: 10 # Размер пакета
initiator: GENERATOR
document-service:
url: http://localhost:80/api/v1- Виртуальные потоки Java 21 — для параллельной обработки
- HikariCP pool — max 20, min idle 5
- JDBC batch size — 100 записей
- Композитные индексы — для search endpoint
- FOR UPDATE SKIP LOCKED — для конкурентной обработки воркерами
- Создать DTO record в
dto/ - Добавить метод в
DocumentServiceинтерфейс - Реализовать в
DocumentServiceImpl - Добавить endpoint в
DocumentController - Написать тесты в
DocumentBaseTests
Файлы в document-service/src/main/resources/liquibase/:
001-init_db.sql— схема002-fill_data.sql— тестовые данныеmaster.xml— master changelog
-- Установить статус DRAFT для ручного тестирования
UPDATE document SET status = 'DRAFT' WHERE id = 1;# Проверить статус контейнера
docker ps | grep document-postgres
# Перезапустить
docker-compose down && docker-compose upjava -version # Должна быть 21+
echo $JAVA_HOME- EXPLAIN.md — оптимизация SQL запросов, планы выполнения
- POSTMAN_COLLECTION.json — коллекция Postman