Files

259 lines
7.6 KiB
Python

"""
会员订阅体系管理 API(管理员)
"""
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from ...database import get_db, SessionLocal
from ...models import MembershipPlan, MembershipSubscription, User, RiskRule
from ...auth import get_current_admin
from ...middleware.log_service import LogService
import uuid
from datetime import datetime
router = APIRouter()
# ============================================================
# 会员计划 CRUD
# ============================================================
@router.get("/plans")
async def get_plans(
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""获取会员计划列表"""
plans = db.query(MembershipPlan).order_by(MembershipPlan.level).all()
return [
{
"id": p.id,
"name": p.name,
"level": p.level,
"duration_type": p.duration_type,
"price": float(p.price) if p.price else 0,
"daily_free_tokens": p.daily_free_tokens,
"features": p.features,
"free_app_ids": p.free_app_ids,
"free_model_ids": p.free_model_ids,
"is_active": p.is_active,
"created_at": p.created_at.isoformat() if p.created_at else None,
"updated_at": p.updated_at.isoformat() if p.updated_at else None,
}
for p in plans
]
@router.post("/plans")
async def create_plan(
name: str,
level: int,
duration_type: str,
price: float,
daily_free_tokens: int = 0,
features: list = None,
free_app_ids: list = None,
free_model_ids: list = None,
is_active: bool = True,
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""创建会员计划"""
plan = MembershipPlan(
id=str(uuid.uuid4()),
name=name,
level=level,
duration_type=duration_type,
price=price,
daily_free_tokens=daily_free_tokens,
features=features,
free_app_ids=free_app_ids,
free_model_ids=free_model_ids,
is_active=is_active,
)
db.add(plan)
db.commit()
LogService.log_audit(
admin_id=admin_user.id,
admin_name=admin_user.username,
action="create_membership_plan",
target_type="membership_plan",
target_id=plan.id,
detail=f"创建会员计划 {name}",
)
return {"message": "会员计划已创建", "id": plan.id}
@router.put("/plans/{id}")
async def update_plan(
id: str,
name: str = None,
level: int = None,
duration_type: str = None,
price: float = None,
daily_free_tokens: int = None,
features: list = None,
free_app_ids: list = None,
free_model_ids: list = None,
is_active: bool = None,
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""更新会员计划"""
plan = db.query(MembershipPlan).filter(MembershipPlan.id == id).first()
if not plan:
raise HTTPException(status_code=404, detail="会员计划不存在")
if name is not None:
plan.name = name
if level is not None:
plan.level = level
if duration_type is not None:
plan.duration_type = duration_type
if price is not None:
plan.price = price
if daily_free_tokens is not None:
plan.daily_free_tokens = daily_free_tokens
if features is not None:
plan.features = features
if free_app_ids is not None:
plan.free_app_ids = free_app_ids
if free_model_ids is not None:
plan.free_model_ids = free_model_ids
if is_active is not None:
plan.is_active = is_active
plan.updated_at = datetime.now()
db.commit()
LogService.log_audit(
admin_id=admin_user.id,
admin_name=admin_user.username,
action="update_membership_plan",
target_type="membership_plan",
target_id=id,
detail=f"更新会员计划 {plan.name}",
)
return {"message": "会员计划已更新"}
@router.delete("/plans/{id}")
async def delete_plan(
id: str,
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""删除会员计划"""
plan = db.query(MembershipPlan).filter(MembershipPlan.id == id).first()
if not plan:
raise HTTPException(status_code=404, detail="会员计划不存在")
db.delete(plan)
db.commit()
LogService.log_audit(
admin_id=admin_user.id,
admin_name=admin_user.username,
action="delete_membership_plan",
target_type="membership_plan",
target_id=id,
detail=f"删除会员计划 {plan.name}",
)
return {"message": "会员计划已删除"}
# ============================================================
# 用户订阅管理
# ============================================================
@router.get("/subscriptions")
async def get_subscriptions(
user_id: str = Query(None),
status: str = Query(None, description="订阅状态: active/expired/downgraded"),
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""获取所有用户订阅列表"""
query = db.query(MembershipSubscription)
if user_id:
query = query.filter(MembershipSubscription.user_id == user_id)
if status:
query = query.filter(MembershipSubscription.status == status)
total = query.count()
subscriptions = query.order_by(
MembershipSubscription.created_at.desc()
).offset((page - 1) * page_size).limit(page_size).all()
return {
"total": total,
"page": page,
"page_size": page_size,
"items": [
{
"id": s.id,
"user_id": s.user_id,
"plan_id": s.plan_id,
"order_id": s.order_id,
"start_at": s.start_at.isoformat() if s.start_at else None,
"end_at": s.end_at.isoformat() if s.end_at else None,
"auto_renew": s.auto_renew,
"status": s.status,
"created_at": s.created_at.isoformat() if s.created_at else None,
}
for s in subscriptions
],
}
# ============================================================
# 自动化规则(风控)
# ============================================================
@router.get("/automation-rules")
async def get_automation_rules(
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""获取自动化规则配置"""
rules = db.query(RiskRule).order_by(RiskRule.created_at.desc()).all()
if not rules:
# 返回默认规则
return {
"rules": [],
"defaults": {
"max_login_attempts": 5,
"token_usage_threshold_per_hour": 1000000,
"token_usage_threshold_per_day": 10000000,
"concurrent_sessions_limit": 5,
},
}
return {
"rules": [
{
"id": r.id,
"name": r.name,
"type": r.type,
"threshold": r.threshold,
"duration": r.duration,
"is_enabled": r.is_enabled,
"created_at": r.created_at.isoformat() if r.created_at else None,
"updated_at": r.updated_at.isoformat() if r.updated_at else None,
}
for r in rules
],
"defaults": {
"max_login_attempts": 5,
"token_usage_threshold_per_hour": 1000000,
"token_usage_threshold_per_day": 10000000,
"concurrent_sessions_limit": 5,
},
}