初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
This commit is contained in:
commit
78ff261b5a
199 files changed
+69126
No files matched your search
@@ -0,0 +1,258 @@
|
||||
"""
|
||||
会员订阅体系管理 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,
|
||||
},
|
||||
}
|
||||
Reference in new issue
Block a user