from fastapi import Depends, APIRouter, HTTPException, Depends from sqlalchemy.orm import Session from app.db import models from app.core.security import get_current_user from app.api import schemas from fastapi.encoders import jsonable_encoder # бд def get_db(): db = models.SessionLocal() try: yield db finally: db.close() messagesRouter = APIRouter( prefix="/messages", tags=[], ) @messagesRouter.get("/history/{contact_id}") async def get_chat_history( contact_id: int, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db), anchor_id: int = None, # ID сообщения, вокруг которого строим выборку limit_before: int = 20, # Сколько сообщений загрузить ДО (старше анкера) limit_after: int = 20 # Сколько сообщений загрузить ПОСЛЕ (новее анкера) ): # Базовый фильтр для получения сообщений конкретного диалога chat_filter = ( ((models.Message.sender_id == current_user.id) & (models.Message.receiver_id == contact_id)) | ((models.Message.sender_id == contact_id) & (models.Message.receiver_id == current_user.id)) ) # КЕЙС 1: Анкер не передан — отдаем самый "хвост" чата (последние свежие сообщения) if anchor_id is None: messages = db.query(models.Message).filter(chat_filter)\ .order_by(models.Message.id.desc())\ .limit(limit_before)\ .all() print(f"DEBUG history (Tail): user={current_user.id}, contact={contact_id}, count={len(messages)}") return jsonable_encoder(messages) # КЕЙС 2: Передан anchor_id — собираем данные "вокруг" него print(f"DEBUG history (Anchor): user={current_user.id}, contact={contact_id}, anchor={anchor_id}") # 1. Тянем сообщения СТАРШЕ анкера (id < anchor_id) # Сортируем DESC, чтобы взять ближайшие к анкеру сообщения older_messages = db.query(models.Message).filter(chat_filter, models.Message.id < anchor_id)\ .order_by(models.Message.id.desc())\ .limit(limit_before)\ .all() # 2. Тянем само якорное сообщение (чтобы оно гарантированно попало в выборку) anchor_message = db.query(models.Message).filter(chat_filter, models.Message.id == anchor_id).first() # 3. Тянем сообщения НОВЕЕ анкера (id > anchor_id) # Сортируем ASC, чтобы взять идущие строго за анкером сообщения newer_messages = db.query(models.Message).filter(chat_filter, models.Message.id > anchor_id)\ .order_by(models.Message.id.asc())\ .limit(limit_after)\ .all() # Собираем все три куска в единый плоский список combined_messages = older_messages + ([anchor_message] if anchor_message else []) + newer_messages # ВАЖНО: Сортируем итоговый массив по убыванию (DESC), # чтобы фронтенд получил структуру в привычном для него хронологическом порядке (от новых к старым) combined_messages.sort(key=lambda msg: msg.id, reverse=True) print(f"DEBUG history (Combined): total_count={len(combined_messages)}, older={len(older_messages)}, newer={len(newer_messages)}") return jsonable_encoder(combined_messages) @messagesRouter.get("/last") async def get_last_messages( contact_id: int, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db), limit: int = 2 ): messages = db.query(models.Message).filter( (models.Message.sender_id == current_user.id) & (models.Message.receiver_id == contact_id) | (models.Message.sender_id == contact_id) & (models.Message.receiver_id == current_user.id) ).order_by(models.Message.timestamp.desc()).limit(limit).all() return jsonable_encoder(messages) @messagesRouter.delete("/all") async def delete_all_messages( current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db), ): """Удалить все сообщения пользователя""" # Удаляем все сообщения, где пользователь либо отправитель, либо получатель db.query(models.Message).filter( (models.Message.sender_id == current_user.id) | (models.Message.receiver_id == current_user.id) ).delete() db.commit() return {"status": "ok", "detail": "Все сообщения удалены"}