feat: add withdraw
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
from src.infrastructure.database.models.base import Base
|
||||
from src.infrastructure.database.models.order import Order
|
||||
from src.infrastructure.database.models.payment import Payment
|
||||
from src.infrastructure.database.models.sbp_withdrawal import SbpWithdrawal
|
||||
from src.infrastructure.database.models.user import UserModel
|
||||
|
||||
|
||||
__all__ = ['Base','Order','Payment','UserModel']
|
||||
__all__ = ['Base','Order','Payment','SbpWithdrawal','UserModel']
|
||||
|
||||
40
src/infrastructure/database/models/sbp_withdrawal.py
Normal file
40
src/infrastructure/database/models/sbp_withdrawal.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from sqlalchemy import DateTime,Enum as SAEnum,ForeignKey,Numeric,String,Text
|
||||
from sqlalchemy.orm import Mapped,mapped_column
|
||||
from src.application.domain.enums import SbpWithdrawalStatus
|
||||
from src.infrastructure.database.models.base import Base
|
||||
from src.infrastructure.database.models.mixins import AuditTimestampsMixin,UlidPrimaryKeyMixin
|
||||
|
||||
|
||||
class SbpWithdrawal(Base,UlidPrimaryKeyMixin,AuditTimestampsMixin):
|
||||
__tablename__='sbp_withdrawals'
|
||||
|
||||
user_id: Mapped[str]=mapped_column(
|
||||
String(26),
|
||||
ForeignKey('users.id',ondelete='RESTRICT'),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
bank_id: Mapped[str]=mapped_column(String(32),nullable=False,index=True)
|
||||
bank_name: Mapped[str]=mapped_column(String(255),nullable=False)
|
||||
phone: Mapped[str]=mapped_column(String(32),nullable=False)
|
||||
wallet_address: Mapped[str]=mapped_column(String(255),nullable=False)
|
||||
usdt_amount: Mapped[Decimal]=mapped_column(Numeric(38,2),nullable=False)
|
||||
rub_amount: Mapped[Decimal]=mapped_column(Numeric(38,2),nullable=False)
|
||||
usdt_exchange_rate: Mapped[Decimal]=mapped_column(Numeric(38,2),nullable=False)
|
||||
service_fee_rate: Mapped[Decimal]=mapped_column(Numeric(8,4),nullable=False)
|
||||
service_fee_usdt: Mapped[Decimal]=mapped_column(Numeric(38,2),nullable=False)
|
||||
mozen_order_id: Mapped[str | None]=mapped_column(String(128),nullable=True,index=True)
|
||||
trace_id: Mapped[str]=mapped_column(String(64),nullable=False,index=True)
|
||||
status: Mapped[SbpWithdrawalStatus]=mapped_column(
|
||||
SAEnum(SbpWithdrawalStatus,name='sbp_withdrawal_status_enum',values_callable=lambda x:[e.value for e in x]),
|
||||
nullable=False,
|
||||
index=True,
|
||||
default=SbpWithdrawalStatus.CREATED,
|
||||
)
|
||||
provider_error: Mapped[str | None]=mapped_column(Text,nullable=True)
|
||||
usdt_received_at: Mapped[datetime | None]=mapped_column(DateTime(timezone=True),nullable=True)
|
||||
payout_created_at: Mapped[datetime | None]=mapped_column(DateTime(timezone=True),nullable=True)
|
||||
payout_completed_at: Mapped[datetime | None]=mapped_column(DateTime(timezone=True),nullable=True)
|
||||
@@ -0,0 +1,139 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import replace
|
||||
from datetime import datetime,timezone
|
||||
from sqlalchemy import desc,select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from src.application.abstractions.repositories import ISbpWithdrawalRepository
|
||||
from src.application.contracts import ILogger
|
||||
from src.application.domain.entities import SbpWithdrawalEntity
|
||||
from src.application.domain.enums import SbpWithdrawalStatus
|
||||
from src.infrastructure.database.models.sbp_withdrawal import SbpWithdrawal
|
||||
|
||||
|
||||
class SbpWithdrawalRepository(ISbpWithdrawalRepository):
|
||||
def __init__(self,session: AsyncSession,logger: ILogger):
|
||||
self._session=session
|
||||
self._logger=logger
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _to_entity(model: SbpWithdrawal) -> SbpWithdrawalEntity:
|
||||
return SbpWithdrawalEntity(
|
||||
id=model.id,
|
||||
created_at=model.created_at,
|
||||
updated_at=model.updated_at,
|
||||
user_id=model.user_id,
|
||||
bank_id=model.bank_id,
|
||||
bank_name=model.bank_name,
|
||||
phone=model.phone,
|
||||
wallet_address=model.wallet_address,
|
||||
usdt_amount=model.usdt_amount,
|
||||
rub_amount=model.rub_amount,
|
||||
usdt_exchange_rate=model.usdt_exchange_rate,
|
||||
service_fee_rate=model.service_fee_rate,
|
||||
service_fee_usdt=model.service_fee_usdt,
|
||||
mozen_order_id=model.mozen_order_id,
|
||||
trace_id=model.trace_id,
|
||||
status=model.status,
|
||||
provider_error=model.provider_error,
|
||||
usdt_received_at=model.usdt_received_at,
|
||||
payout_created_at=model.payout_created_at,
|
||||
payout_completed_at=model.payout_completed_at,
|
||||
)
|
||||
|
||||
|
||||
async def create(self,withdrawal: SbpWithdrawalEntity) -> SbpWithdrawalEntity:
|
||||
values={
|
||||
'user_id':withdrawal.user_id,
|
||||
'bank_id':withdrawal.bank_id,
|
||||
'bank_name':withdrawal.bank_name,
|
||||
'phone':withdrawal.phone,
|
||||
'wallet_address':withdrawal.wallet_address,
|
||||
'usdt_amount':withdrawal.usdt_amount,
|
||||
'rub_amount':withdrawal.rub_amount,
|
||||
'usdt_exchange_rate':withdrawal.usdt_exchange_rate,
|
||||
'service_fee_rate':withdrawal.service_fee_rate,
|
||||
'service_fee_usdt':withdrawal.service_fee_usdt,
|
||||
'mozen_order_id':withdrawal.mozen_order_id,
|
||||
'trace_id':withdrawal.trace_id,
|
||||
'status':withdrawal.status or SbpWithdrawalStatus.CREATED,
|
||||
'provider_error':withdrawal.provider_error,
|
||||
'usdt_received_at':withdrawal.usdt_received_at,
|
||||
'payout_created_at':withdrawal.payout_created_at,
|
||||
'payout_completed_at':withdrawal.payout_completed_at,
|
||||
}
|
||||
if withdrawal.id is not None:
|
||||
values['id']=withdrawal.id
|
||||
model=SbpWithdrawal(**values)
|
||||
self._session.add(model)
|
||||
await self._session.flush()
|
||||
return replace(withdrawal,id=model.id,created_at=model.created_at,updated_at=model.updated_at)
|
||||
|
||||
|
||||
async def get_by_id(self,withdraw_id: str) -> SbpWithdrawalEntity | None:
|
||||
stmt=select(SbpWithdrawal).where(SbpWithdrawal.id==withdraw_id)
|
||||
model=await self._session.scalar(stmt)
|
||||
if model is None:
|
||||
return None
|
||||
return self._to_entity(model)
|
||||
|
||||
|
||||
async def get_by_id_for_user(self,*,withdraw_id: str,user_id: str) -> SbpWithdrawalEntity | None:
|
||||
stmt=select(SbpWithdrawal).where(SbpWithdrawal.id==withdraw_id,SbpWithdrawal.user_id==user_id)
|
||||
model=await self._session.scalar(stmt)
|
||||
if model is None:
|
||||
return None
|
||||
return self._to_entity(model)
|
||||
|
||||
|
||||
async def list_by_user_id(self,*,user_id: str,limit: int,offset: int) -> list[SbpWithdrawalEntity]:
|
||||
stmt=(
|
||||
select(SbpWithdrawal)
|
||||
.where(SbpWithdrawal.user_id==user_id)
|
||||
.order_by(desc(SbpWithdrawal.created_at))
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
)
|
||||
result=await self._session.scalars(stmt)
|
||||
return [self._to_entity(model) for model in result.all()]
|
||||
|
||||
|
||||
async def update_status(
|
||||
self,
|
||||
*,
|
||||
withdraw_id: str,
|
||||
status: SbpWithdrawalStatus,
|
||||
provider_error: str | None = None,
|
||||
) -> None:
|
||||
model=await self._session.get(SbpWithdrawal,withdraw_id)
|
||||
if model is None:
|
||||
return
|
||||
model.status=status
|
||||
model.provider_error=provider_error
|
||||
if status==SbpWithdrawalStatus.USDT_RECEIVED:
|
||||
model.usdt_received_at=datetime.now(timezone.utc)
|
||||
if status==SbpWithdrawalStatus.PAYOUT_PROCESSING:
|
||||
model.payout_created_at=datetime.now(timezone.utc)
|
||||
if status==SbpWithdrawalStatus.PAYOUT_COMPLETED:
|
||||
model.payout_completed_at=datetime.now(timezone.utc)
|
||||
await self._session.flush()
|
||||
|
||||
|
||||
async def update_payout_created(
|
||||
self,
|
||||
*,
|
||||
withdraw_id: str,
|
||||
mozen_order_id: str | None,
|
||||
status: SbpWithdrawalStatus,
|
||||
provider_error: str | None,
|
||||
) -> None:
|
||||
model=await self._session.get(SbpWithdrawal,withdraw_id)
|
||||
if model is None:
|
||||
return
|
||||
model.mozen_order_id=mozen_order_id
|
||||
model.status=status
|
||||
model.provider_error=provider_error
|
||||
model.payout_created_at=datetime.now(timezone.utc)
|
||||
if status==SbpWithdrawalStatus.PAYOUT_COMPLETED:
|
||||
model.payout_completed_at=datetime.now(timezone.utc)
|
||||
await self._session.flush()
|
||||
@@ -1,9 +1,10 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
from src.application.abstractions import IUnitOfWork
|
||||
from src.application.abstractions.repositories import IOrderRepository,IPaymentRepository,IUserRepository
|
||||
from src.application.abstractions.repositories import IOrderRepository,IPaymentRepository,ISbpWithdrawalRepository,IUserRepository
|
||||
from src.application.contracts import ILogger
|
||||
from src.infrastructure.database.repositories.order_repository import OrderRepository
|
||||
from src.infrastructure.database.repositories.payment_repository import PaymentRepository
|
||||
from src.infrastructure.database.repositories.sbp_withdrawal_repository import SbpWithdrawalRepository
|
||||
from src.infrastructure.database.repositories.user_repository import UserRepository
|
||||
|
||||
|
||||
@@ -14,6 +15,7 @@ class UnitOfWork(IUnitOfWork):
|
||||
self._session: AsyncSession = None
|
||||
self._order_repository: IOrderRepository | None = None
|
||||
self._payment_repository: IPaymentRepository | None = None
|
||||
self._sbp_withdrawal_repository: ISbpWithdrawalRepository | None = None
|
||||
self._user_repository: IUserRepository | None = None
|
||||
self._logger: ILogger = logger
|
||||
|
||||
@@ -21,6 +23,7 @@ class UnitOfWork(IUnitOfWork):
|
||||
self._session = self.session_factory()
|
||||
self._order_repository = None
|
||||
self._payment_repository = None
|
||||
self._sbp_withdrawal_repository = None
|
||||
self._user_repository = None
|
||||
return self
|
||||
|
||||
@@ -49,6 +52,13 @@ class UnitOfWork(IUnitOfWork):
|
||||
return self._payment_repository
|
||||
|
||||
|
||||
@property
|
||||
def sbp_withdrawal_repository(self) -> ISbpWithdrawalRepository:
|
||||
if self._sbp_withdrawal_repository is None:
|
||||
self._sbp_withdrawal_repository = SbpWithdrawalRepository(session=self._session,logger=self._logger)
|
||||
return self._sbp_withdrawal_repository
|
||||
|
||||
|
||||
@property
|
||||
def user_repository(self) -> IUserRepository:
|
||||
if self._user_repository is None:
|
||||
|
||||
Reference in New Issue
Block a user