Добавлены новые функции для работы с промокодами в integration_api.py, включая создание и получение промокодов для Telegram-агентов. Обновлены модели и добавлен новый файл generate_sql.py для генерации SQL-скрипта создания таблиц. Обновлены fill_db.py для генерации промокодов при заполнении базы данных. Также обновлены sql_models.py для добавления модели PromoCode и соответствующих связей. Улучшена структура кода и добавлены отладочные сообщения.

This commit is contained in:
Redsandyg 2025-06-15 15:20:28 +03:00
parent 3973d6404d
commit 92df59ad23
7 changed files with 318 additions and 6 deletions

View File

@ -1,11 +1,12 @@
import random import random
from uuid import uuid4 from uuid import uuid4
from sqlmodel import Session from sqlmodel import Session
from sql_models import TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance, IntegrationToken from sql_models import TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance, IntegrationToken, PromoCode
from sqlalchemy import text from sqlalchemy import text
from datetime import datetime, timedelta from datetime import datetime, timedelta
from hashlib import sha256 from hashlib import sha256
from helpers_bff import AUTH_DB_ENGINE, get_password_hash from helpers_bff import AUTH_DB_ENGINE, get_password_hash
import string
# Константа: список user_ids # Константа: список user_ids
@ -71,6 +72,7 @@ def fill_db():
date_list = get_date_list(7) # 8 дней: от недели назад до сегодня date_list = get_date_list(7) # 8 дней: от недели назад до сегодня
with Session(AUTH_DB_ENGINE) as session: with Session(AUTH_DB_ENGINE) as session:
# Очистка таблиц # Очистка таблиц
session.execute(text("DELETE FROM promocode"))
session.execute(text("DELETE FROM sale")) session.execute(text("DELETE FROM sale"))
session.execute(text("DELETE FROM ref")) session.execute(text("DELETE FROM ref"))
session.execute(text("DELETE FROM tgagent")) session.execute(text("DELETE FROM tgagent"))
@ -149,6 +151,35 @@ def fill_db():
session.commit() session.commit()
for tg_agent in tg_agents: for tg_agent in tg_agents:
session.refresh(tg_agent) session.refresh(tg_agent)
# Отладка: количество агентов
agent_count = session.execute(text("SELECT COUNT(*) FROM tgagent")).scalar()
print(f'Агентов в базе: {agent_count}')
# Генерация промокодов для каждого агента с try/except
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
try:
for tg_agent in tg_agents:
promocode_count = random.randint(2, 4)
for _ in range(promocode_count):
promocode = ''.join(random.choices(alphabet, k=8))
# Проверяем уникальность
while session.execute(text("SELECT 1 FROM promocode WHERE promocode = :code"), {"code": promocode}).first():
promocode = ''.join(random.choices(alphabet, k=8))
promo = PromoCode(
promocode=promocode,
perc=10.0,
agent_id=tg_agent.id,
description=random.choice(DESCRIPTIONS),
create_dttm=datetime.utcnow(),
update_dttm=datetime.utcnow()
)
session.add(promo)
session.commit()
except Exception as e:
print(f'Ошибка при добавлении промокодов: {e}')
session.rollback()
# Отладка: количество промокодов
count = session.execute(text("SELECT COUNT(*) FROM promocode")).scalar()
print(f'Промокодов в базе: {count}')
# 3. Refs (минимум 22 на агента) # 3. Refs (минимум 22 на агента)
refs = [] refs = []
desc_count = len(ALL_DESCRIPTIONS) desc_count = len(ALL_DESCRIPTIONS)

23
generate_sql.py Normal file
View File

