Добавлено сохранение заказов в postgres
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 028
|
||||
title: Add PostgreSQL adapter, order repository and persist order after payment link creation
|
||||
status: DONE
|
||||
created: 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/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 -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`
|
||||
Reference in New Issue
Block a user