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