"""admin 反馈工单:列表(读)+ 采纳发金币 / 拒绝写原因(写,带审计)。""" from __future__ import annotations 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)], ) @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, limit: Annotated[int, Query(ge=1, le=100)] = 20, cursor: Annotated[int | None, Query()] = None, ) -> CursorPage[FeedbackOut]: items, next_cursor = queries.list_feedbacks( db, status=status, user_id=user_id, limit=limit, cursor=cursor, ) return CursorPage( items=[FeedbackOut.model_validate(f) for f in items], next_cursor=next_cursor, ) @router.post("/{feedback_id}/handle", response_model=OkResponse, summary="标记反馈已处理") def handle_feedback( feedback_id: int, request: Request, 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="反馈不存在") before = fb.status mutations.update_feedback_status(db, fb, status="adopted", commit=False) write_audit( db, admin, action="feedback.handle", target_type="feedback", target_id=feedback_id, detail={"before": before, "after": "adopted", "deprecated": True}, ip=get_client_ip(request), commit=False, ) db.commit() return OkResponse() @router.post("/{feedback_id}/approve", response_model=OkResponse, summary="采纳反馈并发金币") def approve_feedback( feedback_id: int, body: FeedbackApproveRequest, request: Request, 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="反馈不存在") if fb.status != "pending": raise HTTPException(status_code=400, detail=f"该反馈已审核过(当前 {fb.status}),不可重复操作") note = body.note.strip() if body.note else None mutations.review_feedback( db, fb, status="adopted", admin_id=admin.id, reward_coins=body.reward_coins, review_note=note, commit=False, ) acc, _ = wallet_repo.grant_coins( db, fb.user_id, body.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={ "user_id": fb.user_id, "reward_coins": body.reward_coins, "balance_after": acc.coin_balance, "note": note, }, ip=get_client_ip(request), commit=False, ) db.commit() return OkResponse() @router.post("/{feedback_id}/reject", response_model=OkResponse, summary="拒绝反馈并写明原因") def reject_feedback( feedback_id: int, body: FeedbackRejectRequest, request: Request, 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="反馈不存在") if fb.status != "pending": raise HTTPException(status_code=400, detail=f"该反馈已审核过(当前 {fb.status}),不可重复操作") reason = body.reason.strip() note = body.note.strip() if body.note else None mutations.review_feedback( db, fb, status="rejected", admin_id=admin.id, reject_reason=reason, review_note=note, commit=False, ) write_audit( db, admin, action="feedback.reject", target_type="feedback", target_id=feedback_id, detail={"user_id": fb.user_id, "reason": reason, "note": note}, ip=get_client_ip(request), commit=False, ) db.commit() return OkResponse()