Skip to content

About

Enterprise HelpDesk ticket management platform — Spring Boot microservices + React 19 frontend

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

HelpDesk — Enterprise Ticket Management Platform

Production-ready микросервисная платформа для управления заявками сотрудников: регистрация/JWT-аутентификация, RBAC, CRUD заявок с комментариями и вложениями, полнотекстовый поиск, email-уведомления и полный audit trail — построена на событийной модели поверх Kafka.

Статус: Phase 0–14 завершены. Полная контейнеризация (docker compose up поднимает весь стек одной командой, включая production-grade React frontend), 53 unit/integration теста backend (0 падений), CI/CD-пайплайн (.github/workflows/ci.yml: build → test → static analysis → docker build → local deploy smoke-test → docker push [опционально]).


Содержание


Технологический стек

Слой Технология
Язык / платформа Java 17, Spring Boot 3.3.4
API Gateway Spring Cloud Gateway (реактивный), маршрутизация + JWT-фильтр + rate limiting (Redis)
Безопасность Spring Security 6, JJWT 0.12.6 (access + refresh), BCrypt, RBAC (ADMIN/MANAGER/EMPLOYEE)
Данные PostgreSQL 16, Spring Data JPA/Hibernate, Flyway (своя БД на сервис)
Поиск Elasticsearch 8.x (Spring Data ES + low-level Java Client), русский анализатор
Кэш / rate-limit Redis 7 (JWT blacklist, token-bucket rate limiter)
Сообщения Apache Kafka (KRaft, без ZooKeeper), at-least-once, retry + DLQ, идемпотентные consumers
Файлы MinIO (S3-совместимо) — вложения к заявкам, presigned URLs
Тесты JUnit 5, Mockito, AssertJ, Testcontainers, @EmbeddedKafka, JaCoCo
Документация API springdoc-openapi / Swagger UI на каждом сервисе
Мониторинг Actuator + Prometheus + Grafana (провижининг из коробки), Kibana для логов
Логи Logback + logstash-logback-encoder (JSON), correlation-id через MDC
Маппинг MapStruct, Lombok точечно (DTO/entity)
Контейнеризация Docker multi-stage build (Maven → eclipse-temurin:17-jre-alpine), Docker Compose, NGINX reverse proxy
CI/CD GitHub Actions
Сборка Maven multi-module (parent POM + common + 6 сервисов), Maven Wrapper

Полное обоснование выбора каждой технологии — docs/DESIGN.md, раздел 8.

Архитектура

Микросервисы, каждый — свой bounded context (DDD), внутри — Hexagonal / Ports & Adapters (domain → application → adapter/in → adapter/out). Взаимодействие между сервисами — почти исключительно асинхронное через Kafka (event-carried state transfer); синхронных REST-вызовов между сервисами в горячем пути нет — Gateway и сервисы валидируют JWT локально.

flowchart TB
    subgraph Clients["Clients"]
        WEB["Web / Postman / Mobile"]
    end

    NGINX["NGINX Reverse Proxy"]
    GATEWAY["API Gateway\nJWT filter / CORS / Rate limit / Routing"]

    subgraph Services["Microservices"]
        USER["User Service\nAuth, RBAC, Users"]
        TICKET["Ticket Service\nTickets, Comments, Attachments"]
        NOTIF["Notification Service\nEmail, Templates"]
        SEARCH["Search Service\nElasticsearch"]
        AUDIT["Audit Service\nAudit log"]
    end

    KAFKA["Apache Kafka (KRaft)"]

    subgraph Storage["Data & Infra"]
        PG[(PostgreSQL)]
        ES[(Elasticsearch)]
        REDIS[(Redis)]
        MINIO[(MinIO)]
    end

    subgraph Observability["Monitoring"]
        PROM["Prometheus"]
        GRAF["Grafana"]
    end

    WEB --> NGINX --> GATEWAY
    GATEWAY --> USER & TICKET & SEARCH & AUDIT

    USER & TICKET & AUDIT --> PG
    USER -. blacklist .-> REDIS
    GATEWAY -. rate limit .-> REDIS
    TICKET -. attachments .-> MINIO
    SEARCH --> ES

    USER & TICKET & AUDIT -- events --> KAFKA
    KAFKA --> NOTIF & SEARCH & AUDIT

    USER & TICKET & NOTIF & SEARCH & AUDIT --> PROM --> GRAF
Loading

Остальные диаграммы (ER-модель, sequence diagram сценария создания заявки, deployment diagram, таблица событий Kafka, поэтапный план реализации, обоснование каждого архитектурного решения) — в docs/DESIGN.md, это основной design-документ проекта.

Быстрый старт

docker compose up -d --build

