- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
259 lines
7.6 KiB
Python
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,
|
|
},
|
|
}
|