chore: sync latest pages + docs updates

This commit is contained in:
xiaohei
2026-01-11 12:13:00 +00:00
parent 5e97eb40cb
commit 4ec6f8befd
15 changed files with 923 additions and 1279 deletions
+91 -34
View File
@@ -36,13 +36,58 @@ export default function AgentFactoryPage() {
serviceGateway: "", // 服务网关字段
})
const [platformAgents, setPlatformAgents] = useState<any[]>([])
const [availableModels, setAvailableModels] = useState<any[]>([])
const [deployedCount, setDeployedCount] = useState(0)
const [stats, setStats] = useState({ cpu: 0, memory: 0 })
// 将CPU格式(如"500m"、"250m"、"4000m")转换为核数(如0.5、0.25、4)
// 支持范围:250m-4000m (0.25-4核)
const convertCpuToCores = (cpu: string | number | undefined): string => {
if (!cpu) return "-"
const cpuStr = String(cpu).trim()
// 如果是纯数字,直接返回(已经是核数)
if (/^\d+(\.\d+)?$/.test(cpuStr)) {
const num = Number.parseFloat(cpuStr)
// 如果是整数,不显示小数部分;否则保留最多2位小数
return Number.isInteger(num) ? num.toString() : num.toFixed(2).replace(/\.?0+$/, "")
}
// 如果是millicore格式(如"500m"、"250m"、"4000m"),转换为核数
if (cpuStr.endsWith("m")) {
const millicores = Number.parseFloat(cpuStr.slice(0, -1))
if (!Number.isNaN(millicores)) {
const cores = millicores / 1000
// 如果是整数,不显示小数部分;否则保留最多2位小数
return Number.isInteger(cores) ? cores.toString() : cores.toFixed(2).replace(/\.?0+$/, "")
}
}
return "-"
}
useEffect(() => {
loadAgents()
loadModels()
}, [router])
// 加载模型列表(使用与数据与工具页面相同的接口)
const loadModels = async () => {
try {
const result = await TaijiAPIClient.getUserModels()
if (result?.success && result.data?.models) {
setAvailableModels(result.data.models)
// 如果当前选择的模型不在列表中,使用第一个模型作为默认值
if (result.data.models.length > 0 && !result.data.models.find((m: any) => m.id === deployConfig.model)) {
setDeployConfig({ ...deployConfig, model: result.data.models[0].id })
}
}
} catch (error) {
console.error("Failed to load models:", error)
}
}
const loadAgents = async () => {
try {
setLoading(true)
@@ -85,7 +130,7 @@ export default function AgentFactoryPage() {
setDeployedCount(instances.filter((inst: any) =>
inst.status === "Running" || inst.status === "Pending"
).length)
}
}
// 处理资源配额
if (quotaResult.status === "fulfilled" && quotaResult.value?.success) {
@@ -289,18 +334,18 @@ export default function AgentFactoryPage() {
const quotaPercent = agent.podQuota ? Math.round((agent.podUsed / agent.podQuota) * 100) : 0
return (
<Card key={agent.id} className="border-primary/20 hover:border-primary/40 transition-colors">
<CardHeader className="pb-3">
<div className="flex items-start justify-between mb-2">
<div className="text-4xl">{agent.icon}</div>
<Card key={agent.id} className="border-primary/20 hover:border-primary/40 transition-colors">
<CardHeader className="pb-3">
<div className="flex items-start justify-between mb-2">
<div className="text-4xl">{agent.icon}</div>
<Badge className={isQuotaFull ? "bg-red-500/10 text-red-500" : "bg-green-500/10 text-green-500"}>
{isQuotaFull ? t("已满", "Full") : t("可用", "Available")}
</Badge>
</div>
<CardTitle className="text-base">{agent.name}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">{agent.description}</p>
</div>
<CardTitle className="text-base">{agent.name}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">{agent.description}</p>
{/* 配额使用情况 */}
{agent.podQuota !== undefined && (
@@ -322,16 +367,16 @@ export default function AgentFactoryPage() {
{/* 能力标签 */}
{agent.capabilities && agent.capabilities.length > 0 && (
<div className="space-y-2">
<p className="text-xs font-medium text-muted-foreground">{t("核心能力", "Core Capabilities")}:</p>
<div className="flex flex-wrap gap-1">
<div className="space-y-2">
<p className="text-xs font-medium text-muted-foreground">{t("核心能力", "Core Capabilities")}:</p>
<div className="flex flex-wrap gap-1">
{agent.capabilities.map((cap: string) => (
<Badge key={cap} variant="outline" className="text-xs">
{cap}
</Badge>
))}
</div>
</div>
<Badge key={cap} variant="outline" className="text-xs">
{cap}
</Badge>
))}
</div>
</div>
)}
<Button
@@ -339,11 +384,11 @@ export default function AgentFactoryPage() {
onClick={() => handleDeploy(agent)}
disabled={isQuotaFull}
>
<Zap className="h-4 w-4" />
<Zap className="h-4 w-4" />
{isQuotaFull ? t("配额已满", "Quota Full") : t("部署Agent", "Deploy Agent")}
</Button>
</CardContent>
</Card>
</Button>
</CardContent>
</Card>
)
})}
</div>
@@ -409,13 +454,25 @@ export default function AgentFactoryPage() {
onValueChange={(value) => setDeployConfig({ ...deployConfig, model: value })}
>
<SelectTrigger>
<SelectValue />
<SelectValue placeholder={t("选择模型", "Select model")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="gpt-4o">GPT-4o</SelectItem>
<SelectItem value="gpt-4o-mini">GPT-4o Mini</SelectItem>
<SelectItem value="claude-3.5-sonnet">Claude 3.5 Sonnet</SelectItem>
<SelectItem value="grok-4">Grok 4</SelectItem>
{availableModels.length === 0 ? (
<SelectItem value="" disabled>
{t("加载中...", "Loading...")}
</SelectItem>
) : (
availableModels.map((model) => (
<SelectItem key={model.id} value={model.id}>
<div className="flex flex-col">
<span>{model.name}</span>
{model.description && (
<span className="text-xs text-muted-foreground">{model.description}</span>
)}
</div>
</SelectItem>
))
)}
</SelectContent>
</Select>
</div>
@@ -460,13 +517,13 @@ export default function AgentFactoryPage() {
<div className="flex items-center gap-2">
<Cpu className="h-4 w-4 text-muted-foreground" />
<span>
{selectedAgent?.cpuRequest || "-"} {t("CPU/实例", "CPU/instance")}
{convertCpuToCores(selectedAgent?.cpuLimit || selectedAgent?.cpuRequest)} {t("核/实例", "cores/instance")}
</span>
</div>
<div className="flex items-center gap-2">
<MemoryStick className="h-4 w-4 text-muted-foreground" />
<span>
{selectedAgent?.memoryRequest || "-"} {t("内存/实例", "Memory/instance")}
{selectedAgent?.memoryLimit || selectedAgent?.memoryRequest || "-"} {t("内存/实例", "Memory/instance")}
</span>
</div>
</div>
@@ -477,11 +534,11 @@ export default function AgentFactoryPage() {
</div>
)}
{(selectedAgent?.cpuRequest || selectedAgent?.memoryRequest) && (
<div className="mt-3 pt-3 border-t border-primary/20">
<p className="text-xs text-muted-foreground">
<div className="mt-3 pt-3 border-t border-primary/20">
<p className="text-xs text-muted-foreground">
{t("总资源", "Total resources")}: {deployConfig.agentCount} × ({selectedAgent?.cpuRequest || "-"} CPU, {selectedAgent?.memoryRequest || "-"} {t("内存", "Memory")})
</p>
</div>
</p>
</div>
)}
</div>
</div>
+107 -63
View File
@@ -27,8 +27,16 @@ export default function BillingPage() {
const [showRechargeDialog, setShowRechargeDialog] = useState(false)
const [rechargeAmount, setRechargeAmount] = useState("")
const [balance, setBalance] = useState({ balance: 0, monthlySpent: 0, euBalance: 0 })
const [currentMonth, setCurrentMonth] = useState({
spent: 0,
euConsumed: 0,
avgDailySpent: 0,
predictedTotal: 0,
currency: "CNY",
})
const [comparison, setComparison] = useState({ percentage: 0, direction: "stable" as "up" | "down" | "stable" })
const [euUsageData, setEuUsageData] = useState<Array<{ date: string; eu: number }>>([])
const [costBreakdown, setCostBreakdown] = useState<Array<{ category: string; cost: number; eu: number }>>([])
const [costBreakdown, setCostBreakdown] = useState<Array<{ category: string; name: string; cost: number; percentage: number }>>([])
const [resourceUsage, setResourceUsage] = useState({
cpu: { used: 0, total: 100 },
memory: { used: 0, total: 200 },
@@ -44,65 +52,87 @@ export default function BillingPage() {
try {
setLoading(true)
// 并行加载数据
const [balanceResult, historyResult, costResult] = await Promise.allSettled([
TaijiAPIClient.getBillingBalance(),
TaijiAPIClient.getBillingHistory({
startTime: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
endTime: new Date().toISOString(),
}),
TaijiAPIClient.getBillingHistory({
startTime: new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString(),
endTime: new Date().toISOString(),
}),
])
// 处理余额
if (balanceResult.status === "fulfilled" && balanceResult.value?.success) {
const data = balanceResult.value.data
setBalance({
balance: data.balance || 0,
monthlySpent: data.monthlySpent || 0,
euBalance: data.euBalance || 0,
})
}
// 处理历史数据(用于图表)
if (historyResult.status === "fulfilled" && historyResult.value?.success) {
const records = historyResult.value.data?.records || []
// 按日期聚合EU消耗
const dailyEu: Record<string, number> = {}
records.forEach((record: any) => {
const date = new Date(record.timestamp).toLocaleDateString("en-US", { month: "short", day: "numeric" })
dailyEu[date] = (dailyEu[date] || 0) + (record.eu || 0)
})
setEuUsageData(
Object.entries(dailyEu).map(([date, eu]) => ({ date, eu })).slice(-8),
)
}
// 处理费用明细
if (costResult.status === "fulfilled" && costResult.value?.success) {
const records = costResult.value.data?.records || []
// 按类别聚合费用
const categoryCosts: Record<string, { cost: number; eu: number }> = {}
records.forEach((record: any) => {
const category = record.agentType || "Other"
if (!categoryCosts[category]) {
categoryCosts[category] = { cost: 0, eu: 0 }
}
categoryCosts[category].cost += record.cost || 0
categoryCosts[category].eu += record.eu || 0
})
setCostBreakdown(
Object.entries(categoryCosts).map(([category, data]) => ({
category,
...data,
})),
)
// 使用新的综合接口
const result = await TaijiAPIClient.getBillingOverview()
if (result?.success && result.data) {
const data = result.data
// 处理余额信息
if (data.balance) {
setBalance({
balance: data.balance.cash || 0,
monthlySpent: data.currentMonth?.spent || 0,
euBalance: data.balance.eu || 0,
})
}
// 处理本月数据
if (data.currentMonth) {
setCurrentMonth({
spent: data.currentMonth.spent || 0,
euConsumed: data.currentMonth.euConsumed || 0,
avgDailySpent: data.currentMonth.avgDailySpent || 0,
predictedTotal: data.currentMonth.predictedTotal || 0,
currency: data.currentMonth.currency || "CNY",
})
}
// 处理对比数据
if (data.comparison) {
setComparison({
percentage: data.comparison.percentage || 0,
direction: data.comparison.direction || "stable",
})
}
// 处理EU消费历史(转换日期格式)
if (data.euHistory && Array.isArray(data.euHistory)) {
setEuUsageData(
data.euHistory.map((item: any) => ({
date: new Date(item.date).toLocaleDateString("en-US", { month: "short", day: "numeric" }),
eu: item.euConsumed || 0,
}))
)
}
// 处理费用明细
if (data.costBreakdown?.categories) {
setCostBreakdown(data.costBreakdown.categories)
}
// 处理资源使用情况
if (data.resourceUsage?.resources) {
const resources = data.resourceUsage.resources
const resourceMap: Record<string, { used: number; total: number }> = {}
resources.forEach((resource: any) => {
if (resource.type === "cpu") {
resourceMap.cpu = { used: resource.used || 0, total: resource.limit || 0 }
} else if (resource.type === "memory") {
resourceMap.memory = { used: resource.used || 0, total: resource.limit || 0 }
} else if (resource.type === "storage") {
resourceMap.storage = { used: resource.used || 0, total: resource.limit || 0 }
} else if (resource.type === "api_calls") {
resourceMap.apiCalls = { used: resource.used || 0, total: resource.limit || 0 }
}
})
setResourceUsage({
cpu: resourceMap.cpu || { used: 0, total: 100 },
memory: resourceMap.memory || { used: 0, total: 200 },
storage: resourceMap.storage || { used: 0, total: 1000 },
apiCalls: resourceMap.apiCalls || { used: 0, total: 100000 },
})
}
}
} catch (error) {
console.error("Failed to load billing data:", error)
toast({
title: t("错误", "Error"),
description: t("加载计费数据失败", "Failed to load billing data"),
variant: "destructive",
})
} finally {
setLoading(false)
}
@@ -233,7 +263,7 @@ export default function BillingPage() {
)}
</div>
<p className="text-xs text-muted-foreground mt-1">
{loading ? "" : `${Math.round(balance.monthlySpent * 100)} EU ${t("已消费", "consumed")}`}
{loading ? "" : `${currentMonth.euConsumed.toLocaleString()} EU ${t("已消费", "consumed")}`}
</p>
</CardContent>
</Card>
@@ -245,8 +275,16 @@ export default function BillingPage() {
</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">$34</div>
<p className="text-xs text-green-500 mt-1">-5% {t("vs上月", "vs last month")}</p>
<div className="text-2xl font-bold">
{loading ? (
<span className="inline-block h-7 w-16 animate-pulse bg-muted rounded" />
) : (
`${currentMonth.currency === "CNY" ? "¥" : "$"}${currentMonth.avgDailySpent.toFixed(2)}`
)}
</div>
<p className={`text-xs mt-1 ${comparison.direction === "up" ? "text-red-500" : comparison.direction === "down" ? "text-green-500" : "text-muted-foreground"}`}>
{loading ? "" : `${comparison.percentage > 0 ? "+" : ""}${comparison.percentage.toFixed(1)}% ${t("vs上月", "vs last month")}`}
</p>
</CardContent>
</Card>
@@ -255,7 +293,13 @@ export default function BillingPage() {
<CardTitle className="text-sm font-medium text-muted-foreground">{t("预计", "Projected")}</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">$1,050</div>
<div className="text-2xl font-bold">
{loading ? (
<span className="inline-block h-7 w-16 animate-pulse bg-muted rounded" />
) : (
`${currentMonth.currency === "CNY" ? "¥" : "$"}${currentMonth.predictedTotal.toFixed(2)}`
)}
</div>
<p className="text-xs text-muted-foreground mt-1">{t("月底", "End of month")}</p>
</CardContent>
</Card>
@@ -298,8 +342,8 @@ export default function BillingPage() {
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={250}>
<BarChart data={costBreakdown.length > 0 ? costBreakdown : [{ category: "", cost: 0, eu: 0 }]}>
<XAxis dataKey="category" stroke="oklch(0.45 0 0)" fontSize={11} tickLine={false} />
<BarChart data={costBreakdown.length > 0 ? costBreakdown : [{ name: "", cost: 0, percentage: 0 }]}>
<XAxis dataKey="name" stroke="oklch(0.45 0 0)" fontSize={11} tickLine={false} />
<YAxis stroke="oklch(0.45 0 0)" fontSize={12} tickLine={false} />
<Tooltip
contentStyle={{
+14 -14
View File
@@ -2366,13 +2366,13 @@ export default function ChannelDashboard() {
}
const value = Number.parseFloat(inputValue)
if (!Number.isNaN(value)) {
setCustomAgentResources((prev) => ({
...prev,
[selectedTenantForAgentAllocation?.id]: {
...prev[selectedTenantForAgentAllocation?.id],
cpu: value,
},
}))
setCustomAgentResources((prev) => ({
...prev,
[selectedTenantForAgentAllocation?.id]: {
...prev[selectedTenantForAgentAllocation?.id],
cpu: value,
},
}))
}
}}
className="bg-background"
@@ -2409,13 +2409,13 @@ export default function ChannelDashboard() {
}
const value = Number.parseFloat(inputValue)
if (!Number.isNaN(value)) {
setCustomAgentResources((prev) => ({
...prev,
[selectedTenantForAgentAllocation?.id]: {
...prev[selectedTenantForAgentAllocation?.id],
memory: value,
},
}))
setCustomAgentResources((prev) => ({
...prev,
[selectedTenantForAgentAllocation?.id]: {
...prev[selectedTenantForAgentAllocation?.id],
memory: value,
},
}))
}
}}
className="bg-background"
+413 -104
View File
@@ -12,7 +12,7 @@ import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Badge } from "@/components/ui/badge"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Plus, Search, ExternalLink, Trash2, FileJson, Cloud, Cpu, Code2, Boxes, Workflow, Upload, RefreshCw, Square, Play, Bot } from "lucide-react"
import { Plus, Search, Trash2, FileJson, Cloud, Cpu, Code2, Boxes, Workflow, Upload, RefreshCw, Square, Play, Bot } from "lucide-react"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import {
Dialog,
@@ -34,8 +34,21 @@ export default function DataToolsPage() {
const [showNewToolDialog, setShowNewToolDialog] = useState(false)
const [showTemplateDialog, setShowTemplateDialog] = useState(false)
const [templateType, setTemplateType] = useState<"json" | "storage">("json")
const [templateType, setTemplateType] = useState<"json_api" | "cloud_storage" | "database">("json_api")
const [useJsonUpload, setUseJsonUpload] = useState(false)
const [templateName, setTemplateName] = useState("")
const [templateConfig, setTemplateConfig] = useState({
// JSON API 配置
url: "",
apiKey: "",
queryParams: "",
jsonContent: "",
// 云存储/数据库配置
service: "",
dbType: "",
connectionString: "",
})
const [creatingTemplate, setCreatingTemplate] = useState(false)
const [podConfig, setPodConfig] = useState({
agentCount: 1,
cpuCores: 1,
@@ -47,6 +60,10 @@ export default function DataToolsPage() {
const [selectedServiceGateway, setSelectedServiceGateway] = useState<string>("")
const [selectedDataTemplate, setSelectedDataTemplate] = useState<string>("")
const [dataTemplates, setDataTemplates] = useState<any[]>([])
const [frameworkTemplates, setFrameworkTemplates] = useState<any[]>([])
const [availableModels, setAvailableModels] = useState<any[]>([])
const [toolName, setToolName] = useState<string>("")
const [deploying, setDeploying] = useState(false)
// 自定义Agent管理状态
const [customAgents, setCustomAgents] = useState<any[]>([])
@@ -57,15 +74,69 @@ export default function DataToolsPage() {
loadTools()
loadStats()
loadCustomAgents()
loadFrameworkTemplates()
loadModels()
}, [])
// 加载框架模板列表
const loadFrameworkTemplates = async () => {
try {
const result = await TaijiAPIClient.getCustomAgentTemplates()
if (result?.success && result.data?.templates) {
// 文档返回的是字符串数组:["A2A", "langchain", "MCP"]
// 转换为对象数组以便显示
const templates = result.data.templates.map((template: string | any) => {
if (typeof template === 'string') {
// 如果是字符串,转换为对象
const displayNames: Record<string, string> = {
'A2A': 'A2A框架',
'langchain': 'LangChain框架',
'MCP': 'MCP框架'
}
return {
name: template,
displayName: displayNames[template] || template,
description: ''
}
}
// 如果已经是对象,直接返回(兼容旧格式)
return template
})
setFrameworkTemplates(templates)
}
} catch (error) {
console.error("Failed to load framework templates:", error)
}
}
// 加载模型列表
const loadModels = async () => {
try {
const result = await TaijiAPIClient.getUserModels()
if (result?.success && result.data?.models) {
setAvailableModels(result.data.models)
// 如果当前选择的模型不在列表中,使用第一个模型作为默认值
if (result.data.models.length > 0 && !result.data.models.find((m: any) => m.id === podConfig.model)) {
setPodConfig({ ...podConfig, model: result.data.models[0].id })
}
}
} catch (error) {
console.error("Failed to load models:", error)
}
}
const loadTools = async () => {
try {
setLoading(true)
const result = await TaijiAPIClient.getTools()
if (Array.isArray(result)) {
setTools(result)
// 使用文档规定的接口:GET /api/user/tools
const result = await TaijiAPIClient.getUserTools()
if (result?.success && result.data?.tools) {
setTools(result.data.tools)
// 将已注册的工具作为数据模板选项
setDataTemplates(result.data.tools)
} else if (Array.isArray(result)) {
// 兼容旧格式
setTools(result)
setDataTemplates(result)
}
} catch (error) {
@@ -77,8 +148,16 @@ export default function DataToolsPage() {
const loadStats = async () => {
try {
const result = await TaijiAPIClient.getStats()
if (result) {
// 使用文档规定的接口:GET /api/user/tools/stats
const result = await TaijiAPIClient.getToolsStats()
if (result?.success && result.data) {
setStats({
totalApis: result.data.totalTools || 0,
generatedTools: result.data.generatedTools || 0,
activePods: result.data.activeTools || 0,
})
} else if (result) {
// 兼容旧格式(Data Ingestion Service)
setStats({
totalApis: result.total_apis || 0,
generatedTools: result.generated_tools || 0,
@@ -156,6 +235,191 @@ export default function DataToolsPage() {
}
}
// 删除工具
const handleDeleteTool = async (toolId: string) => {
if (!confirm(t(`确定要删除工具吗?此操作不可恢复。`, `Are you sure you want to delete this tool? This action cannot be undone.`))) {
return
}
try {
const result = await TaijiAPIClient.deleteUserTool(toolId)
if (result?.success) {
alert(t("工具删除成功", "Tool deleted successfully"))
loadTools()
loadStats()
} else {
throw new Error(result?.message || "Delete failed")
}
} catch (error: any) {
console.error("Failed to delete tool:", error)
alert(t("删除失败", "Delete failed") + ": " + (error.message || t("未知错误", "Unknown error")))
}
}
// 创建数据模板(实际调用创建工具接口)
const handleCreateTemplate = async () => {
if (!templateName) {
alert(t("请输入模板名称", "Please enter template name"))
return
}
try {
setCreatingTemplate(true)
let config: any = {}
let toolType = "api" // 默认工具类型
let description = ""
if (templateType === "json_api") {
// JSON API 模板
toolType = "api"
if (useJsonUpload && templateConfig.jsonContent) {
config.jsonContent = templateConfig.jsonContent
description = t("JSON API工具(JSON内容)", "JSON API Tool (JSON Content)")
} else if (templateConfig.url) {
config.endpoint = templateConfig.url
config.method = "GET"
if (templateConfig.apiKey) config.apiKey = templateConfig.apiKey
if (templateConfig.queryParams) config.queryParams = templateConfig.queryParams
description = t("JSON API工具", "JSON API Tool")
} else {
alert(t("请提供API URL或JSON内容", "Please provide API URL or JSON content"))
return
}
} else if (templateType === "cloud_storage") {
// 云存储模板
toolType = "cloud_storage"
if (!templateConfig.service || !templateConfig.connectionString) {
alert(t("请填写存储服务和连接字符串", "Please fill in storage service and connection string"))
return
}
config.service = templateConfig.service
config.connectionString = templateConfig.connectionString
config.endpoint = `storage://${templateConfig.service}`
config.method = "GET"
description = t("云存储工具", "Cloud Storage Tool")
} else if (templateType === "database") {
// 数据库模板
toolType = "database"
if (!templateConfig.dbType || !templateConfig.connectionString) {
alert(t("请填写数据库类型和连接字符串", "Please fill in database type and connection string"))
return
}
config.dbType = templateConfig.dbType
config.connectionString = templateConfig.connectionString
config.endpoint = `database://${templateConfig.dbType}`
config.method = "GET"
description = t("数据库工具", "Database Tool")
}
// 使用创建工具接口 POST /api/user/tools/create
const result = await TaijiAPIClient.createTool({
name: templateName,
description: description || t("数据模板工具", "Data Template Tool"),
type: toolType,
config: config,
})
if (result?.success) {
alert(t("工具创建成功", "Tool created successfully"))
setShowTemplateDialog(false)
// 重置表单
setTemplateName("")
setTemplateConfig({
url: "",
apiKey: "",
queryParams: "",
jsonContent: "",
service: "",
dbType: "",
connectionString: "",
})
setUseJsonUpload(false)
// 刷新工具列表和统计数据
await loadTools()
await loadStats()
} else {
throw new Error(result?.message || "Create tool failed")
}
} catch (error: any) {
console.error("Failed to create tool:", error)
alert(t("创建失败", "Create failed") + ": " + (error.message || t("未知错误", "Unknown error")))
} finally {
setCreatingTemplate(false)
}
}
// 部署Agent(创建自定义Agent)
const handleDeployAgent = async () => {
if (!selectedFramework || !toolName) {
alert(t("请填写所有必填字段", "Please fill in all required fields"))
return
}
try {
setDeploying(true)
// 将CPU和内存转换为K8s格式
const cpuRequest = `${podConfig.cpuCores * 1000}m` // 转换为millicores
const cpuLimit = `${podConfig.cpuCores * 1000}m` // 默认与request相同
const memoryRequest = `${podConfig.memoryGB}Gi`
const memoryLimit = `${podConfig.memoryGB}Gi` // 默认与request相同
// 收集选择的工具ID列表
const selectedTools: string[] = []
if (selectedDataTemplate) {
// 从工具列表中找到对应的工具ID
const tool = tools.find((t) => t.name === selectedDataTemplate || t.id === selectedDataTemplate)
if (tool?.id) {
selectedTools.push(tool.id)
}
}
// 使用创建自定义Agent接口 POST /api/user/custom-agents
// 根据文档:template是必填的模板名称,frameworkTemplate是框架类型(可选)
const frameworkName = selectedFramework.toUpperCase() // MCP/A2A/langchain
const templateName = `${frameworkName.toLowerCase()}-agent` // 生成模板名称,如 "mcp-agent"
const result = await TaijiAPIClient.createCustomAgent({
name: toolName,
template: templateName, // 必填:模板名称
frameworkTemplate: frameworkName, // 可选:框架类型(A2A/langchain/MCP)
description: toolName, // 可选:Agent描述
cpuRequest: cpuRequest, // 必填:CPU请求量(如"500m")
cpuLimit: cpuLimit, // 可选:CPU限制量
memoryRequest: memoryRequest, // 必填:内存请求量(如"1Gi")
memoryLimit: memoryLimit, // 可选:内存限制量
tools: selectedTools.length > 0 ? selectedTools : undefined, // 可选:工具ID列表
})
if (result?.success) {
alert(t("自定义Agent创建成功,正在部署", "Custom Agent created successfully, deploying"))
setShowNewToolDialog(false)
// 重置表单
setSelectedFramework("")
setSelectedServiceGateway("")
setSelectedDataTemplate("")
setToolName("")
setPodConfig({
agentCount: 1,
cpuCores: 1,
memoryGB: 2,
maxAgents: 5,
model: "gpt-4o-mini",
})
// 刷新数据
loadCustomAgents()
loadStats()
} else {
throw new Error(result?.message || "Deployment failed")
}
} catch (error: any) {
console.error("Failed to deploy agent:", error)
alert(t("部署失败", "Deployment failed") + ": " + (error.message || t("未知错误", "Unknown error")))
} finally {
setDeploying(false)
}
}
// 获取状态颜色
const getStatusColor = (status: string) => {
switch (status?.toLowerCase()) {
@@ -297,25 +561,29 @@ export default function DataToolsPage() {
</TableRow>
) : (
tools.map((tool) => (
<TableRow key={tool.name}>
<TableRow key={tool.id || tool.name}>
<TableCell className="font-medium">{tool.name}</TableCell>
<TableCell>
<Badge variant="secondary">{tool.category}</Badge>
<Badge variant="secondary">{tool.type || tool.category || "-"}</Badge>
</TableCell>
<TableCell>
<Badge variant="outline">{tool.method}</Badge>
<Badge variant="outline">{tool.config?.method || tool.method || "-"}</Badge>
</TableCell>
<TableCell className="text-xs text-muted-foreground">{tool.url}</TableCell>
<TableCell className="text-xs text-muted-foreground">{tool.config?.endpoint || tool.config?.url || tool.url || "-"}</TableCell>
<TableCell>
<Badge className="bg-green-500/10 text-green-500">{tool.status}</Badge>
<Badge className="bg-green-500/10 text-green-500">{tool.status || "active"}</Badge>
</TableCell>
<TableCell className="text-sm text-muted-foreground">
{tool.created_at ? new Date(tool.created_at).toLocaleString() : tool.created || "-"}
</TableCell>
<TableCell className="text-sm text-muted-foreground">{tool.created}</TableCell>
<TableCell className="text-right">
<div className="flex justify-end gap-2">
<Button variant="ghost" size="icon">
<ExternalLink className="h-4 w-4" />
</Button>
<Button variant="ghost" size="icon">
<Button
variant="ghost"
size="icon"
title={t("删除工具", "Delete Tool")}
onClick={() => handleDeleteTool(tool.id || tool.name)}
>
<Trash2 className="h-4 w-4 text-destructive" />
</Button>
</div>
@@ -434,12 +702,12 @@ export default function DataToolsPage() {
disabled={actionLoading === agent.name}
onClick={() => handleDeleteCustomAgent(agent.name)}
>
<Trash2 className="h-4 w-4 text-destructive" />
</Button>
</div>
</TableCell>
</TableRow>
))
<Trash2 className="h-4 w-4 text-destructive" />
</Button>
</div>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
@@ -528,24 +796,20 @@ export default function DataToolsPage() {
<SelectValue placeholder={t("选择框架模板", "Select framework template")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="langchain">
<div className="flex items-center gap-2">
<Code2 className="h-4 w-4" />
LangChain - LangChain Framework
</div>
</SelectItem>
<SelectItem value="a2a">
<div className="flex items-center gap-2">
<Boxes className="h-4 w-4" />
A2A - Agent-to-Agent Protocol
</div>
</SelectItem>
<SelectItem value="api">
<div className="flex items-center gap-2">
<Workflow className="h-4 w-4" />
API - Standard REST API
</div>
</SelectItem>
{frameworkTemplates.length === 0 ? (
<SelectItem value="" disabled>
{t("加载中...", "Loading...")}
</SelectItem>
) : (
frameworkTemplates.map((template) => (
<SelectItem key={template.name} value={template.name}>
<div className="flex items-center gap-2">
<Code2 className="h-4 w-4" />
{template.displayName || template.name} - {template.description || ""}
</div>
</SelectItem>
))
)}
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
@@ -555,7 +819,11 @@ export default function DataToolsPage() {
<div className="space-y-2">
<Label>{t("工具名称", "Tool Name")}</Label>
<Input placeholder={t("输入工具名称", "Enter tool name")} />
<Input
placeholder={t("输入工具名称", "Enter tool name")}
value={toolName}
onChange={(e) => setToolName(e.target.value)}
/>
</div>
<div className="border-t pt-4 space-y-4">
@@ -654,46 +922,31 @@ export default function DataToolsPage() {
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>{t("使用的模型", "Model to Use")}</Label>
<Select value={podConfig.model} onValueChange={(value) => setPodConfig({ ...podConfig, model: value })}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="gpt-4o">GPT-4o</SelectItem>
<SelectItem value="gpt-4o-mini">GPT-4o Mini</SelectItem>
<SelectItem value="claude-3.5-sonnet">Claude 3.5 Sonnet</SelectItem>
<SelectItem value="grok-4">Grok 4</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>{t("使用工具", "Tool to Use")}</Label>
<Select value={selectedDataTemplate} onValueChange={setSelectedDataTemplate}>
<SelectTrigger>
<SelectValue placeholder={t("选择已注册的工具", "Select registered tool")} />
</SelectTrigger>
<SelectContent>
{dataTemplates.length === 0 ? (
<SelectItem value="none" disabled>
{t("暂无可用工具", "No available tools")}
<div className="space-y-2">
<Label>{t("使用的模型", "Model to Use")}</Label>
<Select value={podConfig.model} onValueChange={(value) => setPodConfig({ ...podConfig, model: value })}>
<SelectTrigger>
<SelectValue placeholder={t("选择模型", "Select model")} />
</SelectTrigger>
<SelectContent>
{availableModels.length === 0 ? (
<SelectItem value="" disabled>
{t("加载中...", "Loading...")}
</SelectItem>
) : (
availableModels.map((model) => (
<SelectItem key={model.id} value={model.id}>
<div className="flex flex-col">
<span>{model.name}</span>
{model.description && (
<span className="text-xs text-muted-foreground">{model.description}</span>
)}
</div>
</SelectItem>
) : (
dataTemplates.map((template) => (
<SelectItem key={template.name} value={template.name}>
{template.name}
</SelectItem>
))
)}
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
{t("基于数据模板选择工具", "Select tool based on data template")}
</p>
</div>
))
)}
</SelectContent>
</Select>
</div>
<div className="rounded-lg bg-muted p-4 text-sm">
@@ -720,19 +973,18 @@ export default function DataToolsPage() {
<div>
{t("模型", "Model")}: {podConfig.model}
</div>
<div>
{t("使用工具", "Tool")}: {selectedDataTemplate || t("未选择", "Not selected")}
</div>
</div>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setShowNewToolDialog(false)}>
<Button variant="outline" onClick={() => setShowNewToolDialog(false)} disabled={deploying}>
{t("取消", "Cancel")}
</Button>
<Button onClick={() => setShowNewToolDialog(false)}>{t("部署Agent", "Deploy Agent")}</Button>
<Button onClick={handleDeployAgent} disabled={deploying || !selectedFramework || !toolName}>
{deploying ? t("部署中...", "Deploying...") : t("部署Agent", "Deploy Agent")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@@ -750,21 +1002,39 @@ export default function DataToolsPage() {
<div className="space-y-4">
<div className="space-y-2">
<Label>{t("模板类型", "Template Type")}</Label>
<Select value={templateType} onValueChange={(value: "json" | "storage") => setTemplateType(value)}>
<Select value={templateType} onValueChange={(value: "json_api" | "cloud_storage" | "database") => {
setTemplateType(value)
// 重置配置
setTemplateConfig({
url: "",
apiKey: "",
queryParams: "",
jsonContent: "",
service: "",
dbType: "",
connectionString: "",
})
}}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="json">
<SelectItem value="json_api">
<div className="flex items-center gap-2">
<FileJson className="h-4 w-4" />
{t("JSON API 模板", "JSON API Template")}
</div>
</SelectItem>
<SelectItem value="storage">
<SelectItem value="cloud_storage">
<div className="flex items-center gap-2">
<Cloud className="h-4 w-4" />
{t("云存储与数据库模板", "Cloud Storage & Database Template")}
{t("云存储模板", "Cloud Storage Template")}
</div>
</SelectItem>
<SelectItem value="database">
<div className="flex items-center gap-2">
<Cloud className="h-4 w-4" />
{t("数据库模板", "Database Template")}
</div>
</SelectItem>
</SelectContent>
@@ -773,10 +1043,14 @@ export default function DataToolsPage() {
<div className="space-y-2">
<Label>{t("模板名称", "Template Name")}</Label>
<Input placeholder={t("输入模板名称", "Enter template name")} />
<Input
placeholder={t("输入模板名称", "Enter template name")}
value={templateName}
onChange={(e) => setTemplateName(e.target.value)}
/>
</div>
{templateType === "json" ? (
{templateType === "json_api" ? (
<>
<div className="flex items-center space-x-2">
<Checkbox
@@ -815,6 +1089,8 @@ export default function DataToolsPage() {
<Label>{t("数据接口URL", "Data Interface URL")}</Label>
<Input
placeholder="https://api.example.com/data"
value={templateConfig.url}
onChange={(e) => setTemplateConfig({ ...templateConfig, url: e.target.value })}
/>
</div>
<div className="space-y-2">
@@ -822,6 +1098,8 @@ export default function DataToolsPage() {
<Input
placeholder={t("输入API密钥", "Enter API Key")}
type="password"
value={templateConfig.apiKey}
onChange={(e) => setTemplateConfig({ ...templateConfig, apiKey: e.target.value })}
/>
</div>
<div className="space-y-2">
@@ -832,46 +1110,77 @@ export default function DataToolsPage() {
"输入查询参数,例如: page=1&limit=10",
"Enter query parameters, e.g., page=1&limit=10",
)}
value={templateConfig.queryParams}
onChange={(e) => setTemplateConfig({ ...templateConfig, queryParams: e.target.value })}
/>
</div>
</>
)}
</>
) : (
) : templateType === "cloud_storage" ? (
<>
<div className="space-y-2">
<Label>{t("存储服务", "Storage Service")}</Label>
<Select>
<Select value={templateConfig.service} onValueChange={(value) => setTemplateConfig({ ...templateConfig, service: value })}>
<SelectTrigger>
<SelectValue placeholder={t("选择服务", "Select service")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="azure-blob">Azure Blob Storage</SelectItem>
<SelectItem value="google-cloud">Google Cloud Storage</SelectItem>
<SelectItem value="aws-s3">AWS S3</SelectItem>
<SelectItem value="postgresql">PostgreSQL</SelectItem>
<SelectItem value="mysql">MySQL</SelectItem>
<SelectItem value="mongodb">MongoDB</SelectItem>
<SelectItem value="snowflake">Snowflake</SelectItem>
<SelectItem value="azure_blob">Azure Blob Storage</SelectItem>
<SelectItem value="aws_s3">AWS S3</SelectItem>
<SelectItem value="aliyun_oss">阿里云OSS</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>{t("连接字符串", "Connection String")}</Label>
<Input placeholder={t("输入连接字符串", "Enter connection string")} type="password" />
<Input
placeholder={t("输入连接字符串", "Enter connection string")}
type="password"
value={templateConfig.connectionString}
onChange={(e) => setTemplateConfig({ ...templateConfig, connectionString: e.target.value })}
/>
</div>
</>
) : (
<>
<div className="space-y-2">
<Label>{t("数据库类型", "Database Type")}</Label>
<Select value={templateConfig.dbType} onValueChange={(value) => setTemplateConfig({ ...templateConfig, dbType: value })}>
<SelectTrigger>
<SelectValue placeholder={t("选择数据库类型", "Select database type")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="postgresql">PostgreSQL</SelectItem>
<SelectItem value="mysql">MySQL</SelectItem>
<SelectItem value="mongodb">MongoDB</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>{t("连接字符串", "Connection String")}</Label>
<Input
placeholder={t("输入连接字符串", "Enter connection string")}
type="password"
value={templateConfig.connectionString}
onChange={(e) => setTemplateConfig({ ...templateConfig, connectionString: e.target.value })}
/>
</div>
</>
)}
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setShowTemplateDialog(false)}>
<Button variant="outline" onClick={() => setShowTemplateDialog(false)} disabled={creatingTemplate}>
{t("取消", "Cancel")}
</Button>
<Button onClick={() => setShowTemplateDialog(false)}>{t("创建模板", "Create Template")}</Button>
<Button onClick={handleCreateTemplate} disabled={creatingTemplate || !templateName}>
{creatingTemplate ? t("创建中...", "Creating...") : t("创建模板", "Create Template")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</DashboardLayout>
</AuthGuard>
)
+101
View File
@@ -0,0 +1,101 @@
# 租户端(User Portal)业务功能与接口清单
本文档梳理了租户用户端(Tenant User Portal)的所有业务功能点、已对接的接口,以及部分未对接功能的业务数据需求。
**访问地址**: 租户登录后的主界面
**核心模块**: 仪表板、服务网关、数据工具、代理工厂、编排中心、计费资源
---
## 1. 仪表板 (Dashboard - Overview)
**路径**: `/`
| 业务功能 | 功能描述 | 对应接口/方法 | 状态 | 备注/数据需求 |
| :--- | :--- | :--- | :--- | :--- |
| **Hero统计** | 展示活跃Agent、全局API调用、EU余额、系统健康度 | 1. `GET /api/user/dashboard/stats`<br>2. `GET /api/user/billing/balance`<br>3. `GET /api/v1/monitoring/dashboard` | ✅ 已对接 | 并行调用多个接口聚合数据 |
| **EU消耗趋势** | 展示过去24小时的EU消耗曲线 | `GET /api/v1/monitoring/dashboard` (monitoring.euConsumption24h) | ✅ 已对接 | |
| **每日调用统计** | 展示每周/每日的API调用柱状图 | `GET /api/v1/monitoring/dashboard` (monitoring.weeklyApiCalls) | ✅ 已对接 | |
| **模型分布** | 展示不同模型调用比例的饼图 | `GET /api/v1/monitoring/dashboard` (monitoring.modelUsage) | ✅ 已对接 | |
| **系统组件状态** | 展示各服务组件的健康状态与延迟 | `GET /api/v1/monitoring/dashboard` (monitoring.health) | ✅ 已对接 | |
---
## 2. 代理工厂 (Agent Factory)
**路径**: `/agent-factory`
| 业务功能 | 功能描述 | 对应接口/方法 | 状态 | 备注/数据需求 |
| :--- | :--- | :--- | :--- | :--- |
| **可用Agent库** | 展示渠道分配给租户的平台Agent模板及配额状态 | `GET /api/user/platform-agents/available` | ✅ 已对接 | `getUserAvailablePlatformAgents()` |
| **已部署实例** | 展示当前租户已部署的Agent实例数量及列表 | `GET /api/user/platform-agents/instances` | ✅ 已对接 | `getUserPlatformAgentInstances()` |
| **资源使用概览** | 展示租户的CPU/内存总配额与使用量 | `GET /api/user/custom-agent-quota` | ✅ 已对接 | `getUserCustomAgentQuota()` |
| **部署Agent** | 部署新的Agent实例,选择网关、副本数、模型 | `POST /api/user/agents/deploy` | ✅ 已对接 | `deployUserAgent()` <br> 参数: agentId, instances, model, gateway |
---
## 3. 服务网关 (Service Gateway)
**路径**: `/model-gateway`
| 业务功能 | 功能描述 | 对应接口/方法 | 状态 | 备注/数据需求 |
| :--- | :--- | :--- | :--- | :--- |
| **网关统计** | 可用网关数、服务端点数、平均延迟、今日调用 | Aggr: <br>1. `GET /api/user/gateway/apis`<br>2. `GET /api/user/gateway/monitoring`<br>3. `GET /api/user/dashboard/stats` | ✅ 已对接 | 聚合数据 |
| **创建API** | 注册新的网关API路由(JSON/URL) | `POST /api/user/gateway/api/create` | ✅ 已对接 | `createGatewayAPI()` |
| **API列表** | 展示已注册的网关API及其状态 | `GET /api/user/gateway/apis` | ✅ 已对接 | `getGatewayAPIs()` |
| **模型供应商** | 查看可用的模型供应商状态 | `GET /api/providers/models` | ✅ 已对接 | `getModelProviders()` |
---
## 4. 数据与工具 (Data & Tools)
**路径**: `/data-tools`
| 业务功能 | 功能描述 | 对应接口/方法 | 状态 | 备注/数据需求 |
| :--- | :--- | :--- | :--- | :--- |
| **工具统计** | 总工具数、生成的工具、活跃Pods | `GET /stats` (Data Ingestion Svc) | ✅ 已对接 | 直接调用 Data Ingestion Service |
| **工具列表** | 展示可用API工具注册表 | `GET /tools` (Data Ingestion Svc) | ✅ 已对接 | 直接调用 Data Ingestion Service |
| **创建数据模板** | 配置JSON API或云存储数据源 | `POST /api/user/data-templates/create` | ✅ 已对接 | `createDataTemplate()` |
| **生成工具** | 基于模板生成API工具 | `POST /api/user/tools/generate` | ✅ 已对接 | `generateTool()`<br>(目前在前端代码中入口较隐蔽) |
---
## 5. 编排中心 (Orchestration Hub)
**路径**: `/orchestration`
| 业务功能 | 功能描述 | 对应接口/方法 | 状态 | 备注/数据需求 |
| :--- | :--- | :--- | :--- | :--- |
| **工作流列表** | 展示已创建的工作流及其状态 | `GET /api/user/workflows/list` | ✅ 已对接 | `getWorkflows()` |
| **可用Agent选择** | 选择用于编排的Agent (平台+自定义) | 1. `GET /api/user/agents/platform`<br>2. `GET /api/user/custom-agents` | ✅ 已对接 | 注意:此处用的 `/api/user/agents/platform` 与 Agent Factory 的接口略有不同 |
| **创建工作流** | 创建新的Agent工作流 (最大3节点) | `POST /api/user/workflows/create` | ✅ 已对接 | `createWorkflow()` |
| **运行工作流** | 执行指定工作流 | `POST /api/user/workflows/{id}/run` | ✅ 已对接 | `runWorkflow()` |
| **删除工作流** | 删除工作流 | `DELETE /api/user/workflows/{id}` | ✅ 已对接 | `deleteWorkflow()` |
| **并发执行状态** | 展示是否支持并发 | 无 (前端静态) | ❌ 未对接 | 当前显示为"不支持" |
---
## 6. 计费与资源 (Billing & Resources)
**路径**: `/billing`
| 业务功能 | 功能描述 | 对应接口/方法 | 状态 | 备注/数据需求 |
| :--- | :--- | :--- | :--- | :--- |
| **账户余额** | 查看当前账户余额、月度消费、EU余额 | `GET /api/user/billing/balance` | ✅ 已对接 | `getBillingBalance()` |
| **消费历史** | 查看EU消费趋势图表 (7天) | `GET /api/user/billing/history` | ✅ 已对接 | `getBillingHistory()` |
| **费用明细** | 查看按类别(Agent类型)分类的费用统计 | `GET /api/user/billing/history` | ✅ 已对接 | 前端基于 History 数据聚合计算 |
| **充值** | 账户余额充值 | `POST /api/user/billing/recharge` | ✅ 已对接 | `rechargeBalance()` |
| **资源使用监控** | 查看详细资源使用率 (CPU/Mem/Storage/API) | 无 | ❌ 未对接 | **需求数据**: `resourceUsage` 对象。<br>- cpu: {used, total}<br>- memory: {used, total}<br>- storage: {used, total}<br>- apiCalls: {used, total} |
| **预测费用** | 预计月底费用、平均每日费用 | 无 | ❌ 未对接 | **需求数据**: `projectedCost`, `avgDailyCost`。当前为硬编码。 |
| **定价详情** | 展示各项服务的EU定价 | 无 (前端静态) | ⚠️ 静态 | 如果定价策略会变动,建议增加定价查询接口 |
---
## 总结
租户端的前端功能覆盖率较高,核心的 **Agent部署**、**API调用**、**工作流编排** 和 **基础计费** 功能均已完整对接。
**主要待完善点**:
1. **计费详情**: 资源使用监控(CPU/内存/存储的具体数值)和费用预测目前是 Mock 数据。
2. **数据工具**: Data Tools 模块目前直接连接 Data Ingestion Service,需确认是否有租户隔离机制。
3. **接口一致性**: Orchestration 和 Agent Factory 获取平台 Agent 列表使用了两个不同的接口 (`/api/user/agents/platform` vs `/api/user/platform-agents/available`),建议统一。
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

@@ -0,0 +1,26 @@
1、可用angent类型需要查看渠道分配给租户的agnet。
2、已经部署的agnet值得是当前用户下已经run起来的agnet
3、总cpu和内存数是当前租户已经run起来的总和包括副本使用的资源
平台原生agnet库
指的是已经分配的用户的agnet,需要租户手工部署需要选择
接口: POST /api/user/agents/deploy
请求头: Authorization: Bearer {token}
请求参数:
参数名 类型 必填 说明
agentId string 是 Agent ID(UUID格式)
instances integer 是 实例数量
model string 是 使用的模型名称
gateway string 是 服务网关:MCP, A2A, API(不区分大小写)
需要填写这些内容。
并且可用在agnet列表中删除和停止、运行agnet。
[text](租户用户端-代理工程.md)
Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

@@ -0,0 +1,19 @@
1、首行
全部工具总数
含平台和用户自建全部工具总数。
生成的工具
用户自己创建的工具
活跃的工具数
主要是用户正在Agnet使用的工具
可以用工具列表 (可暂不支持平台工具)
支持查看平台和用户自己的工具,并且用户工具可用操作删除和修改,并且有状态可以显示是否正在使用,平台工具用户仅可以使用不能删除和修改。
数据模板
主要是jsonapi上传和云存储与数据库两种类型
其中上传方式采用大模型读取jsop内容,所需要的参数一切以图片为准
![alt text](<屏幕截图 2026-01-09 200010.png>)
@@ -0,0 +1,3 @@
首航基本满足
注意服务端点以用户自建为准。
创建自定义api网关,主要是用户在传入参数可以自定义,路由可以自定义。
Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

+18
View File
@@ -0,0 +1,18 @@
1、顶部展示
活跃代理(暂时可不实现自定义的agnet)
已经run起来的全部agnet包括自定义和平台的agnet
全局API调用(暂时可不实现自定义的agnet)
主要是当前用户下所有agnet的api调用次数。
EU余额
可第二天显示准确额度
系统健康度
主要是监控下面四个系统组件的
图标显示基本满足
系统组件基本满足
taiji-pad-v0/docs/租户需求接口/概述/屏幕截图 2026-01-09 195447.png
+131 -13
View File
@@ -432,7 +432,7 @@ export class TaijiAPIClient {
*/
static async createDataTemplate(data: {
name: string
type: "json_api" | "cloud_storage"
type: "json_api" | "cloud_storage" | "database"
config: any
}) {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/data-templates/create`, {
@@ -480,23 +480,16 @@ export class TaijiAPIClient {
static async createCustomAgent(data: {
name: string
template?: string
cpuRequest?: string
frameworkTemplate?: string
description?: string
cpuRequest: string
cpuLimit?: string
memoryRequest?: string
memoryRequest: string
memoryLimit?: string
tools?: string[]
endpoint?: string
apiKey?: string
envConfig?: Record<string, string>
// 兼容旧参数
description?: string
category?: string
role?: string
goal?: string
tools?: string[]
config?: {
temperature?: number
max_tokens?: number
}
}) {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/custom-agents`, {
method: "POST",
@@ -569,6 +562,109 @@ export class TaijiAPIClient {
return handleResponse(response)
}
/**
* 扩缩容自定义Agent
* PUT /api/user/custom-agents/{name}/scale
*
* @param agentName - Agent名称
* @param data - 资源配置
*/
static async scaleCustomAgent(
agentName: string,
data: {
cpuRequest?: string
cpuLimit?: string
memoryRequest?: string
memoryLimit?: string
}
) {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/custom-agents/${agentName}/scale`, {
method: "PUT",
headers: buildHeaders(),
body: JSON.stringify(data),
})
return handleResponse(response)
}
/**
* 获取Agent框架模板列表
* GET /api/user/custom-agents/templates
*/
static async getCustomAgentTemplates() {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/custom-agents/templates`, {
headers: buildHeaders(),
})
return handleResponse(response)
}
/**
* 创建工具
* POST /api/user/tools/create
*/
static async createTool(data: {
name: string
description: string
type: string
config: any
}) {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/tools/create`, {
method: "POST",
headers: buildHeaders(),
body: JSON.stringify(data),
})
return handleResponse(response)
}
/**
* 修改工具
* PUT /api/user/tools/{tool_id}
*/
static async updateTool(toolId: string, data: {
description?: string
config?: any
}) {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/tools/${toolId}`, {
method: "PUT",
headers: buildHeaders(),
body: JSON.stringify(data),
})
return handleResponse(response)
}
/**
* 删除工具
* DELETE /api/user/tools/{tool_id}
*/
static async deleteUserTool(toolId: string) {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/tools/${toolId}`, {
method: "DELETE",
headers: buildHeaders(),
})
return handleResponse(response)
}
/**
* 获取用户创建的工具列表
* GET /api/user/tools
*/
static async getUserTools() {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/tools`, {
headers: buildHeaders(),
})
return handleResponse(response)
}
/**
* 获取工具统计数据
* GET /api/user/tools/stats
*/
static async getToolsStats() {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/tools/stats`, {
headers: buildHeaders(),
})
return handleResponse(response)
}
/**
* 获取工作流列表
* GET /api/user/workflows/list
@@ -636,6 +732,17 @@ export class TaijiAPIClient {
return handleResponse(response)
}
/**
* 获取计费仪表板综合数据
* GET /api/user/dashboard/billing-overview
*/
static async getBillingOverview() {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/dashboard/billing-overview`, {
headers: buildHeaders(),
})
return handleResponse(response)
}
/**
* 获取余额信息
*/
@@ -2328,4 +2435,15 @@ export class TaijiAPIClient {
}
return []
}
/**
* 获取用户可用的模型列表
* GET /api/user/models
*/
static async getUserModels() {
const response = await fetch(`${API_BASE_URLS.mcpServer}/api/user/models`, {
headers: buildHeaders(),
})
return handleResponse(response)
}
}
-967
View File
@@ -1,967 +0,0 @@
# 租户用户端 - 后端接口需求清单
> **版本**: v1.0.0
> **更新时间**: 2026-01-06
> **说明**: 本文档基于前端业务逻辑分析,列出所有后端接口需求,包括已对接接口和未对接接口,按钮操作接口和数据展示接口
---
## 目录
1. [认证模块 (Authentication)](#认证模块-authentication)
2. [概览模块 (Dashboard Overview)](#概览模块-dashboard-overview)
3. [服务网关模块 (Service Gateway)](#服务网关模块-service-gateway)
4. [数据与工具模块 (Data & Tools)](#数据与工具模块-data--tools)
5. [代理工厂模块 (Agent Factory)](#代理工厂模块-agent-factory)
6. [编排中心模块 (Orchestration Hub)](#编排中心模块-orchestration-hub)
7. [计费与资源模块 (Billing & Resources)](#计费与资源模块-billing--resources)
8. [附录:接口汇总表](#附录接口汇总表)
---
## 认证模块 (Authentication)
### 按钮操作接口
#### B1. 用户登录接口 ✅ 已对接
**触发位置**: 登录页面 → "登录" 按钮
**功能描述**: 租户用户使用邮箱和密码登录系统
**接口**:
```
POST /api/auth/login
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| email | string | 是 | 用户邮箱 |
| password | string | 是 | 用户密码 |
| role | string | 是 | 角色类型,固定为 "user" |
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.token | string | JWT访问令牌 |
| data.refreshToken | string | 刷新令牌 |
| data.user | object | 用户信息 |
**前端调用**: `TaijiAPIClient.login(email, password, "user")`
---
#### B2. 用户登出接口 ✅ 已对接
**触发位置**: 顶部导航栏 → 用户菜单 → "退出登录"
**功能描述**: 用户退出登录,清除会话
**接口**:
```
POST /api/auth/logout
```
**前端调用**: `TaijiAPIClient.logout()`
---
#### B3. 刷新Token接口 ✅ 已对接
**触发位置**: 系统自动调用(Token即将过期时)
**功能描述**: 刷新访问令牌
**接口**:
```
POST /api/auth/refresh
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.token | string | 新的JWT访问令牌 |
| data.refreshToken | string | 新的刷新令牌 |
**前端调用**: `TaijiAPIClient.refreshToken()`
---
#### B4. 修改密码接口 ✅ 已对接
**触发位置**: 用户菜单 → 设置 → 修改密码
**功能描述**: 用户修改自己的密码
**接口**:
```
PUT /api/auth/password
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| old_password | string | 是 | 旧密码 |
| new_password | string | 是 | 新密码 |
**前端调用**: `TaijiAPIClient.changePassword(oldPassword, newPassword)`
---
#### B5. 重新生成API密钥接口 ✅ 已对接
**触发位置**: 顶部导航栏 → 用户菜单 → "密钥管理" → "重新生成" 按钮
**功能描述**: 重新生成用户的API密钥
**接口**:
```
POST /api/auth/keys/regenerate
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.apiKey | string | 新的API密钥 |
| data.message | string | 提示信息 |
**前端调用**: `TaijiAPIClient.regenerateApiKey()`
---
### 数据展示接口
#### D1. 获取API密钥信息接口 ✅ 已对接
**展示位置**: 顶部导航栏 → 用户菜单 → "密钥管理" 对话框
**展示内容**:
- 服务终结点 URL
- API密钥(脱敏显示)
**接口**:
```
GET /api/auth/keys/info
```
**前端调用**: `TaijiAPIClient.getApiKeyInfo()`
---
## 概览模块 (Dashboard Overview)
### 数据展示接口
#### D2. 用户仪表板统计接口 ✅ 已对接
**展示位置**: 概览页面 → 顶部统计卡片区域
**展示内容**:
- 活跃代理数(Active Agents)
- 全局API调用数(Global API Calls)
- EU余额(EU Balance)
- 系统健康度(System Health)
**功能描述**: 获取当前租户的仪表板统计数据
**接口**:
```
GET /api/user/dashboard/stats
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.activeAgents | int | 活跃代理数 |
| data.totalRequests | int | 总请求数 |
| data.euBalance | float | EU余额 |
| data.systemHealth | int | 系统健康度百分比 |
**前端调用**: `TaijiAPIClient.getUserDashboardStats()`
---
#### D3. 监控仪表盘接口 ✅ 已对接
**展示位置**: 概览页面 → 系统组件状态区域
**展示内容**:
- 各服务组件状态(MCP Server、Data Ingestion、API Gateway等)
- 组件延迟信息
**接口**:
```
GET /api/v1/monitoring/dashboard
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| health.services | object | 各服务健康状态 |
| metrics | object | 性能指标 |
**前端调用**: `TaijiAPIClient.getMonitoringDashboard()`
---
#### D4. 计费余额接口 ✅ 已对接
**展示位置**: 概览页面 → EU余额卡片
**展示内容**:
- EU余额
- 账户余额
**接口**:
```
GET /api/user/billing/balance
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.balance | float | 账户余额 |
| data.euBalance | float | EU余额 |
**前端调用**: `TaijiAPIClient.getBillingBalance()`
---
#### D5. 监控趋势数据接口 ✅ 已对接
**展示位置**: 概览页面 → EU消耗图表
**展示内容**:
- 过去24小时EU消耗趋势
**接口**:
```
GET /api/v1/monitoring/trends
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| metric | string | 否 | 指标类型:executions, eu_consumption |
| period | string | 否 | 时间周期:24h, 7d, 30d |
| interval | string | 否 | 时间间隔:1h, 6h, 1d |
**前端调用**: `TaijiAPIClient.getMonitoringTrends({ metric, period, interval })`
---
## 服务网关模块 (Service Gateway)
### 按钮操作接口
#### B6. 创建API接口 ✅ 已对接
**触发位置**: 服务网关页面 → "创建API" 按钮
**功能描述**: 通过上传JSON文件或提供URL创建API接口
**接口**:
```
POST /api/user/gateway/api/create
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | API名称 |
| method | string | 是 | 上传方式:json 或 url |
| content | string | 是 | JSON内容或URL地址 |
**前端调用**: `TaijiAPIClient.createGatewayAPI(name, method, content)`
---
#### B7. 选择网关类型接口 ✅ 已对接
**触发位置**: 服务网关页面 → 网关类型选择
**功能描述**: 选择使用的服务网关类型(MCP、A2A、API)
**接口**:
```
POST /api/user/gateway/select
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| gatewayType | string | 是 | 网关类型:MCP, A2A, API |
**前端调用**: `TaijiAPIClient.selectGateway(gatewayType)`
---
### 数据展示接口
#### D6. 网关API列表接口 ✅ 已对接
**展示位置**: 服务网关页面 → 统计卡片(服务端点数)
**展示内容**:
- 已创建的API列表
- 服务端点数量
**接口**:
```
GET /api/user/gateway/apis
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.apis | array | API列表 |
**前端调用**: `TaijiAPIClient.getGatewayAPIs()`
---
#### D7. 网关监控数据接口 ✅ 已对接
**展示位置**: 服务网关页面 → 监控Tab
**展示内容**:
- 平均延迟
- 今日请求数
- 各提供商请求分布
**接口**:
```
GET /api/user/gateway/monitoring
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.averageLatency | int | 平均延迟(ms) |
| data.requestsPerMinute | int | 每分钟请求数 |
**前端调用**: `TaijiAPIClient.getGatewayMonitoring()`
---
#### D8. 模型提供商列表接口 ✅ 已对接
**展示位置**: 服务网关页面 → 监控Tab → 模型提供商监控
**展示内容**:
- 各模型提供商名称
- 请求数量
- 延迟
- 状态
**接口**:
```
GET /api/providers/models
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.providers | array | 提供商列表 |
**前端调用**: `TaijiAPIClient.getModelProviders()`
---
## 数据与工具模块 (Data & Tools)
### 按钮操作接口
#### B8. 生成工具接口 ✅ 已对接
**触发位置**: 数据与工具页面 → "生成新工具" 按钮 → 对话框 → "部署工具和Pod"
**功能描述**: 根据Agent框架模板生成工具并部署Pod
**接口**:
```
POST /api/user/tools/generate
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | 工具名称 |
| description | string | 否 | 工具描述 |
| frameworkTemplate | string | 是 | 框架模板:langchain, a2a, api |
| gateway | string | 是 | 服务网关:mcp-gateway, a2a-gateway, api-gateway |
| agentCount | int | 是 | Agent个数 |
| cpu | int | 是 | CPU核数 |
| memory | int | 是 | 内存大小(GB) |
| maxScale | int | 是 | 可扩展Agent数量 |
| model | string | 是 | 使用的模型 |
**前端调用**: `TaijiAPIClient.generateTool(data)`
---
#### B9. 创建数据模板接口 ✅ 已对接
**触发位置**: 数据与工具页面 → 数据模板Tab → "创建数据模板" 按钮
**功能描述**: 创建JSON API模板或云存储数据库模板
**接口**:
```
POST /api/user/data-templates/create
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | 模板名称 |
| type | string | 是 | 模板类型:json_api, cloud_storage |
| config | object | 是 | 配置信息(URL、密钥、连接字符串等) |
**前端调用**: `TaijiAPIClient.createDataTemplate(data)`
---
### 数据展示接口
#### D9. 工具列表接口 ✅ 已对接
**展示位置**: 数据与工具页面 → 可用工具Tab → 工具注册表
**展示内容**:
- 工具名称
- 类别
- 方法(GET/POST等)
- 端点URL
- 状态
- 创建时间
**接口**:
```
GET /tools
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| category | string | 否 | 工具类别 |
| limit | int | 否 | 返回数量限制 |
| offset | int | 否 | 偏移量 |
**前端调用**: `TaijiAPIClient.getTools()`
---
#### D10. 统计信息接口 ✅ 已对接
**展示位置**: 数据与工具页面 → 顶部统计卡片
**展示内容**:
- 总API数
- 生成的工具数
- 活跃Pod数
**接口**:
```
GET /stats
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| total_apis | int | 总API数 |
| generated_tools | int | 生成的工具数 |
| active_pods | int | 活跃Pod数 |
**前端调用**: `TaijiAPIClient.getStats()`
---
## 代理工厂模块 (Agent Factory)
### 按钮操作接口
#### B10. 部署Agent接口 ✅ 已对接
**触发位置**: 代理工厂页面 → Agent卡片 → "部署Agent" 按钮 → 对话框 → "确认部署"
**功能描述**: 部署平台原生Agent到用户资源
**接口**:
```
POST /api/user/agents/deploy
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| agentId | string | 是 | Agent ID |
| instances | int | 是 | 实例数量 |
| model | string | 是 | 使用的模型 |
| gateway | string | 是 | 服务网关:MCP, A2A, API |
**前端调用**: `TaijiAPIClient.deployAgent(data)`
---
#### B11. 创建自定义Agent接口 ✅ 已对接
**触发位置**: 代理工厂页面 → 自定义Agent区域(如有)
**功能描述**: 创建租户自定义的Agent
**接口**:
```
POST /api/user/agents/custom/create
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | Agent名称 |
| description | string | 否 | Agent描述 |
| category | string | 否 | 类别 |
| role | string | 是 | 角色定义 |
| goal | string | 是 | 目标定义 |
| tools | array | 否 | 工具列表 |
| config | object | 否 | 配置(temperature, max_tokens等) |
**前端调用**: `TaijiAPIClient.createCustomAgent(data)`
---
### 数据展示接口
#### D11. 平台Agent列表接口 ✅ 已对接
**展示位置**: 代理工厂页面 → 平台原生Agent库
**展示内容**:
- Agent图标
- Agent名称
- Agent描述
- 核心能力标签
- 状态
**接口**:
```
GET /api/user/agents/platform
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.data | array | Agent列表 |
**前端调用**: `TaijiAPIClient.getPlatformAgents()`
---
#### D12. 已部署Agent列表接口 ✅ 已对接
**展示位置**: 代理工厂页面 → 已部署Agent统计卡片
**展示内容**:
- 已部署Agent数量
- 运行中的Agent
**接口**:
```
GET /agents
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| skip | int | 否 | 跳过数量 |
| limit | int | 否 | 返回数量限制 |
**前端调用**: `TaijiAPIClient.getAgents(skip, limit)`
---
#### D13. 自定义Agent列表接口 ✅ 已对接
**展示位置**: 代理工厂页面 → 自定义Agent区域
**展示内容**:
- 自定义Agent列表
**接口**:
```
GET /api/user/agents/custom
```
**前端调用**: `TaijiAPIClient.getCustomAgents()`
---
## 编排中心模块 (Orchestration Hub)
### 按钮操作接口
#### B12. 创建工作流接口 ✅ 已对接
**触发位置**: 编排中心页面 → "创建工作流" 按钮 → 对话框 → "保存工作流"
**功能描述**: 创建Agent工作流(最多3个节点)
**接口**:
```
POST /api/user/workflows/create
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | 工作流名称 |
| description | string | 否 | 工作流描述 |
| gateway | string | 是 | 服务网关:MCP, A2A, API |
| nodes | array | 是 | 节点列表(最多3个) |
| nodes[].agentId | string | 是 | Agent ID |
| nodes[].agentType | string | 是 | Agent类型:platform, custom |
| nodes[].agentName | string | 是 | Agent名称 |
| nodes[].order | int | 是 | 节点顺序 |
**前端调用**: `TaijiAPIClient.createWorkflow(data)`
---
#### B13. 运行工作流接口 ⚠️ 待确认
**触发位置**: 编排中心页面 → 工作流卡片 → "运行" 按钮
**功能描述**: 执行指定的工作流
**接口**:
```
POST /api/user/workflows/{workflow_id}/run
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| workflow_id | string | 是 | 工作流ID(路径参数) |
| input | object | 否 | 输入参数 |
**前端调用**: 待实现
---
#### B14. 删除工作流接口 ⚠️ 待确认
**触发位置**: 编排中心页面 → 工作流卡片 → 删除按钮
**功能描述**: 删除指定的工作流
**接口**:
```
DELETE /api/user/workflows/{workflow_id}
```
**前端调用**: 待实现
---
### 数据展示接口
#### D14. 工作流列表接口 ✅ 已对接
**展示位置**: 编排中心页面 → 我的工作流
**展示内容**:
- 工作流名称
- 状态(running/stopped)
- 节点数量
**接口**:
```
GET /api/user/workflows
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.data | array | 工作流列表 |
| data.data[].id | string | 工作流ID |
| data.data[].name | string | 工作流名称 |
| data.data[].status | string | 状态 |
| data.data[].nodes | array | 节点列表 |
**前端调用**: `TaijiAPIClient.getWorkflows()`
---
## 计费与资源模块 (Billing & Resources)
### 按钮操作接口
#### B15. 充值接口 ✅ 已对接
**触发位置**: 计费与资源页面 → 账户余额卡片 → "充值" 按钮 → 对话框 → "确认充值"
**功能描述**: 为账户充值
**接口**:
```
POST /api/user/billing/recharge
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| amount | float | 是 | 充值金额 |
| paymentMethod | string | 否 | 支付方式:alipay, wechat, card |
**前端调用**: `TaijiAPIClient.rechargeBalance(amount, paymentMethod)`
---
#### B16. 导出账单接口 ⚠️ 待确认
**触发位置**: 计费与资源页面 → "导出" 按钮 → 选择格式
**功能描述**: 导出账单数据为Excel/CSV/PDF格式
**接口**:
```
GET /api/user/billing/history?export={format}
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| startTime | string | 是 | 开始时间 |
| endTime | string | 是 | 结束时间 |
| export | string | 是 | 导出格式:excel, csv, pdf |
**前端调用**: `TaijiAPIClient.getBillingHistory({ ...params, export: format })`
---
### 数据展示接口
#### D15. 计费余额接口 ✅ 已对接
**展示位置**: 计费与资源页面 → 账户余额卡片、EU余额卡片
**展示内容**:
- 账户余额(¥)
- 本月已消费
- EU余额
**接口**:
```
GET /api/user/billing/balance
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.balance | float | 账户余额 |
| data.monthlySpent | float | 本月消费 |
| data.euBalance | float | EU余额 |
**前端调用**: `TaijiAPIClient.getBillingBalance()`
---
#### D16. 计费历史接口 ✅ 已对接
**展示位置**: 计费与资源页面 → EU消费历史图表、费用明细图表
**展示内容**:
- 每日EU消耗趋势
- 按类别的费用明细
**接口**:
```
GET /api/user/billing/history
```
**请求参数**:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| startTime | string | 是 | 开始时间(ISO格式) |
| endTime | string | 是 | 结束时间(ISO格式) |
| customerName | string | 否 | 客户名称筛选 |
| minCalls | int | 否 | 最小调用次数 |
| maxCalls | int | 否 | 最大调用次数 |
| page | int | 否 | 页码 |
| pageSize | int | 否 | 每页数量 |
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.records | array | 计费记录列表 |
| data.records[].timestamp | string | 时间戳 |
| data.records[].eu | float | EU消耗 |
| data.records[].cost | float | 费用 |
| data.records[].agentType | string | Agent类型 |
**前端调用**: `TaijiAPIClient.getBillingHistory(params)`
---
## 附录:接口汇总表
### 按钮操作接口汇总
| 序号 | 接口名称 | 方法 | 路径 | 状态 | 所属模块 |
|------|----------|------|------|------|----------|
| B1 | 用户登录 | POST | /api/auth/login | ✅ 已对接 | 认证 |
| B2 | 用户登出 | POST | /api/auth/logout | ✅ 已对接 | 认证 |
| B3 | 刷新Token | POST | /api/auth/refresh | ✅ 已对接 | 认证 |
| B4 | 修改密码 | PUT | /api/auth/password | ✅ 已对接 | 认证 |
| B5 | 重新生成API密钥 | POST | /api/auth/keys/regenerate | ✅ 已对接 | 认证 |
| B6 | 创建API | POST | /api/user/gateway/api/create | ✅ 已对接 | 服务网关 |
| B7 | 选择网关类型 | POST | /api/user/gateway/select | ✅ 已对接 | 服务网关 |
| B8 | 生成工具 | POST | /api/user/tools/generate | ✅ 已对接 | 数据与工具 |
| B9 | 创建数据模板 | POST | /api/user/data-templates/create | ✅ 已对接 | 数据与工具 |
| B10 | 部署Agent | POST | /api/user/agents/deploy | ✅ 已对接 | 代理工厂 |
| B11 | 创建自定义Agent | POST | /api/user/agents/custom/create | ✅ 已对接 | 代理工厂 |
| B12 | 创建工作流 | POST | /api/user/workflows/create | ✅ 已对接 | 编排中心 |
| B13 | 运行工作流 | POST | /api/user/workflows/{id}/run | ⚠️ 待确认 | 编排中心 |
| B14 | 删除工作流 | DELETE | /api/user/workflows/{id} | ⚠️ 待确认 | 编排中心 |
| B15 | 充值 | POST | /api/user/billing/recharge | ✅ 已对接 | 计费与资源 |
| B16 | 导出账单 | GET | /api/user/billing/history?export= | ⚠️ 待确认 | 计费与资源 |
### 数据展示接口汇总
| 序号 | 接口名称 | 方法 | 路径 | 状态 | 所属模块 |
|------|----------|------|------|------|----------|
| D1 | 获取API密钥信息 | GET | /api/auth/keys/info | ✅ 已对接 | 认证 |
| D2 | 用户仪表板统计 | GET | /api/user/dashboard/stats | ✅ 已对接 | 概览 |
| D3 | 监控仪表盘 | GET | /api/v1/monitoring/dashboard | ✅ 已对接 | 概览 |
| D4 | 计费余额 | GET | /api/user/billing/balance | ✅ 已对接 | 概览 |
| D5 | 监控趋势数据 | GET | /api/v1/monitoring/trends | ✅ 已对接 | 概览 |
| D6 | 网关API列表 | GET | /api/user/gateway/apis | ✅ 已对接 | 服务网关 |
| D7 | 网关监控数据 | GET | /api/user/gateway/monitoring | ✅ 已对接 | 服务网关 |
| D8 | 模型提供商列表 | GET | /api/providers/models | ✅ 已对接 | 服务网关 |
| D9 | 工具列表 | GET | /tools | ✅ 已对接 | 数据与工具 |
| D10 | 统计信息 | GET | /stats | ✅ 已对接 | 数据与工具 |
| D11 | 平台Agent列表 | GET | /api/user/agents/platform | ✅ 已对接 | 代理工厂 |
| D12 | 已部署Agent列表 | GET | /agents | ✅ 已对接 | 代理工厂 |
| D13 | 自定义Agent列表 | GET | /api/user/agents/custom | ✅ 已对接 | 代理工厂 |
| D14 | 工作流列表 | GET | /api/user/workflows | ✅ 已对接 | 编排中心 |
| D15 | 计费余额 | GET | /api/user/billing/balance | ✅ 已对接 | 计费与资源 |
| D16 | 计费历史 | GET | /api/user/billing/history | ✅ 已对接 | 计费与资源 |
---
## 接口统计
### 按状态统计
| 状态 | 数量 | 占比 |
|------|------|------|
| ✅ 已对接 | 29 | 90.6% |
| ⚠️ 待确认 | 3 | 9.4% |
| ❌ 未对接 | 0 | 0% |
| **总计** | **32** | **100%** |
### 按模块统计
| 模块 | 按钮操作接口 | 数据展示接口 | 合计 |
|------|-------------|-------------|------|
| 认证模块 | 5 | 1 | 6 |
| 概览模块 | 0 | 4 | 4 |
| 服务网关模块 | 2 | 3 | 5 |
| 数据与工具模块 | 2 | 2 | 4 |
| 代理工厂模块 | 2 | 3 | 5 |
| 编排中心模块 | 3 | 1 | 4 |
| 计费与资源模块 | 2 | 2 | 4 |
| **总计** | **16** | **16** | **32** |
---
## 待确认接口说明
### B13. 运行工作流接口
**问题**: 前端页面有"运行"按钮,但API客户端中未找到对应的接口实现。
**建议**: 需要后端确认是否已实现 `POST /api/user/workflows/{workflow_id}/run` 接口。
### B14. 删除工作流接口
**问题**: 前端页面有删除按钮,但API客户端中未找到对应的接口实现。
**建议**: 需要后端确认是否已实现 `DELETE /api/user/workflows/{workflow_id}` 接口。
### B16. 导出账单接口
**问题**: 前端有导出功能UI,但实际导出逻辑可能需要后端返回文件流。
**建议**: 需要确认后端是否支持 `export` 参数返回文件下载。
---
## 前端调用示例
### 登录流程
```typescript
// 1. 用户登录
const result = await TaijiAPIClient.login(email, password, "user")
if (result.success) {
// token 自动存储到 localStorage
window.location.href = "/"
}
```
### 仪表板数据加载
```typescript
// 并行加载仪表板数据
const [dashboardStats, monitoringDashboard, billingBalance] = await Promise.allSettled([
TaijiAPIClient.getUserDashboardStats(),
TaijiAPIClient.getMonitoringDashboard(),
TaijiAPIClient.getBillingBalance(),
])
```
### 部署Agent流程
```typescript
// 部署平台Agent
const result = await TaijiAPIClient.deployAgent({
agentId: selectedAgent.id,
instances: deployConfig.agentCount,
model: deployConfig.model,
gateway: deployConfig.serviceGateway as "MCP" | "A2A" | "API",
})
```
### 创建工作流流程
```typescript
// 创建工作流
const result = await TaijiAPIClient.createWorkflow({
name: workflowName,
gateway: selectedGateway as "MCP" | "A2A" | "API",
nodes: workflowNodes.map((nodeId, index) => ({
agentId: nodeId,
agentType: agent?.type === "custom" ? "custom" : "platform",
agentName: agent?.name || "",
order: index + 1,
})),
})
```
---
## 版本历史
| 版本 | 日期 | 更新内容 |
|------|------|----------|
| v1.0.0 | 2026-01-06 | 初始版本,基于前端代码分析生成 |
@@ -1,84 +0,0 @@
# 超级管理员控制台 - 后端接口清单
> **版本**: v1.2.0
> **更新时间**: 2026-01-06
> **说明**: 本文档基于前端业务逻辑分析,列出所有后端接口需求,包括已对接接口和未对接接口,按钮操作接口和数据展示接口
---
## 目录
1. [概览模块 (Overview)](#概览模块-overview)
2. [渠道管理模块 (Channels)](#渠道管理模块-channels)
3. [资源管理模块 (Resources)](#资源管理模块-resources)
4. [监控模块 (Monitoring)](#监控模块-monitoring)
5. [计费模块 (Billing)](#计费模块-billing)
6. [设置模块 (Settings)](#设置模块-settings)
7. [附录:接口汇总表](#附录接口汇总表)
---
## 概览模块 (Overview)
### 数据展示接口
#### D1. 仪表板统计接口 ✅ 已对接
**展示位置**: 概览页面 → 顶部统计卡片区域
**展示内容**:
- 总渠道数(如:5)
- 总租户数(如:7)
- 总收入(如:$0)
**功能描述**: 获取平台整体统计数据,用于概览页面顶部的统计卡片展示
**接口**:
```
GET /api/admin/dashboard/stats
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.totalChannels | int | 总渠道数 |
| data.totalTenants | int | 总租户数 |
| data.totalRevenue | float | 总收入 |
| data.totalAgents | int | 总Agent数(用于活跃指标) |
**前端调用**: `TaijiAPIClient.getAdminDashboardStats()`
---
#### D2. 系统监控指标接口 ✅ 已对接
**展示位置**: 概览页面 → 系统指标卡片
**展示内容**:
- CPU使用率(如:5.9%)
- 内存使用率(如:33.6%)
- 存储使用率(如:64.8%)
- 活跃Agent(如:0%)
**功能描述**: 获取平台整体的系统监控指标
**接口**:
```
GET /api/v1/monitoring/metrics
```
**响应字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| success | bool | 是否成功 |
| data.cpu_usage | float | CPU使用率百分比 |
| data.memory_usage | float | 内存使用率百分比 |
| data.disk_usage | float | 存储使用率百分比 |
| data.system.cpu_usage_percent | float | 备选:CPU使用率 |
| data.system.memory_usage_percent | float | 备选:内存使用率 |
| data.system.disk_usage_percent | float | 备选:存储使用率 |
**前端调用**: `TaijiAPIClient.getMonitoringMetrics()`
---