Рефакторинг структуры проекта: перемещение функций и логики работы с базой данных в новый файл helpers_bff.py. Обновлены импорты в fill_db.py и main.py для использования новых функций. Удалены устаревшие функции и классы из main.py, улучшена организация кода.
This commit is contained in:
parent
155d1002fc
commit
6e804953c0
@ -1,11 +1,11 @@
|
||||
import random
|
||||
from uuid import uuid4
|
||||
from sqlmodel import Session
|
||||
from main import AUTH_DB_ENGINE, TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance
|
||||
from sql_models import TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance
|
||||
from sqlalchemy import text
|
||||
from datetime import datetime, timedelta
|
||||
from hashlib import sha256
|
||||
from passlib.context import CryptContext
|
||||
from helpers_bff import AUTH_DB_ENGINE, get_password_hash
|
||||
|
||||
|
||||
# Константа: список user_ids
|
||||
@ -63,11 +63,6 @@ LOGINS = [
|
||||
ALL_DESCRIPTIONS = DESCRIPTIONS
|
||||
|
||||
# ---
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
def get_password_hash(password):
|
||||
return pwd_context.hash(password)
|
||||
|
||||
def get_date_list(days=7):
|
||||
today = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
return [today - timedelta(days=i) for i in range(days, -1, -1)]
|
||||
|
||||
90
helpers_bff.py
Normal file
90
helpers_bff.py
Normal file
@ -0,0 +1,90 @@
|
||||
from sqlmodel import Session, select, create_engine
|
||||
from passlib.context import CryptContext
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
from bff_models import Token, TransactionStatus
|
||||
from sql_models import Company, TgAgent, Account, AgentBalance, AgentTransaction, PartnerTransaction, Sale, Ref
|
||||
from hashlib import sha256
|
||||
import jwt
|
||||
from jwt.exceptions import InvalidTokenError
|
||||
from fastapi import HTTPException, status, Depends, Request
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
# Конфигурация
|
||||
AUTH_DATABASE_ADDRESS = "sqlite:///partner.db"
|
||||
AUTH_DB_ENGINE = create_engine(AUTH_DATABASE_ADDRESS, echo=True)
|
||||
|
||||
SECRET_KEY = "supersecretkey"
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 60
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
def get_tg_agent_by_tg_id(db: Session, tg_id: int) -> Optional[TgAgent]:
|
||||
statement = select(TgAgent).where(TgAgent.tg_id == tg_id)
|
||||
return db.exec(statement).first()
|
||||
|
||||
def get_db():
|
||||
with Session(AUTH_DB_ENGINE) as session:
|
||||
yield session
|
||||
|
||||
def get_current_account(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
login: str = payload.get("sub")
|
||||
if login is None:
|
||||
raise credentials_exception
|
||||
except InvalidTokenError:
|
||||
raise credentials_exception
|
||||
account = get_account_by_login(db, login)
|
||||
if account is None:
|
||||
raise credentials_exception
|
||||
return account
|
||||
|
||||
async def get_current_tg_agent(request: Request, db: Session = Depends(get_db)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
auth_header = request.headers.get("Authorization")
|
||||
if not auth_header or not auth_header.startswith("Bearer "):
|
||||
raise credentials_exception
|
||||
hash_value = auth_header.replace("Bearer ", "").strip()
|
||||
tg_agent = db.exec(select(TgAgent).where(TgAgent.hash == hash_value)).first()
|
||||
if tg_agent is None:
|
||||
raise credentials_exception
|
||||
return tg_agent
|
||||
|
||||
def authenticate_tg_agent(engine, tg_id: int):
|
||||
with Session(engine) as db:
|
||||
tg_agent = get_tg_agent_by_tg_id(db, tg_id)
|
||||
if not tg_agent:
|
||||
return None
|
||||
return tg_agent
|
||||
|
||||
def create_access_token(data: dict, expires_delta: timedelta = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
def verify_password(plain_password, hashed_password):
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
def get_password_hash(password):
|
||||
return pwd_context.hash(password)
|
||||
|
||||
def get_account_by_login(db: Session, login: str) -> Optional[Account]:
|
||||
statement = select(Account).where(Account.login == login)
|
||||
return db.exec(statement).first()
|
||||
324
main.py
324
main.py
@ -1,204 +1,76 @@
|
||||
import uuid
|
||||
from fastapi import FastAPI, Depends, HTTPException, status, Query, Body, Request
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from sqlmodel import SQLModel, Field, create_engine, Session, select
|
||||
from passlib.context import CryptContext
|
||||
from fastapi import (
|
||||
FastAPI,
|
||||
Depends,
|
||||
HTTPException,
|
||||
status,
|
||||
Query,
|
||||
Body,
|
||||
)
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlmodel import SQLModel, Session, select
|
||||
from typing import Optional, List, Dict
|
||||
from datetime import datetime, timedelta
|
||||
from bff_models import Token, AccountProfileUpdateRequest, AccountPasswordChangeRequest, AgentTransactionResponse, AutoApproveSettingsRequest, ApproveTransactionsRequest, TransactionStatus, RegisterResponse, DashboardCardsResponse, DashboardChartTotalResponse, DashboardChartAgentResponse, StatAgentsResponse, StatReferralsResponse, StatSalesResponse, BillingCardsResponse, BillingChartStatResponse, BillingChartPieResponse, AccountResponse, CompanyProfileResponse, AccountProfileResponse, AccountProfileUpdateResponse, AccountPasswordChangeResponse, AutoApproveSettingsGetResponse, AutoApproveSettingsUpdateResponse, ApproveTransactionsResult, TgAuthResponse, BillingPayoutsTransactionsResponse
|
||||
from tg_models import RefAddRequest, RefResponse, RegisterRequest, TokenRequest, RefAddResponse, RefStatResponse, StatResponse
|
||||
from uuid import uuid4
|
||||
from fastapi.responses import JSONResponse
|
||||
from datetime import timedelta, datetime
|
||||
from bff_models import (
|
||||
Token,
|
||||
RegisterResponse,
|
||||
DashboardCardsResponse,
|
||||
DashboardChartTotalResponse,
|
||||
DashboardChartAgentResponse,
|
||||
StatAgentsResponse,
|
||||
StatReferralsResponse,
|
||||
StatSalesResponse,
|
||||
BillingCardsResponse,
|
||||
BillingChartStatResponse,
|
||||
BillingChartPieResponse,
|
||||
AccountResponse,
|
||||
AccountProfileResponse,
|
||||
AccountProfileUpdateResponse,
|
||||
AccountPasswordChangeResponse,
|
||||
AutoApproveSettingsGetResponse,
|
||||
AutoApproveSettingsUpdateResponse,
|
||||
ApproveTransactionsResult,
|
||||
TgAuthResponse,
|
||||
BillingPayoutsTransactionsResponse,
|
||||
AccountProfileUpdateRequest,
|
||||
AccountPasswordChangeRequest,
|
||||
AutoApproveSettingsRequest,
|
||||
ApproveTransactionsRequest,
|
||||
AgentTransactionResponse,
|
||||
TransactionStatus
|
||||
)
|
||||
from tg_models import RefAddRequest, RefResponse, RegisterRequest, RefAddResponse, RefStatResponse, StatResponse
|
||||
from sql_models import (
|
||||
Company,
|
||||
TgAgent,
|
||||
Ref,
|
||||
Sale,
|
||||
AgentTransaction,
|
||||
PartnerTransaction,
|
||||
AgentBalance,
|
||||
Account
|
||||
)
|
||||
from sqlalchemy import func
|
||||
from hashlib import sha256
|
||||
import jwt
|
||||
from jwt.exceptions import InvalidTokenError
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from enum import Enum
|
||||
|
||||
# Конфигурация
|
||||
AUTH_DATABASE_ADDRESS = "sqlite:///partner.db"
|
||||
|
||||
#SQLModel
|
||||
class Company(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
commission: float # процент комиссии
|
||||
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) # Новое поле для автоподтверждения
|
||||
|
||||
class TgAgent(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
tg_id: int = Field(index=True, unique=True)
|
||||
chat_id: Optional[int] = None
|
||||
phone: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
login: Optional[str] = None
|
||||
hash: Optional[str] = None
|
||||
company_id: int = Field(foreign_key="company.id")
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class Ref(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
tg_agent_id: int = Field(foreign_key="tgagent.id")
|
||||
ref: str
|
||||
description: Optional[str] = None
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class Sale(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
cost: float
|
||||
crediting: float # сколько начислено за продажу
|
||||
ref: int = Field(foreign_key="ref.id")
|
||||
sale_id: str
|
||||
company_id: int = Field(foreign_key="company.id")
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class AgentTransaction(SQLModel, table=True):
|
||||
__tablename__ = "agent_transactions" # Указываем имя таблицы явно
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
tg_agent_id: int = Field(foreign_key="tgagent.id") # ID агента, связь с TgAgent
|
||||
amount: float # Используем float для DECIMAL(15,2)
|
||||
status: str # 'waiting', 'process', 'done', 'reject', 'error'
|
||||
transaction_group: uuid.UUID = Field(default_factory=uuid.uuid4) # UUID для группировки
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class PartnerTransaction(SQLModel, table=True):
|
||||
__tablename__ = "partner_transactions" # Указываем имя таблицы явно
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
company_id: int = Field(foreign_key="company.id") # ID партнера, связь с Company
|
||||
type: str # 'deposit', 'agent_payout', 'service_fee'
|
||||
amount: float # Используем float для DECIMAL(15,2)
|
||||
status: str # 'process', 'done', 'error'
|
||||
transaction_group: uuid.UUID # UUID для группировки, может быть связан с agent_transactions
|
||||
agent_transaction_id: Optional[int] = Field(default=None, foreign_key="agent_transactions.id") # Связь с агентской транзакцией
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class CompanyBalance(SQLModel, table=True):
|
||||
__tablename__ = "company_balances" # Указываем имя таблицы явно
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
company_id: int = Field(foreign_key="company.id", unique=True) # ID компании, уникальный баланс на компанию
|
||||
available_balance: float = Field(default=0.0) # Используем float для DECIMAL(15,2)
|
||||
pending_balance: float = Field(default=0.0) # Используем float для DECIMAL(15,2)
|
||||
updated_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class AgentBalance(SQLModel, table=True):
|
||||
__tablename__ = "agent_balances" # Указываем имя таблицы явно
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
tg_agent_id: int = Field(foreign_key="tgagent.id", unique=True) # ID агента, уникальный баланс на агента
|
||||
available_balance: float = Field(default=0.0) # Используем float для DECIMAL(15,2)
|
||||
frozen_balance: float = Field(default=0.0) # Используем float для DECIMAL(15,2)
|
||||
updated_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class Account(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
login: str = Field(index=True, unique=True)
|
||||
password_hash: str
|
||||
firstName: Optional[str] = None
|
||||
surname: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
company_id: int = Field(foreign_key="company.id")
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class AccountProfileUpdateRequest(BaseModel):
|
||||
firstName: str
|
||||
surname: str
|
||||
email: EmailStr
|
||||
phone: str
|
||||
|
||||
class AccountPasswordChangeRequest(BaseModel):
|
||||
currentPassword: str
|
||||
newPassword: str
|
||||
|
||||
|
||||
class TransactionStatus(str, Enum): # Определяем Enum для статусов
|
||||
WAITING = 'waiting'
|
||||
PROCESS = 'process'
|
||||
DONE = 'done'
|
||||
REJECT = 'reject'
|
||||
ERROR = 'error'
|
||||
NEW = 'new' # Новый статус
|
||||
|
||||
|
||||
# Новая модель ответа для агентских транзакций с именем агента
|
||||
class AgentTransactionResponse(BaseModel):
|
||||
amount: float
|
||||
status: TransactionStatus # Используем Enum
|
||||
transaction_group: uuid.UUID
|
||||
create_dttm: datetime
|
||||
update_dttm: datetime
|
||||
agent_name: Optional[str] = None # Поле для имени агента
|
||||
|
||||
import hashlib
|
||||
from helpers_bff import (
|
||||
AUTH_DB_ENGINE,
|
||||
get_db,
|
||||
get_tg_agent_by_tg_id,
|
||||
get_current_account,
|
||||
get_current_tg_agent,
|
||||
create_access_token,
|
||||
verify_password,
|
||||
get_account_by_login,
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES,
|
||||
pwd_context,
|
||||
)
|
||||
|
||||
# Создание движка базы данных
|
||||
AUTH_DB_ENGINE = create_engine(AUTH_DATABASE_ADDRESS, echo=True)
|
||||
SQLModel.metadata.create_all(AUTH_DB_ENGINE)
|
||||
|
||||
|
||||
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||
|
||||
# FastAPI app
|
||||
app = FastAPI()
|
||||
|
||||
# CRUD
|
||||
|
||||
def get_tg_agent_by_tg_id(db: Session, tg_id: int) -> Optional[TgAgent]:
|
||||
statement = select(TgAgent).where(TgAgent.tg_id == tg_id)
|
||||
return db.exec(statement).first()
|
||||
|
||||
# Dependency
|
||||
|
||||
def get_db():
|
||||
with Session(AUTH_DB_ENGINE) as session:
|
||||
yield session
|
||||
|
||||
def get_current_account(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
login: str = payload.get("sub")
|
||||
if login is None:
|
||||
raise credentials_exception
|
||||
except InvalidTokenError:
|
||||
raise credentials_exception
|
||||
account = get_account_by_login(db, login)
|
||||
if account is None:
|
||||
raise credentials_exception
|
||||
return account
|
||||
|
||||
# Авторизация
|
||||
async def get_current_tg_agent(request: Request, db: Session = Depends(get_db)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
auth_header = request.headers.get("Authorization")
|
||||
if not auth_header or not auth_header.startswith("Bearer "):
|
||||
raise credentials_exception
|
||||
hash_value = auth_header.replace("Bearer ", "").strip()
|
||||
tg_agent = db.exec(select(TgAgent).where(TgAgent.hash == hash_value)).first()
|
||||
if tg_agent is None:
|
||||
raise credentials_exception
|
||||
return tg_agent
|
||||
|
||||
# Регистрация
|
||||
|
||||
|
||||
@app.post("/register", tags=["partner-tg"], response_model=RegisterResponse)
|
||||
def register(req: RegisterRequest, db: Session = Depends(get_db)):
|
||||
@ -219,7 +91,7 @@ def register(req: RegisterRequest, db: Session = Depends(get_db)):
|
||||
company = db.exec(select(Company).where(Company.key == company_key)).first()
|
||||
if not company:
|
||||
raise HTTPException(status_code=400, detail="Компания с таким ключом не найдена")
|
||||
hash_value = sha256(f"{tg_id}sold".encode()).hexdigest()
|
||||
hash_value = hashlib.sha256(f"{tg_id}sold".encode()).hexdigest()
|
||||
new_tg_agent = TgAgent(
|
||||
tg_id=tg_id,
|
||||
chat_id=chat_id,
|
||||
@ -234,30 +106,6 @@ def register(req: RegisterRequest, db: Session = Depends(get_db)):
|
||||
db.refresh(new_tg_agent)
|
||||
return {"msg": "TgAgent registered successfully"}
|
||||
|
||||
def authenticate_tg_agent(engine, tg_id: int):
|
||||
with Session(engine) as db:
|
||||
tg_agent = get_tg_agent_by_tg_id(db, tg_id)
|
||||
if not tg_agent:
|
||||
return None
|
||||
return tg_agent
|
||||
|
||||
|
||||
|
||||
# Авторизация
|
||||
|
||||
SECRET_KEY = "supersecretkey" # Лучше вынести в .env
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 60
|
||||
|
||||
def create_access_token(data: dict, expires_delta: timedelta = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
@app.post("/token", response_model=Token, tags=["bff", "token"])
|
||||
def login_account_for_access_token(
|
||||
@ -299,7 +147,7 @@ def add_ref(req: RefAddRequest, current_tg_agent: TgAgent = Depends(get_current_
|
||||
"""
|
||||
new_ref = Ref(
|
||||
tg_agent_id=current_tg_agent.id,
|
||||
ref=str(uuid4()),
|
||||
ref=str(uuid.uuid4()),
|
||||
description=req.description
|
||||
)
|
||||
db.add(new_ref)
|
||||
@ -662,15 +510,6 @@ def tg_auth(hash: str = Body(..., embed=True), db: Session = Depends(get_db)):
|
||||
return {"msg": "Auth success", "tg_id": tg_agent.tg_id}
|
||||
|
||||
# --- Новый функционал для Account ---
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
def verify_password(plain_password, hashed_password):
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
def get_account_by_login(db: Session, login: str) -> Optional[Account]:
|
||||
statement = select(Account).where(Account.login == login)
|
||||
return db.exec(statement).first()
|
||||
|
||||
|
||||
|
||||
@app.get("/account", tags=["bff", "account"], response_model=AccountResponse)
|
||||
@ -754,9 +593,9 @@ def change_account_password(
|
||||
|
||||
@app.get("/account/agent-transaction", response_model=List[AgentTransactionResponse], tags=["bff", "account"])
|
||||
def get_account_agent_transactions(
|
||||
statuses: Optional[List[TransactionStatus]] = Query(None), # Изменено на List[TransactionStatus]
|
||||
date_start: str = Query(None), # Добавлен параметр date_start
|
||||
date_end: str = Query(None), # Добавлен параметр date_end
|
||||
statuses: Optional[List[TransactionStatus]] = Query(None),
|
||||
date_start: str = Query(None),
|
||||
date_end: str = Query(None),
|
||||
current_account: Account = Depends(get_current_account),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
@ -788,23 +627,26 @@ def get_account_agent_transactions(
|
||||
# Формируем список ответов в формате AgentTransactionResponse
|
||||
agent_transactions_response = []
|
||||
for agent_trans, agent in results:
|
||||
try:
|
||||
status_enum = TransactionStatus(agent_trans.status)
|
||||
except ValueError:
|
||||
# Если статус из БД не соответствует Enum, используем статус ERROR
|
||||
status_enum = TransactionStatus.ERROR
|
||||
agent_transactions_response.append(
|
||||
AgentTransactionResponse(
|
||||
amount=agent_trans.amount,
|
||||
status=agent_trans.status,
|
||||
status=status_enum,
|
||||
transaction_group=agent_trans.transaction_group,
|
||||
create_dttm=agent_trans.create_dttm,
|
||||
update_dttm=agent_trans.update_dttm,
|
||||
agent_name=agent.name # Используем имя агента
|
||||
agent_name=agent.name
|
||||
)
|
||||
)
|
||||
|
||||
return agent_transactions_response
|
||||
|
||||
# Модель запроса для POST /account/auto-approve
|
||||
class AutoApproveSettingsRequest(BaseModel):
|
||||
auto_approve: bool
|
||||
apply_to_current: Optional[bool] = False
|
||||
|
||||
|
||||
|
||||
@app.get("/account/auto-approve", tags=["bff", "account"], response_model=AutoApproveSettingsGetResponse)
|
||||
def get_auto_approve_settings(
|
||||
@ -854,8 +696,8 @@ def update_auto_approve_settings(
|
||||
# Находим соответствующие партнерские транзакции и обновляем их статус
|
||||
partner_transactions_to_update = db.exec(
|
||||
select(PartnerTransaction)
|
||||
.where(PartnerTransaction.agent_transaction_id == agent_trans.id) # Используем связь по ID
|
||||
.where(PartnerTransaction.status == TransactionStatus.PROCESS) # Предполагаем, что связанные партнерские транзакции в статусе PROCESS
|
||||
.where(PartnerTransaction.agent_transaction_id == agent_trans.id)
|
||||
.where(PartnerTransaction.status == TransactionStatus.PROCESS)
|
||||
).all()
|
||||
for partner_trans in partner_transactions_to_update:
|
||||
partner_trans.status = TransactionStatus.NEW
|
||||
@ -867,9 +709,7 @@ def update_auto_approve_settings(
|
||||
|
||||
return {"msg": "Настройка автоматического подтверждения обновлена", "auto_approve_transactions": company.auto_approve_transactions}
|
||||
|
||||
# Модель запроса для POST /account/approve-transactions
|
||||
class ApproveTransactionsRequest(BaseModel):
|
||||
transaction_ids: List[uuid.UUID]
|
||||
|
||||
|
||||
@app.post("/account/approve-transactions", tags=["bff", "account"], response_model=ApproveTransactionsResult)
|
||||
def approve_agent_transactions(
|
||||
@ -893,11 +733,11 @@ def approve_agent_transactions(
|
||||
.join(TgAgent)
|
||||
.where(TgAgent.company_id == company_id)
|
||||
.where(AgentTransaction.transaction_group.in_(req.transaction_ids))
|
||||
.where(AgentTransaction.status == TransactionStatus.WAITING) # Утверждаем только транзакции в статусе 'waiting'
|
||||
.where(AgentTransaction.status == TransactionStatus.WAITING)
|
||||
).all()
|
||||
|
||||
for agent_trans in transactions_to_approve:
|
||||
agent_trans.status = TransactionStatus.NEW # Переводим в статус 'new'
|
||||
agent_trans.status = TransactionStatus.NEW
|
||||
agent_trans.update_dttm = datetime.utcnow()
|
||||
db.add(agent_trans)
|
||||
approved_count += 1
|
||||
|
||||
93
sql_models.py
Normal file
93
sql_models.py
Normal file
@ -0,0 +1,93 @@
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
class Company(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
commission: float # процент комиссии
|
||||
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)
|
||||
|
||||
class TgAgent(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
tg_id: int = Field(index=True, unique=True)
|
||||
chat_id: Optional[int] = None
|
||||
phone: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
login: Optional[str] = None
|
||||
hash: Optional[str] = None
|
||||
company_id: int = Field(foreign_key="company.id")
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class Ref(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
tg_agent_id: int = Field(foreign_key="tgagent.id")
|
||||
ref: str
|
||||
description: Optional[str] = None
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class Sale(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
cost: float
|
||||
crediting: float # сколько начислено за продажу
|
||||
ref: int = Field(foreign_key="ref.id")
|
||||
sale_id: str
|
||||
company_id: int = Field(foreign_key="company.id")
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class AgentTransaction(SQLModel, table=True):
|
||||
__tablename__ = "agent_transactions"
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
tg_agent_id: int = Field(foreign_key="tgagent.id")
|
||||
amount: float
|
||||
status: str
|
||||
transaction_group: uuid.UUID = Field(default_factory=uuid.uuid4)
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class PartnerTransaction(SQLModel, table=True):
|
||||
__tablename__ = "partner_transactions"
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
company_id: int = Field(foreign_key="company.id")
|
||||
type: str
|
||||
amount: float
|
||||
status: str
|
||||
transaction_group: uuid.UUID
|
||||
agent_transaction_id: Optional[int] = Field(default=None, foreign_key="agent_transactions.id")
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class CompanyBalance(SQLModel, table=True):
|
||||
__tablename__ = "company_balances"
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
company_id: int = Field(foreign_key="company.id", unique=True)
|
||||
available_balance: float = Field(default=0.0)
|
||||
pending_balance: float = Field(default=0.0)
|
||||
updated_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class AgentBalance(SQLModel, table=True):
|
||||
__tablename__ = "agent_balances"
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
tg_agent_id: int = Field(foreign_key="tgagent.id", unique=True)
|
||||
available_balance: float = Field(default=0.0)
|
||||
frozen_balance: float = Field(default=0.0)
|
||||
updated_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
class Account(SQLModel, table=True):
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
login: str = Field(index=True, unique=True)
|
||||
password_hash: str
|
||||
firstName: Optional[str] = None
|
||||
surname: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
company_id: int = Field(foreign_key="company.id")
|
||||
create_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
update_dttm: datetime = Field(default_factory=datetime.utcnow)
|
||||
Loading…
x
Reference in New Issue
Block a user