Files
taiji-pda-v0/docs/FRONTEND_CODE_OPTIMIZATION.md
T
xiaohei 4448468157 优化代码:修复重复文件、统一认证函数、添加租户角色管理和管理员列表功能
- 删除重复文件:components/ui/use-toast.ts 和 components/ui/use-mobile.tsx
- 统一 getAuthToken() 函数,在 api-client.ts 中导入并删除重复定义
- 创建 clearAllTokens() 工具函数,统一 token 清除逻辑
- 修复 Toast 延迟时间(从 1000000ms 改为 5000ms)
- 修复 TypeScript 类型错误:在 applicationForm 中添加 providerId 字段
- 修复登录路径:使用 super_admin 角色登录超级管理员
- 移除登录前的后端可达性检查(避免浏览器环境问题)
- 在管理用户对话框中添加租户角色修改功能(租户/计费管理员/运营管理员)
- 移除管理用户对话框中的用户数字段(用户层面不应显示用户数)
- 在设置页面添加当前渠道管理员列表显示功能
- 添加获取和创建渠道管理员的 API 方法
- 更新登录方法的角色类型定义,支持所有角色类型
2025-12-28 13:43:07 +00:00

17 KiB
Raw Blame History

Taiji AI Platform 前端代码优化建议

📌 概述

本文档基于对整个前端项目的代码审查,提出了架构优化、代码质量改进和最佳实践建议。


🔴 高优先级问题

1. API 客户端逻辑错误

问题位置: lib/api-client.ts(第1000+行)

具体问题:

// ❌ 错误的删除Agent接口
static async deleteTool(toolId: string) {
  const response = await fetch(
    `${API_BASE_URLS.dataIngestion}/api/tools/delete?id=${toolId}`,
    { method: "DELETE", headers: buildHeaders() }
  )
  return handleResponse(response)
}

为什么这是错的:

  1. 调用的是 Data Ingestion 服务的删除工具接口,不是 MCP Server 的删除 Agent 接口
  2. 在前端 Agent 管理页面中被错误使用
  3. 参数格式不符合 RESTful 最佳实践(应该在路径中,不是查询字符串)

修复方案:

/**
 * 删除Agent资源(超级管理员)
 */
static async deleteAdminAgent(agentId: string) {
  const response = await fetch(
    `${API_BASE_URLS.mcpServer}/api/admin/resources/agents/${agentId}`,
    {
      method: "DELETE",
      headers: buildHeaders(),
    }
  )
  return handleResponse(response)
}

/**
 * 删除用户自定义Agent
 */
static async deleteUserAgent(agentId: string) {
  const response = await fetch(
    `${API_BASE_URLS.mcpServer}/api/user/agents/${agentId}`,
    {
      method: "DELETE",
      headers: buildHeaders(),
    }
  )
  return handleResponse(response)
}

/**
 * 删除工具(Data Ingestion 服务)
 */
static async deleteTool(toolId: string) {
  const response = await fetch(
    `${API_BASE_URLS.dataIngestion}/api/tools/${toolId}`,
    {
      method: "DELETE",
      headers: buildHeaders(),
    }
  )
  return handleResponse(response)
}

影响的文件:

  • app/admin/dashboard/page.tsx - ResourcesTab 组件
  • app/agent-factory/page.tsx - Agent 删除功能

2. 缺失的 API 实现

问题: 前端代码调用了后端没有实现的 API

2.1 删除渠道接口

前端代码:

// app/admin/dashboard/page.tsx
const handleDeleteChannel = async (channelId: string) => {
  // 当前没有实现,但UI中有删除按钮
}

需要实现: DELETE /api/admin/channels/{channelId}

后端建议:

@router.delete("/channels/{channel_id}", tags=["admin"])
async def delete_channel(
    channel_id: str,
    current_user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session)
):
    """删除渠道(软删除)"""
    # 1. 验证超级管理员权限
    # 2. 检查是否有活跃租户
    # 3. 软删除渠道(更新status字段)
    # 4. 返回成功响应

2.2 更新渠道接口

前端代码:

// app/admin/dashboard/page.tsx
const handleUpdateChannel = async (channelId: string, data: any) => {
  // 当前没有实现
}

需要实现: PUT /api/admin/channels/{channelId}

2.3 工作流相关接口

缺失接口:

  • GET /api/user/workflows - 获取用户工作流列表
  • GET /api/user/workflows/{workflowId} - 获取工作流详情
  • PUT /api/user/workflows/{workflowId} - 更新工作流
  • DELETE /api/user/workflows/{workflowId} - 删除工作流
  • POST /api/user/workflows/{workflowId}/execute - 执行工作流

