Files
call-review-platform/services/ingest-service/app/api/uis.py

97 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from datetime import datetime
from enum import Enum
from typing import List
import logging
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, HttpUrl, Field, field_validator
from sqlalchemy.ext.asyncio import AsyncSession
from app.infrastructure.database import get_db
from app import crud
from app.core.exceptions import CallEventAlreadyExistsError, DatabaseError
logger = logging.getLogger(__name__)
class CallDirection(str, Enum):
"""Направление звонка"""
in_ = "in"
out = "out"
class UisCallEvent(BaseModel):
"""Схема события звонка от UIS"""
eventType: str = Field(..., description="Тип события")
call_session_id: int = Field(..., gt=0, description="Уникальный ID сессии звонка")
direction: CallDirection = Field(..., description="Направление звонка")
employee_id: int = Field(..., gt=0, description="ID сотрудника")
employee_full_name: str = Field(..., min_length=1, description="ФИО сотрудника")
contact_phone_number: str = Field(..., description="Телефон контакта")
called_phone_number: str = Field(..., description="Набранный телефон")
communication_group_name: str = Field(..., description="Группа коммуникации")
start_time: datetime = Field(..., description="Время начала звонка")
finish_time: datetime = Field(..., description="Время окончания звонка")
talk_time_duration: int = Field(..., ge=0, description="Длительность разговора (сек)")
full_record_file_link: HttpUrl = Field(..., description="Ссылка на запись")
campaign_name: str = Field(..., description="Название кампании")
@field_validator('finish_time')
@classmethod
def validate_finish_time(cls, v: datetime, info) -> datetime:
"""Проверяем, что finish_time >= start_time"""
if 'start_time' in info.data and v < info.data['start_time']:
raise ValueError('finish_time must be greater than or equal to start_time')
return v
router = APIRouter()
@router.post("/webhook", status_code=204)
async def create_call_event(
callEvent: UisCallEvent,
db: AsyncSession = Depends(get_db)
) -> None:
"""
Webhook для получения событий звонков от UIS
**Обрабатывает события:**
- Сохраняет событие звонка в базу данных
- Проверяет уникальность call_session_id
- Логирует успешную обработку
**Возможные ошибки:**
- 409 Conflict: Событие с таким call_session_id уже существует
- 422 Unprocessable Entity: Ошибка валидации данных
- 500 Internal Server Error: Ошибка сервера/БД
"""
logger.info(
f"Received webhook event",
extra={
"event_type": callEvent.eventType,
"call_session_id": callEvent.call_session_id,
"employee_id": callEvent.employee_id
}
)
# Преобразуем UisCallEvent в данные для CallEvent (БД)
event_data = {
"call_session_id": callEvent.call_session_id,
"direction": callEvent.direction.value,
"employee_id": callEvent.employee_id,
"last_answered_employee_full_name": callEvent.employee_full_name,
"finish_time": int(callEvent.finish_time.timestamp()),
"talk_time_duration": callEvent.talk_time_duration,
"full_record_file_link": str(callEvent.full_record_file_link),
# Поля, которых нет в UisCallEvent - заполняем значениями по умолчанию
"notification_mnemonic": callEvent.eventType,
"tcm_topcrm_notification_name": callEvent.campaign_name,
"total_time_duration": callEvent.talk_time_duration, # Упрощение
"wait_time_duration": 0, # Нет в запросе
"total_wait_time_duration": 0, # Нет в запросе
"clean_talk_time_duration": callEvent.talk_time_duration, # Упрощение
}
# Сохраняем в БД (исключения обрабатываются в exception handlers)
await crud.create_call_event(db, event_data)