REST API для учёта заказов небольшого магазина или сервиса доставки: создание заказа, смена статусов, поиск, выручка за период. FastAPI, SQLite, Docker. Документация Swagger открывается по адресу /docs.
POST /orders: заказ с позициями, скидкой и комментарием. Одинаковые позиции (sku) в одном запросе склеиваются в одну строку- Повтор без дубля: с заголовком
Idempotency-Keyповторный запрос (обрыв связи, двойной клик «Оформить») возвращает уже созданный заказ. Тест отправляет 10 одинаковых запросов одновременно через разные соединения с базой, и заказ получается один - Деньги в копейках: суммы хранятся целыми числами, поэтому 0.10 × 3 + 0.20 даёт ровно 0.50. Цену можно передать как
450,"450.50"или"450,50" - Статусы с правилами переходов:
new → paid → shipped → delivered, отмена изnewиpaid. Отгрузить неоплаченный или «воскресить» отменённый заказ нельзя. Каждая смена статуса попадает в историю с временем и заметкой (например, номер ТТН) - Если два менеджера одновременно меняют статус одного заказа, второй получит
409и не затрёт чужое изменение GET /orders: фильтры по статусу, телефону и периоду, постраничный выводGET /stats: число заказов по статусам и выручка за период (считаются только оплаченные, отправленные и доставленные заказы)- Доступ по ключу
X-API-Key. Ошибки приходят в одном формате:{"detail": {"code": "...", "message": "..."}} - Телефон приводится к виду
+380...
orders/
domain.py правила: проверка, суммы, переходы статусов (чистая логика)
repository.py SQLite
service.py сценарии: создать, найти, сменить статус, статистика
api.py FastAPI: маршруты, схемы, коды ошибок
main.py сборка приложения из переменных окружения
tests/ 59 тестов
Dockerfile, docker-compose.yml
Правила заказа не зависят от FastAPI и проверяются отдельно. API тестируется настоящим приложением через TestClient.
cp .env.example .env
# впишите API_KEY (сгенерировать: python -c "import secrets; print(secrets.token_urlsafe(32))")
docker compose up -d --buildAPI будет на http://127.0.0.1:8000, документация на http://127.0.0.1:8000/docs. База лежит в томе orders-data и переживает пересборку контейнера. Порт открыт только на 127.0.0.1: наружу API лучше выставлять через обратный прокси с HTTPS (Caddy, nginx).
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # впишите API_KEY
uvicorn orders.main:app --port 8000curl -X POST http://127.0.0.1:8000/orders \
-H "X-API-Key: $API_KEY" -H "Idempotency-Key: checkout-42" -H "Content-Type: application/json" \
-d '{"customer_name": "Анна", "customer_phone": "050 123 45 67",
"items": [{"sku": "TSHIRT-M", "title": "Футболка M", "qty": 2, "price": "450.50"}]}'{"id": 1, "status": "new", "customer": {"name": "Анна", "phone": "+380501234567"},
"items": [{"sku": "TSHIRT-M", "title": "Футболка M", "qty": 2, "price": "450.50", "total": "901.00"}],
"subtotal": "901.00", "discount": "0.00", "total": "901.00", "comment": "",
"created_at": "2030-01-07T12:00:00", "history": [{"status": "new", "at": "2030-01-07T12:00:00", "note": ""}]}pip install -r requirements-dev.txt
python -m pytestSQLite рассчитан на один процесс записи, поэтому в контейнере запускается один воркер uvicorn. Для сотен заказов в минуту и нескольких серверов нужно перевести repository.py на PostgreSQL. Остальной код при этом не меняется.
MIT