@ -0,0 +1,23 @@
from sqlmodel import SQLModel
from sql_models import Company, TgAgent, Ref, Sale, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance, Account, IntegrationToken, PromoCode
from helpers_bff import AUTH_DB_ENGINE
from sqlalchemy.schema import CreateTable
import os
# --- Отладочный вывод ---
print("Таблицы в metadata:", SQLModel.metadata.sorted_tables)
print("Все зарегистрированные модели:", SQLModel.__subclasses__())
# --- Генерация и сохранение SQL-скрипта создания таблиц ---
def get_sql_create_script():
script = []
for table in SQLModel.metadata.sorted_tables:
create_table_sql = str(CreateTable(table).compile(dialect=AUTH_DB_ENGINE.dialect))
script.append(f"{create_table_sql};")
return "\n\n".join(script)
sql_script = get_sql_create_script()
sql_file_path = os.path.join(os.path.dirname(__file__), "sql_create.sql")
with open(sql_file_path, "w", encoding="utf-8") as f:
f.write(sql_script)
print("SQL-скрипт успешно сгенерирован!")

View File

@ -1,14 +1,16 @@
from fastapi import FastAPI, Depends, HTTPException, status, Header, Body from fastapi import FastAPI, Depends, HTTPException, status, Header, Body, Request
from sqlmodel import Session, select, Field from sqlmodel import Session, select, Field
from typing import Optional, List, Dict from typing import Optional, List, Dict
from datetime import datetime, timedelta from datetime import datetime, timedelta
import hashlib import hashlib
import uuid import uuid
from random import choices
import string
from sql_models import Company, IntegrationToken, Ref, Sale, AgentTransaction, PartnerTransaction, AgentBalance, TgAgent, CompanyBalance from sql_models import Company, IntegrationToken, Ref, Sale, AgentTransaction, PartnerTransaction, AgentBalance, TgAgent, CompanyBalance, PromoCode
from integration_models import Token, SaleCreateRequest, SaleCreateResponse, TransactionStatus, WithdrawRequest, WithdrawResponse from integration_models import Token, SaleCreateRequest, SaleCreateResponse, TransactionStatus, WithdrawRequest, WithdrawResponse
from bff_models import RegisterResponse, TgAuthResponse from bff_models import RegisterResponse, TgAuthResponse
from tg_models import RefAddRequest, RefResponse, RefAddResponse, RefStatResponse, RegisterRequest, StatResponse from tg_models import RefAddRequest, RefResponse, RefAddResponse, RefStatResponse, RegisterRequest, StatResponse, PromoCodeAddRequest, PromoCodeResponse
from helpers_bff import AUTH_DB_ENGINE, get_integration_db, create_integration_jwt_token, get_current_company_from_jwt, get_tg_agent_by_tg_id, get_current_tg_agent from helpers_bff import AUTH_DB_ENGINE, get_integration_db, create_integration_jwt_token, get_current_company_from_jwt, get_tg_agent_by_tg_id, get_current_tg_agent
app = FastAPI() app = FastAPI()
@ -304,3 +306,32 @@ def register(req: RegisterRequest, db: Session = Depends(get_integration_db)):
db.commit() db.commit()
db.refresh(new_tg_agent) db.refresh(new_tg_agent)
return {"msg": "TgAgent registered successfully"} return {"msg": "TgAgent registered successfully"}
@app.post("/promocode/add", tags=["agent-tg"], response_model=PromoCodeResponse)
def add_promocode(req: PromoCodeAddRequest, current_tg_agent: TgAgent = Depends(get_current_tg_agent), db: Session = Depends(get_integration_db)):
"""
Создает новый промокод для текущего Telegram-агента.
"""
description = req.description
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
promocode = ''.join(choices(alphabet, k=8))
while db.exec(select(PromoCode).where(PromoCode.promocode == promocode)).first():
promocode = ''.join(choices(alphabet, k=8))
new_code = PromoCode(
promocode=promocode,
perc=10.0,
agent_id=current_tg_agent.id,
description=description
)
db.add(new_code)
db.commit()
db.refresh(new_code)
return PromoCodeResponse(promocode=new_code.promocode, perc=new_code.perc, description=new_code.description)
@app.get("/promocode", tags=["agent-tg"], response_model=List[PromoCodeResponse])
def get_promocodes(current_tg_agent: TgAgent = Depends(get_current_tg_agent), db: Session = Depends(get_integration_db)):
"""
Получает все промокоды текущего Telegram-агента.
"""
codes = db.exec(select(PromoCode).where(PromoCode.agent_id == current_tg_agent.id)).all()
return [PromoCodeResponse(promocode=c.promocode, perc=c.perc, description=c.description) for c in codes]

