feat(backend): кэширование form_draft в check-ocr-status
- Добавлена проверка наличия черновика в БД перед запуском RAG - Если documents_hash совпадает — возвращаем черновик из кэша - Если черновика нет или он устарел — запускаем RAG workflow - Добавлен параметр force_refresh для принудительного обновления - Импортирован db сервис для работы с PostgreSQL
This commit is contained in:
Binary file not shown.
@@ -8,9 +8,11 @@ from typing import Optional, List
|
||||
import httpx
|
||||
import json
|
||||
import uuid
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from ..services.redis_service import redis_service
|
||||
from ..services.database import db
|
||||
from ..config import settings
|
||||
|
||||
router = APIRouter(prefix="/api/v1/documents", tags=["Documents"])
|
||||
@@ -611,20 +613,35 @@ async def generate_documents_list(request: Request):
|
||||
|
||||
|
||||
|
||||
def compute_documents_hash(doc_ids: List[str]) -> str:
|
||||
"""Вычисляет hash от списка document_id для проверки актуальности черновика"""
|
||||
sorted_ids = sorted([d for d in doc_ids if d])
|
||||
hash_input = ','.join(sorted_ids)
|
||||
# Используем простой hash как в n8n (djb2)
|
||||
hash_val = 5381
|
||||
for char in hash_input:
|
||||
hash_val = ((hash_val << 5) + hash_val) + ord(char)
|
||||
return format(abs(hash_val) & 0xFFFFFFFF, 'x').zfill(8)
|
||||
|
||||
|
||||
@router.post("/check-ocr-status")
|
||||
async def check_ocr_status(request: Request):
|
||||
"""
|
||||
Проверка статуса OCR обработки документов.
|
||||
|
||||
Вызывается при нажатии "Продолжить" после загрузки документов.
|
||||
Пушит запрос в Redis канал clpr:check:ocr_status, который слушает n8n workflow.
|
||||
n8n проверяет статус всех документов и публикует результат в ocr_events:{session_id}.
|
||||
|
||||
Логика:
|
||||
1. Проверяем наличие form_draft в payload
|
||||
2. Если черновик есть и documents_hash совпадает — возвращаем его
|
||||
3. Если черновика нет или он устарел — запускаем RAG workflow
|
||||
"""
|
||||
try:
|
||||
body = await request.json()
|
||||
|
||||
claim_id = body.get("claim_id")
|
||||
session_id = body.get("session_id")
|
||||
force_refresh = body.get("force_refresh", False) # Принудительное обновление
|
||||
|
||||
if not claim_id or not session_id:
|
||||
raise HTTPException(
|
||||
@@ -637,10 +654,98 @@ async def check_ocr_status(request: Request):
|
||||
extra={
|
||||
"claim_id": claim_id,
|
||||
"session_id": session_id,
|
||||
"force_refresh": force_refresh,
|
||||
},
|
||||
)
|
||||
|
||||
# Публикуем запрос в Redis для n8n workflow
|
||||
# =====================================================
|
||||
# ШАГ 1: Проверяем наличие черновика в БД
|
||||
# =====================================================
|
||||
if not force_refresh:
|
||||
try:
|
||||
# Получаем form_draft и список документов
|
||||
claim_data = await db.fetch_one("""
|
||||
SELECT
|
||||
c.payload->'form_draft' AS form_draft,
|
||||
(
|
||||
SELECT array_agg(cd.id::text ORDER BY cd.id)
|
||||
FROM clpr_claim_documents cd
|
||||
WHERE cd.claim_id::uuid = c.id
|
||||
) AS document_ids
|
||||
FROM clpr_claims c
|
||||
WHERE c.id = $1::uuid
|
||||
""", claim_id)
|
||||
|
||||
if claim_data and claim_data.get('form_draft'):
|
||||
form_draft = claim_data['form_draft']
|
||||
# Если form_draft — строка, парсим JSON
|
||||
if isinstance(form_draft, str):
|
||||
form_draft = json.loads(form_draft)
|
||||
|
||||
saved_hash = form_draft.get('documents_hash', '')
|
||||
document_ids = claim_data.get('document_ids') or []
|
||||
current_hash = compute_documents_hash(document_ids)
|
||||
|
||||
logger.info(
|
||||
"📋 Draft check",
|
||||
extra={
|
||||
"saved_hash": saved_hash,
|
||||
"current_hash": current_hash,
|
||||
"docs_count": len(document_ids),
|
||||
},
|
||||
)
|
||||
|
||||
# ✅ Черновик актуален — возвращаем его!
|
||||
if saved_hash == current_hash:
|
||||
logger.info(
|
||||
"✅ Using cached form_draft",
|
||||
extra={
|
||||
"claim_id": claim_id,
|
||||
"hash": saved_hash,
|
||||
},
|
||||
)
|
||||
|
||||
# Публикуем событие что данные готовы
|
||||
event_data = {
|
||||
"event_type": "form_draft_ready",
|
||||
"status": "ready",
|
||||
"message": "Черновик формы готов",
|
||||
"claim_id": claim_id,
|
||||
"session_id": session_id,
|
||||
"form_draft": form_draft,
|
||||
"from_cache": True,
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
}
|
||||
|
||||
await redis_service.publish(
|
||||
f"ocr_events:{session_id}",
|
||||
json.dumps(event_data, ensure_ascii=False)
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"status": "ready",
|
||||
"message": "Черновик формы готов (из кэша)",
|
||||
"from_cache": True,
|
||||
"form_draft": form_draft,
|
||||
"listen_channel": f"ocr_events:{session_id}",
|
||||
}
|
||||
else:
|
||||
logger.info(
|
||||
"🔄 Draft outdated, running RAG",
|
||||
extra={
|
||||
"reason": "documents_hash mismatch",
|
||||
"saved_hash": saved_hash,
|
||||
"current_hash": current_hash,
|
||||
},
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ Draft check failed: {e}, proceeding with RAG")
|
||||
|
||||
# =====================================================
|
||||
# ШАГ 2: Черновика нет или устарел — запускаем RAG
|
||||
# =====================================================
|
||||
event_data = {
|
||||
"claim_id": claim_id,
|
||||
"session_token": session_id,
|
||||
@@ -655,7 +760,7 @@ async def check_ocr_status(request: Request):
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"✅ OCR status check published",
|
||||
"✅ OCR status check published (running RAG)",
|
||||
extra={
|
||||
"channel": channel,
|
||||
"subscribers": subscribers,
|
||||
@@ -665,7 +770,9 @@ async def check_ocr_status(request: Request):
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Запрос на проверку OCR статуса отправлен",
|
||||
"status": "processing",
|
||||
"message": "Запрос на обработку документов отправлен",
|
||||
"from_cache": False,
|
||||
"channel": channel,
|
||||
"listen_channel": f"ocr_events:{session_id}",
|
||||
}
|
||||
|
||||
@@ -431,3 +431,4 @@ return [
|
||||
- `ticket_form/docs/SQL_CLAIMSAVE_FINAL_FIXED_NEW_FLOW_WITH_UPLOADED_FIXED.sql` — SQL для сохранения документов
|
||||
- `ticket_form/frontend/src/components/form/generateConfirmationFormHTML.ts` — шаблон формы заявления
|
||||
|
||||
|
||||
|
||||
@@ -97,3 +97,4 @@ check_all_ready (FALSE) → (конец)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -30,3 +30,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,3 +18,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,3 +19,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,3 +18,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user