| """ |
| 用户反馈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)}") |
|
|