Первая сборка компилирует все 6 сервисов внутри Docker (Maven) — может занять несколько минут. После поднятия стека:

Что URL
Web-приложение (React SPA) http://localhost (если порт 80 занят локально — NGINX_PORT=8079 docker compose up -d) — NGINX отдаёт SPA на / и проксирует /api/** на Gateway
API через NGINX http://localhost/api/v1/...
API напрямую через Gateway http://localhost:8080
Frontend напрямую (минуя корневой NGINX) http://localhost:5173
Grafana http://localhost:3000 (admin/admin)
Prometheus http://localhost:9090
Kibana http://localhost:5602
MinIO Console http://localhost:9001
MailHog (перехват писем) http://localhost:8025

Пример сквозного сценария через Gateway:

curl -X POST http://localhost:8080/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"demo@helpdesk.io","password":"password123","fullName":"Demo User","department":"IT","position":"Engineer"}'

curl -X POST http://localhost:8080/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"demo@helpdesk.io","password":"password123"}'
# -> access/refresh token, далее Authorization: Bearer <token> на /tickets, /search, /audit-logs

Сборка без Docker

Глобальный Maven не требуется — используйте Maven Wrapper:

./mvnw clean verify        # Linux/macOS
mvnw.cmd clean verify       # Windows

Инфраструктура для локального запуска сервисов из IDE:

docker compose up -d postgres kafka redis elasticsearch minio minio-init mailhog

Сервисы и порты

Сервис Прямой порт Swagger UI Назначение
Frontend (React SPA) 5173 — Enterprise UI: дашборд, заявки, поиск, пользователи, мониторинг
Gateway 8080 — (проксирует) Единая точка входа, JWT-фильтр, rate limiting, роутинг
User Service 8081 http://localhost:8081/swagger-ui.html Регистрация, логин, refresh/logout, профиль, RBAC
Ticket Service 8082 http://localhost:8082/swagger-ui.html CRUD заявок, комментарии, вложения (MinIO)
Notification Service 8083 http://localhost:8083/swagger-ui.html Kafka-consumer → email-уведомления (Thymeleaf-шаблоны)
Search Service 8084 http://localhost:8084/swagger-ui.html Индексация в Elasticsearch, полнотекстовый поиск/фильтры/агрегации
Audit Service 8085 http://localhost:8085/swagger-ui.html Consumer всех доменных событий + API истории изменений

Все обращения "снаружи" по назначению идут через Gateway (8080) или NGINX (80); прямые порты сервисов открыты для отладки/Swagger и для доступа изнутри docker-сети.

Frontend

Production-grade enterprise UI поверх существующего backend API — без единой правки Java-кода (см. docs/DESIGN.md; backend остаётся как задокументировано выше).

Технологический стек

Слой Технология
Язык / сборка TypeScript (strict), React 19, Vite 8
Роутинг React Router 7 (data router, protected/role-gated routes)
Серверное состояние TanStack Query v5 (кэш, инвалидация, optimistic updates)
Клиентское состояние Zustand (auth/theme/UI/notifications stores, persist в localStorage)
HTTP Axios — единый клиент с JWT-интерцептором и автоматическим refresh на 401
Формы React Hook Form + Zod-схемы
UI-кит TailwindCSS 4 + shadcn/ui-стиль поверх Radix UI примитивов
Таблицы TanStack Table v8 (сортировка, серверная пагинация)
Графики Recharts (Pie/Bar/Area) — единая цветовая семантика со статус-бейджами
Анимации Framer Motion (переходы страниц, мобильное меню)
Command Palette cmdk (⌘K / Ctrl+K)
Toasts react-hot-toast
Даты Day.js (+ relativeTime, ru locale)
Линтинг/форматирование ESLint 10 (flat config, typescript-eslint) + Prettier

Архитектура (Feature-based)

frontend/src/
├── app/            # providers, router, query client, theme provider
├── layouts/         # AppLayout (sidebar/topbar/breadcrumbs), AuthLayout
├── components/
│   ├── ui/          # shadcn-style примитивы (Button, Dialog, Select, Table, ...)
│   └── common/       # DataTable, SearchInput, Pagination, StatusBadge, ConfirmDialog, ...
├── features/         # по одной папке на бизнес-фичу
│   ├── auth/          # login/register, JWT refresh, useCurrentUser
│   ├── dashboard/      # stats, charts, activity feed, audit API
│   ├── tickets/        # список/детали/CRUD/комментарии/вложения
│   ├── users/          # admin-панель пользователей
│   ├── search/         # Elasticsearch-поиск + агрегации
│   ├── notifications/   # bell dropdown, toast, история (client-derived, см. ниже)
│   ├── monitoring/      # /actuator/health по всем сервисам
│   └── profile/         # профиль, тема оформления
├── store/            # zustand: authStore, themeStore, uiStore, notificationStore
├── services/          # apiClient (axios + interceptors), authEvents
├── types/             # ApiResponse<T>/PageResponse<T>, доменные типы (Ticket, User, ...)
└── hooks/, utils/, constants/, styles/

