"""admin 反馈工单:列表筛选 + 审核采纳/拒绝(带金币发放与审计)。""" from __future__ import annotations from datetime import datetime from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, Query, Request from app.admin.audit import write_audit from app.admin.deps import AdminDb, get_client_ip, get_current_admin, require_role from app.admin.repositories import mutations, queries from app.admin.schemas.common import CursorPage, OkResponse from app.admin.schemas.feedback import ( FeedbackApproveRequest, FeedbackOut, FeedbackRejectRequest, FeedbackSummary, ) from app.models.admin import AdminUser from app.models.feedback import Feedback from app.repositories import wallet as wallet_repo router = APIRouter( prefix="/admin/api/feedbacks", tags=["admin-feedback"], dependencies=[Depends(get_current_admin)], ) def _ensure_pending(fb: Feedback) -> None: if fb.status not in {"pending", "new"}: raise HTTPException(status_code=400, detail="反馈已审核") @router.get("", response_model=CursorPage[FeedbackOut], summary="反馈工单列表") def list_feedbacks( db: AdminDb, status: Annotated[str | None, Query()] = None, user_id: Annotated[int | None, Query()] = None, content: Annotated[str | None, Query(max_length=100)] = None, created_from: Annotated[datetime | None, Query()] = None, created_to: Annotated[datetime | None, Query()] = None, sort_by: Annotated[str, Query(pattern="^(id|created_at)$")] = "id", sort_order: Annotated[str, Query(pattern="^(asc|desc)$")] = "desc", limit: Annotated[int, Query(ge=1, le=100)] = 20, cursor: Annotated[int | None, Query()] = None, ) -> CursorPage[FeedbackOut]: items, next_cursor, total = queries.list_feedbacks( db, status=status, user_id=user_id, content=content, created_from=created_from, created_to=created_to, sort_by=sort_by, sort_order=sort_order, limit=limit, cursor=cursor, ) return CursorPage( items=[FeedbackOut.model_validate(f) for f in items], next_cursor=next_cursor, total=total, ) @router.get("/summary", response_model=FeedbackSummary, summary="反馈审核统计(各状态计数)") def feedback_summary(db: AdminDb) -> FeedbackSummary: return FeedbackSummary.model_validate(queries.feedback_summary(db)) @router.post("/{feedback_id}/handle", response_model=OkResponse, summary="标记反馈已处理") def handle_feedback( feedback_id: int, _admin: Annotated[AdminUser, Depends(require_role("operator"))], db: AdminDb, ) -> OkResponse: fb = db.get(Feedback, feedback_id) if fb is None: raise HTTPException(status_code=404, detail="反馈不存在") raise HTTPException(status_code=400, detail="请使用采纳或拒绝接口审核反馈") @router.post("/{feedback_id}/approve", response_model=FeedbackOut, summary="采纳反馈并发金币") def approve_feedback( feedback_id: int, payload: FeedbackApproveRequest, request: Request, admin: Annotated[AdminUser, Depends(require_role("operator"))], db: AdminDb, ) -> FeedbackOut: fb = db.get(Feedback, feedback_id) if fb is None: raise HTTPException(status_code=404, detail="反馈不存在") _ensure_pending(fb) before = fb.status mutations.review_feedback( db, fb, status="adopted", reward_coins=payload.reward_coins, review_note=payload.note, reviewed_by_admin_id=admin.id, commit=False, ) wallet_repo.grant_coins( db, fb.user_id, payload.reward_coins, biz_type="feedback_reward", ref_id=str(fb.id), remark="意见反馈被采纳", ) write_audit( db, admin, action="feedback.approve", target_type="feedback", target_id=feedback_id, detail={ "before": before, "after": "adopted", "reward_coins": payload.reward_coins, "note": payload.note, }, ip=get_client_ip(request), commit=False, ) db.commit() db.refresh(fb) return FeedbackOut.model_validate(fb) @router.post("/{feedback_id}/reject", response_model=FeedbackOut, summary="拒绝采纳反馈") def reject_feedback( feedback_id: int, payload: FeedbackRejectRequest, request: Request, admin: Annotated[AdminUser, Depends(require_role("operator"))], db: AdminDb, ) -> FeedbackOut: fb = db.get(Feedback, feedback_id) if fb is None: raise HTTPException(status_code=404, detail="反馈不存在") _ensure_pending(fb) before = fb.status mutations.review_feedback( db, fb, status="rejected", reject_reason=payload.reason, review_note=payload.note, reviewed_by_admin_id=admin.id, commit=False, ) write_audit( db, admin, action="feedback.reject", target_type="feedback", target_id=feedback_id, detail={ "before": before, "after": "rejected", "reason": payload.reason, "note": payload.note, }, ip=get_client_ip(request), commit=False, ) db.commit() db.refresh(fb) return FeedbackOut.model_validate(fb)