Добавлены новые модели для интеграционных токенов в bff_models.py и sql_models.py. Реализованы функции для создания, обновления и удаления токенов в main.py, а также обновлено заполнение базы данных в fill_db.py для генерации токенов. Обновлены запросы к базе данных для учета новых полей и логики работы с токенами.
This commit is contained in:
parent
57188186c0
commit
076cdd1828
@ -1,4 +1,4 @@
|
|||||||
from pydantic import BaseModel, Field, EmailStr
|
from pydantic import BaseModel, Field, EmailStr, ConfigDict
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import uuid
|
import uuid
|
||||||
@ -169,3 +169,21 @@ class AutoApproveSettingsUpdateResponse(BaseModel):
|
|||||||
class ApproveTransactionsResult(BaseModel):
|
class ApproveTransactionsResult(BaseModel):
|
||||||
msg: str
|
msg: str
|
||||||
approved_count: int
|
approved_count: int
|
||||||
|
|
||||||
|
# New models for integration tokens
|
||||||
|
class IntegrationTokenResponse(BaseModel):
|
||||||
|
id: int
|
||||||
|
description: str
|
||||||
|
masked_token: str
|
||||||
|
rawToken: Optional[str] = None
|
||||||
|
create_dttm: datetime
|
||||||
|
use_dttm: Optional[datetime] = None
|
||||||
|
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
class IntegrationTokenCreateRequest(BaseModel):
|
||||||
|
description: str
|
||||||
|
|
||||||
|
class IntegrationTokenUpdateRequest(BaseModel):
|
||||||
|
id: int
|
||||||
|
description: str
|
||||||
42
fill_db.py
42
fill_db.py
@ -1,7 +1,7 @@
|
|||||||
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
|
from sql_models import TgAgent, Ref, Sale, Account, Company, AgentTransaction, PartnerTransaction, CompanyBalance, AgentBalance, IntegrationToken
|
||||||
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
|
||||||
@ -90,6 +90,23 @@ def fill_db():
|
|||||||
session.add(company)
|
session.add(company)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(company)
|
session.refresh(company)
|
||||||
|
|
||||||
|
# 0.1 IntegrationTokens
|
||||||
|
for _ in range(3): # Создаем 3 токена для каждой компании
|
||||||
|
new_token_value = str(uuid4()) # Генерируем уникальный токен
|
||||||
|
token_hash = sha256(new_token_value.encode()).hexdigest() # Хешируем токен для хранения
|
||||||
|
masked_token = new_token_value[:5] + "***********************" + new_token_value[-4:] # Генерируем замаскированный токен
|
||||||
|
|
||||||
|
integration_token = IntegrationToken(
|
||||||
|
description=random.choice(DESCRIPTIONS), # Используем существующие описания
|
||||||
|
token_hash=token_hash,
|
||||||
|
masked_token=masked_token,
|
||||||
|
company_id=company.id,
|
||||||
|
use_dttm=random.choice(date_list) if random.random() < 0.7 else None # Пример: 70% токенов будут иметь дату использования
|
||||||
|
)
|
||||||
|
session.add(integration_token)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
# 1. Accounts
|
# 1. Accounts
|
||||||
accounts = []
|
accounts = []
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
@ -158,16 +175,23 @@ def fill_db():
|
|||||||
for _ in range(sale_count):
|
for _ in range(sale_count):
|
||||||
cost = round(random.uniform(100, 1000), 2)
|
cost = round(random.uniform(100, 1000), 2)
|
||||||
crediting = round(cost * random.uniform(0.5, 1.0), 2)
|
crediting = round(cost * random.uniform(0.5, 1.0), 2)
|
||||||
dt = random.choice(date_list)
|
|
||||||
|
# Генерируем случайную дату и время в пределах последних 7 дней
|
||||||
|
end_dttm = datetime.utcnow()
|
||||||
|
start_dttm = end_dttm - timedelta(days=7)
|
||||||
|
time_diff = end_dttm - start_dttm
|
||||||
|
random_seconds = random.uniform(0, time_diff.total_seconds())
|
||||||
|
sale_dttm = start_dttm + timedelta(seconds=random_seconds)
|
||||||
|
|
||||||
sale = Sale(
|
sale = Sale(
|
||||||
cost=cost,
|
cost=cost,
|
||||||
crediting=crediting,
|
crediting=crediting,
|
||||||
ref=ref.id,
|
ref=ref.id,
|
||||||
sale_id=str(uuid4()),
|
sale_id=str(uuid4()),
|
||||||
company_id=company.id,
|
company_id=company.id,
|
||||||
sale_date=dt,
|
sale_dttm=sale_dttm,
|
||||||
create_dttm=dt,
|
create_dttm=sale_dttm, # create_dttm также будет случайным в этом диапазоне
|
||||||
update_dttm=dt
|
update_dttm=sale_dttm # update_dttm также будет случайным в этом диапазоне
|
||||||
)
|
)
|
||||||
session.add(sale)
|
session.add(sale)
|
||||||
session.commit()
|
session.commit()
|
||||||
@ -204,12 +228,20 @@ def fill_db():
|
|||||||
PARTNER_TRANSACTION_TYPES = ['deposit', 'agent_payout', 'service_fee']
|
PARTNER_TRANSACTION_TYPES = ['deposit', 'agent_payout', 'service_fee']
|
||||||
PARTNER_TRANSACTION_STATUSES = ['process', 'done', 'error', 'new']
|
PARTNER_TRANSACTION_STATUSES = ['process', 'done', 'error', 'new']
|
||||||
|
|
||||||
|
waiting_transactions_to_ensure = 7
|
||||||
|
waiting_transactions_count = 0
|
||||||
|
|
||||||
for tg_agent in tg_agents:
|
for tg_agent in tg_agents:
|
||||||
# Генерируем несколько групп транзакций для каждого агента
|
# Генерируем несколько групп транзакций для каждого агента
|
||||||
for _ in range(random.randint(3, 6)): # От 3 до 6 групп на агента
|
for _ in range(random.randint(3, 6)): # От 3 до 6 групп на агента
|
||||||
transaction_group_id = uuid4()
|
transaction_group_id = uuid4()
|
||||||
dt = random.choice(date_list)
|
dt = random.choice(date_list)
|
||||||
agent_trans_amount = round(random.uniform(500, 3000), 2)
|
agent_trans_amount = round(random.uniform(500, 3000), 2)
|
||||||
|
|
||||||
|
if waiting_transactions_count < waiting_transactions_to_ensure:
|
||||||
|
agent_trans_status = 'waiting'
|
||||||
|
waiting_transactions_count += 1
|
||||||
|
else:
|
||||||
agent_trans_status = random.choice(AGENT_TRANSACTION_STATUSES)
|
agent_trans_status = random.choice(AGENT_TRANSACTION_STATUSES)
|
||||||
|
|
||||||
# Создаем AgentTransaction
|
# Создаем AgentTransaction
|
||||||
|
|||||||
111
main.py
111
main.py
@ -8,7 +8,7 @@ from fastapi import (
|
|||||||
Body,
|
Body,
|
||||||
)
|
)
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
from sqlmodel import SQLModel, Session, select
|
from sqlmodel import SQLModel, Session, select, Field
|
||||||
from typing import Optional, List, Dict
|
from typing import Optional, List, Dict
|
||||||
from datetime import timedelta, datetime
|
from datetime import timedelta, datetime
|
||||||
from bff_models import (
|
from bff_models import (
|
||||||
@ -37,7 +37,10 @@ from bff_models import (
|
|||||||
AutoApproveSettingsRequest,
|
AutoApproveSettingsRequest,
|
||||||
ApproveTransactionsRequest,
|
ApproveTransactionsRequest,
|
||||||
AgentTransactionResponse,
|
AgentTransactionResponse,
|
||||||
TransactionStatus
|
TransactionStatus,
|
||||||
|
IntegrationTokenResponse,
|
||||||
|
IntegrationTokenCreateRequest,
|
||||||
|
IntegrationTokenUpdateRequest
|
||||||
)
|
)
|
||||||
from tg_models import RefAddRequest, RefResponse, RegisterRequest, RefAddResponse, RefStatResponse, StatResponse
|
from tg_models import RefAddRequest, RefResponse, RegisterRequest, RefAddResponse, RefStatResponse, StatResponse
|
||||||
from sql_models import (
|
from sql_models import (
|
||||||
@ -48,7 +51,8 @@ from sql_models import (
|
|||||||
AgentTransaction,
|
AgentTransaction,
|
||||||
PartnerTransaction,
|
PartnerTransaction,
|
||||||
AgentBalance,
|
AgentBalance,
|
||||||
Account
|
Account,
|
||||||
|
IntegrationToken
|
||||||
)
|
)
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
import hashlib
|
import hashlib
|
||||||
@ -65,6 +69,7 @@ from helpers_bff import (
|
|||||||
pwd_context,
|
pwd_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Создание движка базы данных
|
# Создание движка базы данных
|
||||||
SQLModel.metadata.create_all(AUTH_DB_ENGINE)
|
SQLModel.metadata.create_all(AUTH_DB_ENGINE)
|
||||||
|
|
||||||
@ -236,11 +241,11 @@ def get_dashboard_chart_total(current_account: Account = Depends(get_current_acc
|
|||||||
# Группируем продажи по дате (день)
|
# Группируем продажи по дате (день)
|
||||||
result = db.exec(
|
result = db.exec(
|
||||||
select(
|
select(
|
||||||
func.strftime('%Y-%m-%d', Sale.sale_date).label('date'),
|
func.strftime('%Y-%m-%d', Sale.sale_dttm).label('date'),
|
||||||
func.sum(Sale.cost).label('revenue'),
|
func.sum(Sale.cost).label('revenue'),
|
||||||
func.count(Sale.id).label('sales')
|
func.count(Sale.id).label('sales')
|
||||||
).where(Sale.company_id == current_account.company_id).group_by(func.strftime('%Y-%m-%d', Sale.sale_date))
|
).where(Sale.company_id == current_account.company_id).group_by(func.strftime('%Y-%m-%d', Sale.sale_dttm))
|
||||||
.order_by(func.strftime('%Y-%m-%d', Sale.sale_date))
|
.order_by(func.strftime('%Y-%m-%d', Sale.sale_dttm))
|
||||||
).all()
|
).all()
|
||||||
# Преобразуем результат в нужный формат
|
# Преобразуем результат в нужный формат
|
||||||
data = [
|
data = [
|
||||||
@ -368,9 +373,9 @@ def get_sales_stat(
|
|||||||
"""
|
"""
|
||||||
sales_query = select(Sale).where(Sale.company_id == current_account.company_id)
|
sales_query = select(Sale).where(Sale.company_id == current_account.company_id)
|
||||||
if date_start:
|
if date_start:
|
||||||
sales_query = sales_query.where(Sale.sale_date >= date_start)
|
sales_query = sales_query.where(Sale.sale_dttm >= date_start)
|
||||||
if date_end:
|
if date_end:
|
||||||
sales_query = sales_query.where(Sale.sale_date <= date_end)
|
sales_query = sales_query.where(Sale.sale_dttm <= date_end)
|
||||||
sales = db.exec(sales_query).all()
|
sales = db.exec(sales_query).all()
|
||||||
ref_ids = list(set(sale.ref for sale in sales))
|
ref_ids = list(set(sale.ref for sale in sales))
|
||||||
refs = db.exec(select(Ref).where(Ref.id.in_(ref_ids))).all() if ref_ids else []
|
refs = db.exec(select(Ref).where(Ref.id.in_(ref_ids))).all() if ref_ids else []
|
||||||
@ -745,3 +750,93 @@ def approve_agent_transactions(
|
|||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
return {"msg": f"Переведено в статус NEW {approved_count} транзакций", "approved_count": approved_count}
|
return {"msg": f"Переведено в статус NEW {approved_count} транзакций", "approved_count": approved_count}
|
||||||
|
|
||||||
|
# --- Новый функционал для интеграционных токенов ---
|
||||||
|
|
||||||
|
@app.get("/account/integration-tokens", tags=["bff", "account"], response_model=List[IntegrationTokenResponse])
|
||||||
|
def get_integration_tokens(
|
||||||
|
current_account: Account = Depends(get_current_account),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Возвращает список интеграционных токенов для компании текущего пользователя.
|
||||||
|
"""
|
||||||
|
tokens = db.exec(select(IntegrationToken).where(IntegrationToken.company_id == current_account.company_id)).all()
|
||||||
|
return tokens # Позволяем FastAPI самостоятельно сериализовать объекты IntegrationToken в IntegrationTokenResponse
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/account/integration-tokens", tags=["bff", "account"], response_model=IntegrationTokenResponse)
|
||||||
|
def create_integration_token(
|
||||||
|
req: IntegrationTokenCreateRequest,
|
||||||
|
current_account: Account = Depends(get_current_account),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Создает новый интеграционный токен для компании текущего пользователя.
|
||||||
|
Возвращает созданный токен (замаскированный).
|
||||||
|
"""
|
||||||
|
new_token_value = str(uuid.uuid4()) # Генерируем уникальный токен
|
||||||
|
token_hash = hashlib.sha256(new_token_value.encode()).hexdigest() # Хешируем токен для хранения
|
||||||
|
|
||||||
|
# Генерируем замаскированный токен
|
||||||
|
masked_token = new_token_value[:5] + "***********************" + new_token_value[-4:]
|
||||||
|
|
||||||
|
new_integration_token = IntegrationToken(
|
||||||
|
description=req.description,
|
||||||
|
token_hash=token_hash,
|
||||||
|
masked_token=masked_token,
|
||||||
|
company_id=current_account.company_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
db.add(new_integration_token)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(new_integration_token)
|
||||||
|
|
||||||
|
# Создаем объект ответа, используя model_validate для извлечения данных из new_integration_token
|
||||||
|
response_token = IntegrationTokenResponse.model_validate(new_integration_token)
|
||||||
|
response_token.rawToken = new_token_value # Добавляем незамаскированный токен
|
||||||
|
|
||||||
|
return response_token
|
||||||
|
|
||||||
|
@app.post("/account/integration-tokens/update-description", tags=["bff", "account"], response_model=Dict[str, str])
|
||||||
|
def update_integration_token_description(
|
||||||
|
req: IntegrationTokenUpdateRequest,
|
||||||
|
current_account: Account = Depends(get_current_account),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Обновляет описание интеграционного токена по его ID.
|
||||||
|
"""
|
||||||
|
token = db.exec(
|
||||||
|
select(IntegrationToken).where(IntegrationToken.id == req.id).where(IntegrationToken.company_id == current_account.company_id)
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
raise HTTPException(status_code=404, detail="Токен не найден")
|
||||||
|
|
||||||
|
token.description = req.description
|
||||||
|
token.update_dttm = datetime.utcnow() # Обновляем дату изменения, если поле существует
|
||||||
|
db.add(token)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(token)
|
||||||
|
return {"msg": "Описание токена обновлено успешно"}
|
||||||
|
|
||||||
|
@app.delete("/account/integration-tokens/{token_id}", tags=["bff", "account"], response_model=Dict[str, str])
|
||||||
|
def delete_integration_token(
|
||||||
|
token_id: int,
|
||||||
|
current_account: Account = Depends(get_current_account),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Удаляет интеграционный токен по его ID для компании текущего пользователя.
|
||||||
|
"""
|
||||||
|
token = db.exec(
|
||||||
|
select(IntegrationToken).where(IntegrationToken.id == token_id).where(IntegrationToken.company_id == current_account.company_id)
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
raise HTTPException(status_code=404, detail="Токен не найден")
|
||||||
|
|
||||||
|
db.delete(token)
|
||||||
|
db.commit()
|
||||||
|
return {"msg": "Токен удален успешно"}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
from typing import Optional
|
from typing import Optional, List
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import uuid
|
import uuid
|
||||||
from sqlmodel import SQLModel, Field
|
from sqlmodel import SQLModel, Field, Relationship
|
||||||
|
from sqlalchemy import Column, String
|
||||||
|
|
||||||
class Company(SQLModel, table=True):
|
class Company(SQLModel, table=True):
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
@ -12,6 +13,8 @@ class Company(SQLModel, table=True):
|
|||||||
update_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")
|
||||||
|
|
||||||
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)
|
||||||
tg_id: int = Field(index=True, unique=True)
|
tg_id: int = Field(index=True, unique=True)
|
||||||
@ -92,3 +95,16 @@ class Account(SQLModel, table=True):
|
|||||||
company_id: int = Field(foreign_key="company.id")
|
company_id: int = Field(foreign_key="company.id")
|
||||||
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)
|
||||||
|
|
||||||
|
# Новая модель для интеграционных токенов
|
||||||
|
class IntegrationToken(SQLModel, table=True):
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
description: str
|
||||||
|
token_hash: str = Field(sa_column=Column(String, unique=True, index=True))
|
||||||
|
masked_token: str = Field(sa_column=Column(String))
|
||||||
|
company_id: int = Field(foreign_key="company.id")
|
||||||
|
create_dttm: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
||||||
|
update_dttm: datetime = Field(default_factory=datetime.utcnow, nullable=False)
|
||||||
|
use_dttm: Optional[datetime] = None
|
||||||
|
|
||||||
|
company: Company = Relationship(back_populates="integration_tokens")
|
||||||
Loading…
x
Reference in New Issue
Block a user