2025-07-18 13:47:12 +00:00
|
|
|
|
"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有速率限制,默认为每分钟100个请求。超过限制会返回429状态码。
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex justify-between items-center mt-4">
|
2025-09-23 07:09:47 +00:00
|
|
|
|
<Button variant="outline" className="gap-2">
|
2025-07-18 13:47:12 +00:00
|
|
|
|
<FileJson className="h-4 w-4" />
|
|
|
|
|
|
下载OpenAPI规范
|
|
|
|
|
|
</Button>
|
2025-09-23 07:09:47 +00:00
|
|
|
|
<Button variant="outline" className="gap-2">
|
2025-07-18 13:47:12 +00:00
|
|
|
|
<Code className="h-4 w-4" />
|
|
|
|
|
|
下载SDK
|
|
|
|
|
|
</Button>
|
2025-09-23 07:09:47 +00:00
|
|
|
|
<Button className="gap-2">
|
2025-07-18 13:47:12 +00:00
|
|
|
|
<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)`
|