View File

@ -1,3 +1,4 @@
import uuid
from fastapi import ( from fastapi import (
FastAPI, FastAPI,
Depends, Depends,
@ -62,6 +63,7 @@ from helpers_bff import (
ACCESS_TOKEN_EXPIRE_MINUTES, ACCESS_TOKEN_EXPIRE_MINUTES,
pwd_context, pwd_context,
) )
import os
# Создание движка базы данных # Создание движка базы данных
@ -71,6 +73,8 @@ SQLModel.metadata.create_all(AUTH_DB_ENGINE)
app = FastAPI() app = FastAPI()
@app.post("/token", response_model=Token, tags=["bff", "token"]) @app.post("/token", response_model=Token, tags=["bff", "token"])
def login_account_for_access_token( def login_account_for_access_token(
form_data: OAuth2PasswordRequestForm = Depends(), form_data: OAuth2PasswordRequestForm = Depends(),

175
sql_create.sql Normal file
View File

@ -0,0 +1,175 @@
CREATE TABLE company (
id INTEGER NOT NULL,
name VARCHAR NOT NULL,
commission FLOAT NOT NULL,
agent_commission FLOAT NOT NULL,
"key" VARCHAR NOT NULL,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
auto_approve_transactions BOOLEAN NOT NULL,
PRIMARY KEY (id)
)
;
CREATE TABLE account (
id INTEGER NOT NULL,
login VARCHAR NOT NULL,
password_hash VARCHAR NOT NULL,
"firstName" VARCHAR,
surname VARCHAR,
phone VARCHAR,
email VARCHAR,
company_id INTEGER NOT NULL,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(company_id) REFERENCES company (id)
)
;
CREATE TABLE company_balances (
id INTEGER NOT NULL,
company_id INTEGER NOT NULL,
available_balance FLOAT NOT NULL,
pending_balance FLOAT NOT NULL,
updated_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE (company_id),
FOREIGN KEY(company_id) REFERENCES company (id)
)
;
CREATE TABLE integrationtoken (
id INTEGER NOT NULL,
description VARCHAR NOT NULL,
token_hash VARCHAR,
masked_token VARCHAR,
company_id INTEGER NOT NULL,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
use_dttm DATETIME,
PRIMARY KEY (id),
FOREIGN KEY(company_id) REFERENCES company (id)
)
;
CREATE TABLE tgagent (
id INTEGER NOT NULL,
tg_id INTEGER NOT NULL,
chat_id INTEGER,
phone VARCHAR,
name VARCHAR,
login VARCHAR,
hash VARCHAR,
company_id INTEGER NOT NULL,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(company_id) REFERENCES company (id)
)
;
CREATE TABLE agent_balances (
id INTEGER NOT NULL,
tg_agent_id INTEGER NOT NULL,
available_balance FLOAT NOT NULL,
frozen_balance FLOAT NOT NULL,
updated_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
UNIQUE (tg_agent_id),
FOREIGN KEY(tg_agent_id) REFERENCES tgagent (id)
)
;
CREATE TABLE agent_transactions (
id INTEGER NOT NULL,
tg_agent_id INTEGER NOT NULL,
amount FLOAT NOT NULL,
status VARCHAR NOT NULL,
transaction_group CHAR(32) NOT NULL,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(tg_agent_id) REFERENCES tgagent (id),
UNIQUE (transaction_group)
)
;
CREATE TABLE promocode (
id INTEGER NOT NULL,
promocode VARCHAR NOT NULL,
perc FLOAT NOT NULL,
agent_id INTEGER NOT NULL,
description VARCHAR,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(agent_id) REFERENCES tgagent (id)
)
;
CREATE TABLE ref (
id INTEGER NOT NULL,
tg_agent_id INTEGER NOT NULL,
ref VARCHAR NOT NULL,
description VARCHAR,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(tg_agent_id) REFERENCES tgagent (id)
)
;
CREATE TABLE partner_transactions (
id INTEGER NOT NULL,
company_id INTEGER NOT NULL,
type VARCHAR NOT NULL,
amount FLOAT NOT NULL,
status VARCHAR NOT NULL,
transaction_group CHAR(32) NOT NULL,
agent_transaction_id INTEGER,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(company_id) REFERENCES company (id),
FOREIGN KEY(agent_transaction_id) REFERENCES agent_transactions (id)
)
;
CREATE TABLE sale (
id INTEGER NOT NULL,
cost FLOAT NOT NULL,
crediting FLOAT NOT NULL,
ref INTEGER NOT NULL,
sale_id VARCHAR NOT NULL,
company_id INTEGER NOT NULL,
sale_dttm DATETIME NOT NULL,
create_dttm DATETIME NOT NULL,
update_dttm DATETIME NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(ref) REFERENCES ref (id),
FOREIGN KEY(company_id) REFERENCES company (id)
)
;

View File

@ -15,6 +15,11 @@ class Company(SQLModel, table=True):
auto_approve_transactions: bool = Field(default=False) # Отвечает за автоматическое одобрение агентских транзакций на вывод. auto_approve_transactions: bool = Field(default=False) # Отвечает за автоматическое одобрение агентских транзакций на вывод.
integration_tokens: List["IntegrationToken"] = Relationship(back_populates="company") integration_tokens: List["IntegrationToken"] = Relationship(back_populates="company")
tg_agents: List["TgAgent"] = Relationship(back_populates="company")
sales: List["Sale"] = Relationship(back_populates="company")
partner_transactions: List["PartnerTransaction"] = Relationship(back_populates="company")
company_balance: Optional["CompanyBalance"] = Relationship(back_populates="company")
accounts: List["Account"] = Relationship(back_populates="company")
class TgAgent(SQLModel, table=True): class TgAgent(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
@ -28,6 +33,12 @@ class TgAgent(SQLModel, table=True):
create_dttm: datetime = Field(default_factory=datetime.utcnow) create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow) update_dttm: datetime = Field(default_factory=datetime.utcnow)
company: "Company" = Relationship(back_populates="tg_agents")
refs: List["Ref"] = Relationship(back_populates="tg_agent")
agent_transactions: List["AgentTransaction"] = Relationship(back_populates="tg_agent")
agent_balance: Optional["AgentBalance"] = Relationship(back_populates="tg_agent")
promocodes: List["PromoCode"] = Relationship(back_populates="agent")
class Ref(SQLModel, table=True): class Ref(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
tg_agent_id: int = Field(foreign_key="tgagent.id") tg_agent_id: int = Field(foreign_key="tgagent.id")
@ -36,6 +47,9 @@ class Ref(SQLModel, table=True):
create_dttm: datetime = Field(default_factory=datetime.utcnow) create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow) update_dttm: datetime = Field(default_factory=datetime.utcnow)
tg_agent: "TgAgent" = Relationship(back_populates="refs")
sales: List["Sale"] = Relationship(back_populates="ref_obj")
class Sale(SQLModel, table=True): class Sale(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
cost: float cost: float
@ -47,6 +61,9 @@ class Sale(SQLModel, table=True):
create_dttm: datetime = Field(default_factory=datetime.utcnow) create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow) update_dttm: datetime = Field(default_factory=datetime.utcnow)
ref_obj: "Ref" = Relationship(back_populates="sales")
company: "Company" = Relationship(back_populates="sales")
class AgentTransaction(SQLModel, table=True): class AgentTransaction(SQLModel, table=True):
__tablename__ = "agent_transactions" __tablename__ = "agent_transactions"
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
@ -57,6 +74,9 @@ class AgentTransaction(SQLModel, table=True):
create_dttm: datetime = Field(default_factory=datetime.utcnow) create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow) update_dttm: datetime = Field(default_factory=datetime.utcnow)
tg_agent: "TgAgent" = Relationship(back_populates="agent_transactions")
partner_transactions: List["PartnerTransaction"] = Relationship(back_populates="agent_transaction")
class PartnerTransaction(SQLModel, table=True): class PartnerTransaction(SQLModel, table=True):
__tablename__ = "partner_transactions" __tablename__ = "partner_transactions"
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
@ -69,6 +89,9 @@ class PartnerTransaction(SQLModel, table=True):
create_dttm: datetime = Field(default_factory=datetime.utcnow) create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow) update_dttm: datetime = Field(default_factory=datetime.utcnow)
company: "Company" = Relationship(back_populates="partner_transactions")
agent_transaction: Optional["AgentTransaction"] = Relationship(back_populates="partner_transactions")
class CompanyBalance(SQLModel, table=True): class CompanyBalance(SQLModel, table=True):
__tablename__ = "company_balances" __tablename__ = "company_balances"
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
@ -77,6 +100,8 @@ class CompanyBalance(SQLModel, table=True):
pending_balance: float = Field(default=0.0) pending_balance: float = Field(default=0.0)
updated_dttm: datetime = Field(default_factory=datetime.utcnow) updated_dttm: datetime = Field(default_factory=datetime.utcnow)
company: "Company" = Relationship(back_populates="company_balance")
class AgentBalance(SQLModel, table=True): class AgentBalance(SQLModel, table=True):
__tablename__ = "agent_balances" __tablename__ = "agent_balances"
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
@ -85,6 +110,8 @@ class AgentBalance(SQLModel, table=True):
frozen_balance: float = Field(default=0.0) frozen_balance: float = Field(default=0.0)
updated_dttm: datetime = Field(default_factory=datetime.utcnow) updated_dttm: datetime = Field(default_factory=datetime.utcnow)
tg_agent: "TgAgent" = Relationship(back_populates="agent_balance")
class Account(SQLModel, table=True): class Account(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
login: str = Field(index=True, unique=True) login: str = Field(index=True, unique=True)
@ -97,6 +124,8 @@ class Account(SQLModel, table=True):
create_dttm: datetime = Field(default_factory=datetime.utcnow) create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow) update_dttm: datetime = Field(default_factory=datetime.utcnow)
company: "Company" = Relationship(back_populates="accounts")
# Новая модель для интеграционных токенов # Новая модель для интеграционных токенов
class IntegrationToken(SQLModel, table=True): class IntegrationToken(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True) id: Optional[int] = Field(default=None, primary_key=True)
@ -108,4 +137,15 @@ class IntegrationToken(SQLModel, table=True):
update_dttm: datetime = Field(default_factory=datetime.utcnow, nullable=False) update_dttm: datetime = Field(default_factory=datetime.utcnow, nullable=False)
use_dttm: Optional[datetime] = None use_dttm: Optional[datetime] = None
company: Company = Relationship(back_populates="integration_tokens") company: "Company" = Relationship(back_populates="integration_tokens")
class PromoCode(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
promocode: str = Field(index=True, unique=True)
perc: float = Field(default=10.0)
agent_id: int = Field(foreign_key="tgagent.id")
description: Optional[str] = None
create_dttm: datetime = Field(default_factory=datetime.utcnow)
update_dttm: datetime = Field(default_factory=datetime.utcnow)
agent: "TgAgent" = Relationship(back_populates="promocodes")

View File

@ -37,4 +37,12 @@ class RefStatResponse(BaseModel):
class StatResponse(BaseModel): class StatResponse(BaseModel):
totalSales: int totalSales: int
totalIncome: float totalIncome: float
availableWithdrawal: float availableWithdrawal: float
class PromoCodeAddRequest(BaseModel):
description: Optional[str] = None
class PromoCodeResponse(BaseModel):
promocode: str
perc: float
description: Optional[str] = None