🟡 中优先级问题

3. API 客户端架构问题

问题: lib/api-client.ts 已有 1443 行,单一责任原则违反

当前状况:

api-client.ts: 1443 行
├── 认证相关: ~200 行
├── 用户接口: ~400 行
├── 渠道接口: ~300 行
├── 管理员接口: ~300 行
└── 工具函数: ~100 行

建议方案 - 分离为多个模块:

lib/
├── api-client.ts (基础工具和导出)
├── api/
│   ├── auth.ts (认证相关)
│   ├── user.ts (用户接口)
│   ├── channel.ts (渠道接口)
│   ├── admin.ts (管理员接口)
│   ├── tools.ts (工具相关)
│   └── providers.ts (供应商相关)
└── types/
    ├── auth.ts
    ├── agent.ts
    ├── billing.ts
    └── common.ts

代码示例:

// lib/api/auth.ts
import { APIResponse, buildHeaders, handleResponse, API_BASE_URLS } from '../api-client'

export class AuthAPI {
  static async login(
    email: string,
    password: string,
    role: 'user' | 'channel' | 'admin' | 'provider' = 'user'
  ): Promise<APIResponse<{ token: string; refreshToken?: string; user?: any }>> {
    const response = await fetch(`${API_BASE_URLS.mcpServer}/api/auth/login`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, password, role }),
    })
    return handleResponse(response)
  }

  static async logout() {
    // ...
  }
}

// lib/api-client.ts (重构后)
export { AuthAPI } from './api/auth'
export { UserAPI } from './api/user'
export { ChannelAPI } from './api/channel'
export { AdminAPI } from './api/admin'

使用方式:

import { AuthAPI, UserAPI, AdminAPI } from '@/lib/api-client'

// 登录
const result = await AuthAPI.login(email, password)

// 部署Agent
const deployment = await UserAPI.deployAgent(agentId, instances, model)

// 获取平台统计
const stats = await AdminAPI.getDashboardStats()

4. 全局状态管理缺失

问题:

// ❌ 反面例子 - 多个地方重复调用
const [agents, setAgents] = useState<any[]>([])
const [loading, setLoading] = useState(true)

useEffect(() => {
  const loadAgents = async () => {
    try {
      const result = await TaijiAPIClient.getPlatformAgents()
      setAgents(result?.data?.data || [])
    } catch (error) {
      console.error("Failed to load agents:", error)
    }
  }
  loadAgents()
}, [])

这个模式在 agent-factory.tsx, orchestration.tsx, billing.tsx 等多个文件中重复出现。

建议方案 - 使用 Zustand 创建全局状态:

// lib/store/agent-store.ts
import { create } from 'zustand'
import { UserAPI } from '@/lib/api-client'

interface AgentState {
  agents: any[]
  loading: boolean
  error: string | null
  
  // Actions
  fetchAgents: () => Promise<void>
  deployAgent: (agentId: string, instances: number, model: string) => Promise<void>
  getAgentById: (id: string) => any | null
}

export const useAgentStore = create<AgentState>((set, get) => ({
  agents: [],
  loading: false,
  error: null,

  fetchAgents: async () => {
    set({ loading: true, error: null })
    try {
      const result = await UserAPI.getPlatformAgents()
      set({ agents: result?.data?.data || [] })
    } catch (error) {
      set({ error: error?.message || 'Failed to fetch agents' })
    } finally {
      set({ loading: false })
    }
  },

  deployAgent: async (agentId: string, instances: number, model: string) => {
    try {
      const result = await UserAPI.deployAgent({
        agentId,
        instances,
        model,
        gateway: 'MCP'
      })
      // 部署后刷新列表
      await get().fetchAgents()
    } catch (error) {
      set({ error: error?.message })
    }
  },

  getAgentById: (id: string) => {
    return get().agents.find(a => a.id === id)
  }
}))

使用方式:

// app/agent-factory/page.tsx
import { useAgentStore } from '@/lib/store/agent-store'

export default function AgentFactoryPage() {
  const { agents, loading, fetchAgents, deployAgent } = useAgentStore()

  useEffect(() => {
    fetchAgents()
  }, [fetchAgents])

  return (
    <div>
      {loading ? <Spinner /> : agents.map(agent => (...))}
    </div>
  )
}

5. 错误处理不统一

问题:

// ❌ 多种错误处理方式混乱
try {
  const result = await TaijiAPIClient.login(...)
  if (result?.success && result.data?.token) {
    // 处理成功
  }
} catch (error) {
  // 处理异常
  console.error("Failed to login:", error)
  toast({ ... })
}

