889 lines
34 KiB
TypeScript
889 lines
34 KiB
TypeScript
"use client"
|
||
|
||
import { useState, useEffect } from "react"
|
||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } 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 {
|
||
Copy,
|
||
CheckCircle2,
|
||
Search,
|
||
Tags,
|
||
Brain,
|
||
Database,
|
||
FileText,
|
||
Package,
|
||
ArrowLeft,
|
||
Code,
|
||
Zap,
|
||
Key,
|
||
DollarSign,
|
||
ChevronDown,
|
||
ChevronRight,
|
||
Play,
|
||
Terminal,
|
||
BookOpen,
|
||
Server,
|
||
Download,
|
||
ExternalLink,
|
||
Link2,
|
||
Share2,
|
||
} from "lucide-react"
|
||
import Link from "next/link"
|
||
|
||
// API端点接口
|
||
interface APIEndpoint {
|
||
id: string
|
||
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
||
path: string
|
||
name: string
|
||
description: string
|
||
category: string
|
||
auth: boolean
|
||
price: number
|
||
params?: { name: string; type: string; required: boolean; desc: string }[]
|
||
response?: string
|
||
example?: string
|
||
}
|
||
|
||
// API分类
|
||
const API_CATEGORIES = [
|
||
{ id: 'query', name: '用户查询', icon: Search, description: '查询用户画像和基本信息' },
|
||
{ id: 'tag', name: '标签服务', icon: Tags, description: '获取和应用用户标签' },
|
||
{ id: 'ai', name: 'AI服务', icon: Brain, description: 'AI对话、分析和智能打标' },
|
||
{ id: 'data', name: '数据服务', icon: Database, description: '数据源管理和数据导入' },
|
||
{ id: 'report', name: '报告服务', icon: FileText, description: '生成数据分析报告' },
|
||
{ id: 'package', name: '流量包', icon: Package, description: '创建和导出流量包' },
|
||
]
|
||
|
||
// 预定义API端点
|
||
const API_ENDPOINTS: APIEndpoint[] = [
|
||
// 用户查询
|
||
{
|
||
id: 'api_1',
|
||
method: 'GET',
|
||
path: '/api/shensheshou/user',
|
||
name: '用户画像查询',
|
||
description: '根据手机号或QQ查询完整用户画像,返回字段根据API密钥权限决定',
|
||
category: 'query',
|
||
auth: true,
|
||
price: 1,
|
||
params: [
|
||
{ name: 'phone', type: 'string', required: false, desc: '11位手机号' },
|
||
{ name: 'qq', type: 'string', required: false, desc: 'QQ号码' },
|
||
{ name: 'fields', type: 'string', required: false, desc: '指定返回字段,逗号分隔' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": {
|
||
"phone": "138****8000",
|
||
"rfm_score": 85,
|
||
"user_level": "A",
|
||
"tags": ["高价值", "活跃用户"],
|
||
"last_active": "2026-01-30"
|
||
},
|
||
"credits_used": 5,
|
||
"credits_remaining": 995
|
||
}`,
|
||
example: `curl -X GET "https://api.shensheshou.com/api/shensheshou?endpoint=user&phone=13800138000&fields=rfm_score,tags" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx"`,
|
||
},
|
||
{
|
||
id: 'api_2',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/users/batch',
|
||
name: '批量用户查询',
|
||
description: '批量查询多个用户的画像信息,最多支持100个用户/次,享受批量折扣',
|
||
category: 'query',
|
||
auth: true,
|
||
price: 0.8,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: users/batch' },
|
||
{ name: 'phones', type: 'array', required: false, desc: '手机号数组,最多100个' },
|
||
{ name: 'qqs', type: 'array', required: false, desc: 'QQ号数组,最多100个' },
|
||
{ name: 'fields', type: 'array', required: false, desc: '指定返回字段数组' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": [
|
||
{ "phone": "138****8000", "rfm_score": 85, "user_level": "A" },
|
||
{ "phone": "139****9000", "rfm_score": 72, "user_level": "B" }
|
||
],
|
||
"total": 2,
|
||
"credits_used": 8,
|
||
"credits_remaining": 992
|
||
}`,
|
||
example: `curl -X POST "https://api.shensheshou.com/api/shensheshou" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx" \\
|
||
-H "Content-Type: application/json" \\
|
||
-d '{
|
||
"endpoint": "users/batch",
|
||
"phones": ["13800138000", "13900139000"],
|
||
"fields": ["rfm_score", "user_level", "tags"]
|
||
}'`,
|
||
},
|
||
// 标签服务
|
||
{
|
||
id: 'api_3',
|
||
method: 'GET',
|
||
path: '/api/shensheshou/tags',
|
||
name: '标签列表',
|
||
description: '获取系统中所有可用标签及其分类和使用统计',
|
||
category: 'tag',
|
||
auth: true,
|
||
price: 0.5,
|
||
params: [
|
||
{ name: 'category', type: 'string', required: false, desc: '标签分类筛选' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": [
|
||
{ "id": "tag_1", "name": "高价值", "category": "价值标签", "count": 12500 },
|
||
{ "id": "tag_2", "name": "活跃用户", "category": "行为标签", "count": 35800 }
|
||
],
|
||
"total": 45,
|
||
"credits_used": 0.5
|
||
}`,
|
||
example: `curl -X GET "https://api.shensheshou.com/api/shensheshou?endpoint=tags&category=价值标签" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx"`,
|
||
},
|
||
{
|
||
id: 'api_4',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/tags/apply',
|
||
name: '应用标签',
|
||
description: '为指定用户批量应用标签',
|
||
category: 'tag',
|
||
auth: true,
|
||
price: 2,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: tags/apply' },
|
||
{ name: 'user_id', type: 'string', required: true, desc: '用户ID或手机号' },
|
||
{ name: 'tags', type: 'array', required: true, desc: '要应用的标签ID数组' },
|
||
{ name: 'overwrite', type: 'boolean', required: false, desc: '是否覆盖现有标签,默认false' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": {
|
||
"user_id": "13800138000",
|
||
"applied_tags": ["tag_1", "tag_2"],
|
||
"timestamp": "2026-01-31T14:30:00Z"
|
||
},
|
||
"credits_used": 2
|
||
}`,
|
||
example: `curl -X POST "https://api.shensheshou.com/api/shensheshou" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx" \\
|
||
-H "Content-Type: application/json" \\
|
||
-d '{
|
||
"endpoint": "tags/apply",
|
||
"user_id": "13800138000",
|
||
"tags": ["tag_1", "tag_2"],
|
||
"overwrite": false
|
||
}'`,
|
||
},
|
||
// AI服务
|
||
{
|
||
id: 'api_5',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/ai/chat',
|
||
name: 'AI智能对话',
|
||
description: '与神射手AI进行对话,支持自然语言查询和数据分析',
|
||
category: 'ai',
|
||
auth: true,
|
||
price: 5,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: ai/chat' },
|
||
{ name: 'message', type: 'string', required: true, desc: '对话内容' },
|
||
{ name: 'context', type: 'object', required: false, desc: '上下文信息' },
|
||
{ name: 'model', type: 'string', required: false, desc: 'AI模型: qwen/deepseek' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"response": {
|
||
"content": "根据查询,共有 1,234 位高价值用户...",
|
||
"data": { "count": 1234, "avg_rfm": 82 },
|
||
"suggestions": ["可以进一步筛选活跃度", "建议导出为流量包"]
|
||
},
|
||
"credits_used": 5
|
||
}`,
|
||
example: `curl -X POST "https://api.shensheshou.com/api/shensheshou" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx" \\
|
||
-H "Content-Type: application/json" \\
|
||
-d '{
|
||
"endpoint": "ai/chat",
|
||
"message": "帮我分析RFM评分大于80的用户群体",
|
||
"model": "qwen"
|
||
}'`,
|
||
},
|
||
{
|
||
id: 'api_6',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/ai/analyze',
|
||
name: 'AI数据分析',
|
||
description: 'AI自动分析用户群体特征并生成洞察报告',
|
||
category: 'ai',
|
||
auth: true,
|
||
price: 10,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: ai/analyze' },
|
||
{ name: 'type', type: 'string', required: true, desc: '分析类型: rfm/behavior/preference/churn' },
|
||
{ name: 'filters', type: 'object', required: false, desc: '用户筛选条件' },
|
||
{ name: 'depth', type: 'string', required: false, desc: '分析深度: quick/standard/deep' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": {
|
||
"type": "rfm",
|
||
"summary": "RFM分析完成",
|
||
"insights": [
|
||
{ "metric": "RFM平均分", "value": 72, "trend": "+5%" },
|
||
{ "metric": "高价值用户", "value": 12500, "trend": "+8%" }
|
||
],
|
||
"recommendations": [
|
||
"建议对RFM分数65-75区间的用户进行激活营销"
|
||
]
|
||
},
|
||
"credits_used": 10
|
||
}`,
|
||
example: `curl -X POST "https://api.shensheshou.com/api/shensheshou" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx" \\
|
||
-H "Content-Type: application/json" \\
|
||
-d '{
|
||
"endpoint": "ai/analyze",
|
||
"type": "rfm",
|
||
"filters": { "user_level": ["A", "B"] },
|
||
"depth": "standard"
|
||
}'`,
|
||
},
|
||
{
|
||
id: 'api_7',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/ai/tag',
|
||
name: 'AI智能打标',
|
||
description: 'AI自动分析用户数据并智能打标签',
|
||
category: 'ai',
|
||
auth: true,
|
||
price: 8,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: ai/tag' },
|
||
{ name: 'user_ids', type: 'array', required: true, desc: '用户ID数组' },
|
||
{ name: 'tag_types', type: 'array', required: false, desc: '指定标签类型' },
|
||
{ name: 'model', type: 'string', required: false, desc: 'AI模型选择' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": {
|
||
"processed": 50,
|
||
"tagsApplied": 150,
|
||
"summary": "已为50个用户完成AI智能打标"
|
||
},
|
||
"credits_used": 25
|
||
}`,
|
||
},
|
||
// 数据服务
|
||
{
|
||
id: 'api_8',
|
||
method: 'GET',
|
||
path: '/api/shensheshou/sources',
|
||
name: '数据源列表',
|
||
description: '获取所有已接入的数据源及其连接状态和数据量',
|
||
category: 'data',
|
||
auth: true,
|
||
price: 0.5,
|
||
response: `{
|
||
"success": true,
|
||
"data": [
|
||
{ "id": "KR_腾讯", "name": "腾讯数据", "status": "connected", "records": 700000000 },
|
||
{ "id": "KR_微博", "name": "微博数据", "status": "connected", "records": 140000000 }
|
||
],
|
||
"credits_used": 0.5
|
||
}`,
|
||
example: `curl -X GET "https://api.shensheshou.com/api/shensheshou?endpoint=sources" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx"`,
|
||
},
|
||
{
|
||
id: 'api_9',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/ingest',
|
||
name: '数据导入',
|
||
description: '导入外部数据到神射手平台,自动触发AI标签引擎处理',
|
||
category: 'data',
|
||
auth: true,
|
||
price: 3,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: ingest' },
|
||
{ name: 'source', type: 'string', required: true, desc: '数据源标识,如: cunkebao' },
|
||
{ name: 'data', type: 'array', required: true, desc: '用户数据数组' },
|
||
{ name: 'auto_tag', type: 'boolean', required: false, desc: '是否自动打标,默认true' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": {
|
||
"source": "cunkebao",
|
||
"imported": 100,
|
||
"auto_tagged": 100,
|
||
"task_id": "task_1706698200000"
|
||
},
|
||
"credits_used": 40
|
||
}`,
|
||
example: `curl -X POST "https://api.shensheshou.com/api/shensheshou" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx" \\
|
||
-H "Content-Type: application/json" \\
|
||
-d '{
|
||
"endpoint": "ingest",
|
||
"source": "cunkebao",
|
||
"data": [
|
||
{ "phone": "13800138000", "name": "张三", "source": "微信" },
|
||
{ "phone": "13900139000", "name": "李四", "source": "抖音" }
|
||
],
|
||
"auto_tag": true
|
||
}'`,
|
||
},
|
||
// 报告服务
|
||
{
|
||
id: 'api_10',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/report/generate',
|
||
name: '生成分析报告',
|
||
description: 'AI自动生成数据分析报告,支持多种模板和格式',
|
||
category: 'report',
|
||
auth: true,
|
||
price: 15,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: report/generate' },
|
||
{ name: 'template', type: 'string', required: true, desc: '报告模板: user_insight/rfm_analysis/trend_report' },
|
||
{ name: 'date_range', type: 'object', required: false, desc: '日期范围 {start, end}' },
|
||
{ name: 'format', type: 'string', required: false, desc: '输出格式: pdf/html/json' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": {
|
||
"template": "rfm_analysis",
|
||
"report_id": "report_1706698200000",
|
||
"status": "generating",
|
||
"estimatedTime": "5分钟",
|
||
"downloadUrl": null
|
||
},
|
||
"credits_used": 15
|
||
}`,
|
||
},
|
||
// 流量包
|
||
{
|
||
id: 'api_11',
|
||
method: 'GET',
|
||
path: '/api/shensheshou/packages',
|
||
name: '流量包列表',
|
||
description: '获取所有已创建的流量包',
|
||
category: 'package',
|
||
auth: true,
|
||
price: 0.5,
|
||
response: `{
|
||
"success": true,
|
||
"data": [
|
||
{ "id": "pkg_1", "name": "高价值用户包", "count": 12500, "createdAt": "2026-01-28" },
|
||
{ "id": "pkg_2", "name": "活跃用户包", "count": 35800, "createdAt": "2026-01-25" }
|
||
],
|
||
"credits_used": 0.5
|
||
}`,
|
||
example: `curl -X GET "https://api.shensheshou.com/api/shensheshou?endpoint=packages" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx"`,
|
||
},
|
||
{
|
||
id: 'api_12',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/packages/create',
|
||
name: '创建流量包',
|
||
description: '根据筛选条件创建用户流量包',
|
||
category: 'package',
|
||
auth: true,
|
||
price: 5,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: packages/create' },
|
||
{ name: 'name', type: 'string', required: true, desc: '流量包名称' },
|
||
{ name: 'criteria', type: 'object', required: true, desc: '筛选条件' },
|
||
{ name: 'export_fields', type: 'array', required: false, desc: '导出字段' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": {
|
||
"id": "pkg_1706698200000",
|
||
"name": "高价值活跃用户",
|
||
"criteria": { "rfm_score": { "$gte": 80 } },
|
||
"count": 8500,
|
||
"status": "created",
|
||
"createdAt": "2026-01-31T14:30:00Z"
|
||
},
|
||
"credits_used": 5
|
||
}`,
|
||
example: `curl -X POST "https://api.shensheshou.com/api/shensheshou" \\
|
||
-H "Authorization: Bearer sk-archer-xxxxx" \\
|
||
-H "X-API-Secret: sec-xxxxx" \\
|
||
-H "Content-Type: application/json" \\
|
||
-d '{
|
||
"endpoint": "packages/create",
|
||
"name": "高价值活跃用户",
|
||
"criteria": {
|
||
"rfm_score": { "$gte": 80 },
|
||
"user_level": ["A", "B"]
|
||
},
|
||
"export_fields": ["phone", "rfm_score", "tags"]
|
||
}'`,
|
||
},
|
||
{
|
||
id: 'api_13',
|
||
method: 'POST',
|
||
path: '/api/shensheshou/packages/export',
|
||
name: '导出流量包',
|
||
description: '导出流量包数据到指定目标(邮箱、飞书、Webhook)',
|
||
category: 'package',
|
||
auth: true,
|
||
price: 10,
|
||
params: [
|
||
{ name: 'endpoint', type: 'string', required: true, desc: '固定值: packages/export' },
|
||
{ name: 'package_id', type: 'string', required: true, desc: '流量包ID' },
|
||
{ name: 'target', type: 'string', required: true, desc: '导出目标: email/feishu/webhook' },
|
||
{ name: 'config', type: 'object', required: false, desc: '导出配置(邮箱地址、Webhook URL等)' },
|
||
],
|
||
response: `{
|
||
"success": true,
|
||
"data": {
|
||
"package_id": "pkg_1706698200000",
|
||
"target": "email",
|
||
"status": "exporting",
|
||
"task_id": "export_1706698200000",
|
||
"estimatedTime": "2分钟"
|
||
},
|
||
"credits_used": 10
|
||
}`,
|
||
},
|
||
]
|
||
|
||
export default function APIDocsPage() {
|
||
const [activeCategory, setActiveCategory] = useState('all')
|
||
const [apiBaseUrl, setApiBaseUrl] = useState('')
|
||
const [copiedId, setCopiedId] = useState<string | null>(null)
|
||
const [searchQuery, setSearchQuery] = useState('')
|
||
const [expandedEndpoints, setExpandedEndpoints] = useState<Set<string>>(new Set())
|
||
|
||
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 border-green-200'
|
||
case 'POST': return 'bg-blue-100 text-blue-700 border-blue-200'
|
||
case 'PUT': return 'bg-yellow-100 text-yellow-700 border-yellow-200'
|
||
case 'DELETE': return 'bg-red-100 text-red-700 border-red-200'
|
||
default: return 'bg-gray-100 text-gray-700 border-gray-200'
|
||
}
|
||
}
|
||
|
||
const toggleEndpoint = (id: string) => {
|
||
const newSet = new Set(expandedEndpoints)
|
||
if (newSet.has(id)) {
|
||
newSet.delete(id)
|
||
} else {
|
||
newSet.add(id)
|
||
}
|
||
setExpandedEndpoints(newSet)
|
||
}
|
||
|
||
const filteredEndpoints = API_ENDPOINTS.filter(e => {
|
||
const matchCategory = activeCategory === 'all' || e.category === activeCategory
|
||
const matchSearch = !searchQuery ||
|
||
e.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||
e.path.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||
e.description.toLowerCase().includes(searchQuery.toLowerCase())
|
||
return matchCategory && matchSearch
|
||
})
|
||
|
||
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 className="flex items-center gap-4">
|
||
<Link href="/data-market/api">
|
||
<Button variant="ghost" size="sm">
|
||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||
返回
|
||
</Button>
|
||
</Link>
|
||
<div>
|
||
<h1 className="text-2xl font-bold text-gray-900">API文档</h1>
|
||
<p className="text-sm text-gray-500 mt-1">神射手开放API完整文档 · 支持AI直接对接</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2">
|
||
<Badge>v1.0</Badge>
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={() => copyToClipboard(`${apiBaseUrl}/api/docs?format=openapi`, 'api-link')}
|
||
>
|
||
{copiedId === 'api-link' ? (
|
||
<CheckCircle2 className="h-4 w-4 mr-2 text-green-500" />
|
||
) : (
|
||
<Link2 className="h-4 w-4 mr-2" />
|
||
)}
|
||
复制API链接
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={() => window.open(`${apiBaseUrl}/api/docs?format=download`, '_blank')}
|
||
>
|
||
<Download className="h-4 w-4 mr-2" />
|
||
下载OpenAPI
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
onClick={() => window.open(`${apiBaseUrl}/api/docs?format=markdown`, '_blank')}
|
||
>
|
||
<FileText className="h-4 w-4 mr-2" />
|
||
下载Markdown
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* AI对接提示卡片 */}
|
||
<Card className="border-0 shadow-sm bg-gradient-to-r from-cyan-50 to-blue-50 border-l-4 border-l-cyan-500">
|
||
<CardContent className="p-4">
|
||
<div className="flex items-start justify-between">
|
||
<div className="flex items-start gap-3">
|
||
<div className="p-2 rounded-lg bg-cyan-100">
|
||
<Brain className="h-5 w-5 text-cyan-600" />
|
||
</div>
|
||
<div>
|
||
<h3 className="font-semibold text-gray-900 mb-1">AI直接对接</h3>
|
||
<p className="text-sm text-gray-600 mb-2">将以下链接提供给AI(如ChatGPT、Claude),即可直接理解并调用神射手API</p>
|
||
<div className="flex items-center gap-2 p-2 rounded-lg bg-white/80 font-mono text-sm">
|
||
<code className="text-cyan-700 flex-1 truncate">{apiBaseUrl}/api/docs?format=openapi</code>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
className="h-7 shrink-0"
|
||
onClick={() => copyToClipboard(`${apiBaseUrl}/api/docs?format=openapi`, 'openapi-link')}
|
||
>
|
||
{copiedId === 'openapi-link' ? (
|
||
<CheckCircle2 className="h-4 w-4 text-green-500" />
|
||
) : (
|
||
<Copy className="h-4 w-4" />
|
||
)}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-col gap-2 text-xs">
|
||
<div className="flex items-center gap-2 p-2 rounded-lg bg-white/80">
|
||
<Badge variant="outline" className="text-xs">OpenAPI 3.0</Badge>
|
||
<span className="text-gray-500">标准格式</span>
|
||
</div>
|
||
<div className="flex items-center gap-2 p-2 rounded-lg bg-white/80">
|
||
<Badge variant="outline" className="text-xs">Markdown</Badge>
|
||
<span className="text-gray-500">可读文档</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* 快速入门 */}
|
||
<Card className="border-0 shadow-sm bg-gradient-to-r from-purple-50 to-blue-50">
|
||
<CardContent className="p-6">
|
||
<div className="flex items-start gap-4">
|
||
<div className="p-3 rounded-lg bg-purple-100">
|
||
<BookOpen className="h-6 w-6 text-purple-600" />
|
||
</div>
|
||
<div className="flex-1">
|
||
<h3 className="text-lg font-semibold text-gray-900 mb-2">快速入门</h3>
|
||
<div className="grid grid-cols-3 gap-4 mb-4">
|
||
<div className="p-3 rounded-lg bg-white/80">
|
||
<div className="flex items-center gap-2 mb-1">
|
||
<span className="w-5 h-5 rounded-full bg-purple-500 text-white text-xs flex items-center justify-center">1</span>
|
||
<span className="font-medium text-sm">获取密钥</span>
|
||
</div>
|
||
<p className="text-xs text-gray-500">在密钥管理页面创建API密钥</p>
|
||
</div>
|
||
<div className="p-3 rounded-lg bg-white/80">
|
||
<div className="flex items-center gap-2 mb-1">
|
||
<span className="w-5 h-5 rounded-full bg-purple-500 text-white text-xs flex items-center justify-center">2</span>
|
||
<span className="font-medium text-sm">配置权限</span>
|
||
</div>
|
||
<p className="text-xs text-gray-500">设置可访问的数据字段</p>
|
||
</div>
|
||
<div className="p-3 rounded-lg bg-white/80">
|
||
<div className="flex items-center gap-2 mb-1">
|
||
<span className="w-5 h-5 rounded-full bg-purple-500 text-white text-xs flex items-center justify-center">3</span>
|
||
<span className="font-medium text-sm">开始调用</span>
|
||
</div>
|
||
<p className="text-xs text-gray-500">使用密钥调用API接口</p>
|
||
</div>
|
||
</div>
|
||
<div className="p-4 rounded-lg bg-gray-900">
|
||
<p className="text-xs text-gray-400 mb-2">基础请求示例</p>
|
||
<pre className="text-green-400 text-sm overflow-x-auto">
|
||
{`curl -X GET "${apiBaseUrl}/api/shensheshou?endpoint=user&phone=13800138000" \\
|
||
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||
-H "X-API-Secret: YOUR_API_SECRET"`}
|
||
</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* 搜索和分类 */}
|
||
<div className="flex items-center gap-4">
|
||
<div className="relative flex-1 max-w-md">
|
||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" />
|
||
<Input
|
||
placeholder="搜索API..."
|
||
className="pl-10"
|
||
value={searchQuery}
|
||
onChange={e => setSearchQuery(e.target.value)}
|
||
/>
|
||
</div>
|
||
<div className="flex items-center gap-2 overflow-x-auto">
|
||
<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>
|
||
</div>
|
||
|
||
{/* API端点列表 */}
|
||
<div className="space-y-4">
|
||
{filteredEndpoints.map(endpoint => (
|
||
<Card key={endpoint.id} className="border-0 shadow-sm bg-white/80 overflow-hidden">
|
||
<div
|
||
className="p-4 cursor-pointer hover:bg-gray-50 transition-colors"
|
||
onClick={() => toggleEndpoint(endpoint.id)}
|
||
>
|
||
<div className="flex items-center justify-between">
|
||
<div className="flex items-center gap-3">
|
||
<Badge className={`${getMethodColor(endpoint.method)} font-mono border`}>
|
||
{endpoint.method}
|
||
</Badge>
|
||
<code className="text-sm font-mono text-gray-700">{endpoint.path}</code>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
className="h-6 w-6 p-0"
|
||
onClick={(e) => {
|
||
e.stopPropagation()
|
||
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>
|
||
<div className="flex items-center gap-3">
|
||
<Badge variant="outline" className="text-xs">
|
||
<DollarSign className="h-3 w-3 mr-1" />
|
||
{endpoint.price} 信用点
|
||
</Badge>
|
||
{endpoint.auth && (
|
||
<Badge variant="outline" className="text-xs">
|
||
<Key className="h-3 w-3 mr-1" />
|
||
需认证
|
||
</Badge>
|
||
)}
|
||
{expandedEndpoints.has(endpoint.id) ? (
|
||
<ChevronDown className="h-4 w-4 text-gray-400" />
|
||
) : (
|
||
<ChevronRight className="h-4 w-4 text-gray-400" />
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="mt-2">
|
||
<h3 className="font-semibold text-gray-900">{endpoint.name}</h3>
|
||
<p className="text-sm text-gray-500">{endpoint.description}</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 展开详情 */}
|
||
{expandedEndpoints.has(endpoint.id) && (
|
||
<div className="border-t p-4 bg-gray-50 space-y-4">
|
||
{/* 参数 */}
|
||
{endpoint.params && endpoint.params.length > 0 && (
|
||
<div>
|
||
<h4 className="text-sm font-medium text-gray-700 mb-2">请求参数</h4>
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full text-sm">
|
||
<thead>
|
||
<tr className="text-left text-gray-500 border-b">
|
||
<th className="pb-2 pr-4">参数名</th>
|
||
<th className="pb-2 pr-4">类型</th>
|
||
<th className="pb-2 pr-4">必填</th>
|
||
<th className="pb-2">说明</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{endpoint.params.map((param, i) => (
|
||
<tr key={i} className="border-b last:border-0">
|
||
<td className="py-2 pr-4">
|
||
<code className="bg-gray-100 px-2 py-0.5 rounded text-purple-600">{param.name}</code>
|
||
</td>
|
||
<td className="py-2 pr-4">
|
||
<Badge variant="outline" className="text-xs">{param.type}</Badge>
|
||
</td>
|
||
<td className="py-2 pr-4">
|
||
{param.required ? (
|
||
<Badge className="bg-red-100 text-red-700 text-xs">必填</Badge>
|
||
) : (
|
||
<span className="text-gray-400">可选</span>
|
||
)}
|
||
</td>
|
||
<td className="py-2 text-gray-600">{param.desc}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 调用示例 */}
|
||
{endpoint.example && (
|
||
<div>
|
||
<div className="flex items-center justify-between mb-2">
|
||
<h4 className="text-sm font-medium text-gray-700">调用示例</h4>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={() => copyToClipboard(endpoint.example!, endpoint.id + '_example')}
|
||
>
|
||
{copiedId === endpoint.id + '_example' ? (
|
||
<CheckCircle2 className="h-4 w-4 text-green-500" />
|
||
) : (
|
||
<Copy className="h-4 w-4" />
|
||
)}
|
||
<span className="ml-1">复制</span>
|
||
</Button>
|
||
</div>
|
||
<pre className="bg-gray-900 text-green-400 p-4 rounded-lg text-sm overflow-x-auto">
|
||
{endpoint.example}
|
||
</pre>
|
||
</div>
|
||
)}
|
||
|
||
{/* 响应示例 */}
|
||
{endpoint.response && (
|
||
<div>
|
||
<div className="flex items-center justify-between mb-2">
|
||
<h4 className="text-sm font-medium text-gray-700">响应示例</h4>
|
||
<Badge className="bg-green-100 text-green-700">200 OK</Badge>
|
||
</div>
|
||
<pre className="bg-gray-100 p-4 rounded-lg text-sm font-mono text-gray-700 overflow-x-auto">
|
||
{endpoint.response}
|
||
</pre>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</Card>
|
||
))}
|
||
</div>
|
||
|
||
{/* 错误码说明 */}
|
||
<Card className="border-0 shadow-sm bg-white/80">
|
||
<CardHeader>
|
||
<CardTitle className="text-lg">错误码说明</CardTitle>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<table className="w-full text-sm">
|
||
<thead>
|
||
<tr className="text-left text-gray-500 border-b">
|
||
<th className="pb-2 pr-4">HTTP状态码</th>
|
||
<th className="pb-2 pr-4">错误类型</th>
|
||
<th className="pb-2">说明</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr className="border-b">
|
||
<td className="py-2 pr-4"><Badge className="bg-green-100 text-green-700">200</Badge></td>
|
||
<td className="py-2 pr-4">成功</td>
|
||
<td className="py-2 text-gray-600">请求成功处理</td>
|
||
</tr>
|
||
<tr className="border-b">
|
||
<td className="py-2 pr-4"><Badge className="bg-yellow-100 text-yellow-700">400</Badge></td>
|
||
<td className="py-2 pr-4">请求错误</td>
|
||
<td className="py-2 text-gray-600">参数缺失或格式错误</td>
|
||
</tr>
|
||
<tr className="border-b">
|
||
<td className="py-2 pr-4"><Badge className="bg-red-100 text-red-700">401</Badge></td>
|
||
<td className="py-2 pr-4">认证失败</td>
|
||
<td className="py-2 text-gray-600">API密钥无效或缺失</td>
|
||
</tr>
|
||
<tr className="border-b">
|
||
<td className="py-2 pr-4"><Badge className="bg-orange-100 text-orange-700">403</Badge></td>
|
||
<td className="py-2 pr-4">权限不足</td>
|
||
<td className="py-2 text-gray-600">无权访问该资源或字段</td>
|
||
</tr>
|
||
<tr className="border-b">
|
||
<td className="py-2 pr-4"><Badge className="bg-gray-100 text-gray-700">404</Badge></td>
|
||
<td className="py-2 pr-4">未找到</td>
|
||
<td className="py-2 text-gray-600">请求的资源不存在</td>
|
||
</tr>
|
||
<tr className="border-b">
|
||
<td className="py-2 pr-4"><Badge className="bg-purple-100 text-purple-700">429</Badge></td>
|
||
<td className="py-2 pr-4">限流</td>
|
||
<td className="py-2 text-gray-600">超出调用频率限制或信用点不足</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="py-2 pr-4"><Badge className="bg-red-100 text-red-700">500</Badge></td>
|
||
<td className="py-2 pr-4">服务器错误</td>
|
||
<td className="py-2 text-gray-600">服务器内部错误,请稍后重试</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|