фиксы

This commit is contained in:
Artem Tsyrulnikov
2026-01-08 21:50:04 +03:00
parent 3e5bdb8164
commit eb54b01835
20 changed files with 462 additions and 405 deletions

View File

@@ -33,6 +33,7 @@ from .project.delete_project import delete_project
from .project.disconnect_project_by_tg_id import disconnect_project_by_tg_id
from .project.get_project import get_project
from .project.get_workspace_projects import get_workspace_projects
from .project.move_project_to_workspace import move_project_to_workspace
from .project.tg_add_project import tg_add_project
from .project.update_project_invite_link_type import update_project_invite_link_type
from .project.update_project_permissions import update_project_permissions
@@ -167,6 +168,8 @@ class Database(typing.Protocol):
async def delete_project(self, workspace_id: UUID, project_id: UUID) -> None: ...
async def check_channel_exists_in_workspace(self, channel_id: UUID, workspace_id: UUID) -> bool: ...
async def get_active_purchase(self, project_id: UUID, creative_id: UUID) -> domain.Purchase | None: ...
async def get_purchase(self, workspace_id: UUID, purchase_id: UUID) -> domain.Purchase | None: ...
@@ -423,6 +426,7 @@ class Usecase:
get_project = get_project
archive_project = archive_project
delete_project = delete_project
move_project_to_workspace = move_project_to_workspace
disconnect_project_by_tg_id = disconnect_project_by_tg_id
update_project_permissions = update_project_permissions
update_project_invite_link_type = update_project_invite_link_type

View File

@@ -0,0 +1,72 @@
import logging
import uuid
from typing import TYPE_CHECKING
from src import domain, dto
if TYPE_CHECKING:
from .. import Usecase
log = logging.getLogger(__name__)
async def move_project_to_workspace(
self: 'Usecase',
user_id: uuid.UUID,
source_workspace_id: uuid.UUID,
project_id: uuid.UUID,
target_workspace_id: uuid.UUID,
) -> dto.ProjectOutput:
# Получаем project из source workspace
project = await self.database.get_project(source_workspace_id, project_id=project_id)
if not project:
raise domain.ProjectNotFound(project_id)
await project.fetch_related('channel')
# Проверяем что target workspace существует
target_workspace = await self.database.get_workspace(target_workspace_id)
if not target_workspace:
raise domain.WorkspaceNotFound(target_workspace_id)
# Проверяем permissions в source workspace (scope на конкретный project)
await self.ensure_workspace_permission(
source_workspace_id, user_id, domain.PermissionKey.PROJECTS_WRITE, for_project_id=project_id
)
# Проверяем permissions в target workspace
await self.ensure_workspace_permission(target_workspace_id, user_id, domain.PermissionKey.PROJECTS_WRITE)
# Проверяем что канал не существует в target workspace (UNIQUE constraint)
if await self.database.check_channel_exists_in_workspace(project.channel.id, target_workspace_id):
raise domain.ProjectChannelConflict()
# Выполняем перенос в транзакции
async with self.database.transaction():
# Удаляем project-scoped permissions (они теряют смысл в новом workspace)
await domain.WorkspaceUserPermissionScope.filter(project_id=project_id).delete()
# Обновляем workspace_id
project.workspace_id = target_workspace_id
await self.database.update_project(project)
# Логирование операции
log.info(
'Project moved to another workspace',
extra={
'project_id': str(project_id),
'channel_id': str(project.channel.id),
'source_workspace_id': str(source_workspace_id),
'target_workspace_id': str(target_workspace_id),
'user_id': str(user_id),
},
)
return dto.ProjectOutput(
id=project.id,
telegram_id=project.channel.telegram_id,
title=project.channel.title,
username=project.channel.username,
status=project.status,
purchase_invite_type_default=project.purchase_invite_type_default,
)