建议方案 - 创建统一的错误处理器:

// lib/error-handler.ts
export class APIError extends Error {
  constructor(
    public code: string,
    public message: string,
    public statusCode: number = 500,
    public details?: any
  ) {
    super(message)
  }
}

export function handleAPIError(error: any): APIError {
  // 如果是已知的APIError,直接返回
  if (error instanceof APIError) {
    return error
  }

  // 如果是响应错误
  if (error.response) {
    const { status, data } = error.response
    return new APIError(
      data?.error?.code || 'UNKNOWN_ERROR',
      data?.error?.message || data?.detail || 'An error occurred',
      status,
      data?.error?.details
    )
  }

  // 网络错误
  if (error.message?.includes('Failed to fetch')) {
    return new APIError(
      'NETWORK_ERROR',
      'Unable to connect to the server. Please check your connection.',
      0
    )
  }

  // 其他错误
  return new APIError(
    'UNKNOWN_ERROR',
    error.message || 'An unexpected error occurred',
    500
  )
}

// 使用
try {
  const result = await AuthAPI.login(email, password)
  if (result.success) {
    // ...
  }
} catch (error) {
  const apiError = handleAPIError(error)
  toast({
    title: "错误",
    description: apiError.message,
    variant: "destructive"
  })
  logger.error('Login failed', { error: apiError })
}

6. Token 刷新机制不完善

问题:

// ❌ 当Token过期时,用户必须重新登录
if (response.status === 401) {
  // 清除token并重定向到登录
  localStorage.removeItem("auth_token")
  router.push("/login")
}

建议方案 - 实现自动 Token 刷新:

// lib/api-client.ts
async function handleResponse<T = any>(response: Response): Promise<T> {
  // Token过期 (401) 时自动刷新
  if (response.status === 401) {
    try {
      const refreshResult = await refreshToken()
      if (refreshResult.success && refreshResult.data?.token) {
        // 重试原请求
        const retryResponse = await fetch(response.url, {
          ...response,
          headers: {
            ...response.headers,
            Authorization: `Bearer ${refreshResult.data.token}`
          }
        })
        return handleResponse<T>(retryResponse)
      }
    } catch (error) {
      // 刷新失败,重定向到登录
      clearAuth()
      window.location.href = '/login'
    }
  }

  if (!response.ok) {
    const error = await response.json().catch(() => ({ detail: response.statusText }))
    throw new APIError(
      (error as any).error?.code || 'UNKNOWN',
      (error as any).detail || `HTTP error! status: ${response.status}`,
      response.status
    )
  }

  return response.json() as Promise<T>
}

🟢 低优先级建议

7. 代码质量改进

7.1 添加类型定义

// 📂 lib/types/index.ts

export interface User {
  id: string
  email: string
  name: string
  role: UserRole
  subscription_tier: SubscriptionTier
  balance: number
  status: 'active' | 'inactive' | 'suspended'
}

export interface Agent {
  id: string
  name: string
  type: 'platform' | 'custom'
  description: string
  category: string
  status: 'available' | 'unavailable' | 'error'
  capabilities: string[]
  required_resources: {
    cpu: number
    memory: number
  }
}

export type UserRole = 'user' | 'channel' | 'admin' | 'super_admin' | 'provider'
export type SubscriptionTier = 'free' | 'pro' | 'enterprise'

7.2 环境变量管理

// 📂 lib/env.ts
const requiredEnvVars = [
  'NEXT_PUBLIC_MCP_SERVER_URL',
  'NEXT_PUBLIC_DATA_INGESTION_URL',
  'NEXT_PUBLIC_API_GATEWAY_URL'
]

for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    throw new Error(`Missing required environment variable: ${envVar}`)
  }
}

export const env = {
  MCP_SERVER_URL: process.env.NEXT_PUBLIC_MCP_SERVER_URL!,
  DATA_INGESTION_URL: process.env.NEXT_PUBLIC_DATA_INGESTION_URL!,
  API_GATEWAY_URL: process.env.NEXT_PUBLIC_API_GATEWAY_URL!,
}

7.3 请求拦截器

// 📂 lib/api-interceptor.ts
export class RequestInterceptor {
  private static instance: RequestInterceptor

  static getInstance() {
    if (!this.instance) {
      this.instance = new RequestInterceptor()
    }
    return this.instance
  }

  async intercept(url: string, options: RequestInit) {
    // 1. 添加认证header
    const token = getAuthToken()
    if (token) {
      options.headers = {
        ...options.headers,
        'Authorization': `Bearer ${token}`
      }
    }

    // 2. 添加请求ID用于追踪
    const requestId = crypto.randomUUID()
    options.headers = {
      ...options.headers,
      'X-Request-ID': requestId
    }

    // 3. 记录请求
    console.log(`[${requestId}] ${options.method} ${url}`)

    return { url, options }
  }
}

