Files
users/components/data-integration/api-documentation.tsx
v0 ce0a716d02 fix: support default and named export for useDebounce hook
Ensure compatibility with both default and named imports for hook.

#VERCEL_SKIP

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
2025-09-23 07:09:47 +00:00

665 lines
24 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import { Copy, Check, Code, FileJson, Play } from "lucide-react"
import { useToast } from "@/components/ui/use-toast"
interface ApiEndpoint {
id: string
name: string
method: "GET" | "POST" | "PUT" | "DELETE"
path: string
description: string
parameters: {
name: string
type: string
required: boolean
description: string
}[]
requestBody?: {
type: string
example: string
}
responses: {
code: string
description: string
example: string
}[]
authentication: "API Key" | "OAuth 2.0" | "None"
}
export function ApiDocumentation() {
const { toast } = useToast()
const [activeTab, setActiveTab] = useState("user-data")
const [copiedEndpoint, setCopiedEndpoint] = useState<string | null>(null)
// 模拟API端点数据
const apiEndpoints: Record<string, ApiEndpoint[]> = {
"user-data": [
{
id: "get-users",
name: "获取用户列表",
method: "GET",
path: "/api/users",
description: "获取系统中的用户列表,支持分页、筛选和排序",
parameters: [
{
name: "page",
type: "number",
required: false,
description: "页码默认为1",
},
{
name: "limit",
type: "number",
required: false,
description: "每页数量默认为20最大为100",
},
{
name: "sort",
type: "string",
required: false,
description: "排序字段例如name,-createdAt-表示降序)",
},
{
name: "filter",
type: "string",
required: false,
description: "筛选条件例如status=active",
},
],
responses: [
{
code: "200",
description: "成功",
example: `{
"data": [
{
"id": "user_123",
"name": "张三",
"phoneNumber": "138****1234",
"registrationDate": "2023-01-15T08:30:00Z",
"lastActiveTime": "2023-07-20T14:25:30Z",
"tags": ["高价值", "活跃用户"]
},
// 更多用户...
],
"pagination": {
"page": 1,
"limit": 20,
"total": 156,
"pages": 8
}
}`,
},
{
code: "400",
description: "请求参数错误",
example: `{
"error": "Bad Request",
"message": "Invalid filter format",
"code": "INVALID_FILTER"
}`,
},
{
code: "401",
description: "未授权",
example: `{
"error": "Unauthorized",
"message": "API key is invalid or expired",
"code": "INVALID_API_KEY"
}`,
},
],
authentication: "API Key",
},
{
id: "get-user",
name: "获取用户详情",
method: "GET",
path: "/api/users/{id}",
description: "根据用户ID获取用户详细信息",
parameters: [
{
name: "id",
type: "string",
required: true,
description: "用户ID",
},
],
responses: [
{
code: "200",
description: "成功",
example: `{
"id": "user_123",
"name": "张三",
"phoneNumber": "138****1234",
"identityNumber": "310******1234",
"email": "zhangsan@example.com",
"registrationDate": "2023-01-15T08:30:00Z",
"lastActiveTime": "2023-07-20T14:25:30Z",
"tags": ["高价值", "活跃用户"],
"devices": [
{
"id": "device_456",
"type": "mobile",
"model": "iPhone 13",
"imei": "123456789012345",
"lastActiveTime": "2023-07-20T14:25:30Z"
}
],
"userValue": {
"rfm": {
"recency": 5,
"frequency": 4,
"monetary": 5,
"score": 4.7
},
"lifetimeValue": 12500
}
}`,
},
{
code: "404",
description: "用户不存在",
example: `{
"error": "Not Found",
"message": "User with ID user_999 not found",
"code": "USER_NOT_FOUND"
}`,
},
],
authentication: "API Key",
},
],
"user-portrait": [
{
id: "get-user-portrait",
name: "获取用户画像",
method: "GET",
path: "/api/user-portrait/{id}",
description: "根据用户ID获取用户画像数据",
parameters: [
{
name: "id",
type: "string",
required: true,
description: "用户ID",
},
],
responses: [
{
code: "200",
description: "成功",
example: `{
"userId": "user_123",
"basicInfo": {
"name": "张三",
"age": 28,
"gender": "male",
"location": "上海市"
},
"behaviorTags": ["夜间活跃", "周末购物", "高频App使用"],
"interestTags": ["科技", "旅游", "美食"],
"consumptionPattern": {
"averageOrderValue": 320,
"purchaseFrequency": "每周2-3次",
"preferredCategories": ["电子产品", "服装"]
},
"rfmAnalysis": {
"recency": 5,
"frequency": 4,
"monetary": 5,
"score": 4.7,
"segment": "高价值用户"
},
"deviceInfo": [
{
"type": "mobile",
"model": "iPhone 13",
"osVersion": "iOS 16.5",
"usageFrequency": "高"
}
],
"channelPreference": ["App", "微信小程序"],
"riskScore": 0.2
}`,
},
],
authentication: "API Key",
},
],
"ai-analysis": [
{
id: "analyze-user-behavior",
name: "用户行为分析",
method: "POST",
path: "/api/ai/analyze-behavior",
description: "使用AI分析用户行为数据生成洞察报告",
parameters: [],
requestBody: {
type: "application/json",
example: `{
"userId": "user_123",
"timeRange": {
"start": "2023-01-01T00:00:00Z",
"end": "2023-07-20T23:59:59Z"
},
"analysisDepth": "deep",
"includeTags": true,
"includeRecommendations": true
}`,
},
responses: [
{
code: "200",
description: "成功",
example: `{
"userId": "user_123",
"analysisTime": "2023-07-21T10:15:30Z",
"timeRange": {
"start": "2023-01-01T00:00:00Z",
"end": "2023-07-20T23:59:59Z"
},
"behaviorPatterns": [
{
"pattern": "夜间活跃",
"confidence": 0.92,
"description": "用户主要在晚上9点至凌晨1点活跃",
"supportingData": {
"activeTimeDistribution": {
"morning": 0.15,
"afternoon": 0.25,
"evening": 0.60
}
}
},
// 更多行为模式...
],
"insights": [
{
"type": "preference",
"description": "用户对科技类产品有强烈兴趣,尤其是智能家居设备",
"confidence": 0.85
},
// 更多洞察...
],
"recommendations": [
{
"type": "marketing",
"description": "建议在晚间时段推送智能家居相关促销信息",
"expectedImpact": "高"
},
// 更多建议...
]
}`,
},
],
authentication: "API Key",
},
],
}
const handleCopyCode = (code: string, endpointId: string) => {
navigator.clipboard.writeText(code)
setCopiedEndpoint(endpointId)
toast({
title: "已复制到剪贴板",
description: "代码已成功复制到剪贴板",
})
setTimeout(() => setCopiedEndpoint(null), 2000)
}
return (
<div className="space-y-4">
<Card>
<CardHeader>
<CardTitle>API文档</CardTitle>
<CardDescription>API接口文档和使用说明</CardDescription>
</CardHeader>
<CardContent>
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4">
<TabsList className="grid grid-cols-3 w-full max-w-md">
<TabsTrigger value="user-data"></TabsTrigger>
<TabsTrigger value="user-portrait"></TabsTrigger>
<TabsTrigger value="ai-analysis">AI分析</TabsTrigger>
</TabsList>
{Object.entries(apiEndpoints).map(([category, endpoints]) => (
<TabsContent key={category} value={category} className="space-y-4">
{endpoints.map((endpoint) => (
<Card key={endpoint.id} className="border shadow-sm">
<CardHeader className="pb-2">
<div className="flex justify-between items-start">
<div>
<CardTitle className="text-lg">{endpoint.name}</CardTitle>
<CardDescription>{endpoint.description}</CardDescription>
</div>
<Badge
className={
endpoint.method === "GET"
? "bg-blue-100 text-blue-800"
: endpoint.method === "POST"
? "bg-green-100 text-green-800"
: endpoint.method === "PUT"
? "bg-yellow-100 text-yellow-800"
: "bg-red-100 text-red-800"
}
>
{endpoint.method}
</Badge>
</div>
</CardHeader>
<CardContent className="pb-2">
<div className="flex items-center gap-2 mb-4">
<code className="bg-muted px-2 py-1 rounded text-sm font-mono">{endpoint.path}</code>
<Button
variant="ghost"
size="icon"
onClick={() => handleCopyCode(endpoint.path, `path-${endpoint.id}`)}
>
{copiedEndpoint === `path-${endpoint.id}` ? (
<Check className="h-4 w-4" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
<Accordion type="single" collapsible className="w-full">
{endpoint.parameters.length > 0 && (
<AccordionItem value="parameters">
<AccordionTrigger></AccordionTrigger>
<AccordionContent>
<div className="rounded-md border">
<table className="min-w-full divide-y divide-border">
<thead>
<tr className="bg-muted/50">
<th className="px-4 py-2 text-left text-sm font-medium"></th>
<th className="px-4 py-2 text-left text-sm font-medium"></th>
<th className="px-4 py-2 text-left text-sm font-medium"></th>
<th className="px-4 py-2 text-left text-sm font-medium"></th>
</tr>
</thead>
<tbody className="divide-y divide-border">
{endpoint.parameters.map((param, index) => (
<tr key={index}>
<td className="px-4 py-2 text-sm font-mono">{param.name}</td>
<td className="px-4 py-2 text-sm">{param.type}</td>
<td className="px-4 py-2 text-sm">
{param.required ? (
<Badge className="bg-red-100 text-red-800"></Badge>
) : (
<Badge className="bg-gray-100 text-gray-800"></Badge>
)}
</td>
<td className="px-4 py-2 text-sm">{param.description}</td>
</tr>
))}
</tbody>
</table>
</div>
</AccordionContent>
</AccordionItem>
)}
{endpoint.requestBody && (
<AccordionItem value="request-body">
<AccordionTrigger></AccordionTrigger>
<AccordionContent>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Badge className="bg-purple-100 text-purple-800">{endpoint.requestBody.type}</Badge>
<Button
variant="ghost"
size="sm"
className="h-7"
onClick={() => handleCopyCode(endpoint.requestBody!.example, `req-${endpoint.id}`)}
>
{copiedEndpoint === `req-${endpoint.id}` ? (
<Check className="h-3 w-3 mr-1" />
) : (
<Copy className="h-3 w-3 mr-1" />
)}
</Button>
</div>
<pre className="bg-muted p-4 rounded-md overflow-x-auto text-sm font-mono">
{endpoint.requestBody.example}
</pre>
</div>
</AccordionContent>
</AccordionItem>
)}
<AccordionItem value="responses">
<AccordionTrigger></AccordionTrigger>
<AccordionContent>
<div className="space-y-4">
{endpoint.responses.map((response, index) => (
<div key={index} className="space-y-2">
<div className="flex items-center gap-2">
<Badge
className={
response.code.startsWith("2")
? "bg-green-100 text-green-800"
: response.code.startsWith("4")
? "bg-red-100 text-red-800"
: "bg-yellow-100 text-yellow-800"
}
>
{response.code}
</Badge>
<span className="text-sm font-medium">{response.description}</span>
<Button
variant="ghost"
size="sm"
className="h-7"
onClick={() => handleCopyCode(response.example, `res-${endpoint.id}-${index}`)}
>
{copiedEndpoint === `res-${endpoint.id}-${index}` ? (
<Check className="h-3 w-3 mr-1" />
) : (
<Copy className="h-3 w-3 mr-1" />
)}
</Button>
</div>
<pre className="bg-muted p-4 rounded-md overflow-x-auto text-sm font-mono">
{response.example}
</pre>
</div>
))}
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="authentication">
<AccordionTrigger></AccordionTrigger>
<AccordionContent>
<div className="p-2">
<Badge className="bg-blue-100 text-blue-800">{endpoint.authentication}</Badge>
{endpoint.authentication === "API Key" && (
<div className="mt-2 text-sm">
<p></p>
<code className="bg-muted px-2 py-1 rounded text-sm font-mono mt-1 block">
X-API-Key: your_api_key_here
</code>
</div>
)}
{endpoint.authentication === "OAuth 2.0" && (
<div className="mt-2 text-sm">
<p></p>
<code className="bg-muted px-2 py-1 rounded text-sm font-mono mt-1 block">
Authorization: Bearer your_access_token_here
</code>
</div>
)}
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="code-examples">
<AccordionTrigger></AccordionTrigger>
<AccordionContent>
<div className="space-y-4">
<div>
<div className="flex items-center gap-2 mb-2">
<Badge className="bg-blue-100 text-blue-800">JavaScript</Badge>
<Button
variant="ghost"
size="sm"
className="h-7"
onClick={() => handleCopyCode(jsExample, `js-${endpoint.id}`)}
>
{copiedEndpoint === `js-${endpoint.id}` ? (
<Check className="h-3 w-3 mr-1" />
) : (
<Copy className="h-3 w-3 mr-1" />
)}
</Button>
</div>
<pre className="bg-muted p-4 rounded-md overflow-x-auto text-sm font-mono">
{jsExample}
</pre>
</div>
<div>
<div className="flex items-center gap-2 mb-2">
<Badge className="bg-green-100 text-green-800">Python</Badge>
<Button
variant="ghost"
size="sm"
className="h-7"
onClick={() => handleCopyCode(pythonExample, `py-${endpoint.id}`)}
>
{copiedEndpoint === `py-${endpoint.id}` ? (
<Check className="h-3 w-3 mr-1" />
) : (
<Copy className="h-3 w-3 mr-1" />
)}
</Button>
</div>
<pre className="bg-muted p-4 rounded-md overflow-x-auto text-sm font-mono">
{pythonExample}
</pre>
</div>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</CardContent>
</Card>
))}
</TabsContent>
))}
</Tabs>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>API使用指南</CardTitle>
<CardDescription>使API</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<h3 className="text-lg font-medium">1. API密钥</h3>
<p className="text-sm text-muted-foreground">
使API之前API密钥API密钥
</p>
</div>
<div className="space-y-2">
<h3 className="text-lg font-medium">2. </h3>
<p className="text-sm text-muted-foreground">API请求都需要进行认证API密钥</p>
<pre className="bg-muted p-4 rounded-md overflow-x-auto text-sm font-mono">
X-API-Key: your_api_key_here
</pre>
</div>
<div className="space-y-2">
<h3 className="text-lg font-medium">3. </h3>
<p className="text-sm text-muted-foreground">API支持JSON格式的请求和响应</p>
<pre className="bg-muted p-4 rounded-md overflow-x-auto text-sm font-mono">
Content-Type: application/json Accept: application/json
</pre>
</div>
<div className="space-y-2">
<h3 className="text-lg font-medium">4. </h3>
<p className="text-sm text-muted-foreground">
API使用标准的HTTP状态码表示请求的结果
</p>
<pre className="bg-muted p-4 rounded-md overflow-x-auto text-sm font-mono">
{`{
"error": "错误类型",
"message": "错误详细信息",
"code": "错误代码"
}`}
</pre>
</div>
<div className="space-y-2">
<h3 className="text-lg font-medium">5. </h3>
<p className="text-sm text-muted-foreground">
API有速率限制100429
</p>
</div>
<div className="flex justify-between items-center mt-4">
<Button variant="outline" className="gap-2">
<FileJson className="h-4 w-4" />
OpenAPI规范
</Button>
<Button variant="outline" className="gap-2">
<Code className="h-4 w-4" />
SDK
</Button>
<Button className="gap-2">
<Play className="h-4 w-4" />
API测试工具
</Button>
</div>
</CardContent>
</Card>
</div>
)
}
// 示例代码
const jsExample = `// 使用fetch API调用
const apiKey = 'your_api_key_here';
fetch('https://api.example.com/api/users', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));`
const pythonExample = `# 使用requests库调用
import requests
api_key = 'your_api_key_here'
headers = {
'Content-Type': 'application/json',
'X-API-Key': api_key
}
response = requests.get('https://api.example.com/api/users', headers=headers)
data = response.json()
print(data)`