Routing

react-router-dom (createBrowserRouter), защищённые роуты через ProtectedRoute (редирект на /login без токена) и RoleGuard (ADMIN/MANAGER-only для /users, /monitoring). Тяжёлые страницы подключены через React.lazy + Suspense — код-сплиттинг по фичам, а не один бандл. ErrorBoundary оборачивает <Outlet/> на уровне layout'а.

State Management

  • Серверное состояние — исключительно TanStack Query: ни одного useEffect для загрузки данных, кэш-инвалидация по queryKey, optimistic update на смене статуса заявки с rollback при ошибке.
  • Клиентское состояние — Zustand: JWT-токены и профиль (authStore, персистится), тема (themeStore), состояние сайдбара/command-palette (uiStore).
  • Auto-refresh JWT — единый axios-интерцептор ловит 401, дедуплицирует параллельные refresh-запросы (refreshPromise-мьютекс), ретраит исходный запрос и обновляет токены в сторе; при неудаче — authEvents.emitSessionExpired() → редирект на /login.
  • Notifications без изменения backend — у notification-service нет REST API (это чистый Kafka-consumer → email). Панель уведомлений опрашивает GET /tickets каждые 20с и сравнивает снэпшоты (useNotificationWatcher), формируя реальные события (назначение, смена статуса, новая заявка для ADMIN/MANAGER) — без единой моковой записи, но и без правок Java-кода.

Известные ограничения, обусловленные backend API

Реализовано строго поверх существующих эндпоинтов (без правок Java):

  • GET /api/v1/users и GET /api/v1/tickets не поддерживают server-side поиск — для этого есть отдельная Search-страница на Elasticsearch; списки Users/Tickets используют быстрый client-side фильтр по уже загруженной странице.
  • Смена статуса заявки разрешена только исполнителю или ADMIN/MANAGER (TicketApplicationService.changeStatus) — UI скрывает контрол статуса для автора заявки без назначения, а не показывает его с последующим 403.
  • В UserManagementUseCase нет смены роли, реактивации после деактивации и смены пароля — соответствующие действия не выведены в UI, а не имитированы моками.
  • GET /api/v1/audit/logs* доступен только ADMIN/MANAGER — вкладка "История" на заявке и Activity Feed на дашборде для EMPLOYEE заменены на собственные заявки.
  • /actuator/health каждого сервиса проксируется через NGINX по same-origin путям (/health/<service>/..., см. infra/nginx/nginx.conf) — часть сервисов не отдаёт permissive CORS для прямых cross-port запросов из браузера.

Screenshots

Dashboard Ticket detail
Dashboard Ticket detail
Search (Elasticsearch) Users admin
Search Users
Dark theme Mobile
Dark mode Mobile

Build

cd frontend
npm install
npm run dev      # Vite dev server :5173, проксирует /api и /health/* на локальные сервисы
npm run build     # tsc -b && vite build -> frontend/dist (code-split, ~155KB gzip основной бандл)
npm run lint      # ESLint
npm run format     # Prettier --write

Deploy

Собирается и разворачивается вместе с остальным стеком — отдельного запуска не требует:

