refactor: change search router

This commit is contained in:
2026-06-15 11:01:39 +03:00
parent 6f8206c867
commit db30feb0e2
6 changed files with 90 additions and 64 deletions

View File

@@ -6,10 +6,10 @@ from src.application.commands.create_organization import CreateOrganizationComma
from src.application.commands.create_organization_wallets import CreateOrganizationWalletsCommand from src.application.commands.create_organization_wallets import CreateOrganizationWalletsCommand
from src.application.commands.put_organization_document import PutOrganizationDocumentCommand from src.application.commands.put_organization_document import PutOrganizationDocumentCommand
from src.application.commands.set_password import SetPasswordCommand from src.application.commands.set_password import SetPasswordCommand
from src.application.commands.party_search_command import SearchPartiesCommand
from src.application.commands.organization_commands import ( from src.application.commands.organization_commands import (
ListOrganizationsCommand, ListOrganizationsCommand,
GetOrganizationCommand, GetOrganizationCommand,
SearchPartiesCommand,
UpdateOrganizationCommand, UpdateOrganizationCommand,
) )
from src.application.commands.organization_document_commands import ( from src.application.commands.organization_document_commands import (

View File

@@ -4,8 +4,7 @@ from typing import Any
from src.application.abstractions import IUnitOfWork from src.application.abstractions import IUnitOfWork
from src.application.contracts import ILogger from src.application.contracts import ILogger
from src.application.domain.entities.organization import LegalEntityEntity, PartySearchEntity from src.application.domain.entities.organization import LegalEntityEntity
from src.application.domain.enums.account_type import AccountType
from src.infrastructure.database.decorators import transactional from src.infrastructure.database.decorators import transactional
@@ -31,49 +30,6 @@ class ListOrganizationsCommand:
return items, total return items, total
class SearchPartiesCommand:
def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger):
self._unit_of_work = unit_of_work
self._logger = logger
@transactional
async def __call__(self, *, query: str, limit: int = 50, offset: int = 0) -> tuple[list[PartySearchEntity], int]:
query = query.strip()
if not query:
return [], 0
fetch_limit = limit + offset
legal_items = await self._unit_of_work.legal_entity_repository.list_all(
limit=fetch_limit,
offset=0,
search=query,
)
individual_items = await self._unit_of_work.user_repository.search_individuals(
query=query,
limit=fetch_limit,
offset=0,
)
legal_total = await self._unit_of_work.legal_entity_repository.count_all(search=query)
individual_total = await self._unit_of_work.user_repository.count_individuals(query=query)
items = [self._legal_to_search_entity(item) for item in legal_items] + individual_items
items.sort(key=lambda item: item.created_at.timestamp() if item.created_at else 0, reverse=True)
return items[offset:offset + limit], legal_total + individual_total
def _legal_to_search_entity(self, item: LegalEntityEntity) -> PartySearchEntity:
return PartySearchEntity(
id=item.id,
account_type=AccountType.LEGAL_ENTITY,
user_id=item.user_id,
email=None,
name=item.name,
inn=item.inn,
phone=item.contact_phone,
status=item.status,
kyc_verified=item.kyc_verified,
created_at=item.created_at,
)
class GetOrganizationCommand: class GetOrganizationCommand:
def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger): def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger):
self._unit_of_work = unit_of_work self._unit_of_work = unit_of_work

View File

@@ -0,0 +1,52 @@
from __future__ import annotations
from typing import Any
from src.application.abstractions import IUnitOfWork
from src.application.contracts import ILogger
from src.application.domain.entities.organization import LegalEntityEntity, PartySearchEntity
from src.application.domain.enums.account_type import AccountType
from src.infrastructure.database.decorators import transactional
class SearchPartiesCommand:
def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger):
self._unit_of_work = unit_of_work
self._logger = logger
@transactional
async def __call__(self, *, query: str, limit: int = 50, offset: int = 0) -> tuple[list[PartySearchEntity], int]:
query = query.strip()
if not query:
return [], 0
fetch_limit = limit + offset
legal_items = await self._unit_of_work.legal_entity_repository.list_all(
limit=fetch_limit,
offset=0,
search=query,
)
individual_items = await self._unit_of_work.user_repository.search_individuals(
query=query,
limit=fetch_limit,
offset=0,
)
legal_total = await self._unit_of_work.legal_entity_repository.count_all(search=query)
individual_total = await self._unit_of_work.user_repository.count_individuals(query=query)
items = [self._legal_to_search_entity(item) for item in legal_items] + individual_items
items.sort(key=lambda item: item.created_at.timestamp() if item.created_at else 0, reverse=True)
return items[offset:offset + limit], legal_total + individual_total
def _legal_to_search_entity(self, item: LegalEntityEntity) -> PartySearchEntity:
return PartySearchEntity(
id=item.id,
account_type=AccountType.LEGAL_ENTITY,
user_id=item.user_id,
email=None,
name=item.name,
inn=item.inn,
phone=item.contact_phone,
status=item.status,
kyc_verified=item.kyc_verified,
created_at=item.created_at,
)

