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)}")