Fighter90/warehouse-api

★ 0Forks 0GoGitHub ↗Compare

README

Warehouse API - Система учёта склада мерча Бизнес-клуба

📋 Описание

У Бизнес-клуба есть небольшой склад мерча и оборудования. У каждой позиции есть название, количество на складе и номер полки. Менеджеры хотят:

  1. быстро находить вещи и видеть остатки,
  2. безопасно «списывать» единицы при выдаче,
  3. управлять данными через простую админку.

🎯 Соответствие требованиям

✅ Бэкенд-сервис со следующими сущностями:

  • users - пользователи системы с ролями (админ/пользователь)
  • items - товары на складе (название, количество, номер полки)
  • movements - журнал операций (списания с указанием пользователя и времени)

✅ REST API под авторизацией (JWT)

  • GET /api/items - список всех позиций с фильтрами по имени/номеру полки и пагинацией
  • GET /api/items/{id} - получить конкретную позицию по UUID
  • POST /api/items/{id}/take - списание товара
    • Тело: {"qty": N, "reason": "..."}
    • В movements сохраняется user, qty, reason, timestamp
    • При qty > item.quantity возвращаем 409 Conflict

✅ Инфраструктура

  • docker-compose.yml - поднимает PostgreSQL БД и сервис
  • Один командный запуск: docker compose up -d

✅ Документация

  • README.md - полная документация с примерами
  • API_EXAMPLES.md - коллекция curl примеров
  • 📘 Swagger UI - интерактивная документация API
  • Все эндпоинты протестированы - 12/12 тестов прошли успешно

🚀 Возможности

  • ✅ REST API с JWT авторизацией
  • ✅ Swagger UI - интерактивная документация и тестирование API
  • ✅ Веб-админка - удобный интерфейс для управления складом
  • ✅ CRUD операции для товаров
  • ✅ Журнал движений товаров (списания/добавления)
  • ✅ Управление пользователями
  • ✅ Разделение прав доступа (администраторы/пользователи)
  • ✅ Фильтрация и поиск по товарам
  • ✅ Пагинация результатов
  • ✅ PostgreSQL база данных
  • ✅ Docker контейнеризация
  • ✅ Автоматические миграции
  • ✅ Тестовые данные для демонстрации
  • ✅ Полное покрытие тестами (Unit + Integration + API + Admin)
  • ✅ Thread-safe операции с блокировками на уровне БД
  • ✅ Транзакции для обеспечения целостности данных

📋 Требования

  • Docker и Docker Compose
  • Go 1.24+ (для локальной разработки)
  • Make (опционально, для упрощения команд)

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

Запуск через Docker (рекомендуется)

# 1. Клонируйте репозиторий
git clone [email protected]:Fighter90/warehouse-api.git
cd warehouse-api

# 2. Запустите приложение одной командой
docker compose up -d

# 3. Проверьте статус
curl http://localhost:8080/health

Приложение будет доступно на:

  • API: http://localhost:8080
  • Swagger UI: http://localhost:8080/swagger/index.html 📘
  • Админ-панель: http://localhost:8080/admin/login 🎨
  • Health Check: http://localhost:8080/health

Использование Makefile

# Показать все доступные команды
make help

# Запустить в Docker
make docker-up

# Запустить для разработки (с выводом ссылок)
make dev

# Запустить тесты
make test

# Запустить API тесты (требуется запущенное приложение)
make test-api

# Посмотреть логи
make docker-logs

# Сгенерировать Swagger документацию
make swagger

Запуск локально (для разработки)

# 1. Установите зависимости
go mod download

# 2. Запустите PostgreSQL
docker compose up -d postgres

# 3. Настройте переменные окружения
export DB_HOST=localhost
export DB_PORT=5432
export DB_USER=warehouse
export DB_PASSWORD=warehouse_password
export DB_NAME=warehouse_db
export JWT_SECRET=your-secret-key
export APP_ENV=development

# 4. Запустите приложение
go run cmd/server/main.go
# или
make run

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

Unit тесты

# Запустить все unit тесты
make test

# С подробным выводом
make test-unit

# С coverage отчётом
make test-coverage

Результаты:

  • ✅ Все тесты проходят: ok hse/tests 1.212s
  • ✅ Покрытие: models, services, middleware, config

