8.7 KiB
8.7 KiB
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
- Добавить PostgreSQL-адаптер (
app/adapters/postgres/) для управления async-сессиями SQLAlchemy (AsyncEngine,async_sessionmaker). - Добавить репозиторий заявок (
app/repositories/order/) с методомcreate_order()для сохранения данных заявки вместе со ссылкой на оплату в PostgreSQL. - Определить SQLAlchemy model для таблицы заявок в
app/repositories/order/models.py. - Создать Alembic-миграцию для создания таблицы заявок.
- Расширить
AggregatorService.init_payment(): после успешного полученияpayment_urlот TBank adapter сохранять данныеInitPaymentRequestвместе сpayment_urlчерез order repository. - Добавить секцию конфигурации PostgreSQL (
PostgresConfig) вapp/config.pyи пример вconfig.yaml. - Добавить сервис 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.pyMUST быть обновлён для включения секции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/init-paymentпродолжают возвращать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 -qpoetry run pytest tests/services/test_init_payment.py -qpoetry run pytest tests/config/test_config_sections.py -qpoetry run pytest tests/smoke/test_app_import.py -qpoetry run pytest -qpython3 spec/gen_spec_index.py --check