chore: 以本地为准,上传全部并替换 GitHub

This commit is contained in:
卡若
2026-02-03 11:36:53 +08:00
parent 1219166526
commit b404bf546e
131 changed files with 37618 additions and 3930 deletions

View File

@@ -0,0 +1,452 @@
"use client"
import { useState, useEffect } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Label } from "@/components/ui/label"
import {
Server,
Copy,
CheckCircle2,
Key,
RefreshCw,
ExternalLink,
Code,
Zap,
Users,
Search,
Tags,
Brain,
Database,
FileText,
Package,
Activity,
} from "lucide-react"
// API端点接口
interface APIEndpoint {
id: string
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
path: string
name: string
description: string
category: string
auth: boolean
params?: { name: string; type: string; required: boolean; desc: string }[]
response?: string
}
// API分类
const API_CATEGORIES = [
{ id: 'query', name: '用户查询', icon: Search },
{ id: 'tag', name: '标签服务', icon: Tags },
{ id: 'ai', name: 'AI服务', icon: Brain },
{ id: 'data', name: '数据服务', icon: Database },
{ id: 'report', name: '报告服务', icon: FileText },
{ id: 'package', name: '流量包', icon: Package },
]
// 预定义API端点
const API_ENDPOINTS: APIEndpoint[] = [
// 用户查询
{
id: 'api_1',
method: 'GET',
path: '/api/shensheshou/user',
name: '用户画像查询',
description: '根据手机号或QQ查询完整用户画像',
category: 'query',
auth: true,
params: [
{ name: 'phone', type: 'string', required: false, desc: '11位手机号' },
{ name: 'qq', type: 'string', required: false, desc: 'QQ号码' },
],
response: '{ "user": { "phone": "138xxx", "rfm": 85, "level": "A", "tags": [...] } }',
},
{
id: 'api_2',
method: 'POST',
path: '/api/shensheshou/users/batch',
name: '批量用户查询',
description: '批量查询多个用户的画像信息',
category: 'query',
auth: true,
params: [
{ name: 'phones', type: 'array', required: false, desc: '手机号数组' },
{ name: 'qqs', type: 'array', required: false, desc: 'QQ号数组' },
],
},
// 标签服务
{
id: 'api_3',
method: 'GET',
path: '/api/shensheshou/tags',
name: '标签列表',
description: '获取所有可用标签',
category: 'tag',
auth: true,
params: [
{ name: 'category', type: 'string', required: false, desc: '标签分类' },
],
},
{
id: 'api_4',
method: 'POST',
path: '/api/shensheshou/tags/apply',
name: '应用标签',
description: '为用户应用指定标签',
category: 'tag',
auth: true,
params: [
{ name: 'userId', type: 'string', required: true, desc: '用户ID' },
{ name: 'tags', type: 'array', required: true, desc: '标签ID数组' },
],
},
// AI服务
{
id: 'api_5',
method: 'POST',
path: '/api/shensheshou/chat',
name: 'AI对话',
description: '与神射手AI进行对话支持自然语言查询',
category: 'ai',
auth: true,
params: [
{ name: 'message', type: 'string', required: true, desc: '对话内容' },
],
response: '{ "success": true, "response": { "content": "查询结果...", "data": {...} } }',
},
{
id: 'api_6',
method: 'POST',
path: '/api/shensheshou/ai/analyze',
name: 'AI数据分析',
description: 'AI自动分析数据并生成洞察',
category: 'ai',
auth: true,
params: [
{ name: 'type', type: 'string', required: true, desc: '分析类型: rfm/behavior/preference' },
{ name: 'filters', type: 'object', required: false, desc: '筛选条件' },
],
},
{
id: 'api_7',
method: 'POST',
path: '/api/shensheshou/ai/tag',
name: 'AI智能打标',
description: 'AI自动为用户打标签',
category: 'ai',
auth: true,
params: [
{ name: 'source', type: 'string', required: true, desc: '数据源' },
{ name: 'model', type: 'string', required: false, desc: 'AI模型: qwen/deepseek' },
],
},
// 数据服务
{
id: 'api_8',
method: 'GET',
path: '/api/shensheshou/sources',
name: '数据源列表',
description: '获取所有数据源连接状态',
category: 'data',
auth: true,
},
{
id: 'api_9',
method: 'POST',
path: '/api/shensheshou/ingest',
name: '数据导入',
description: '导入外部数据并通过AI标签引擎处理',
category: 'data',
auth: true,
params: [
{ name: 'source', type: 'string', required: true, desc: '数据源标识' },
{ name: 'target', type: 'string', required: true, desc: '目标表' },
],
},
// 报告服务
{
id: 'api_10',
method: 'POST',
path: '/api/shensheshou/report/generate',
name: '生成报告',
description: 'AI自动生成数据分析报告',
category: 'report',
auth: true,
params: [
{ name: 'template', type: 'string', required: true, desc: '报告模板ID' },
{ name: 'dateRange', type: 'string', required: false, desc: '日期范围' },
],
},
// 流量包
{
id: 'api_11',
method: 'GET',
path: '/api/shensheshou/packages',
name: '流量包列表',
description: '获取所有流量包',
category: 'package',
auth: true,
},
{
id: 'api_12',
method: 'POST',
path: '/api/shensheshou/packages/create',
name: '创建流量包',
description: '根据筛选条件创建流量包',
category: 'package',
auth: true,
params: [
{ name: 'name', type: 'string', required: true, desc: '流量包名称' },
{ name: 'criteria', type: 'object', required: true, desc: '筛选条件' },
],
},
{
id: 'api_13',
method: 'POST',
path: '/api/shensheshou/packages/send',
name: '发送流量包',
description: '发送流量包到邮箱/飞书/微信',
category: 'package',
auth: true,
params: [
{ name: 'packageId', type: 'string', required: true, desc: '流量包ID' },
{ name: 'targets', type: 'array', required: true, desc: '发送目标' },
],
},
]
export default function APIServicePage() {
const [activeCategory, setActiveCategory] = useState('all')
const [apiBaseUrl, setApiBaseUrl] = useState('')
const [showKeyDialog, setShowKeyDialog] = useState(false)
const [copiedId, setCopiedId] = useState<string | null>(null)
useEffect(() => {
const host = typeof window !== 'undefined' ? window.location.origin : ''
setApiBaseUrl(host)
}, [])
const copyToClipboard = (text: string, id: string) => {
navigator.clipboard.writeText(text)
setCopiedId(id)
setTimeout(() => setCopiedId(null), 2000)
}
const getMethodColor = (method: string) => {
switch (method) {
case 'GET': return 'bg-green-100 text-green-700'
case 'POST': return 'bg-blue-100 text-blue-700'
case 'PUT': return 'bg-yellow-100 text-yellow-700'
case 'DELETE': return 'bg-red-100 text-red-700'
default: return 'bg-gray-100 text-gray-700'
}
}
const filteredEndpoints = activeCategory === 'all'
? API_ENDPOINTS
: API_ENDPOINTS.filter(e => e.category === activeCategory)
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-blue-50/30 to-purple-50/20">
<div className="p-6 space-y-6">
{/* 顶部标题 */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-gray-900">API服务</h1>
<p className="text-sm text-gray-500 mt-1">API</p>
</div>
<div className="flex items-center gap-3">
<Button variant="outline" onClick={() => setShowKeyDialog(true)}>
<Key className="h-4 w-4 mr-2" />
API密钥
</Button>
<Button variant="outline">
<ExternalLink className="h-4 w-4 mr-2" />
API文档
</Button>
</div>
</div>
{/* API基础信息 */}
<Card className="border-0 shadow-sm bg-gradient-to-r from-purple-50 to-blue-50">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div>
<h3 className="font-semibold text-gray-900 mb-1">API基础地址</h3>
<div className="flex items-center gap-2">
<code className="bg-white px-4 py-2 rounded-lg text-purple-600 font-mono">
{apiBaseUrl || 'https://your-domain.com'}
</code>
<Button
variant="ghost"
size="sm"
onClick={() => copyToClipboard(apiBaseUrl, 'base')}
>
{copiedId === 'base' ? <CheckCircle2 className="h-4 w-4 text-green-500" /> : <Copy className="h-4 w-4" />}
</Button>
</div>
</div>
<div className="text-right">
<div className="text-sm text-gray-500">API版本</div>
<Badge>v1.0</Badge>
</div>
</div>
</CardContent>
</Card>
{/* 认证说明 */}
<Card className="border-0 shadow-sm bg-white/80">
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
</CardHeader>
<CardContent>
<div className="p-4 rounded-lg bg-gray-50">
<p className="text-sm text-gray-600 mb-3">API请求需要在Header中携带API密钥</p>
<pre className="bg-gray-900 text-green-400 p-4 rounded-lg text-sm overflow-x-auto">
{`curl -X GET "${apiBaseUrl}/api/shensheshou/user?phone=13800138000" \\
-H "Authorization: Bearer YOUR_API_KEY" \\
-H "Content-Type: application/json"`}
</pre>
</div>
</CardContent>
</Card>
{/* API分类筛选 */}
<div className="flex items-center gap-2 overflow-x-auto pb-2">
<Button
variant={activeCategory === 'all' ? 'default' : 'outline'}
size="sm"
onClick={() => setActiveCategory('all')}
>
({API_ENDPOINTS.length})
</Button>
{API_CATEGORIES.map(cat => {
const count = API_ENDPOINTS.filter(e => e.category === cat.id).length
const Icon = cat.icon
return (
<Button
key={cat.id}
variant={activeCategory === cat.id ? 'default' : 'outline'}
size="sm"
onClick={() => setActiveCategory(cat.id)}
>
<Icon className="h-4 w-4 mr-1" />
{cat.name} ({count})
</Button>
)
})}
</div>
{/* API端点列表 */}
<div className="space-y-3">
{filteredEndpoints.map(endpoint => (
<Card key={endpoint.id} className="border-0 shadow-sm bg-white/80">
<CardContent className="p-4">
<div className="flex items-start justify-between mb-3">
<div className="flex items-center gap-3">
<Badge className={`${getMethodColor(endpoint.method)} font-mono`}>
{endpoint.method}
</Badge>
<code className="text-sm font-mono text-gray-700">{endpoint.path}</code>
<Button
variant="ghost"
size="sm"
onClick={() => copyToClipboard(`${apiBaseUrl}${endpoint.path}`, endpoint.id)}
>
{copiedId === endpoint.id ? (
<CheckCircle2 className="h-3 w-3 text-green-500" />
) : (
<Copy className="h-3 w-3" />
)}
</Button>
</div>
{endpoint.auth && (
<Badge variant="outline" className="text-xs">
<Key className="h-3 w-3 mr-1" />
</Badge>
)}
</div>
<h3 className="font-semibold text-gray-900 mb-1">{endpoint.name}</h3>
<p className="text-sm text-gray-500 mb-3">{endpoint.description}</p>
{/* 参数 */}
{endpoint.params && endpoint.params.length > 0 && (
<div className="mb-3">
<h4 className="text-xs font-medium text-gray-500 mb-2"></h4>
<div className="space-y-1">
{endpoint.params.map((param, i) => (
<div key={i} className="flex items-center gap-2 text-sm">
<code className="bg-gray-100 px-2 py-0.5 rounded text-purple-600">{param.name}</code>
<Badge variant="outline" className="text-xs">{param.type}</Badge>
{param.required && <Badge className="bg-red-100 text-red-700 text-xs"></Badge>}
<span className="text-gray-500">{param.desc}</span>
</div>
))}
</div>
</div>
)}
{/* 响应示例 */}
{endpoint.response && (
<div>
<h4 className="text-xs font-medium text-gray-500 mb-2"></h4>
<pre className="bg-gray-100 p-2 rounded text-xs font-mono text-gray-700 overflow-x-auto">
{endpoint.response}
</pre>
</div>
)}
</CardContent>
</Card>
))}
</div>
{/* API密钥弹窗 */}
<Dialog open={showKeyDialog} onOpenChange={setShowKeyDialog}>
<DialogContent>
<DialogHeader>
<DialogTitle>API密钥管理</DialogTitle>
<DialogDescription>API</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label>API密钥</Label>
<div className="flex items-center gap-2">
<Input value="sk-archer-xxxxxxxxxxxxx" readOnly className="font-mono" />
<Button variant="outline" size="icon">
<Copy className="h-4 w-4" />
</Button>
</div>
<p className="text-xs text-gray-500"> 2024-01-15</p>
</div>
<div className="p-3 rounded-lg bg-yellow-50 text-sm text-yellow-700">
API密钥
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setShowKeyDialog(false)}></Button>
<Button variant="destructive">
<RefreshCw className="h-4 w-4 mr-2" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
)
}