""" Draft API Routes - Автосохранение драфтов форм """ from fastapi import APIRouter, HTTPException from pydantic import BaseModel from typing import Optional, Dict, Any from datetime import datetime import json from ..services.database import db import logging router = APIRouter(prefix="/api/v1/draft", tags=["Draft"]) logger = logging.getLogger(__name__) class DraftSaveRequest(BaseModel): """Запрос на сохранение драфта""" session_id: str # Уникальный ID сессии пользователя step: int # Текущий шаг формы (1, 2, 3) data: Dict[str, Any] # Данные формы user_agent: Optional[str] = None ip_address: Optional[str] = None @router.post("/save") async def save_draft(request: DraftSaveRequest): """ Автосохранение драфта формы Используется для аналитики: - Где пользователи бросают заполнение - Сколько времени проводят на каждом шаге - Какие поля вызывают проблемы """ # Защита: валидация session_id if not request.session_id or len(request.session_id) > 255: raise HTTPException(status_code=400, detail="Invalid session_id") # Защита: валидация step if request.step not in [1, 2, 3]: raise HTTPException(status_code=400, detail="Invalid step number") try: # Сериализуем данные в JSON form_data_json = json.dumps(request.data, ensure_ascii=False) # SQL для upsert (insert or update) query = """ INSERT INTO claims_draft ( session_id, current_step, form_data, user_agent, ip_address, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (session_id) DO UPDATE SET current_step = EXCLUDED.current_step, form_data = EXCLUDED.form_data, user_agent = EXCLUDED.user_agent, ip_address = EXCLUDED.ip_address, updated_at = EXCLUDED.updated_at RETURNING id """ now = datetime.now() result = await db.fetchval( query, request.session_id, request.step, form_data_json, request.user_agent, request.ip_address, now, now ) logger.info(f"✅ Draft saved: session={request.session_id}, step={request.step}") return { "success": True, "message": "Драфт сохранен", "draft_id": result } except Exception as e: logger.error(f"Draft save error: {e}") # Не падаем с ошибкой - просто логируем # Автосохранение не должно блокировать пользователя return { "success": False, "message": "Ошибка сохранения драфта" } @router.get("/stats") async def get_draft_stats(): """ Статистика по драфтам Показывает: - Сколько людей бросают на каждом шаге - Среднее время на шаге - Количество драфтов за период """ try: # Статистика по шагам step_stats_query = """ SELECT current_step, COUNT(*) as count, COUNT(DISTINCT session_id) as unique_users FROM claims_draft WHERE created_at >= NOW() - INTERVAL '7 days' GROUP BY current_step ORDER BY current_step """ step_stats = await db.fetch(step_stats_query) # Общая статистика total_drafts_query = """ SELECT COUNT(*) as total FROM claims_draft WHERE created_at >= NOW() - INTERVAL '7 days' """ total = await db.fetchval(total_drafts_query) return { "success": True, "period": "last_7_days", "total_drafts": total, "by_step": [ { "step": row["current_step"], "count": row["count"], "unique_users": row["unique_users"] } for row in step_stats ] } except Exception as e: logger.error(f"Draft stats error: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/list") async def list_recent_drafts(limit: int = 50): """ Список последних драфтов Для просмотра что люди заполняют """ try: query = """ SELECT id, session_id, current_step, form_data, created_at, updated_at, user_agent, ip_address FROM claims_draft ORDER BY updated_at DESC LIMIT $1 """ drafts = await db.fetch(query, limit) return { "success": True, "count": len(drafts), "drafts": [ { "id": row["id"], "session_id": row["session_id"], "step": row["current_step"], "data": json.loads(row["form_data"]) if row["form_data"] else {}, "created_at": row["created_at"].isoformat(), "updated_at": row["updated_at"].isoformat(), "user_agent": row["user_agent"], "ip_address": row["ip_address"] } for row in drafts ] } except Exception as e: logger.error(f"Draft list error: {e}") raise HTTPException(status_code=500, detail=str(e))