feat: LocalPro Finder — отзывы, рейтинги, чат, AI-оценка, диагностика, подписки
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
"""Чат — WebSocket + REST для сообщений"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, update
|
||||
import uuid
|
||||
|
||||
from ...core.database import async_session_factory, Chat, ChatMessage, ContentType, ChatStatus
|
||||
from ...utils.auth import get_current_user
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class SendMessageRequest(BaseModel):
|
||||
chat_id: uuid.UUID
|
||||
content_type: str = "text" # text | image | file | voice
|
||||
content: str | None = None
|
||||
media_url: str | None = None
|
||||
reply_to_id: uuid.UUID | None = None
|
||||
|
||||
|
||||
@router.post("/messages")
|
||||
async def send_message(req: SendMessageRequest, session: AsyncSession = Depends(async_session_factory), user=Depends(get_current_user)):
|
||||
"""Отправить сообщение в чат."""
|
||||
|
||||
chat = await session.get(Chat, req.chat_id)
|
||||
if not chat or chat.status != ChatStatus.ACTIVE:
|
||||
raise HTTPException(400, "Чат неактивен")
|
||||
|
||||
message = ChatMessage(
|
||||
chat_id=req.chat_id,
|
||||
sender_id=user.id,
|
||||
content_type=ContentType(req.content_type),
|
||||
content=req.content,
|
||||
media_url=req.media_url,
|
||||
reply_to_id=req.reply_to_id,
|
||||
)
|
||||
session.add(message)
|
||||
|
||||
from datetime import datetime
|
||||
chat.last_message_at = datetime.utcnow()
|
||||
await session.commit()
|
||||
|
||||
return {"id": str(message.id), "status": "sent"}
|
||||
|
||||
|
||||
@router.get("/messages/{chat_id}")
|
||||
async def get_messages(chat_id: uuid.UUID, limit: int = 50, before: uuid.UUID | None = Query(None), session: AsyncSession = Depends(async_session_factory)):
|
||||
"""Получить историю сообщений чата."""
|
||||
|
||||
query = select(ChatMessage).where(ChatMessage.chat_id == chat_id)
|
||||
if before:
|
||||
query = query.where(ChatMessage.id < before)
|
||||
query = query.order_by(ChatMessage.created_at.desc()).limit(limit)
|
||||
|
||||
result = await session.execute(query)
|
||||
messages = [m.mapped() for m in result.scalars().all()]
|
||||
return list(reversed(messages))
|
||||
|
||||
|
||||
@router.get("/chats")
|
||||
async def get_user_chats(user=Depends(get_current_user), session: AsyncSession = Depends(async_session_factory)):
|
||||
"""Получить все чаты пользователя."""
|
||||
|
||||
query = select(Chat).where(
|
||||
(Chat.client_id == user.id) | (Chat.master_id == user.id),
|
||||
Chat.status.in_([ChatStatus.ACTIVE, ChatStatus.COMPLETED])
|
||||
).order_by(Chat.last_message_at.desc())
|
||||
|
||||
result = await session.execute(query)
|
||||
return [c.mapped() for c in result.scalars().all()]
|
||||
|
||||
|
||||
@router.post("/chats/{chat_id}/mark-read")
|
||||
async def mark_as_read(chat_id: uuid.UUID, user=Depends(get_current_user), session: AsyncSession = Depends(async_session_factory)):
|
||||
"""Отметить сообщения как прочитанные."""
|
||||
|
||||
await session.execute(
|
||||
update(ChatMessage)
|
||||
.where((ChatMessage.chat_id == chat_id) & (ChatMessage.sender_id != user.id) & (ChatMessage.read_at.is_(None)))
|
||||
.values(read_at=datetime.utcnow())
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
@router.post("/chats/{chat_id}/archive")
|
||||
async def archive_chat(chat_id: uuid.UUID, user=Depends(get_current_user), session: AsyncSession = Depends(async_session_factory)):
|
||||
"""Заархивировать чат."""
|
||||
|
||||
chat = await session.get(Chat, chat_id)
|
||||
if not chat or (chat.client_id != user.id and chat.master_id != user.id):
|
||||
raise HTTPException(403)
|
||||
|
||||
chat.status = ChatStatus.ARCHIVED
|
||||
await session.commit()
|
||||
|
||||
|
||||
@router.delete("/messages/{message_id}")
|
||||
async def delete_message(message_id: uuid.UUID, user=Depends(get_current_user), session: AsyncSession = Depends(async_session_factory)):
|
||||
"""Удалить сообщение (только отправитель в течение 24ч)."""
|
||||
|
||||
message = await session.get(ChatMessage, message_id)
|
||||
if not message or message.sender_id != user.id:
|
||||
raise HTTPException(403)
|
||||
|
||||
await session.delete(message)
|
||||
await session.commit()
|
||||
|
||||
|
||||
@router.post("/chats/{chat_id}/search")
|
||||
async def search_messages(chat_id: uuid.UUID, query: str = Query(...), limit: int = 20, session: AsyncSession = Depends(async_session_factory)):
|
||||
"""Поиск по сообщениям чата."""
|
||||
|
||||
result = await session.execute(
|
||||
select(ChatMessage)
|
||||
.where((ChatMessage.chat_id == chat_id) & (ChatMessage.content.ilike(f"%{query}%")))
|
||||
.order_by(ChatMessage.created_at.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
return [m.mapped() for m in result.scalars().all()]
|
||||
Reference in New Issue
Block a user