From db30feb0e2f509bd491e9712231e526765344876 Mon Sep 17 00:00:00 2001 From: dev1lfreak Date: Mon, 15 Jun 2026 11:01:39 +0300 Subject: [PATCH] refactor: change search router --- src/application/commands/__init__.py | 2 +- .../commands/organization_commands.py | 46 +--------------- .../commands/party_search_command.py | 52 +++++++++++++++++++ src/presentation/routing/__init__.py | 4 +- src/presentation/routing/organizations.py | 17 ------ src/presentation/routing/search.py | 33 ++++++++++++ 6 files changed, 90 insertions(+), 64 deletions(-) create mode 100644 src/application/commands/party_search_command.py create mode 100644 src/presentation/routing/search.py diff --git a/src/application/commands/__init__.py b/src/application/commands/__init__.py index c41eda8..db54da1 100644 --- a/src/application/commands/__init__.py +++ b/src/application/commands/__init__.py @@ -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.put_organization_document import PutOrganizationDocumentCommand from src.application.commands.set_password import SetPasswordCommand +from src.application.commands.party_search_command import SearchPartiesCommand from src.application.commands.organization_commands import ( ListOrganizationsCommand, GetOrganizationCommand, - SearchPartiesCommand, UpdateOrganizationCommand, ) from src.application.commands.organization_document_commands import ( diff --git a/src/application/commands/organization_commands.py b/src/application/commands/organization_commands.py index c7e4bc8..23562a4 100644 --- a/src/application/commands/organization_commands.py +++ b/src/application/commands/organization_commands.py @@ -4,8 +4,7 @@ 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.application.domain.entities.organization import LegalEntityEntity from src.infrastructure.database.decorators import transactional @@ -31,49 +30,6 @@ class ListOrganizationsCommand: 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: def __init__(self, unit_of_work: IUnitOfWork, logger: ILogger): self._unit_of_work = unit_of_work diff --git a/src/application/commands/party_search_command.py b/src/application/commands/party_search_command.py new file mode 100644 index 0000000..6a0b5cd --- /dev/null +++ b/src/application/commands/party_search_command.py @@ -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, + ) \ No newline at end of file diff --git a/src/presentation/routing/__init__.py b/src/presentation/routing/__init__.py index 2dfb85c..f24abcb 100644 --- a/src/presentation/routing/__init__.py +++ b/src/presentation/routing/__init__.py @@ -6,6 +6,7 @@ from src.presentation.routing.jwt import jwt_router from src.presentation.routing.organizations import organizations_router from src.presentation.routing.purchase_requests import purchase_requests_router from src.presentation.routing.users import users_router +from src.presentation.routing.search import search_router v1_router = APIRouter(prefix='/v1') 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(documents_router) v1_router.include_router(purchase_requests_router) -v1_router.include_router(users_router) \ No newline at end of file +v1_router.include_router(users_router) +v1_router.include_router(search_router) \ No newline at end of file diff --git a/src/presentation/routing/organizations.py b/src/presentation/routing/organizations.py index dda18db..c278489 100644 --- a/src/presentation/routing/organizations.py +++ b/src/presentation/routing/organizations.py @@ -42,8 +42,6 @@ from src.presentation.schemas.organization import ( from src.presentation.schemas.entity import ( CreateWalletsResponse, MnemonicResponse, - PartySearchResponse, - PartySearchListResponse, PurchaseRequestResponse, PurchaseRequestListResponse, SecretKeyResponse, @@ -93,21 +91,6 @@ async def create_organization( 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) async def get_organization( organization_id: str, diff --git a/src/presentation/routing/search.py b/src/presentation/routing/search.py new file mode 100644 index 0000000..c19fae5 --- /dev/null +++ b/src/presentation/routing/search.py @@ -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, + ) \ No newline at end of file