API тесты

# Запустить API тесты (требуется запущенное приложение)
make test-api

# Или напрямую
./test_api.sh

Проверяются:

  • ✅ Health check
  • ✅ Аутентификация (login)
  • ✅ Получение списка товаров
  • ✅ Поиск по названию
  • ✅ Фильтр по номеру полки
  • ✅ Получение товара по ID
  • ✅ Списание товара
  • ✅ Проверка 409 при недостатке товара
  • ✅ Журнал движений
  • ✅ Создание товара (админ)
  • ✅ Проверка 401 без авторизации

📦 Структура проекта

hse/
├── cmd/
│   └── server/          # Точка входа приложения
│       └── main.go
├── internal/
│   ├── auth/            # JWT утилиты и генерация токенов
│   │   └── jwt.go
│   ├── config/          # Конфигурация приложения
│   ├── database/        # Подключение к БД
│   ├── handlers/        # HTTP обработчики (REST API)
│   ├── middleware/      # Middleware (auth, logger, cors)
│   │   ├── auth.go      # JWT аутентификация middleware
│   │   ├── cors.go      # CORS middleware
│   │   └── logger.go    # HTTP логирование
│   ├── models/          # Модели данных (User, Item, Movement)
│   └── services/        # Бизнес-логика
├── tests/               # Unit и integration тесты
├── migrations/          # SQL миграции
├── docker-compose.yml   # Docker конфигурация
├── Dockerfile          # Multi-stage Docker образ
├── Makefile            # Автоматизация команд
├── test_api.sh         # Скрипт API тестов
├── API_EXAMPLES.md     # Примеры использования API
└── README.md           # Документация

🏗️ Архитектурные улучшения

Проект следует принципам чистой архитектуры и SOLID:

Разделение ответственности:

  • internal/auth/ - JWT утилиты (генерация и валидация токенов)
  • internal/middleware/ - только middleware функции
  • internal/handlers/ - HTTP обработчики
  • internal/services/ - бизнес-логика
  • internal/models/ - модели данных

Каждый файл выполняет одну задачу:

  • cors.go - только CORS политика
  • logger.go - только HTTP логирование
  • auth.go - только middleware для проверки JWT
  • jwt.go - только генерация и работа с JWT токенами

🔐 Аутентификация

Все API эндпоинты (кроме /auth/login) требуют JWT токен в заголовке:

Authorization: Bearer <ваш_токен>

Учётные данные по умолчанию

Администратор:

Тестовый пользователь:

📚 API Endpoints

Аутентификация

POST /auth/login - Вход в систему

curl -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "admin",
    "password": "admin123"
  }'

Ответ:

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "uuid",
    "username": "admin",
    "email": "[email protected]",
    "is_admin": true
  }
}

Товары (Items)

GET /api/items - Получить список товаров

curl -X GET "http://localhost:8080/api/items?limit=10&offset=0" \
  -H "Authorization: Bearer <токен>"

Параметры запроса:

  • name - фильтр по названию (частичное совпадение, регистронезависимый)
  • shelf_number - фильтр по номеру полки
  • limit - количество элементов (по умолчанию 10, макс 100)
  • offset - смещение для пагинации

GET /api/items/{id} - Получить товар по ID

curl -X GET http://localhost:8080/api/items/{id} \
  -H "Authorization: Bearer <токен>"

POST /api/items/{id}/take - Списать товар ⭐

curl -X POST http://localhost:8080/api/items/{id}/take \
  -H "Authorization: Bearer <токен>" \
  -H "Content-Type: application/json" \
  -d '{
    "qty": 5,
    "reason": "Выдано на конференцию"
  }'

Ответ при успехе (200 OK):

{
  "message": "Товар успешно списан",
  "movement_id": "uuid",
  "quantity": 5
}

Ответ при недостатке товара (409 Conflict):

{
  "error": "недостаточно товара на складе: доступно 3, запрошено 5"
}

POST /api/items - Создать товар (только администраторы)

curl -X POST http://localhost:8080/api/items \
  -H "Authorization: Bearer <токен_администратора>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Новый товар",
    "quantity": 100,
    "shelf_number": "E1",
    "description": "Описание товара"
  }'

