Files
video/server/build/lib/app/api/v1/admin.py
T
2026-10-16 13:51:00 +08:00

300 lines
14 KiB
Python

"""管理后台路由(/admin/*,对应《03-接口文档》第 11 章)
角色矩阵(super 拥有全部权限):
- reviewer:仅审核 → 待审核列表 / 审核视频
- operator:套餐、活动、看板 → 仪表盘 / 套餐 CRUD / 赠送
- 其余接口仅 super
"""
import redis.asyncio as aioredis
from fastapi import APIRouter, Depends, Query, Request
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.response import ok
from app.database import get_db
from app.deps import AdminIdentity, require_roles
from app.redis_client import get_redis
from app.schemas import admin as schema
from app.services import admin_service
router = APIRouter(prefix="/admin", tags=["管理后台"])
# 角色依赖:super 自动放行
require_reviewer = require_roles("reviewer")
require_operator = require_roles("operator")
require_super = require_roles()
def _ip(request: Request) -> str:
return request.client.host if request.client else ""
# ---------- 认证 / 看板 ----------
@router.post("/auth/login")
async def login(req: schema.AdminLoginReq, request: Request, db: AsyncSession = Depends(get_db)):
return ok(await admin_service.login(db, req, _ip(request)))
@router.get("/dashboard")
async def dashboard(db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_operator)):
return ok(await admin_service.dashboard(db))
# ---------- 视频 ----------
@router.get("/videos/pending")
async def list_pending_videos(page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_reviewer)):
return ok(await admin_service.list_pending_videos(db, page, page_size))
@router.get("/videos")
async def list_videos(keyword: str | None = Query(None), status: int | None = Query(None),
author_id: int | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_videos(db, keyword, status, author_id, page, page_size))
@router.post("/videos/{video_id}/review")
async def review_video(video_id: int, req: schema.VideoReviewReq, request: Request,
db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_reviewer)):
return ok(await admin_service.review_video(db, redis, admin, video_id, req, _ip(request)))
@router.post("/videos/{video_id}/offline")
async def offline_video(video_id: int, req: schema.ReasonReq, request: Request,
db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_super)):
await admin_service.offline_video(db, redis, admin, video_id, req.reason, _ip(request))
return ok()
# ---------- 用户 / 积分 ----------
@router.get("/users")
async def list_users(keyword: str | None = Query(None), status: int | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_users(db, keyword, status, page, page_size))
@router.get("/users/{user_id}")
async def get_user_detail(user_id: int, db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.get_user_detail(db, user_id))
@router.put("/users/{user_id}/status")
async def set_user_status(user_id: int, req: schema.UserStatusReq, request: Request,
db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.set_user_status(db, redis, admin, user_id, req, _ip(request)))
@router.post("/points/adjust")
async def adjust_points(req: schema.PointsAdjustReq, request: Request,
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.adjust_points(db, admin, req, _ip(request)))
@router.get("/points/records")
async def list_point_records(user_id: int | None = Query(None), biz_type: int | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_point_records(db, user_id, biz_type, page, page_size))
# ---------- 订单 / 套餐 ----------
@router.get("/orders")
async def list_orders(order_no: str | None = Query(None), user_id: int | None = Query(None),
pay_method: int | None = Query(None), status: int | None = Query(None),
start_date: str | None = Query(None), end_date: str | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_orders(
db, order_no, user_id, pay_method, status, start_date, end_date, page, page_size
))
@router.post("/orders/{order_no}/resettle")
async def resettle_order(order_no: str, req: schema.ResettleReq, request: Request,
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.resettle_order(db, admin, order_no, req, _ip(request)))
@router.post("/orders/{order_no}/refund")
async def refund_order(order_no: str, req: schema.RefundReq, request: Request,
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.refund_order(db, admin, order_no, req, _ip(request)))
@router.get("/packages")
async def list_packages(db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_operator)):
return ok(await admin_service.list_packages(db))
@router.post("/packages")
async def create_package(req: schema.PackageCreateReq, request: Request,
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_operator)):
return ok(await admin_service.create_package(db, admin, req, _ip(request)))
@router.put("/packages/{package_id}")
async def update_package(package_id: int, req: schema.PackageUpdateReq, request: Request,
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_operator)):
return ok(await admin_service.update_package(db, admin, package_id, req, _ip(request)))
@router.delete("/packages/{package_id}")
async def delete_package(package_id: int, request: Request, db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_operator)):
return ok(await admin_service.delete_package(db, admin, package_id, _ip(request)))
# ---------- 赠送 / 日志 ----------
@router.post("/gift")
async def gift(req: schema.GiftReq, request: Request, db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_operator)):
return ok(await admin_service.gift(db, admin, req, _ip(request)))
@router.get("/logs")
async def list_logs(admin_id: int | None = Query(None), action: str | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_logs(db, admin_id, action, page, page_size))
# ---------- 提现 ----------
@router.get("/withdrawals")
async def list_withdrawals(status: int | None = Query(None), user_id: int | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_withdrawals(db, status, user_id, page, page_size))
@router.post("/withdrawals/{withdraw_no}/review")
async def review_withdrawal(withdraw_no: str, req: schema.WithdrawReviewReq, request: Request,
db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.review_withdrawal(db, redis, admin, withdraw_no, req, _ip(request)))
# ---------- 推荐 ----------
@router.get("/referrals")
async def list_referrals(inviter_id: int | None = Query(None), status: int | None = Query(None),
risk_flag: str | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_referrals(db, inviter_id, status, risk_flag, page, page_size))
@router.post("/referrals/{referral_id}/review")
async def review_referral(referral_id: int, req: schema.ReferralReviewReq, request: Request,
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.review_referral(db, admin, referral_id, req, _ip(request)))
# ---------- 配置 ----------
@router.get("/configs")
async def list_configs(db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_configs(db))
@router.put("/configs/{config_key}")
async def update_config(config_key: str, req: schema.ConfigUpdateReq, request: Request,
db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.update_config(db, redis, admin, config_key, req.value, _ip(request)))
# ---------- 评论 / 举报 ----------
@router.get("/comments")
async def list_comments(video_id: int | None = Query(None), user_id: int | None = Query(None),
keyword: str | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_comments(db, video_id, user_id, keyword, page, page_size))
@router.delete("/comments/{comment_id}")
async def delete_comment(comment_id: int, req: schema.ReasonReq, request: Request,
db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_super)):
await admin_service.delete_comment(db, redis, admin, comment_id, req.reason, _ip(request))
return ok()
@router.get("/reports")
async def list_reports(status: int | None = Query(None), target_type: int | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_reports(db, status, target_type, page, page_size))
@router.post("/reports/{report_id}/review")
async def review_report(report_id: int, req: schema.ReportReviewReq, request: Request,
db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.review_report(db, redis, admin, report_id, req, _ip(request)))
# ---------- 敏感词 ----------
@router.get("/sensitive-words")
async def list_sensitive_words(keyword: str | None = Query(None),
page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.list_sensitive_words(db, keyword, page, page_size))
@router.post("/sensitive-words")
async def add_sensitive_word(req: schema.SensitiveWordAddReq, request: Request,
db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_super)):
return ok(await admin_service.add_sensitive_word(db, redis, admin, req.word, _ip(request)))
@router.delete("/sensitive-words/{word_id}")
async def delete_sensitive_word(word_id: int, request: Request, db: AsyncSession = Depends(get_db),
redis: aioredis.Redis = Depends(get_redis),
admin: AdminIdentity = Depends(require_super)):
await admin_service.delete_sensitive_word(db, redis, admin, word_id, _ip(request))
return ok()