Files
g2s-aggregator/spec/tasks/028_add_postgresql_order_persistence.md
T
2026-05-13 16:35:22 +03:00

8.7 KiB
Raw Blame History

id, title, status, created
id title status created
028 Add PostgreSQL adapter, order repository and persist order after payment link creation DONE 2026-04-12

Context

После создания ссылки на оплату через TBank adapter данные заявки нигде не сохраняются. Необходимо добавить PostgreSQL-адаптер для управления подключением к базе данных, репозиторий для сохранения данных заявки и интегрировать сохранение в существующий flow AggregatorService.init_payment(). Зависимости sqlalchemy (2.0.49) и asyncpg (0.31.0) уже присутствуют в pyproject.toml. Alembic (1.18.4) также доступен для миграций.

Goal

  1. Добавить PostgreSQL-адаптер (app/adapters/postgres/) для управления async-сессиями SQLAlchemy (AsyncEngine, async_sessionmaker).
  2. Добавить репозиторий заявок (app/repositories/order/) с методом create_order() для сохранения данных заявки вместе со ссылкой на оплату в PostgreSQL.
  3. Определить SQLAlchemy model для таблицы заявок в app/repositories/order/models.py.
  4. Создать Alembic-миграцию для создания таблицы заявок.
  5. Расширить AggregatorService.init_payment(): после успешного получения payment_url от TBank adapter сохранять данные InitPaymentRequest вместе с payment_url через order repository.
  6. Добавить секцию конфигурации PostgreSQL (PostgresConfig) в app/config.py и пример в config.yaml.
  7. Добавить сервис PostgreSQL в docker-compose.yml.

Constraints

  • PostgreSQL adapter (app/adapters/postgres/) MUST содержать только управление подключением (engine, session factory). Без бизнес-логики, без SQL-запросов.
  • Repository (app/repositories/order/) MUST содержать только операции с базой данных. Без бизнес-решений, без workflow-логики.
  • Service MUST оркестрировать вызовы TBank adapter и order repository. Если сохранение в БД завершается ошибкой после успешного получения payment_url, Service MUST всё равно вернуть payment_url клиенту (сохранение не должно блокировать ответ); ошибку сохранения логировать.
  • SQLAlchemy model MUST использовать sqlalchemy.orm.DeclarativeBase (SQLAlchemy 2.0 style).
  • Для миграций использовать Alembic с async-конфигурацией (asyncpg).
  • Конфигурация PostgreSQL MUST быть в отдельной секции postgres в config.yaml с обязательным полем dsn; config.yaml уже в .gitignore.
  • _RequiredYamlSections в app/config.py MUST быть обновлён для включения секции postgres.
  • Order repository передаётся в AggregatorService через dependency injection (новый параметр конструктора).
  • PostgreSQL adapter создаётся в wiring (_build_aggregator_service) в controller и передаёт session factory в order repository.
  • Таблица заявок MUST содержать как минимум: id (UUID, PK), order_uuid (str, unique), payment_url (str), price (int, копейки), tariff_code (int), sender (JSONB), recipient (JSONB), from_location (JSONB), to_location (JSONB), packages (JSONB), services (JSONB, nullable), comment (str, nullable), created_at (timestamp with timezone, server default).
  • Scope НЕ включает: чтение/обновление/удаление заявок, API-endpoint для списка заявок, webhook-обработку платёжных уведомлений, изменения price flow, address suggestion flow.
  • НЕ изменять существующие тесты TBank adapter, не изменять поведение price и address suggestion endpoints.

Acceptance criteria

  • В app/adapters/postgres/ существует модуль с функцией создания AsyncEngine и async_sessionmaker из конфигурации.
  • В app/repositories/order/ существует OrderRepository с async-методом create_order(session, order_data), сохраняющим запись заявки.
  • В app/repositories/order/models.py определена SQLAlchemy ORM model таблицы orders со всеми обязательными полями.
  • Alembic инициализирован с async-конфигурацией; существует миграция для создания таблицы orders.
  • AggregatorService.__init__() принимает опциональный order_repository через DI.
  • AggregatorService.init_payment() после успешного получения payment_url вызывает order_repository.create_order() с данными из InitPaymentRequest и payment_url.
  • Если order_repository.create_order() выбрасывает исключение, init_payment() логирует ошибку и возвращает InitPaymentResponse(payment_url=...) без ошибки клиенту.
  • В app/config.py добавлена PostgresConfig с полем dsn: str.
  • Секция postgres присутствует в _RequiredYamlSections.
  • В docker-compose.yml добавлен сервис postgres и app зависит от него.
  • Wiring в _build_aggregator_service создаёт PostgreSQL engine, session factory, OrderRepository и передаёт его в AggregatorService.
  • Запросы к эндпоинту POST /api/v1/delivery/order продолжают возвращать InitPaymentResponse с payment_url.

Definition of Done

  • Создан модуль app/adapters/postgres/ с engine/session factory.
  • Создан app/repositories/order/repository.py с OrderRepository.create_order().
  • Создан app/repositories/order/models.py с ORM model таблицы orders.
  • Alembic инициализирован (alembic.ini, alembic/), создана миграция для таблицы orders.
  • Добавлена PostgresConfig в app/config.py; _RequiredYamlSections обновлён.
  • AggregatorService принимает order_repository через DI и использует его в init_payment().
  • Ошибки сохранения заявки не блокируют возврат payment_url клиенту.
  • Обновлён wiring в app/controllers/v1/delivery.py.
  • Добавлен сервис postgres в docker-compose.yml.
  • config.yaml пример содержит секцию postgres.
  • config.test.yaml содержит секцию postgres (может использовать sqlite или тестовый DSN).
  • Все существующие тесты продолжают проходить.
  • Добавлены новые тесты.

Tests

  • Добавить tests/repositories/order/test_repository.py: проверка create_order() с in-memory SQLite async engine (SQLAlchemy async); проверка, что все обязательные поля сохраняются; проверка обработки дублирования order_uuid (unique constraint).
  • Обновить tests/services/test_init_payment.py: добавить test case, где order_repository.create_order() вызывается после успешного создания payment link; добавить test case, где order_repository.create_order() выбрасывает исключение, а init_payment() всё равно возвращает payment_url.
  • Обновить tests/config/test_config_sections.py для проверки наличия секции postgres в yaml.
  • При необходимости обновить tests/smoke/test_app_import.py для проверки wiring order repository.

Commands

  • poetry run pytest tests/repositories/order/test_repository.py -q
  • poetry run pytest tests/services/test_init_payment.py -q
  • poetry run pytest tests/config/test_config_sections.py -q
  • poetry run pytest tests/smoke/test_app_import.py -q
  • poetry run pytest -q
  • python3 spec/gen_spec_index.py --check