PUT /api/items/{id} - Обновить товар (только администраторы)

curl -X PUT http://localhost:8080/api/items/{id} \
  -H "Authorization: Bearer <токен_администратора>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Обновлённое название",
    "quantity": 150
  }'

DELETE /api/items/{id} - Удалить товар (только администраторы)

curl -X DELETE http://localhost:8080/api/items/{id} \
  -H "Authorization: Bearer <токен_администратора>"

Движения (Movements) - Журнал операций

GET /api/movements - Получить журнал движений

curl -X GET "http://localhost:8080/api/movements?limit=10&offset=0" \
  -H "Authorization: Bearer <токен>"

Параметры запроса:

  • item_id - фильтр по ID товара
  • user_id - фильтр по ID пользователя
  • movement_type - фильтр по типу (take, add)
  • limit - количество элементов
  • offset - смещение

Ответ:

{
  "movements": [
    {
      "id": "uuid",
      "item_id": "uuid",
      "item": {
        "id": "uuid",
        "name": "Футболка с логотипом",
        "quantity": 45,
        "shelf_number": "A1"
      },
      "user_id": "uuid",
      "user": {
        "id": "uuid",
        "username": "admin",
        "email": "[email protected]"
      },
      "quantity": 5,
      "reason": "Выдано на конференцию",
      "movement_type": "take",
      "created_at": "2025-01-01T10:30:00Z"
    }
  ],
  "total": 1,
  "limit": 10,
  "offset": 0
}

GET /api/movements/{id} - Получить движение по ID

curl -X GET http://localhost:8080/api/movements/{id} \
  -H "Authorization: Bearer <токен>"

Пользователи (Users) - только администраторы

GET /api/users - Получить список пользователей

curl -X GET "http://localhost:8080/api/users?limit=10&offset=0" \
  -H "Authorization: Bearer <токен_администратора>"

GET /api/users/{id} - Получить пользователя по ID

curl -X GET http://localhost:8080/api/users/{id} \
  -H "Authorization: Bearer <токен_администратора>"

POST /api/users - Создать пользователя

curl -X POST http://localhost:8080/api/users \
  -H "Authorization: Bearer <токен_администратора>" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "newuser",
    "email": "[email protected]",
    "password": "password123"
  }'

DELETE /api/users/{id} - Удалить пользователя

curl -X DELETE http://localhost:8080/api/users/{id} \
  -H "Authorization: Bearer <токен_администратора>"

🗄 База данных

Схема базы данных

users - пользователи системы

  • id (UUID, PK)
  • username (string, unique)
  • email (string, unique)
  • password_hash (string)
  • is_admin (boolean)
  • created_at, updated_at, deleted_at

items - товары на складе

  • id (UUID, PK)
  • name (string)
  • quantity (integer, >= 0)
  • shelf_number (string)
  • description (text)
  • created_at, updated_at, deleted_at

movements - журнал движений

  • id (UUID, PK)
  • item_id (UUID, FK -> items)
  • user_id (UUID, FK -> users)
  • quantity (integer, > 0)
  • reason (text)
  • movement_type (enum: take, add)
  • created_at

Миграции

Миграции применяются автоматически при запуске приложения через GORM AutoMigrate.

Для ручного управления используйте SQL файлы в папке migrations/:

  • 001_init.up.sql - создание таблиц
  • 001_init.down.sql - откат миграции

🔒 Безопасность

  • ✅ Пароли хешируются с помощью bcrypt
  • ✅ JWT токены с секретным ключом (настраивается через JWT_SECRET)
  • ✅ Разделение прав доступа (администраторы/пользователи)
  • ✅ Валидация входных данных через binding tags
  • ✅ SQL инъекции предотвращены через GORM
  • ✅ Thread-safe операции с SELECT FOR UPDATE
  • ✅ Транзакции для атомарности операций списания
  • ✅ CORS настроен для кросс-доменных запросов

⚙️ Переменные окружения

Настраиваются в docker-compose.yml или через .env файл:

# База данных
DB_HOST=postgres
DB_PORT=5432
DB_USER=warehouse
DB_PASSWORD=warehouse_password
DB_NAME=warehouse_db
DB_SSLMODE=disable

# Сервер
SERVER_PORT=8080
SERVER_HOST=0.0.0.0

