feat: 完善超级管理员控制台API集成

- 删除资源管理中的假数据,改为从API获取
- 添加删除Agent和模型供应商功能
- 完善创建渠道、资源管理、审批申请等功能的API集成
- 修复计费统计功能,接入后端API
- 添加缺失API接口清单文档
- 优化API响应格式处理,支持多种响应结构
- 修复审批申请对话框,添加供应商申请审批功能
This commit is contained in:
xiaohei
2025-12-25 09:31:26 +00:00
parent ac64923da5
commit f734de61eb
8 changed files with 898 additions and 550 deletions
File diff suppressed because it is too large Load Diff
+8 -6
View File
@@ -344,11 +344,6 @@ export default function DataToolsPage() {
<Input placeholder={t("输入工具名称", "Enter tool name")} />
</div>
<div className="space-y-2">
<Label>{t("API端点", "API Endpoint")}</Label>
<Input placeholder="https://api.example.com/endpoint" />
</div>
<div className="border-t pt-4 space-y-4">
<h3 className="font-semibold flex items-center gap-2">
<Cpu className="h-4 w-4" />
@@ -514,7 +509,14 @@ export default function DataToolsPage() {
<>
<div className="space-y-2">
<Label>{t("数据接口URL", "Data Interface URL")}</Label>
<Input placeholder="https://api.example.com/data" />
<Input
placeholder="https://api.example.com/data"
disabled
className="bg-muted cursor-not-allowed"
/>
<p className="text-xs text-muted-foreground">
{t("端点URL由系统自动配置,不支持自定义", "Endpoint URL is automatically configured by the system and cannot be customized")}
</p>
</div>
<div className="space-y-2">
<Label>{t("查询参数", "Query Parameters")}</Label>
+8 -1
View File
@@ -1,6 +1,6 @@
"use client"
import { useEffect } from "react"
import { useEffect, useState } from "react"
import { useRouter } from "next/navigation"
import { isAuthenticated } from "@/lib/auth"
import { DashboardLayout } from "@/components/dashboard-layout"
@@ -8,13 +8,20 @@ import { DashboardOverview } from "@/components/dashboard-overview"
export default function Home() {
const router = useRouter()
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
if (!isAuthenticated()) {
router.push("/login")
}
}, [router])
// 在客户端挂载前返回 null,避免 Hydration 错误
if (!mounted) {
return null
}
if (!isAuthenticated()) {
return null
}
+38 -10
View File
@@ -1,8 +1,8 @@
"use client"
import { useEffect } from "react"
import { useEffect, useState } from "react"
import { useRouter, usePathname } from "next/navigation"
import { isAuthenticated } from "@/lib/auth"
import { isAuthenticated, isAdminAuthenticated, isChannelAuthenticated } from "@/lib/auth"
interface AuthGuardProps {
children: React.ReactNode
@@ -13,15 +13,33 @@ interface AuthGuardProps {
export function AuthGuard({ children, requireAuth = true, redirectTo = "/login" }: AuthGuardProps) {
const router = useRouter()
const pathname = usePathname()
const [mounted, setMounted] = useState(false)
const [isAuth, setIsAuth] = useState(false)
// 检查是否是登录页面
const isLoginPage = pathname === "/login" || pathname === "/channel/login" || pathname === "/admin/login"
const isAdminPage = pathname.startsWith("/admin")
const isChannelPage = pathname.startsWith("/channel")
useEffect(() => {
setMounted(true)
// 根据页面类型检查对应的认证状态
let auth = false
if (isAdminPage) {
auth = isAdminAuthenticated()
} else if (isChannelPage) {
auth = isChannelAuthenticated()
} else {
auth = isAuthenticated()
}
setIsAuth(auth)
// 登录页面不需要认证
if (isLoginPage) {
// 如果已登录,重定向到首页
if (isAuthenticated()) {
// 如果已登录,重定向到对应的首页
if (auth) {
if (pathname === "/channel/login") {
router.push("/channel/dashboard")
} else if (pathname === "/admin/login") {
@@ -34,20 +52,30 @@ export function AuthGuard({ children, requireAuth = true, redirectTo = "/login"
}
// 需要认证的页面
if (requireAuth && !isAuthenticated()) {
if (requireAuth && !auth) {
// 根据路径判断重定向到哪个登录页
if (pathname.startsWith("/channel")) {
if (isChannelPage) {
router.push("/channel/login")
} else if (pathname.startsWith("/admin")) {
} else if (isAdminPage) {
router.push("/admin/login")
} else {
router.push(redirectTo)
}
}
}, [pathname, requireAuth, redirectTo, router, isLoginPage])
}, [pathname, requireAuth, redirectTo, router, isLoginPage, isAdminPage, isChannelPage])
// 在客户端挂载前,返回一个占位符以避免 Hydration 错误
if (!mounted) {
// 登录页面在挂载前直接渲染
if (isLoginPage) {
return <>{children}</>
}
// 其他页面在挂载前返回 null(等待客户端检查)
return null
}
// 如果是登录页面且已登录,不渲染内容(等待重定向)
if (isLoginPage && isAuthenticated()) {
if (isLoginPage && isAuth) {
return null
}
@@ -57,7 +85,7 @@ export function AuthGuard({ children, requireAuth = true, redirectTo = "/login"
}
// 如果需要认证但未登录,不渲染内容(等待重定向)
if (requireAuth && !isAuthenticated()) {
if (requireAuth && !isAuth) {
return null
}
+9 -3
View File
@@ -55,16 +55,22 @@ export function DashboardLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname()
const { language, setLanguage, t } = useLanguage()
const [showApiKeyDialog, setShowApiKeyDialog] = useState(false)
// 使用固定的初始值,避免服务器端和客户端不一致
const [apiKey, setApiKey] = useState("sk_live_1234567890abcdefghijklmnopqrstuvwxyz")
const [serviceEndpoint, setServiceEndpoint] = useState("https://api.taiji-ai.com/v1")
const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text)
if (typeof window !== "undefined" && navigator.clipboard) {
navigator.clipboard.writeText(text)
}
}
const regenerateApiKey = () => {
const newKey = `sk_live_${Math.random().toString(36).substring(2, 15)}${Math.random().toString(36).substring(2, 15)}`
setApiKey(newKey)
// 只在客户端执行,确保服务器端和客户端一致
if (typeof window !== "undefined") {
const newKey = `sk_live_${Math.random().toString(36).substring(2, 15)}${Math.random().toString(36).substring(2, 15)}`
setApiKey(newKey)
}
}
return (
+3 -1
View File
@@ -14,11 +14,13 @@ interface LanguageContextType {
const LanguageContext = createContext<LanguageContextType | undefined>(undefined)
export function LanguageProvider({ children }: { children: React.ReactNode }) {
// 始终从 "zh" 开始,确保服务器端和客户端初始状态一致
const [language, setLanguage] = useState<Language>("zh")
useEffect(() => {
// 只在客户端挂载后读取 localStorage
const savedLang = localStorage.getItem("language") as Language
if (savedLang) {
if (savedLang && (savedLang === "zh" || savedLang === "en")) {
setLanguage(savedLang)
}
}, [])
+161
View File
@@ -0,0 +1,161 @@
# 缺失的 API 接口清单
根据前端代码和 API 文档的对比,以下接口在 API 文档中**没有提供**:
## 1. 删除 Agent 接口
**前端使用位置**: `/app/admin/dashboard/page.tsx` (资源管理标签页)
**当前实现**:
- 前端使用 `TaijiAPIClient.deleteTool(agent.id || agent.name)` 来删除 Agent
- 这个接口实际上是删除 Data Ingestion 服务中的工具,而不是删除 Agent 资源
**需要的接口**:
```
DELETE /api/admin/resources/agents/{agent_id}
```
**请求示例**:
```bash
curl -X DELETE "http://localhost:8002/api/admin/resources/agents/agent-uuid-1" \
-H "Authorization: Bearer <admin_token>"
```
**响应示例**:
```json
{
"success": true,
"message": "Agent资源已删除"
}
```
**说明**:
- 当前前端代码错误地使用了 `deleteTool` 接口来删除 Agent
- 应该提供一个专门的删除 Agent 资源的接口
- 删除应该是软删除,仅标记为不活跃
---
## 2. 删除渠道接口
**前端使用位置**: `/app/admin/dashboard/page.tsx` (渠道管理标签页)
**当前状态**:
- 前端UI中有"删除渠道"按钮,但没有实现对应的API调用
**需要的接口**:
```
DELETE /api/admin/channels/{channel_id}
```
**请求示例**:
```bash
curl -X DELETE "http://localhost:8002/api/admin/channels/channel-uuid-1" \
-H "Authorization: Bearer <admin_token>"
```
**响应示例**:
```json
{
"success": true,
"message": "渠道已删除"
}
```
**说明**:
- 删除渠道前应该检查是否有关联的租户
- 如果有租户,应该提示或阻止删除
- 删除应该是软删除,仅标记为不活跃
---
## 3. 更新渠道信息接口
**前端使用位置**: `/app/admin/dashboard/page.tsx` (渠道管理标签页)
**当前状态**:
- 前端可能有编辑渠道信息的功能,但需要确认是否有对应的API
**需要的接口**:
```
PUT /api/admin/channels/{channel_id}
```
**请求体**:
```json
{
"name": "合作渠道A(更新)",
"email": "new-email@channel-a.com",
"commissionRate": 12.0,
"status": "active"
}
```
**响应示例**:
```json
{
"success": true,
"data": {
"id": "channel-uuid-1",
"name": "合作渠道A(更新)",
"email": "new-email@channel-a.com"
},
"message": "渠道信息更新成功"
}
```
---
## 4. 更新 Agent 资源配置接口
**前端使用位置**: `/app/admin/dashboard/page.tsx` (资源管理标签页 - Agent资源配置对话框)
**当前状态**:
- 前端有配置 Agent CPU 和内存的对话框,但保存时没有调用API
**需要的接口**:
```
PUT /api/admin/resources/agents/{agent_id}/config
```
**请求体**:
```json
{
"cpu": 4.0,
"memory": 8.0,
"maxInstances": 10
}
```
**响应示例**:
```json
{
"success": true,
"message": "Agent资源配置更新成功"
}
```
---
## 总结
### 必须实现的接口(前端已使用):
1. ❌ **DELETE /api/admin/resources/agents/{agent_id}** - 删除Agent资源
- 当前前端错误地使用了 `deleteTool` 接口
### 建议实现的接口(前端UI已存在但未实现):
2. ❌ **DELETE /api/admin/channels/{channel_id}** - 删除渠道
3. ⚠️ **PUT /api/admin/channels/{channel_id}** - 更新渠道信息
4. ⚠️ **PUT /api/admin/resources/agents/{agent_id}/config** - 更新Agent资源配置
### 已实现的接口(前端已使用):
✅ 所有其他接口都已实现并在API文档中有说明
---
## 修复建议
1. **立即修复**: 实现 `DELETE /api/admin/resources/agents/{agent_id}` 接口,并更新前端代码使用正确的接口
2. **优先级高**: 实现 `DELETE /api/admin/channels/{channel_id}` 接口,完善渠道管理功能
3. **优先级中**: 实现渠道和Agent的更新接口,提升管理功能的完整性
+14
View File
@@ -25,6 +25,20 @@ export function isAuthenticated(): boolean {
return getAuthToken() !== null
}
export function isAdminAuthenticated(): boolean {
if (typeof window !== "undefined") {
return localStorage.getItem("admin_token") !== null
}
return false
}
export function isChannelAuthenticated(): boolean {
if (typeof window !== "undefined") {
return localStorage.getItem("channel_token") !== null
}
return false
}
export function clearAuth(): void {
if (typeof window !== "undefined") {
localStorage.removeItem("auth_token")