View File

@@ -6,6 +6,7 @@ from src.presentation.routing.jwt import jwt_router
from src.presentation.routing.organizations import organizations_router from src.presentation.routing.organizations import organizations_router
from src.presentation.routing.purchase_requests import purchase_requests_router from src.presentation.routing.purchase_requests import purchase_requests_router
from src.presentation.routing.users import users_router from src.presentation.routing.users import users_router
from src.presentation.routing.search import search_router
v1_router = APIRouter(prefix='/v1') v1_router = APIRouter(prefix='/v1')
v1_router.include_router(auth_router) v1_router.include_router(auth_router)
@@ -13,4 +14,5 @@ v1_router.include_router(jwt_router)
v1_router.include_router(organizations_router) v1_router.include_router(organizations_router)
v1_router.include_router(documents_router) v1_router.include_router(documents_router)
v1_router.include_router(purchase_requests_router) v1_router.include_router(purchase_requests_router)
v1_router.include_router(users_router) v1_router.include_router(users_router)
v1_router.include_router(search_router)

View File

@@ -42,8 +42,6 @@ from src.presentation.schemas.organization import (
from src.presentation.schemas.entity import ( from src.presentation.schemas.entity import (
CreateWalletsResponse, CreateWalletsResponse,
MnemonicResponse, MnemonicResponse,
PartySearchResponse,
PartySearchListResponse,
PurchaseRequestResponse, PurchaseRequestResponse,
PurchaseRequestListResponse, PurchaseRequestListResponse,
SecretKeyResponse, SecretKeyResponse,
@@ -93,21 +91,6 @@ async def create_organization(
return organization_to_response(org) return organization_to_response(org)
@organizations_router.get('/search', response_model=PartySearchListResponse)
async def search_parties(
q: str = Query(min_length=1, max_length=255),
limit: int = Query(default=50, ge=1, le=200),
offset: int = Query(default=0, ge=0),
auth: AdminAuthContext = Depends(require_admin_access),
command: SearchPartiesCommand = Depends(get_search_parties_command),
):
items, total = await command(query=q, limit=limit, offset=offset)
return PartySearchListResponse(
items=[party_search_to_response(x) for x in items],
total=total,
)
@organizations_router.get('/{organization_id}', response_model=OrganizationResponse) @organizations_router.get('/{organization_id}', response_model=OrganizationResponse)
async def get_organization( async def get_organization(
organization_id: str, organization_id: str,

View File

@@ -0,0 +1,33 @@
from fastapi import APIRouter, Depends, Query, status
from src.application.commands import (
SearchPartiesCommand,
)
from src.application.domain.dto import AdminAuthContext
from src.presentation.decorators.admin_auth import require_admin_access
from src.presentation.dependencies.commands import (
get_search_parties_command,
)
from src.presentation.schemas.mappers import (
party_search_to_response,
)
from src.presentation.schemas.entity import (
PartySearchResponse,
PartySearchListResponse,
)
search_router = APIRouter(prefix='/search', tags=['search'])
@search_router.get('', response_model=PartySearchListResponse)
async def search_parties(
q: str = Query(min_length=1, max_length=255),
limit: int = Query(default=50, ge=1, le=200),
offset: int = Query(default=0, ge=0),
# auth: AdminAuthContext = Depends(require_admin_access),
command: SearchPartiesCommand = Depends(get_search_parties_command),
):
items, total = await command(query=q, limit=limit, offset=offset)
return PartySearchListResponse(
items=[party_search_to_response(x) for x in items],
total=total,
)