Ticket form: new stack + description step

This commit is contained in:
AI Assistant
2025-11-14 19:06:36 +03:00
parent 3d121054ab
commit 3306d01e0d
16 changed files with 488 additions and 73 deletions

View File

@@ -14,8 +14,8 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Открываем порт
EXPOSE 8100
EXPOSE 8200
# Запускаем приложение
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8100"]
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8200"]

View File

@@ -2,11 +2,20 @@
Claims API Routes - Обработка заявок
"""
from fastapi import APIRouter, HTTPException
from .models import ClaimCreateRequest, ClaimResponse
from .models import (
ClaimCreateRequest,
ClaimResponse,
TicketFormDescriptionRequest,
)
import uuid
from datetime import datetime
import json
import logging
from ..services.redis_service import redis_service
from ..config import settings
router = APIRouter(prefix="/api/v1/claims", tags=["Claims"])
logger = logging.getLogger(__name__)
@router.post("/create", response_model=ClaimResponse)
@@ -49,3 +58,43 @@ async def get_claim(claim_id: str):
"message": "Заявка в обработке"
}
@router.post("/description")
async def publish_ticket_form_description(payload: TicketFormDescriptionRequest):
"""
Публикует свободное описание проблемы в Redis канал ticket_form:description
(слушается воркфлоу в n8n)
"""
try:
channel = payload.channel or f"{settings.redis_prefix}description"
event = {
"type": "ticket_form_description",
"session_id": payload.session_id,
"claim_id": payload.claim_id,
"phone": payload.phone,
"email": payload.email,
"description": payload.problem_description.strip(),
"source": payload.source,
"timestamp": datetime.utcnow().isoformat(),
}
logger.info(
"📝 TicketForm description received",
extra={"session_id": payload.session_id, "claim_id": payload.claim_id},
)
await redis_service.publish(channel, json.dumps(event, ensure_ascii=False))
logger.info(
"📡 TicketForm description published",
extra={"channel": channel, "session_id": payload.session_id},
)
return {
"success": True,
"channel": channel,
"event": event,
}
except Exception as e:
logger.exception("❌ Failed to publish ticket form description")
raise HTTPException(
status_code=500,
detail=f"Не удалось опубликовать описание: {e}"
)

View File

@@ -62,3 +62,14 @@ class ClaimResponse(BaseModel):
claim_number: Optional[str] = None
message: str
class TicketFormDescriptionRequest(BaseModel):
"""Отправка свободного описания проблемы (Ticket Form)"""
session_id: str = Field(..., description="ID клиентской сессии")
claim_id: Optional[str] = Field(None, description="ID заявки (если уже создана)")
phone: Optional[str] = Field(None, description="Номер телефона заявителя")
email: Optional[str] = Field(None, description="Email заявителя")
problem_description: str = Field(..., min_length=10, description="Свободное описание ситуации")
source: str = Field("ticket_form", description="Источник события")
channel: Optional[str] = Field(None, description="Переопределение Redis канала (опционально)")

View File

@@ -1,23 +1,28 @@
"""
Конфигурация приложения
"""
from pathlib import Path
from pydantic_settings import BaseSettings
from functools import lru_cache
from typing import List
BASE_DIR = Path(__file__).resolve().parents[2]
ENV_PATH = BASE_DIR / ".env"
class Settings(BaseSettings):
# ============================================
# APPLICATION
# ============================================
app_name: str = "ERV Insurance Platform"
app_name: str = "Ticket Form Intake Platform"
app_env: str = "development"
debug: bool = True
# API
api_v1_prefix: str = "/api/v1"
backend_url: str = "http://localhost:8100"
frontend_url: str = "http://localhost:5173"
backend_url: str = "http://localhost:8200"
frontend_url: str = "http://localhost:5175"
# ============================================
# DATABASE (PostgreSQL)
@@ -49,7 +54,7 @@ class Settings(BaseSettings):
redis_port: int = 6379
redis_password: str = "CRM_Redis_Pass_2025_Secure!"
redis_db: int = 0
redis_prefix: str = "erv:"
redis_prefix: str = "ticket_form:"
@property
def redis_url(self) -> str:
@@ -147,7 +152,7 @@ class Settings(BaseSettings):
# ============================================
# CORS
# ============================================
cors_origins: str = "http://localhost:5173,http://147.45.146.17:5173,https://erv-claims.clientright.ru,http://crm.clientright.ru"
cors_origins: str = "http://localhost:5175,http://127.0.0.1:5175,http://147.45.146.17:5175"
@property
def cors_origins_list(self) -> List[str]:
@@ -168,10 +173,10 @@ class Settings(BaseSettings):
# LOGGING
# ============================================
log_level: str = "INFO"
log_file: str = "/app/logs/erv_platform.log"
log_file: str = "/app/logs/ticket_form_backend.log"
class Config:
env_file = "/var/www/fastuser/data/www/crm.clientright.ru/erv_platform/.env"
env_file = str(ENV_PATH)
case_sensitive = False
extra = "ignore" # Игнорируем лишние поля из .env

View File

@@ -1,5 +1,5 @@
"""
ERV Insurance Platform - FastAPI Backend
Ticket Form Intake Platform - FastAPI Backend
"""
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
@@ -28,7 +28,7 @@ async def lifespan(app: FastAPI):
Lifecycle events: startup and shutdown
"""
# STARTUP
logger.info("🚀 Starting ERV Platform...")
logger.info("🚀 Starting Ticket Form Intake Platform...")
try:
# Подключаем PostgreSQL
@@ -60,25 +60,25 @@ async def lifespan(app: FastAPI):
except Exception as e:
logger.warning(f"⚠️ S3 storage not available: {e}")
logger.info("ERV Platform started successfully!")
logger.info("Ticket Form Intake Platform started successfully!")
yield
# SHUTDOWN
logger.info("🛑 Shutting down ERV Platform...")
logger.info("🛑 Shutting down Ticket Form Intake Platform...")
await db.disconnect()
await redis_service.disconnect()
await rabbitmq_service.disconnect()
await policy_service.close()
logger.info("👋 ERV Platform stopped")
logger.info("👋 Ticket Form Intake Platform stopped")
# Создаём FastAPI приложение
app = FastAPI(
title="ERV Insurance Platform API",
description="API для обработки страховых обращений",
title="Ticket Form Intake API",
description="API для обработки обращений Ticket Form",
version="1.0.0",
lifespan=lifespan
)
@@ -106,10 +106,10 @@ app.include_router(n8n_proxy.router) # 🔒 Безопасный proxy к n8n w
async def root():
"""Главная страница API"""
return {
"message": "🚀 ERV Insurance Platform API",
"message": "🚀 Ticket Form Intake API",
"version": "1.0.0",
"status": "running",
"docs": "http://147.45.146.17:8100/docs"
"docs": f"{settings.backend_url}/docs"
}
@@ -181,10 +181,10 @@ async def test():
"success": True,
"message": "✅ Backend API работает!",
"services": {
"redis": "localhost:6379",
"postgres": "147.45.189.234:5432",
"ocr": "147.45.146.17:8001",
"rabbitmq": "185.197.75.249:5672"
"redis": f"{settings.redis_host}:{settings.redis_port}",
"postgres": f"{settings.postgres_host}:{settings.postgres_port}",
"ocr": settings.ocr_api_url,
"rabbitmq": f"{settings.rabbitmq_host}:{settings.rabbitmq_port}"
}
}
@@ -193,7 +193,7 @@ async def test():
async def info():
"""Информация о платформе"""
return {
"platform": "ERV Insurance Claims",
"platform": settings.app_name,
"version": "1.0.0",
"tech_stack": {
"backend": "Python FastAPI",
@@ -204,9 +204,9 @@ async def info():
"storage": "S3 Timeweb"
},
"features": [
"OCR документов (паспорт, билеты)",
"AI автозаполнение (Gemini Vision)",
"Проверка рейсов (FlightAware)",
"OCR документов",
"AI автозаполнение",
"Проверка статуса выплат",
"СБП выплаты",
"Интеграция с CRM Vtiger"
]
@@ -215,4 +215,4 @@ async def info():
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8100)
uvicorn.run(app, host="0.0.0.0", port=8200)

View File

@@ -213,7 +213,7 @@ class OCRService:
"https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": f"Bearer {self.ai_api_key}",
"HTTP-Referer": "http://147.45.146.17:8100",
"HTTP-Referer": settings.backend_url,
"Content-Type": "application/json"
},
json={