У Бизнес-клуба есть небольшой склад мерча и оборудования. У каждой позиции есть название, количество на складе и номер полки. Менеджеры хотят:
- быстро находить вещи и видеть остатки,
- безопасно «списывать» единицы при выдаче,
- управлять данными через простую админку.
- users - пользователи системы с ролями (админ/пользователь)
- items - товары на складе (название, количество, номер полки)
- movements - журнал операций (списания с указанием пользователя и времени)
GET /api/items- список всех позиций с фильтрами по имени/номеру полки и пагинациейGET /api/items/{id}- получить конкретную позицию по UUIDPOST /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 (опционально, для упрощения команд)
# 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
# Показать все доступные команды
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 тесты
make test
# С подробным выводом
make test-unit
# С coverage отчётом
make test-coverageРезультаты:
- ✅ Все тесты проходят:
ok hse/tests 1.212s - ✅ Покрытие: models, services, middleware, config
# Запустить 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 для проверки JWTjwt.go- только генерация и работа с JWT токенами
Все API эндпоинты (кроме /auth/login) требуют JWT токен в заголовке:
Authorization: Bearer <ваш_токен>
Администратор:
- Username:
admin - Password:
admin123 - Email:
[email protected]
Тестовый пользователь:
- Username:
testuser - Password:
test123 - Email:
[email protected]
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
}
}curl -X GET "http://localhost:8080/api/items?limit=10&offset=0" \
-H "Authorization: Bearer <токен>"Параметры запроса:
name- фильтр по названию (частичное совпадение, регистронезависимый)shelf_number- фильтр по номеру полкиlimit- количество элементов (по умолчанию 10, макс 100)offset- смещение для пагинации
curl -X GET http://localhost:8080/api/items/{id} \
-H "Authorization: Bearer <токен>"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"
}curl -X POST http://localhost:8080/api/items \
-H "Authorization: Bearer <токен_администратора>" \
-H "Content-Type: application/json" \
-d '{
"name": "Новый товар",
"quantity": 100,
"shelf_number": "E1",
"description": "Описание товара"
}'curl -X PUT http://localhost:8080/api/items/{id} \
-H "Authorization: Bearer <токен_администратора>" \
-H "Content-Type: application/json" \
-d '{
"name": "Обновлённое название",
"quantity": 150
}'curl -X DELETE http://localhost:8080/api/items/{id} \
-H "Authorization: Bearer <токен_администратора>"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
}curl -X GET http://localhost:8080/api/movements/{id} \
-H "Authorization: Bearer <токен>"curl -X GET "http://localhost:8080/api/users?limit=10&offset=0" \
-H "Authorization: Bearer <токен_администратора>"curl -X GET http://localhost:8080/api/users/{id} \
-H "Authorization: Bearer <токен_администратора>"curl -X POST http://localhost:8080/api/users \
-H "Authorization: Bearer <токен_администратора>" \
-H "Content-Type: application/json" \
-d '{
"username": "newuser",
"email": "[email protected]",
"password": "password123"
}'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" | jqmake docker-logs # Все логи
make docker-logs-app # Только приложение
make docker-logs-db # Только база данныхmake db-console
# или
docker compose exec postgres psql -U warehouse -d warehouse_dbcurl http://localhost:8080/health200 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 ✅$ make test-api
12/12 тестов пройдено успешно ✅$ make build
Бинарник создан: bin/warehouse (19M) ✅$ 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