Ticket form: new stack + description step
This commit is contained in:
@@ -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"]
|
||||
|
||||
|
||||
@@ -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}"
|
||||
)
|
||||
|
||||
|
||||
@@ -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 канала (опционально)")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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={
|
||||
|
||||
Reference in New Issue
Block a user