docker compose up -d --build
  • frontend/Dockerfile — multi-stage: node:20-alpine (npm ci && npm run build) → nginx:alpine, отдающий dist/ со SPA-fallback (try_files ... /index.html).
  • Корневой infra/nginx/nginx.conf маршрутизирует / на контейнер frontend, /api/** и /actuator/** — на gateway, /health/<service>/** — напрямую на каждый backend-сервис (см. "State Management" выше).
  • Frontend-контейнер также содержит собственный /api и /health/* proxy к gateway/сервисам — работает самостоятельно, даже если открыт напрямую (http://localhost:5173), в обход корневого NGINX.

Структура репозитория

helpdesk-platform/
├── pom.xml                    # parent POM: dependencyManagement, plugins, Java 17
├── common/                    # общая библиотека: Kafka-события, ApiResponse<T>, исключения, JwtVerifier
├── gateway/                   # Spring Cloud Gateway
├── user-service/              # auth, users, RBAC
├── ticket-service/            # tickets, comments, attachments (MinIO)
├── notification-service/      # Kafka consumer -> email
├── search-service/            # индексация в Elasticsearch + поиск
├── audit-service/             # audit log
├── frontend/                  # React 19 + TypeScript SPA (см. раздел Frontend)
│   ├── src/
│   ├── Dockerfile              # multi-stage: npm build -> nginx:alpine
│   └── nginx.conf              # SPA fallback + /api, /health/* proxy
├── infra/
│   ├── nginx/nginx.conf        # корневой reverse-proxy: "/" -> frontend, "/api" -> gateway
│   └── monitoring/{prometheus,grafana}/...
├── docker-compose.yml
├── .github/workflows/ci.yml
└── docs/
    ├── DESIGN.md               # design-документ: требования, архитектура, диаграммы, ADR
    └── screenshots/            # скриншоты frontend (см. раздел Frontend)

Каждый сервис следует единой пакетной структуре (Hexagonal Architecture):

<service>/src/main/java/.../
  domain/                   # сущности/value objects, чистая бизнес-логика, без Spring
  application/port/{in,out}/ # use-case интерфейсы и порты наружу
  application/service/       # реализация use-case'ов
  adapter/in/web/            # REST controllers, DTO, mapper
  adapter/in/messaging/      # Kafka listeners (где применимо)
  adapter/out/persistence/   # JPA entities, repositories, мапперы
  adapter/out/messaging/     # Kafka producers
  config/                    # Security, Kafka, OpenAPI, MinIO/Redis config

Тестирование

53 unit/integration теста, 0 падений, во всех 8 модулях (JUnit 5 + Mockito + AssertJ), измерение покрытия через JaCoCo (target/site/jacoco/ в каждом модуле после mvn verify).

Категории тестов: application-service (use-case логика на моках портов), controller-тесты (@WebMvcTest + реальный SecurityConfig + MockMvc — проверяют реальную цепочку Spring Security, а не только замоканные use case'ы), а также Testcontainers-интеграционные тесты (реальные PostgreSQL/Kafka/Elasticsearch в контейнерах) и @EmbeddedKafka-тесты для consumer-логики. См. известные ограничения по поводу Testcontainers на этой машине.

./mvnw clean verify

CI/CD

.github/workflows/ci.yml — пайплайн из 5 джобов на GitHub-раннерах (нативный Linux Docker, поэтому все Testcontainers-тесты выполняются полностью):

  1. build-and-test — mvn clean verify, публикация surefire/JaCoCo отчётов как артефактов
  2. static-analysis — SpotBugs (информационно, continue-on-error)
  3. docker-build — сборка образов всех 6 сервисов
  4. deploy-local-smoke-test — поднимает весь стек, дожидается healthy Gateway, проверяет реальным HTTP-запросом через NGINX → Gateway → User Service
  5. docker-push — отключён по умолчанию (if: false); включается добавлением DOCKERHUB_USERNAME/DOCKERHUB_TOKEN в секреты репозитория

Известные ограничения окружения разработки

Ниже — артефакты конкретной машины разработки (Windows + Docker Desktop), не дефекты кода; на GitHub Actions (нативный Linux) всё перечисленное работает без исключений:

  • Testcontainers через npipe на этом Docker Desktop не поднимает контейнеры (Could not find a valid Docker environment) — соответствующие integration-тесты (*IntegrationTest с @Testcontainers) написаны и включены в реактор, но исключены из локального mvn test.
  • Порты 5432/9200/5601/80 были заняты другими локальными сервисами на машине разработки — сервисы вынесены на альтернативные хост-порты (5433/9201/5602, NGINX_PORT); внутри docker-сети все обращения идут по стандартным портам/именам контейнеров.

Roadmap / возможные улучшения

Не реализовано намеренно (см. docs/DESIGN.md, раздел «Импровизация») — рассматривалось как избыточное для текущего масштаба или не давало достаточной ценности при заданных сроках:

  • Outbox Pattern для продюсеров событий (устраняет проблему двойной записи БД + Kafka)
  • OpenTelemetry + Jaeger — полноценная распределённая трассировка (сейчас — единый correlation-id через MDC/заголовки, без span-дерева)
  • Resilience4j / Circuit Breaker на исходящих вызовах (сейчас между сервисами нет синхронных вызовов в горячем пути, поэтому не было явной необходимости)
  • API-версионирование (/api/v2/...) — задел есть (/api/v1/...), вторая версия не нужна
  • CQRS/SAGA — намеренно не введены: оркестрация текущих сценариев не требует их сложности
  • Feature Flags
  • Keycloak / OAuth2 вместо собственной JWT-реализации — рассмотрено, но для demo-масштаба собственная реализация давала больше контроля над учебной/демонстрационной ясностью кода

About

Enterprise HelpDesk ticket management platform — Spring Boot microservices + React 19 frontend

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages