message / app /api /feedback.py
hunian
docs(core): 更新README和前端资源,完善插件系统和用户反馈功能
454f1d5
Raw
History Blame Contribute Delete
5.11 kB
"""
用户反馈API端点
"""
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field, field_validator
from typing import List, Optional
from datetime import datetime
from pathlib import Path
import json
import logging
from app.config.settings import settings
from app.config.constants import FEEDBACKS_FILE
router = APIRouter()
logger = logging.getLogger(__name__)
class FeedbackCreate(BaseModel):
"""反馈创建模型"""
username: str = Field(..., min_length=1, max_length=50, description="用户名")
content: str = Field(..., min_length=1, max_length=1000, description="反馈内容")
@field_validator("username")
@classmethod
def validate_username(cls, v):
v = v.strip()
if not v:
raise ValueError("用户名不能为空")
return v
@field_validator("content")
@classmethod
def validate_content(cls, v):
v = v.strip()
if not v:
raise ValueError("反馈内容不能为空")
return v
class FeedbackResponse(BaseModel):
"""反馈响应模型"""
id: str
username: str
content: str
timestamp: str
def _get_feedbacks_file_path() -> Path:
"""获取反馈文件路径"""
data_dir = settings.DATA_DIR
data_dir.mkdir(parents=True, exist_ok=True)
return data_dir / FEEDBACKS_FILE
# 确保反馈文件存在且格式正确
def _initialize_feedback_file():
"""初始化反馈文件"""
feedbacks_file = _get_feedbacks_file_path()
if not feedbacks_file.exists() or feedbacks_file.stat().st_size == 0:
try:
with open(feedbacks_file, "w", encoding="utf-8") as f:
json.dump([], f, ensure_ascii=False)
logger.info("反馈文件初始化完成")
except Exception as e:
logger.error(f"初始化反馈文件失败: {e}")
# 应用启动时初始化
_initialize_feedback_file()
def _load_feedbacks() -> List[FeedbackResponse]:
"""加载反馈记录"""
feedbacks_file = _get_feedbacks_file_path()
if not feedbacks_file.exists():
return []
# 检查文件大小,避免空文件解析错误
if feedbacks_file.stat().st_size == 0:
return []
try:
with open(feedbacks_file, "r", encoding="utf-8") as f:
content = f.read().strip()
if not content:
return []
data = json.loads(content)
return [FeedbackResponse(**item) for item in data]
except json.JSONDecodeError as e:
logger.error(f"JSON解析失败: {e}")
return []
except Exception as e:
logger.error(f"加载反馈记录失败: {e}")
return []
def _save_feedbacks(feedbacks: List[FeedbackResponse]) -> bool:
"""保存反馈记录"""
feedbacks_file = _get_feedbacks_file_path()
try:
with open(feedbacks_file, "w", encoding="utf-8") as f:
json.dump([fb.dict() for fb in feedbacks], f, ensure_ascii=False, indent=2)
f.flush()
return True
except Exception as e:
logger.error(f"保存反馈记录失败: {e}")
return False
@router.get("/", response_model=List[FeedbackResponse])
async def get_feedbacks():
"""获取所有反馈记录"""
feedbacks = _load_feedbacks()
return sorted(feedbacks, key=lambda x: x.timestamp, reverse=True)
@router.post("/", response_model=FeedbackResponse)
async def create_feedback(feedback: FeedbackCreate):
"""创建新反馈"""
try:
feedbacks = _load_feedbacks()
new_feedback = FeedbackResponse(
id=datetime.now().strftime("%Y%m%d%H%M%S"),
username=feedback.username,
content=feedback.content,
timestamp=datetime.now().strftime("%Y-%m-%d %H:%M"),
)
feedbacks.append(new_feedback)
if _save_feedbacks(feedbacks):
logger.info(f"新反馈已提交: {feedback.username}")
return new_feedback
else:
raise HTTPException(status_code=500, detail="保存反馈失败")
except HTTPException:
raise
except Exception as e:
logger.error(f"创建反馈失败: {e}")
raise HTTPException(status_code=500, detail=f"创建反馈失败: {str(e)}")
@router.delete("/{feedback_id}")
async def delete_feedback(feedback_id: str):
"""删除反馈记录"""
try:
feedbacks = _load_feedbacks()
original_count = len(feedbacks)
feedbacks = [fb for fb in feedbacks if fb.id != feedback_id]
if len(feedbacks) == original_count:
raise HTTPException(
status_code=404, detail=f"反馈记录 {feedback_id} 不存在"
)
if _save_feedbacks(feedbacks):
return {"success": True, "message": "反馈记录已删除"}
else:
raise HTTPException(status_code=500, detail="保存失败")
except HTTPException:
raise
except Exception as e:
logger.error(f"删除反馈失败: {e}")
raise HTTPException(status_code=500, detail=f"删除反馈失败: {str(e)}")