更新租户配额

This commit is contained in:
zhanggangyong
2026-01-12 16:47:41 +00:00
parent 3d5fd1f843
commit 1154b4e7ae
5 changed files with 1927 additions and 29 deletions
+176 -1
View File
@@ -18,7 +18,8 @@ from models import (
BillingRecord, Application, ModelProvider,
ChannelProviderAccess, ProviderApplication,
ChannelCustomAgentQuota, ResourceApplication, PlatformAgentQuota,
PlatformAgentTemplateConfig, AgentBillingRecord, ModelBillingRecord
PlatformAgentTemplateConfig, AgentBillingRecord, ModelBillingRecord,
TenantCustomAgentQuota, TenantModelKey
)
from app.auth import require_auth, get_password_hash
from app.schemas import (
@@ -3682,3 +3683,177 @@ async def get_platform_agents_status(
"summary": summary,
})
# ============= 渠道租户资源分配查看 =============
@router.get("/channels/{channel_id}/tenants/resources", response_model=SuccessResponse)
async def get_channel_tenants_resources(
channel_id: str,
principal: dict = Depends(require_auth),
db: AsyncSession = Depends(get_db)
):
"""
查看渠道下所有租户被分配的资源
返回渠道下每个租户的:
- 自定义 Agent 配额(cpu_quota, memory_quota, cpu_used, memory_used, agent_count)
- 平台 Agent 配额(各模板的 pod_quota, pod_used)
- 模型配额(各模型的 rpm, tpm)
权限:view:tenants (super_admin, billing_admin, operations_admin, channel_admin)
渠道管理员只能查看自己渠道的数据
"""
_verify_read_permission(principal)
role = _get_role(principal)
channel_admin_channel_id = _get_channel_id(principal)
# 验证渠道ID格式
try:
channel_uuid = uuid.UUID(channel_id)
except ValueError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="无效的渠道ID格式"
)
# 渠道管理员只能查看自己渠道的数据
if role in ["channel_admin", "billing_admin", "operations_admin"] and channel_admin_channel_id:
if channel_uuid != channel_admin_channel_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="只能查看所属渠道的租户资源"
)
# 验证渠道存在
channel_result = await db.execute(
select(Channel).where(Channel.id == channel_uuid)
)
channel = channel_result.scalar_one_or_none()
if not channel:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="渠道不存在"
)
# 获取渠道下所有租户
tenants_result = await db.execute(
select(User).where(User.channel_id == channel_uuid)
)
tenants = tenants_result.scalars().all()
# 构建租户资源数据
tenants_resources = []
for tenant in tenants:
tenant_data = {
"tenantId": str(tenant.id),
"tenantName": tenant.name,
"tenantEmail": tenant.email,
"status": tenant.status,
"createdAt": tenant.created_at.isoformat() if tenant.created_at else None,
}
# 1. 获取自定义 Agent 配额
custom_quota_result = await db.execute(
select(TenantCustomAgentQuota).where(
TenantCustomAgentQuota.tenant_id == tenant.id
)
)
custom_quota = custom_quota_result.scalar_one_or_none()
if custom_quota:
tenant_data["customAgentQuota"] = {
"cpuQuota": float(custom_quota.cpu_quota),
"memoryQuota": float(custom_quota.memory_quota),
"cpuUsed": float(custom_quota.cpu_used),
"memoryUsed": float(custom_quota.memory_used),
"agentCount": custom_quota.agent_count,
}
else:
tenant_data["customAgentQuota"] = None
# 2. 获取平台 Agent 配额
platform_quotas_result = await db.execute(
select(PlatformAgentQuota).where(
and_(
PlatformAgentQuota.target_id == tenant.id,
PlatformAgentQuota.target_type == "tenant"
)
)
)
platform_quotas = platform_quotas_result.scalars().all()
tenant_data["platformAgents"] = [
{
"templateName": pq.template_name,
"podQuota": pq.pod_quota,
"podUsed": pq.pod_used,
"cpuPerPod": pq.cpu_per_pod,
"memoryPerPod": pq.memory_per_pod,
}
for pq in platform_quotas
]
# 3. 获取模型配额(从 TenantModelKey 获取)
model_keys_result = await db.execute(
select(TenantModelKey).where(
and_(
TenantModelKey.tenant_id == tenant.id,
TenantModelKey.status == "active"
)
)
)
model_keys = model_keys_result.scalars().all()
tenant_data["models"] = [
{
"modelName": mk.model_name,
"rpmLimit": mk.rpm_limit,
"tpmLimit": mk.tpm_limit,
"maxBudget": float(mk.max_budget) if mk.max_budget else None,
"budgetDuration": mk.budget_duration,
}
for mk in model_keys
]
tenants_resources.append(tenant_data)
# 汇总统计
summary = {
"totalTenants": len(tenants),
"tenantsWithCustomAgents": len([t for t in tenants_resources if t["customAgentQuota"]]),
"tenantsWithPlatformAgents": len([t for t in tenants_resources if t["platformAgents"]]),
"tenantsWithModels": len([t for t in tenants_resources if t["models"]]),
"totalCustomAgentCpuQuota": sum(
t["customAgentQuota"]["cpuQuota"] for t in tenants_resources if t["customAgentQuota"]
),
"totalCustomAgentMemoryQuota": sum(
t["customAgentQuota"]["memoryQuota"] for t in tenants_resources if t["customAgentQuota"]
),
"totalCustomAgentCpuUsed": sum(
t["customAgentQuota"]["cpuUsed"] for t in tenants_resources if t["customAgentQuota"]
),
"totalCustomAgentMemoryUsed": sum(
t["customAgentQuota"]["memoryUsed"] for t in tenants_resources if t["customAgentQuota"]
),
"totalPlatformAgentPodQuota": sum(
sum(pa["podQuota"] for pa in t["platformAgents"]) for t in tenants_resources
),
"totalPlatformAgentPodUsed": sum(
sum(pa["podUsed"] for pa in t["platformAgents"]) for t in tenants_resources
),
}
return SuccessResponse(
data={
"channelId": str(channel_uuid),
"channelName": channel.name,
"tenants": tenants_resources,
"summary": summary,
},
message="获取渠道租户资源分配成功"
)
+238
View File
@@ -3655,3 +3655,241 @@ async def get_channel_tenant_agent_billing_summary(
}
)
# ============= 渠道租户资源分配查看 =============
@router.get("/tenants/resources/summary", response_model=SuccessResponse)
async def get_channel_tenants_resources_summary(
channel_id_param: Optional[str] = Query(None, alias="channel_id", description="渠道ID(超级管理员必填)"),
principal: dict = Depends(require_auth),
db: AsyncSession = Depends(get_db)
):
"""
查看渠道下所有租户被分配的资源汇总
返回渠道下每个租户的:
- 自定义 Agent 配额(cpu_quota, memory_quota, cpu_used, memory_used, agent_count)
- 平台 Agent 配额(各模板的 pod_quota, pod_used)
- 模型配额(各模型的 rpm, tpm)
权限:view:tenants (channel_admin, billing_admin, operations_admin, super_admin)
注意:
- 超级管理员必须提供 channel_id 参数
- 其他管理员自动使用自己所属的渠道
"""
_verify_permission(principal, "view:tenants")
role = _get_role(principal)
user_channel_id = _get_channel_id(principal)
# 确定目标渠道ID
if role == "super_admin":
if not channel_id_param:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="超级管理员必须提供 channel_id 参数"
)
try:
channel_id = uuid.UUID(channel_id_param)
except ValueError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="无效的渠道ID格式"
)
elif user_channel_id:
channel_id = user_channel_id
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="无法获取渠道ID"
)
# 验证渠道存在
channel_result = await db.execute(
select(Channel).where(Channel.id == channel_id)
)
channel = channel_result.scalar_one_or_none()
if not channel:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="渠道不存在"
)
# 获取渠道下所有租户
tenants_result = await db.execute(
select(User).where(User.channel_id == channel_id)
)
tenants = tenants_result.scalars().all()
# 构建租户资源数据
tenants_resources = []
for tenant in tenants:
tenant_data = {
"tenantId": str(tenant.id),
"tenantName": tenant.name,
"tenantEmail": tenant.email,
"status": tenant.status,
"subscriptionTier": tenant.subscription_tier,
"balance": float(tenant.balance) if tenant.balance else 0,
"createdAt": tenant.created_at.isoformat() if tenant.created_at else None,
}
# 1. 获取自定义 Agent 配额
custom_quota_result = await db.execute(
select(TenantCustomAgentQuota).where(
TenantCustomAgentQuota.tenant_id == tenant.id
)
)
custom_quota = custom_quota_result.scalar_one_or_none()
if custom_quota:
tenant_data["customAgentQuota"] = {
"cpuQuota": float(custom_quota.cpu_quota),
"memoryQuota": float(custom_quota.memory_quota),
"cpuUsed": float(custom_quota.cpu_used),
"memoryUsed": float(custom_quota.memory_used),
"agentCount": custom_quota.agent_count,
}
else:
tenant_data["customAgentQuota"] = None
# 2. 获取平台 Agent 配额
platform_quotas_result = await db.execute(
select(PlatformAgentQuota).where(
and_(
PlatformAgentQuota.target_id == tenant.id,
PlatformAgentQuota.target_type == "tenant"
)
)
)
platform_quotas = platform_quotas_result.scalars().all()
tenant_data["platformAgents"] = [
{
"templateName": pq.template_name,
"podQuota": pq.pod_quota,
"podUsed": pq.pod_used,
"cpuPerPod": pq.cpu_per_pod,
"memoryPerPod": pq.memory_per_pod,
}
for pq in platform_quotas
]
# 3. 获取模型配额(从 TenantModelKey 获取)
model_keys_result = await db.execute(
select(TenantModelKey).where(
and_(
TenantModelKey.tenant_id == tenant.id,
TenantModelKey.status == "active"
)
)
)
model_keys = model_keys_result.scalars().all()
tenant_data["models"] = [
{
"modelName": mk.model_name,
"rpmLimit": mk.rpm_limit,
"tpmLimit": mk.tpm_limit,
"maxBudget": float(mk.max_budget) if mk.max_budget else None,
"budgetDuration": mk.budget_duration,
}
for mk in model_keys
]
tenants_resources.append(tenant_data)
# 汇总统计
summary = {
"totalTenants": len(tenants),
"tenantsWithCustomAgents": len([t for t in tenants_resources if t["customAgentQuota"]]),
"tenantsWithPlatformAgents": len([t for t in tenants_resources if t["platformAgents"]]),
"tenantsWithModels": len([t for t in tenants_resources if t["models"]]),
"totalCustomAgentCpuQuota": sum(
t["customAgentQuota"]["cpuQuota"] for t in tenants_resources if t["customAgentQuota"]
),
"totalCustomAgentMemoryQuota": sum(
t["customAgentQuota"]["memoryQuota"] for t in tenants_resources if t["customAgentQuota"]
),
"totalCustomAgentCpuUsed": sum(
t["customAgentQuota"]["cpuUsed"] for t in tenants_resources if t["customAgentQuota"]
),
"totalCustomAgentMemoryUsed": sum(
t["customAgentQuota"]["memoryUsed"] for t in tenants_resources if t["customAgentQuota"]
),
"totalPlatformAgentPodQuota": sum(
sum(pa["podQuota"] for pa in t["platformAgents"]) for t in tenants_resources
),
"totalPlatformAgentPodUsed": sum(
sum(pa["podUsed"] for pa in t["platformAgents"]) for t in tenants_resources
),
"totalModelsAllocated": sum(
len(t["models"]) for t in tenants_resources
),
}
# 获取渠道自身的资源配额,用于对比
channel_custom_quota_result = await db.execute(
select(ChannelCustomAgentQuota).where(
ChannelCustomAgentQuota.channel_id == channel_id
)
)
channel_custom_quota = channel_custom_quota_result.scalar_one_or_none()
channel_quota = {
"customAgentQuota": {
"cpuQuota": float(channel_custom_quota.cpu_quota) if channel_custom_quota else 0,
"memoryQuota": float(channel_custom_quota.memory_quota) if channel_custom_quota else 0,
"cpuAllocated": float(channel_custom_quota.cpu_allocated) if channel_custom_quota else 0,
"memoryAllocated": float(channel_custom_quota.memory_allocated) if channel_custom_quota else 0,
} if channel_custom_quota else None
}
# 获取渠道的平台 Agent 配额
channel_platform_quotas_result = await db.execute(
select(PlatformAgentQuota).where(
and_(
PlatformAgentQuota.target_id == channel_id,
PlatformAgentQuota.target_type == "channel"
)
)
)
channel_platform_quotas = channel_platform_quotas_result.scalars().all()
channel_quota["platformAgents"] = [
{
"templateName": cpq.template_name,
"podQuota": cpq.pod_quota,
"podUsed": cpq.pod_used,
"cpuPerPod": cpq.cpu_per_pod,
"memoryPerPod": cpq.memory_per_pod,
}
for cpq in channel_platform_quotas
]
# 获取渠道分配的模型
channel_models_result = await db.execute(
select(ResourceAllocation).where(
and_(
ResourceAllocation.target_id == channel_id,
ResourceAllocation.target_type == "channel",
ResourceAllocation.resource_type == "model"
)
)
)
channel_models = channel_models_result.scalars().all()
channel_quota["models"] = [str(cm.resource_id) for cm in channel_models]
return SuccessResponse(
data={
"channelId": str(channel_id),
"channelName": channel.name,
"channelQuota": channel_quota,
"tenants": tenants_resources,
"summary": summary,
},
message="获取渠道租户资源分配成功"
)