# JWT
JWT_SECRET=your-secret-key-change-in-production

# Приложение
APP_ENV=development

# Администратор по умолчанию
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
ADMIN_EMAIL=[email protected]

🧪 Тестовые данные

При запуске в режиме APP_ENV=development автоматически создаются:

Пользователи:

  • admin / admin123 (администратор)
  • testuser / test123 (обычный пользователь)

Товары:

  • Футболка с логотипом (50 шт, полка A1)
  • Кружка керамическая (30 шт, полка A2)
  • Блокнот брендированный (100 шт, полка B1)
  • Ручка шариковая (200 шт, полка B2)
  • Флешка USB 16GB (25 шт, полка C1)
  • Бейдж именной (75 шт, полка C2)
  • Худи с принтом (40 шт, полка A3)
  • Сумка-шоппер (35 шт, полка D1)

📝 Примеры использования

Полный список примеров curl команд смотрите в API_EXAMPLES.md

Быстрый пример: Списание товара

# 1. Запуск приложения
docker compose up -d

# 2. Вход как администратор
TOKEN=$(curl -s -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin123"}' | \
  grep -o '"token":"[^"]*' | cut -d'"' -f4)

# 3. Получить список товаров
curl -s http://localhost:8080/api/items \
  -H "Authorization: Bearer $TOKEN" | jq

# 4. Списать 5 единиц товара
curl -X POST http://localhost:8080/api/items/{item_id}/take \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"qty":5,"reason":"Выдано участникам meetup"}' | jq

# 5. Просмотреть журнал движений
curl -s http://localhost:8080/api/movements \
  -H "Authorization: Bearer $TOKEN" | jq

🐛 Отладка

Просмотр логов

make docker-logs          # Все логи
make docker-logs-app      # Только приложение
make docker-logs-db       # Только база данных

Подключение к базе данных

make db-console
# или
docker compose exec postgres psql -U warehouse -d warehouse_db

Проверка здоровья

curl http://localhost:8080/health

📊 Коды ответов HTTP

  • 200 OK - Успешный запрос
  • 201 Created - Ресурс создан
  • 400 Bad Request - Неверный формат данных
  • 401 Unauthorized - Требуется аутентификация
  • 403 Forbidden - Недостаточно прав
  • 404 Not Found - Ресурс не найден
  • 409 Conflict - Конфликт (недостаточно товара на складе)
  • 500 Internal Server Error - Внутренняя ошибка сервера

🛑 Остановка приложения

# Остановить контейнеры
make docker-down

# Остановить и удалить volumes (очистит БД!)
make docker-clean

✅ Проверка проекта

Все тесты проходят

$ make test
ok hse/tests 1.212s ✅

API тесты проходят

$ make test-api
12/12 тестов пройдено успешно ✅

Проект компилируется

$ make build
Бинарник создан: bin/warehouse (19M) ✅

Docker образ собирается

$ make docker-build
Docker образ успешно собран ✅

📄 Дополнительная документация

  • ADMIN.md - Полное руководство по веб-админке
  • API_EXAMPLES.md - Полная коллекция примеров curl запросов
  • ARCHITECTURE.md - Архитектура приложения
  • DEPLOYMENT.md - Инструкции по развертыванию
  • TESTING.md - Подробности о тестировании

🎨 Веб-админка

Проект включает полноценную веб-админку для удобного управления складом через браузер.

Доступ к админке

После запуска приложения откройте: http://localhost:8080/admin/login

Учётные данные:

  • Логин: admin
  • Пароль: admin123

Возможности админки

✅ Панель управления (Dashboard)

  • Статистика по товарам и операциям
  • Последние добавленные товары
  • Недавние движения

✅ Управление товарами

  • Просмотр всех товаров с поиском и фильтрами
  • Создание новых товаров
  • Редактирование существующих
  • Удаление товаров

✅ Журнал движений

  • Просмотр всех операций со складом
  • Информация о пользователе и времени
  • Тип операции (списание/приход)

✅ Управление пользователями

  • Просмотр всех пользователей
  • Создание новых пользователей
  • Назначение ролей (админ/пользователь)
  • Удаление пользователей

Подробная документация: ADMIN.md

Contributors

Fighter90

Issues