Бэкенд-сервис для генерации аналитических XLSX-отчетов по продажам. Тяжелая работа по сбору данных и формированию файлов вынесена из HTTP-запроса в отдельного worker'а через Redis Queue.
Сервис позволяет:
- хранить данные о категориях, товарах, заказах и позициях заказов;
- ставить задачи на генерацию аналитических отчетов по продажам за выбранный период;
- асинхронно собирать данные и формировать многолистовые XLSX-отчеты;
- отслеживать статус задачи и скачивать готовый файл.
Это коммерчески приближенный шаблон, подходящий как пример проекта на FastAPI + PostgreSQL + Redis + Worker.
Пользователь / Frontend
↓
FastAPI Backend
↓
PostgreSQL — хранит заказы, товары, отчеты и статусы
↓
Redis Queue — хранит задачи на генерацию отчетов
↓
Worker — собирает данные и формирует XLSX
↓
File Storage — хранит готовые отчеты
- FastAPI Backend — принимает HTTP-запросы, валидирует данные, создает задачи отчетов, отдает статус и файл.
- PostgreSQL — основное хранилище: пользователи (имя клиента), категории, товары, заказы, позиции заказов, задачи отчетов.
- Redis Queue — Redis List, реализующий очередь FIFO (
LPUSH/BRPOP). - Worker — отдельный процесс, который читает задачи и формирует XLSX.
- File Storage — каталог
storage/reports, смонтированный в общий Docker-том между API и worker.
Генерация отчета — тяжелая операция: выборка из БД, агрегация, формирование XLSX, запись на диск. Это могут быть тысячи строк и несколько секунд работы. Делать это в HTTP-запросе нельзя:
- запрос будет долго висеть;
- любая ошибка сети приведет к потере результата;
- увеличится нагрузка на API-инстансы;
- невозможно масштабировать обработку независимо от приема запросов.
Redis Queue:
- развязывает API и обработку (асинхронность);
- даёт возможность поднять несколько worker'ов и масштабировать обработку горизонтально;
- сохраняет задачи в памяти Redis даже если worker временно недоступен;
- легче и дешевле, чем полноценный broker для нашего сценария.
Требуется Docker и Docker Compose.
docker-compose up --buildПоднимутся 4 сервиса:
postgres— БД на порту 5432;redis— на порту 6379;api— FastAPI на http://localhost:8000;worker— фоновая обработка задач.
Документация API: http://localhost:8000/docs
| Метод | URL | Описание |
|---|---|---|
POST |
/seed |
Заполняет БД демонстрационными данными |
POST |
/reports/sales |
Создает задачу на генерацию отчета |
GET |
/reports |
Список всех задач отчетов |
GET |
/reports/{report_id} |
Информация о задаче |
GET |
/reports/{report_id}/download |
Скачать готовый XLSX |
GET |
/health |
Health-check |
Тело запроса:
{
"date_from": "2026-05-01",
"date_to": "2026-05-31",
"format": "xlsx"
}Если date_from > date_to — ответ 422.
Ответ 201:
{ "id": "f8b...uuid", "status": "queued" }404— отчет не найден;409— отчет ещё не готов;404— файл отсутствует на диске;200— XLSX-файл.
curl -X POST http://localhost:8000/seedСоздаются:
- 5 категорий товаров;
- 30 товаров;
- 120 заказов за последние 90 дней;
- 1–5 позиций в каждом заказе;
- статусы распределены: ~45% completed, 25% paid, 15% created, 15% cancelled.
curl -X POST http://localhost:8000/reports/sales \
-H "Content-Type: application/json" \
-d '{"date_from":"2026-02-01","date_to":"2026-05-31","format":"xlsx"}'Сервис:
- Валидирует даты.
- Создаёт запись
ReportTaskсо статусомqueued. - Кладёт сообщение в Redis Queue
reports:queue. - Возвращает
idиstatus.
curl http://localhost:8000/reports/{report_id}Возможные статусы:
queued— задача создана, ждёт worker'а;processing— worker взял задачу;done— отчёт сформирован, файл доступен для скачивания;failed— обработка завершилась ошибкой (error_messageсодержит детали).
curl -OJ http://localhost:8000/reports/{report_id}/downloadПолученный файл — стандартный .xlsx, открывается в Excel / LibreOffice / Numbers.
Файл содержит 5 листов.
Аккуратная таблица ключевых показателей:
- название отчёта;
- период;
- дата генерации;
- общее количество заказов;
- оплаченных заказов;
- отменённых заказов;
- общая выручка (денежный формат);
- средний чек (денежный формат);
- продано товаров (шт.);
- самая прибыльная категория;
- самый продаваемый товар.
| Дата | Заказов | Товаров продано | Выручка | Средний чек |
| Категория | Заказов | Товаров продано | Выручка | Доля от выручки |
Доля форматируется как процент.
| Место | Товар | Категория | Количество продаж | Выручка |
| ID заказа | Дата | Клиент | Статус | Товар | Категория | Кол-во | Цена за единицу | Сумма позиции |
Оформление:
- жирные белые заголовки на тёмно-синей заливке;
- зафиксированная строка заголовков (freeze panes);
- автофильтры на табличных листах;
- денежный и процентный форматы;
- настроенная ширина колонок.
Выручка считается только по заказам со статусами
paidиcompleted— это коммерчески корректная логика «реализованной выручки».
Worker — отдельный процесс (python -m app.workers.report_worker). Алгоритм:
- Слушает очередь Redis
reports:queueкомандойBLPOPс таймаутом. - Получив задачу, парсит JSON-сообщение:
{ "report_id": "uuid", "report_type": "sales", "params": { "date_from": "...", "date_to": "...", "format": "xlsx" }, "attempt": 1 } - Находит
ReportTaskв БД, меняет статус наprocessing, ставитstarted_at. - Выбирает заказы за указанный период из PostgreSQL.
- Считает аналитику: суммарные показатели, разбивку по дням, по категориям, топ товаров, детализацию.
- Генерирует XLSX через
openpyxl. - Сохраняет файл в
storage/reports/report_{uuid}.xlsx. - Записывает путь в
file_path, меняет статус наdone, ставитfinished_at. - При ошибке — перекладывает задачу обратно в очередь с инкрементом
attempt. После исчерпанияattempt >= 3— статус задачи становитсяfailed, текст ошибки сохраняется вerror_message. - Если в периоде нет заказов — отчёт всё равно формируется с нулевыми показателями.
# 1. Создаём демонстрационные данные
curl -X POST http://localhost:8000/seed
# 2. Создаём задачу отчёта
curl -X POST http://localhost:8000/reports/sales \
-H "Content-Type: application/json" \
-d '{"date_from":"2026-02-20","date_to":"2026-05-20","format":"xlsx"}'
# → {"id":"<REPORT_ID>","status":"queued"}
# 3. Смотрим статус (повторяем, пока не будет done)
curl http://localhost:8000/reports/<REPORT_ID>
# 4. Скачиваем готовый отчёт
curl -OJ http://localhost:8000/reports/<REPORT_ID>/download
# 5. (опционально) список всех задач
curl http://localhost:8000/reportsapp/
├── main.py
├── config.py
├── database.py
├── redis_client.py
├── models/
│ ├── product.py
│ ├── order.py
│ └── report.py
├── schemas/
│ ├── report.py
│ └── seed.py
├── routes/
│ ├── reports.py
│ └── seed.py
├── services/
│ ├── report_service.py
│ ├── queue_service.py
│ ├── sales_analytics_service.py
│ └── file_service.py
├── workers/
│ └── report_worker.py
├── utils/
│ ├── report_generator.py
│ └── seed_generator.py
└── repositories/
├── report_repository.py
├── order_repository.py
└── product_repository.py
storage/
└── reports/
docker-compose.yml
Dockerfile
requirements.txt
README.md