93 lines
5.0 KiB
Python
93 lines
5.0 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.exc import IntegrityError
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api.pagination import page_result
|
|
from app.core.database import get_db
|
|
from app.core.dependencies import get_current_admin, get_current_user
|
|
from app.core.responses import api_success
|
|
from app.models.admin import Admin
|
|
from app.models.chat import ChatMessage, ChatSession
|
|
from app.models.feedback import MessageFeedback
|
|
from app.models.user import User
|
|
from app.services.admin_service import OperationLogService
|
|
from app.services.admin_permission_service import require_permission
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class FeedbackCreate(BaseModel):
|
|
messageId: int = Field(gt=0)
|
|
content: str = Field(min_length=1, max_length=200)
|
|
|
|
|
|
@router.post("")
|
|
def create_feedback(payload: FeedbackCreate, db: Session = Depends(get_db), user: User = Depends(get_current_user)) -> dict:
|
|
message = db.scalar(select(ChatMessage).where(ChatMessage.id == payload.messageId, ChatMessage.user_id == user.id))
|
|
if message is None or message.role != "assistant" or message.message_status != "FINISHED":
|
|
raise HTTPException(status_code=404, detail="反馈的回答不存在")
|
|
content = payload.content.strip()
|
|
if not content:
|
|
raise HTTPException(status_code=400, detail="请填写反馈内容")
|
|
existing = db.scalar(select(MessageFeedback).where(MessageFeedback.user_id == user.id, MessageFeedback.message_id == message.id))
|
|
if existing:
|
|
raise HTTPException(status_code=409, detail="这条回答已经反馈过了")
|
|
item = MessageFeedback(user_id=user.id, session_id=message.session_id, message_id=message.id, content=content)
|
|
db.add(item)
|
|
try:
|
|
db.commit()
|
|
except IntegrityError as exc:
|
|
db.rollback()
|
|
raise HTTPException(status_code=409, detail="这条回答已经反馈过了") from exc
|
|
db.refresh(item)
|
|
return api_success({"id": item.id})
|
|
|
|
|
|
@router.get("/admin/list")
|
|
def feedback_list(readStatus: str = Query(default="all", pattern="^(all|read|unread)$"), page: int = Query(default=1, ge=1), pageSize: int = Query(default=20, ge=10, le=100), db: Session = Depends(get_db), _admin: Admin = Depends(get_current_admin)) -> dict:
|
|
require_permission(_admin, "feedback.view")
|
|
query = select(MessageFeedback, User, ChatMessage).join(User, User.id == MessageFeedback.user_id).join(ChatMessage, ChatMessage.id == MessageFeedback.message_id)
|
|
if readStatus != "all":
|
|
query = query.where(MessageFeedback.is_read == (1 if readStatus == "read" else 0))
|
|
total = db.scalar(select(func.count()).select_from(query.order_by(None).subquery())) or 0
|
|
rows = db.execute(query.order_by(MessageFeedback.created_at.desc()).offset((page - 1) * pageSize).limit(pageSize)).all()
|
|
return api_success(page_result([_summary(*row) for row in rows], total=total, page=page, page_size=pageSize))
|
|
|
|
|
|
@router.get("/admin/{feedback_id}")
|
|
def feedback_detail(feedback_id: int, db: Session = Depends(get_db), admin: Admin = Depends(get_current_admin)) -> dict:
|
|
row = db.execute(select(MessageFeedback, User, ChatMessage, ChatSession).join(User, User.id == MessageFeedback.user_id).join(ChatMessage, ChatMessage.id == MessageFeedback.message_id).join(ChatSession, ChatSession.id == MessageFeedback.session_id).where(MessageFeedback.id == feedback_id)).first()
|
|
if row is None:
|
|
raise HTTPException(status_code=404, detail="反馈不存在")
|
|
require_permission(admin, "feedback.view")
|
|
feedback, user, target, session = row
|
|
if not feedback.is_read:
|
|
feedback.is_read = 1
|
|
feedback.read_by = admin.id
|
|
feedback.read_at = datetime.now(UTC).replace(tzinfo=None)
|
|
db.commit()
|
|
messages = db.scalars(select(ChatMessage).where(ChatMessage.session_id == feedback.session_id, ChatMessage.id <= target.id).order_by(ChatMessage.id.asc()).limit(200)).all()
|
|
return api_success({**_summary(feedback, user, target), "sessionTitle": session.title, "messages": [{"id": m.id, "role": m.role, "content": m.content, "createdAt": m.created_at, "isTarget": m.id == target.id} for m in messages]})
|
|
|
|
|
|
@router.delete("/admin/{feedback_id}")
|
|
def delete_feedback(feedback_id: int, db: Session = Depends(get_db), admin: Admin = Depends(get_current_admin)) -> dict:
|
|
require_permission(admin, "feedback.delete")
|
|
item = db.get(MessageFeedback, feedback_id)
|
|
if item is None:
|
|
raise HTTPException(status_code=404, detail="反馈不存在")
|
|
db.delete(item)
|
|
OperationLogService.write(db, admin_id=admin.id, module="feedback", action="delete", target_id=feedback_id)
|
|
db.commit()
|
|
return api_success()
|
|
|
|
|
|
def _summary(item: MessageFeedback, user: User, message: ChatMessage) -> dict:
|
|
return {"id": item.id, "userId": user.id, "userName": user.name, "userPhone": user.phone, "messageId": message.id, "messageContent": message.content, "content": item.content, "isRead": bool(item.is_read), "createdAt": item.created_at}
|