7.4 日志系统

// 📂 lib/logger.ts
export class Logger {
  static info(message: string, data?: any) {
    console.log(`[INFO] ${new Date().toISOString()} - ${message}`, data)
  }

  static error(message: string, error?: any) {
    console.error(`[ERROR] ${new Date().toISOString()} - ${message}`, error)
  }

  static warn(message: string, data?: any) {
    console.warn(`[WARN] ${new Date().toISOString()} - ${message}`, data)
  }

  static debug(message: string, data?: any) {
    if (process.env.NODE_ENV === 'development') {
      console.debug(`[DEBUG] ${new Date().toISOString()} - ${message}`, data)
    }
  }
}

8. 性能优化建议

8.1 实现缓存策略

// 📂 lib/cache.ts
export class CacheManager {
  private static cache = new Map<string, { data: any; expiry: number }>()

  static set(key: string, data: any, ttl: number = 5 * 60 * 1000) {
    this.cache.set(key, {
      data,
      expiry: Date.now() + ttl
    })
  }

  static get(key: string) {
    const item = this.cache.get(key)
    if (!item) return null

    if (Date.now() > item.expiry) {
      this.cache.delete(key)
      return null
    }

    return item.data
  }

  static clear(key?: string) {
    if (key) {
      this.cache.delete(key)
    } else {
      this.cache.clear()
    }
  }
}

// 使用
const getPlatformAgents = async () => {
  const cacheKey = 'platform_agents'
  
  // 先查缓存
  const cached = CacheManager.get(cacheKey)
  if (cached) return cached

  // 缓存未命中,调用API
  const result = await UserAPI.getPlatformAgents()
  
  // 缓存结果(5分钟)
  if (result.success) {
    CacheManager.set(cacheKey, result.data, 5 * 60 * 1000)
  }

  return result.data
}

8.2 代码分割优化

// 📂 app/admin/layout.tsx
const AdminDashboard = dynamic(
  () => import('./dashboard/page'),
  { loading: () => <Spinner /> }
)

9. 测试建议

9.1 单元测试

// 📂 lib/__tests__/api-client.test.ts
import { AuthAPI } from '@/lib/api-client'

describe('AuthAPI', () => {
  it('should login successfully with correct credentials', async () => {
    const result = await AuthAPI.login('test@example.com', 'password')
    expect(result.success).toBe(true)
    expect(result.data?.token).toBeDefined()
  })

  it('should fail login with incorrect credentials', async () => {
    expect(async () => {
      await AuthAPI.login('test@example.com', 'wrongpassword')
    }).rejects.toThrow()
  })
})

9.2 集成测试

// 📂 __tests__/integration/agent-deployment.test.ts
describe('Agent Deployment Flow', () => {
  it('should complete full agent deployment flow', async () => {
    // 1. 登录
    const loginResult = await AuthAPI.login(...)
    
    // 2. 获取Agent列表
    const agents = await UserAPI.getPlatformAgents()
    
    // 3. 部署Agent
    const deployment = await UserAPI.deployAgent(...)
    
    // 4. 验证部署成功
    expect(deployment.success).toBe(true)
  })
})

📋 优化实施计划

Phase 1 (第1周) - 高优先级修复

  • 分离 api-client.ts 为多个模块
  • 修复 Agent 删除接口逻辑
  • 实现缺失的 API(删除/更新渠道)
  • 添加 Token 自动刷新机制

Phase 2 (第2周) - 中优先级改进

  • 实现全局状态管理(Zustand)
  • 添加统一的错误处理
  • 添加日志系统
  • 优化 API 调用(缓存、去重)

Phase 3 (第3周) - 代码质量

  • 添加完整的类型定义
  • 实现请求拦截器
  • 添加单元测试
  • 性能优化(代码分割、缓存)

Phase 4 (第4周) - 测试和文档

  • 集成测试
  • 性能测试
  • 更新API文档
  • 编写开发指南

总结

通过上述优化,可以显著提升项目的:

  • ✅ 可维护性: 清晰的架构和分离的关注点
  • ✅ 可扩展性: 模块化设计便于添加新功能
  • ✅ 性能: 缓存、请求优化、代码分割
  • ✅ 可靠性: 统一的错误处理和日志记录
  • ✅ 开发效率: 减少重复代码,提高代码复用率

预期工作量: 2-3周
优先级排序: 高→中→低