Files
shaguabijia-app-server/app/admin/routers/feedback.py
T
lowmaster-chen e1bd0e3ef7 feat: 接入数据大盘聚合与美团 CPS 对账
改了什么:新增新版大盘日期窗口聚合、广告收益拆分、比价耗时字段、美团 CPS 拉单入库与大盘展示,并同步反馈审核和邀请奖励下线口径。
验证:python -m pytest tests/test_admin_read.py tests/test_admin_write.py tests/test_compare_record.py tests/test_invite.py tests/test_feedback.py -q。
2026-06-28 09:56:13 +08:00

149 lines
4.7 KiB
Python

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