166 lines
6.2 KiB
Python
166 lines
6.2 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import dataclass
|
|
|
|
from app.models import (
|
|
ApplicationCreate,
|
|
ApplicationRecord,
|
|
ApplicationStatus,
|
|
ExternalSyncStatus,
|
|
)
|
|
from app.repositories.applications import ApplicationRepository
|
|
from app.services.external_client import ExternalTicketClient
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class CreateOutcome:
|
|
application: ApplicationRecord | None
|
|
created_local: bool
|
|
sent_external: bool
|
|
external_error: str | None = None
|
|
|
|
|
|
class ApplicationService:
|
|
def __init__(
|
|
self,
|
|
repo: ApplicationRepository,
|
|
external_client: ExternalTicketClient,
|
|
channel_local_enabled: bool = True,
|
|
channel_external_enabled: bool = True,
|
|
) -> None:
|
|
self._repo = repo
|
|
self._external_client = external_client
|
|
self._channel_local_enabled = channel_local_enabled
|
|
self._channel_external_enabled = channel_external_enabled
|
|
|
|
@property
|
|
def channel_local_enabled(self) -> bool:
|
|
return self._channel_local_enabled
|
|
|
|
@property
|
|
def channel_external_enabled(self) -> bool:
|
|
return self._channel_external_enabled
|
|
|
|
async def create_application(
|
|
self,
|
|
customer_id: int | None,
|
|
customer_name: str,
|
|
chat_id: int | None,
|
|
category: str,
|
|
description: str,
|
|
attachments: list[dict],
|
|
source: str = "bot",
|
|
) -> CreateOutcome:
|
|
if not self._channel_local_enabled and not self._channel_external_enabled:
|
|
raise ValueError("Both creation channels are disabled.")
|
|
|
|
application: ApplicationRecord | None = None
|
|
if self._channel_local_enabled:
|
|
application = self._repo.create(
|
|
ApplicationCreate(
|
|
customer_id=customer_id,
|
|
customer_name=customer_name,
|
|
chat_id=chat_id,
|
|
category=category,
|
|
description=description,
|
|
attachments_json=json.dumps(attachments, ensure_ascii=False),
|
|
source=source,
|
|
)
|
|
)
|
|
elif self._channel_external_enabled:
|
|
# Локальный канал выключен: создаем "виртуальную" сущность без записи в БД.
|
|
# Для v1 это только транспорт в внешнюю систему.
|
|
application = ApplicationRecord(
|
|
id=0,
|
|
customer_id=customer_id,
|
|
customer_name=customer_name,
|
|
chat_id=chat_id,
|
|
category=category,
|
|
description=description,
|
|
attachments_json=json.dumps(attachments, ensure_ascii=False),
|
|
status=ApplicationStatus.NEW.value,
|
|
external_sync_status=ExternalSyncStatus.NOT_SENT.value,
|
|
external_id=None,
|
|
external_error=None,
|
|
source=source,
|
|
created_at="",
|
|
updated_at="",
|
|
)
|
|
|
|
if not self._channel_external_enabled:
|
|
if application and self._channel_local_enabled:
|
|
self._repo.update_external_sync(
|
|
application_id=application.id,
|
|
sync_status=ExternalSyncStatus.DISABLED.value,
|
|
external_id=None,
|
|
external_error="external_channel_disabled",
|
|
)
|
|
application = self._repo.get(application.id)
|
|
return CreateOutcome(
|
|
application=application,
|
|
created_local=self._channel_local_enabled,
|
|
sent_external=False,
|
|
)
|
|
|
|
if not self._external_client.enabled:
|
|
if application and self._channel_local_enabled:
|
|
self._repo.update_external_sync(
|
|
application_id=application.id,
|
|
sync_status=ExternalSyncStatus.FAILED.value,
|
|
external_id=None,
|
|
external_error="external_url_not_configured",
|
|
)
|
|
application = self._repo.get(application.id)
|
|
return CreateOutcome(
|
|
application=application,
|
|
created_local=self._channel_local_enabled,
|
|
sent_external=False,
|
|
external_error="external_url_not_configured",
|
|
)
|
|
|
|
assert application is not None
|
|
result = await self._external_client.create_ticket(application)
|
|
if result.ok:
|
|
if self._channel_local_enabled and application.id > 0:
|
|
self._repo.update_external_sync(
|
|
application_id=application.id,
|
|
sync_status=ExternalSyncStatus.SENT.value,
|
|
external_id=result.external_id,
|
|
external_error=None,
|
|
)
|
|
application = self._repo.get(application.id)
|
|
return CreateOutcome(
|
|
application=application,
|
|
created_local=self._channel_local_enabled,
|
|
sent_external=True,
|
|
)
|
|
|
|
if self._channel_local_enabled and application.id > 0:
|
|
self._repo.update_external_sync(
|
|
application_id=application.id,
|
|
sync_status=ExternalSyncStatus.FAILED.value,
|
|
external_id=None,
|
|
external_error=result.error,
|
|
)
|
|
application = self._repo.get(application.id)
|
|
|
|
return CreateOutcome(
|
|
application=application,
|
|
created_local=self._channel_local_enabled,
|
|
sent_external=False,
|
|
external_error=result.error,
|
|
)
|
|
|
|
def list_applications(self, status: str | None = None, search: str | None = None) -> list[ApplicationRecord]:
|
|
return self._repo.list(status=status, search=search)
|
|
|
|
def get_application(self, application_id: int) -> ApplicationRecord | None:
|
|
return self._repo.get(application_id)
|
|
|
|
def get_events(self, application_id: int):
|
|
return self._repo.get_events(application_id)
|
|
|
|
def update_status(self, application_id: int, status: str, actor: str, reason: str | None = None):
|
|
return self._repo.update_status(application_id, status, actor=actor, reason=reason)
|
|
|