forked from xiaohei/taiji-AI-PAD
更新租户配额
This commit is contained in:
@@ -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="获取渠道租户资源分配成功"
|
||||
)
|
||||
|
||||
|
||||
@@ -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="获取渠道租户资源分配成功"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user