File size: 5,113 Bytes
454f1d5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | """
用户反馈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)}")
|