From 736f04bb7e14acf7d8438e4f3f80fc4eb034e241 Mon Sep 17 00:00:00 2001 From: Redsandyg Date: Wed, 11 Jun 2025 11:01:26 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=B8=D0=B4=D0=B5=D0=BD=D1=82=D0=B8=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=D1=82=D0=BE=D1=80=D1=8B=20API=20=D0=B8=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=B3=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D1=80=D0=B0=D1=82=D0=BE=D1=80=20=D1=83=D0=BD=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D1=85=20=D0=B8=D0=B4=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=82=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=D0=B6.=20=D0=92=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B4=D0=B0=D0=B6=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=BA=D0=B8=20=D0=BD=D0=B0=20=D0=BD=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D1=87=D0=B8=D0=B5=20=D0=B1=D0=B0=D0=BB=D0=B0=D0=BD=D1=81=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B0=D0=B3=D0=B5=D0=BD=D1=82=D0=B0=20=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=B0=D0=BD=D0=B8=D0=B8,=20=D0=B0=20=D1=82?= =?UTF-8?q?=D0=B0=D0=BA=D0=B6=D0=B5=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BE?= =?UTF-8?q?=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B1?= =?UTF-8?q?=D0=B0=D0=BB=D0=B0=D0=BD=D1=81=D0=BE=D0=B2.=20=D0=A3=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=83=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=D0=BD=D1=8C=20=D0=B8=D0=B7=D0=BE=D0=BB=D1=8F?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B7=D0=B0=D0=BA=D1=86=D0=B8=D0=B9.=20=D0=9E=D0=B1?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D0=B8=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=8F=D1=81=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- call_sale_api.py | 7 +++-- integration_api.py | 73 ++++++++++++++++------------------------------ sql_models.py | 2 +- 3 files changed, 30 insertions(+), 52 deletions(-) diff --git a/call_sale_api.py b/call_sale_api.py index 3157e2f..6f4f5db 100644 --- a/call_sale_api.py +++ b/call_sale_api.py @@ -1,17 +1,18 @@ +import uuid import requests import json # Конфигурация API BASE_URL = "http://127.0.0.1:8001" -API_KEY = "7c06945b-f4b8-4929-8350-b9841405a609" -REF = "8c514fcb-7a79-4ed1-9c8a-0af2fcab88c0" +API_KEY = "672a1437-70e8-461f-9bff-20f5ce4a023d" +REF = "9bd1a6bd-98e1-48f4-a120-3b3d016011c0" # Данные для запроса на создание продажи # Замените эти значения на актуальные для вашей продажи sale_data = { "cost": 100.50, # Стоимость продажи "ref": REF, # Ваш реферальный код - "sale_id": "UNIQUE_SALE_ID_12345" # Уникальный идентификатор продажи для вашей компании + "sale_id": str(uuid.uuid4()) # Уникальный идентификатор продажи для вашей компании } # Эндпоинты diff --git a/integration_api.py b/integration_api.py index c7e60d1..98928b4 100644 --- a/integration_api.py +++ b/integration_api.py @@ -61,8 +61,14 @@ async def create_sale( """ Регистрирует новую продажу в системе. """ + # Устанавливаем уровень изоляции для текущей транзакции + db.connection(execution_options={'isolation_level': 'SERIALIZABLE'}) + # 1. Найти Ref по `ref` и `company.id` # Сначала находим TgAgent, связанный с компанией, затем Ref + + + tg_agent = db.exec( select(TgAgent) .join(Ref) @@ -90,7 +96,18 @@ async def create_sale( # 3. Рассчитать crediting crediting_amount = req.cost * (company.agent_commission / 100.0) - # 4. Создать Sale + # 4. Проверить и обновить AgentBalance и CompanyBalance + # AgentBalance + agent_balance = db.exec(select(AgentBalance).where(AgentBalance.tg_agent_id == tg_agent.id)).first() + if not agent_balance: + raise HTTPException(status_code=404, detail="Баланс агента не найден") + + # CompanyBalance + company_balance = db.exec(select(CompanyBalance).where(CompanyBalance.company_id == company.id)).first() + if not company_balance: + raise HTTPException(status_code=404, detail="Баланс компании не найден") + + # 5. Создать Sale new_sale = Sale( cost=req.cost, crediting=crediting_amount, @@ -100,28 +117,9 @@ async def create_sale( sale_dttm=datetime.utcnow() ) db.add(new_sale) - db.commit() - db.refresh(new_sale) - - # 5. Обновить AgentBalance и CompanyBalance - # AgentBalance - agent_balance = db.exec(select(AgentBalance).where(AgentBalance.tg_agent_id == tg_agent.id)).first() - if not agent_balance: - agent_balance = AgentBalance(tg_agent_id=tg_agent.id, available_balance=0.0, frozen_balance=0.0) - db.add(agent_balance) - db.commit() - db.refresh(agent_balance) - - # CompanyBalance - company_balance = db.exec(select(CompanyBalance).where(CompanyBalance.company_id == company.id)).first() - if not company_balance: - company_balance = CompanyBalance(company_id=company.id, available_balance=0.0, pending_balance=0.0) - db.add(company_balance) - db.commit() - db.refresh(company_balance) # Создать AgentTransaction - agent_transaction_status = TransactionStatus.NEW if company.auto_approve_transactions else TransactionStatus.WAITING + agent_transaction_status = TransactionStatus.DONE # auto_approve_transactions отвечает только за апрув агентских транзакций на вывод agent_transaction = AgentTransaction( tg_agent_id=tg_agent.id, amount=crediting_amount, @@ -129,38 +127,17 @@ async def create_sale( transaction_group=uuid.uuid4() ) db.add(agent_transaction) - db.commit() - db.refresh(agent_transaction) - # Создать PartnerTransaction - partner_transaction_status = TransactionStatus.NEW if company.auto_approve_transactions else TransactionStatus.PROCESS - partner_transaction = PartnerTransaction( - company_id=company.id, - type="sale_crediting", - amount=crediting_amount, - status=partner_transaction_status.value, - transaction_group=agent_transaction.transaction_group, - agent_transaction_id=agent_transaction.id - ) - db.add(partner_transaction) - db.commit() - db.refresh(partner_transaction) + # Обновление балансов для продаж - всегда в замороженный/ожидающий баланс + agent_balance.frozen_balance += crediting_amount + company_balance.pending_balance -= crediting_amount + company_balance.updated_dttm = datetime.utcnow() - # Обновление балансов в зависимости от auto_approve_transactions - if company.auto_approve_transactions: - agent_balance.available_balance += crediting_amount - company_balance.available_balance -= crediting_amount - company_balance.updated_dttm = datetime.utcnow() - else: - agent_balance.frozen_balance += crediting_amount - company_balance.pending_balance -= crediting_amount - company_balance.updated_dttm = datetime.utcnow() - - db.add(agent_balance) - db.add(company_balance) db.commit() + db.refresh(new_sale) db.refresh(agent_balance) db.refresh(company_balance) + db.refresh(agent_transaction) return { "msg": "Продажа успешно зарегистрирована", diff --git a/sql_models.py b/sql_models.py index 1ac2564..4c871c2 100644 --- a/sql_models.py +++ b/sql_models.py @@ -12,7 +12,7 @@ class Company(SQLModel, table=True): key: str = Field(index=True, unique=True) create_dttm: datetime = Field(default_factory=datetime.utcnow) update_dttm: datetime = Field(default_factory=datetime.utcnow) - auto_approve_transactions: bool = Field(default=False) + auto_approve_transactions: bool = Field(default=False) # Отвечает за автоматическое одобрение агентских транзакций на вывод. integration_tokens: List["IntegrationToken"] = Relationship(back_populates="company")