"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 { Label } from "@/components/ui/label" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Switch } from "@/components/ui/switch" import { Textarea } from "@/components/ui/textarea" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Server, Copy, CheckCircle2, Key, RefreshCw, ExternalLink, Code, Zap, Users, Search, Tags, Brain, Database, FileText, Package, Activity, Plus, Settings, ArrowDownToLine, ArrowUpFromLine, Shield, Clock, Eye, EyeOff, Trash2, Edit, BarChart3, AlertCircle, CheckCircle, XCircle, Play, } from "lucide-react" // 接入方类型 interface Partner { id: string name: string type: 'external' | 'internal' // 外部系统/内部系统 description: string apiKey: string apiSecret: string status: 'active' | 'inactive' | 'pending' permissions: { dataIngest: boolean // 数据流入权限(传入数据) dataQuery: boolean // 数据查询权限(获取标签) tagEnrich: boolean // 标签完善权限 batchProcess: boolean // 批量处理权限 } quotaConfig: { dailyLimit: number // 每日限额 monthlyLimit: number // 每月限额 rateLimit: number // 速率限制(次/秒) } statistics: { totalCalls: number todayCalls: number monthCalls: number lastCallTime: string } createdAt: string updatedAt: string } // 调用记录类型 interface CallLog { id: string partnerId: string partnerName: string endpoint: string method: string status: 'success' | 'error' | 'pending' responseTime: number requestSize: number responseSize: number timestamp: string errorMessage?: string } // 开放API端点定义 interface OpenAPIEndpoint { id: string method: 'GET' | 'POST' | 'PUT' | 'DELETE' path: string name: string description: string category: 'ingest' | 'query' | 'tag' | 'batch' params: { name: string; type: string; required: boolean; desc: string }[] response: string example: { request: string response: string } } // 预定义接入方 const DEFAULT_PARTNERS: Partner[] = [ { id: 'ckb_001', name: '存客宝', type: 'external', description: '私域流量管理系统,提供流量词上报和用户画像获取', apiKey: 'sk-ckb-xxxxxxxxxx', apiSecret: 'sec-ckb-xxxxxxxxxx', status: 'active', permissions: { dataIngest: true, dataQuery: true, tagEnrich: true, batchProcess: true, }, quotaConfig: { dailyLimit: 10000, monthlyLimit: 300000, rateLimit: 100, }, statistics: { totalCalls: 158420, todayCalls: 1520, monthCalls: 45800, lastCallTime: '2026-01-31 18:45:22', }, createdAt: '2025-06-15', updatedAt: '2026-01-31', }, { id: 'dlm_001', name: '点了码', type: 'external', description: '线下扫码营销系统,同步用户行为数据', apiKey: 'sk-dlm-xxxxxxxxxx', apiSecret: 'sec-dlm-xxxxxxxxxx', status: 'active', permissions: { dataIngest: true, dataQuery: true, tagEnrich: false, batchProcess: false, }, quotaConfig: { dailyLimit: 5000, monthlyLimit: 150000, rateLimit: 50, }, statistics: { totalCalls: 89200, todayCalls: 820, monthCalls: 28500, lastCallTime: '2026-01-31 18:30:15', }, createdAt: '2025-08-20', updatedAt: '2026-01-31', }, { id: 'feishu_001', name: '飞书机器人', type: 'internal', description: '企业内部飞书机器人,用于用户查询和报告推送', apiKey: 'sk-fs-xxxxxxxxxx', apiSecret: 'sec-fs-xxxxxxxxxx', status: 'active', permissions: { dataIngest: false, dataQuery: true, tagEnrich: false, batchProcess: false, }, quotaConfig: { dailyLimit: 1000, monthlyLimit: 30000, rateLimit: 10, }, statistics: { totalCalls: 12500, todayCalls: 156, monthCalls: 4200, lastCallTime: '2026-01-31 18:50:00', }, createdAt: '2025-10-10', updatedAt: '2026-01-31', }, ] // 开放API端点列表 const OPEN_API_ENDPOINTS: OpenAPIEndpoint[] = [ // 数据流入接口 { id: 'ingest_1', method: 'POST', path: '/api/open-api/ingest/user', name: '用户数据流入', description: '第三方系统传入用户数据,神射手自动完善标签', category: 'ingest', params: [ { name: 'phone', type: 'string', required: true, desc: '手机号(11位)' }, { name: 'name', type: 'string', required: false, desc: '用户姓名' }, { name: 'source', type: 'string', required: true, desc: '数据来源标识' }, { name: 'tags', type: 'array', required: false, desc: '原始标签数组' }, { name: 'extra', type: 'object', required: false, desc: '扩展字段' }, ], response: '{ "success": true, "data": { "userId": "xxx", "enriched": true, "tags": [...] } }', example: { request: `{ "phone": "13800138000", "name": "张三", "source": "cunkebao", "tags": ["高意向", "电商用户"], "extra": { "wechatId": "wxid_xxx" } }`, response: `{ "success": true, "data": { "userId": "usr_xxx", "enriched": true, "tags": ["高意向", "电商用户", "A级用户", "一线城市"], "rfm_score": 85, "user_level": "A" } }`, }, }, { id: 'ingest_2', method: 'POST', path: '/api/open-api/ingest/batch', name: '批量数据流入', description: '批量传入用户数据,支持最多1000条/次', category: 'ingest', params: [ { name: 'users', type: 'array', required: true, desc: '用户数组' }, { name: 'source', type: 'string', required: true, desc: '数据来源标识' }, { name: 'async', type: 'boolean', required: false, desc: '是否异步处理' }, ], response: '{ "success": true, "data": { "taskId": "xxx", "total": 1000, "processed": 1000 } }', example: { request: `{ "source": "cunkebao", "async": true, "users": [ { "phone": "13800138001", "name": "张三" }, { "phone": "13800138002", "name": "李四" } ] }`, response: `{ "success": true, "data": { "taskId": "task_xxx", "total": 2, "processed": 0, "status": "processing" } }`, }, }, // 数据查询接口 { id: 'query_1', method: 'GET', path: '/api/open-api/query/user', name: '用户画像查询', description: '根据手机号/QQ获取完整用户画像和标签', category: 'query', params: [ { name: 'phone', type: 'string', required: false, desc: '手机号' }, { name: 'qq', type: 'string', required: false, desc: 'QQ号' }, { name: 'fields', type: 'string', required: false, desc: '返回字段(逗号分隔)' }, ], response: '{ "success": true, "data": { "phone": "xxx", "tags": [...], "rfm": {...} } }', example: { request: `GET /api/open-api/query/user?phone=13800138000&fields=tags,rfm,level`, response: `{ "success": true, "data": { "phone": "138****8000", "name": "张*", "tags": ["高价值用户", "电商活跃", "一线城市"], "rfm": { "r": 5, "f": 4, "m": 5, "score": 93 }, "level": "S", "traffic_pool": "钻石池" } }`, }, }, { id: 'query_2', method: 'POST', path: '/api/open-api/query/batch', name: '批量用户查询', description: '批量查询用户画像,支持最多500个/次', category: 'query', params: [ { name: 'phones', type: 'array', required: false, desc: '手机号数组' }, { name: 'qqs', type: 'array', required: false, desc: 'QQ号数组' }, { name: 'fields', type: 'array', required: false, desc: '返回字段数组' }, ], response: '{ "success": true, "data": { "users": [...], "notFound": [...] } }', example: { request: `{ "phones": ["13800138001", "13800138002"], "fields": ["tags", "level"] }`, response: `{ "success": true, "data": { "users": [ { "phone": "138****8001", "tags": [...], "level": "A" }, { "phone": "138****8002", "tags": [...], "level": "B" } ], "notFound": [] } }`, }, }, // 标签服务接口 { id: 'tag_1', method: 'POST', path: '/api/open-api/tag/enrich', name: '标签完善', description: '对用户进行AI标签完善,返回新增标签', category: 'tag', params: [ { name: 'phone', type: 'string', required: true, desc: '手机号' }, { name: 'strategy', type: 'string', required: false, desc: '打标策略:rfm/behavior/preference' }, { name: 'force', type: 'boolean', required: false, desc: '是否强制刷新' }, ], response: '{ "success": true, "data": { "newTags": [...], "allTags": [...] } }', example: { request: `{ "phone": "13800138000", "strategy": "rfm", "force": false }`, response: `{ "success": true, "data": { "newTags": ["高价值用户", "活跃用户"], "allTags": ["高价值用户", "活跃用户", "电商用户", "一线城市"], "rfm_score": 89 } }`, }, }, { id: 'tag_2', method: 'GET', path: '/api/open-api/tag/list', name: '标签列表', description: '获取所有可用标签及其统计', category: 'tag', params: [ { name: 'category', type: 'string', required: false, desc: '标签分类' }, { name: 'source', type: 'string', required: false, desc: '来源筛选' }, ], response: '{ "success": true, "data": { "tags": [...], "total": 100 } }', example: { request: `GET /api/open-api/tag/list?category=value`, response: `{ "success": true, "data": { "tags": [ { "id": "tag_001", "name": "高价值用户", "count": 125000 }, { "id": "tag_002", "name": "优质用户", "count": 850000 } ], "total": 45 } }`, }, }, // 批量处理接口 { id: 'batch_1', method: 'POST', path: '/api/open-api/batch/task', name: '创建批量任务', description: '创建异步批量处理任务', category: 'batch', params: [ { name: 'type', type: 'string', required: true, desc: '任务类型:enrich/export/sync' }, { name: 'config', type: 'object', required: true, desc: '任务配置' }, { name: 'callback', type: 'string', required: false, desc: '完成回调URL' }, ], response: '{ "success": true, "data": { "taskId": "xxx", "status": "pending" } }', example: { request: `{ "type": "enrich", "config": { "source": "cunkebao", "strategy": "rfm", "dateRange": "2026-01" }, "callback": "https://your-domain.com/webhook" }`, response: `{ "success": true, "data": { "taskId": "task_xxx", "status": "pending", "estimatedTime": "10分钟" } }`, }, }, { id: 'batch_2', method: 'GET', path: '/api/open-api/batch/status', name: '查询任务状态', description: '查询批量任务执行状态', category: 'batch', params: [ { name: 'taskId', type: 'string', required: true, desc: '任务ID' }, ], response: '{ "success": true, "data": { "status": "completed", "progress": 100 } }', example: { request: `GET /api/open-api/batch/status?taskId=task_xxx`, response: `{ "success": true, "data": { "taskId": "task_xxx", "status": "completed", "progress": 100, "total": 10000, "processed": 10000, "failed": 5, "completedAt": "2026-01-31T18:45:00Z" } }`, }, }, ] // 调用日志数据 const DEFAULT_CALL_LOGS: CallLog[] = [ { id: 'log_1', partnerId: 'ckb_001', partnerName: '存客宝', endpoint: '/api/open-api/ingest/user', method: 'POST', status: 'success', responseTime: 45, requestSize: 256, responseSize: 512, timestamp: '2026-01-31 18:50:22' }, { id: 'log_2', partnerId: 'ckb_001', partnerName: '存客宝', endpoint: '/api/open-api/query/user', method: 'GET', status: 'success', responseTime: 32, requestSize: 64, responseSize: 1024, timestamp: '2026-01-31 18:49:55' }, { id: 'log_3', partnerId: 'dlm_001', partnerName: '点了码', endpoint: '/api/open-api/ingest/batch', method: 'POST', status: 'success', responseTime: 1250, requestSize: 45000, responseSize: 2048, timestamp: '2026-01-31 18:48:30' }, { id: 'log_4', partnerId: 'ckb_001', partnerName: '存客宝', endpoint: '/api/open-api/tag/enrich', method: 'POST', status: 'success', responseTime: 89, requestSize: 128, responseSize: 768, timestamp: '2026-01-31 18:47:10' }, { id: 'log_5', partnerId: 'feishu_001', partnerName: '飞书机器人', endpoint: '/api/open-api/query/user', method: 'GET', status: 'error', responseTime: 15, requestSize: 32, responseSize: 128, timestamp: '2026-01-31 18:46:00', errorMessage: '用户不存在' }, ] export default function OpenAPIPage() { const [partners, setPartners] = useState(DEFAULT_PARTNERS) const [callLogs, setCallLogs] = useState(DEFAULT_CALL_LOGS) const [activeTab, setActiveTab] = useState('guide') const [selectedCategory, setSelectedCategory] = useState('all') const [showAddDialog, setShowAddDialog] = useState(false) const [showKeyDialog, setShowKeyDialog] = useState(false) const [showDetailDialog, setShowDetailDialog] = useState(false) const [selectedPartner, setSelectedPartner] = useState(null) const [selectedEndpoint, setSelectedEndpoint] = useState(null) const [copiedId, setCopiedId] = useState(null) const [apiBaseUrl, setApiBaseUrl] = useState('') const [newPartner, setNewPartner] = useState({ name: '', type: 'external' as 'external' | 'internal', description: '', permissions: { dataIngest: true, dataQuery: true, tagEnrich: false, batchProcess: false, }, quotaConfig: { dailyLimit: 5000, monthlyLimit: 150000, rateLimit: 50, }, }) 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 getCategoryIcon = (category: string) => { switch (category) { case 'ingest': return case 'query': return case 'tag': return case 'batch': return default: return } } const getCategoryName = (category: string) => { switch (category) { case 'ingest': return '数据流入' case 'query': return '数据查询' case 'tag': return '标签服务' case 'batch': return '批量处理' default: return category } } const getStatusBadge = (status: string) => { switch (status) { case 'active': return 已启用 case 'inactive': return 已停用 case 'pending': return 待审核 case 'success': return 成功 case 'error': return 失败 default: return {status} } } const filteredEndpoints = selectedCategory === 'all' ? OPEN_API_ENDPOINTS : OPEN_API_ENDPOINTS.filter(e => e.category === selectedCategory) const handleAddPartner = () => { const newId = `partner_${Date.now()}` const apiKey = `sk-${newPartner.type.slice(0, 3)}-${Math.random().toString(36).slice(2, 12)}` const apiSecret = `sec-${newPartner.type.slice(0, 3)}-${Math.random().toString(36).slice(2, 12)}` const partner: Partner = { id: newId, name: newPartner.name, type: newPartner.type, description: newPartner.description, apiKey, apiSecret, status: 'pending', permissions: newPartner.permissions, quotaConfig: newPartner.quotaConfig, statistics: { totalCalls: 0, todayCalls: 0, monthCalls: 0, lastCallTime: '-', }, createdAt: new Date().toISOString().split('T')[0], updatedAt: new Date().toISOString().split('T')[0], } setPartners([...partners, partner]) setShowAddDialog(false) setNewPartner({ name: '', type: 'external', description: '', permissions: { dataIngest: true, dataQuery: true, tagEnrich: false, batchProcess: false, }, quotaConfig: { dailyLimit: 5000, monthlyLimit: 150000, rateLimit: 50, }, }) } const togglePartnerStatus = (partnerId: string) => { setPartners(partners.map(p => { if (p.id === partnerId) { return { ...p, status: p.status === 'active' ? 'inactive' : 'active' } } return p })) } const togglePermission = (partnerId: string, permission: keyof Partner['permissions']) => { setPartners(partners.map(p => { if (p.id === partnerId) { return { ...p, permissions: { ...p.permissions, [permission]: !p.permissions[permission], }, } } return p })) } // 计算统计数据 const totalCalls = partners.reduce((sum, p) => sum + p.statistics.todayCalls, 0) const activePartners = partners.filter(p => p.status === 'active').length const totalQuota = partners.reduce((sum, p) => sum + p.quotaConfig.dailyLimit, 0) return (
{/* 顶部标题 */}

开放API管理

第三方系统接入管理,数据流入流出控制

{/* 统计卡片 */}

接入方数量

{partners.length}

{activePartners} 个已启用

今日调用

{totalCalls.toLocaleString()}

总配额 {totalQuota.toLocaleString()}

数据流入

1,520

+12.5% 较昨日

标签完善

856

98.5% 成功率

{/* 快速开始指南 */}

快速开始

1

获取密钥

点击接入方的"密钥"按钮获取 API Key 和 Secret

2

配置权限

在"配置"中开启数据流入、查询、标签完善等权限

3

调用接口

在请求头中携带 Authorization: Bearer API_KEY 即可调用

# 示例:数据流入

curl -X POST "{apiBaseUrl}/api/open-api?endpoint=ingest/user" \

-H "Authorization: Bearer sk-xxx" \

-d '{`{"phone":"138xxx","source":"ckb"}`}'

{/* 主体内容 */} 使用说明 接入方管理 API端点 调用日志 {/* 使用说明 */}
{/* 接口概述 */} 接口概述

神射手开放API为第三方系统提供用户数据对接能力,支持存客宝、点了码等系统接入。

数据流入 传入用户数据,自动完善标签
数据查询 查询用户画像、标签、RFM等
标签完善 AI智能标签完善
批量处理 大批量异步任务处理
{/* 认证方式 */} 认证方式

所有API请求需在Header中携带API密钥:

Authorization: Bearer YOUR_API_KEY

# 可选:增强安全性

X-API-Secret: YOUR_API_SECRET

API Key 和 Secret 在接入方管理中获取

{/* 数据流入示例 */} 数据流入示例

# 存客宝传入用户数据

POST /api/open-api?endpoint=ingest/user

{`{`}

"phone": "13800138000",

"name": "张三",

"source": "cunkebao",

"tags": ["高意向", "电商用户"]

{`}`}

返回完善后的标签:高意向、电商用户、A级用户、一线城市...

{/* 数据查询示例 */} 数据查询示例

# 查询用户画像

GET /api/open-api?endpoint=query/user&phone=13800138000

# 响应

{`{`}

"phone": "138****8000",

"tags": ["高价值用户", "电商活跃"],

"level": "A",

"rfm": {`{ "r": 5, "f": 4, "m": 5, "score": 93 }`}

{`}`}

{/* 常用端点 */} 常用端点
POST

ingest/user

单用户数据流入

POST

ingest/batch

批量数据流入

GET

query/user

用户画像查询

POST

tag/enrich

AI标签完善

{/* 接入方管理 */}
{partners.map(partner => (
{partner.type === 'external' ? ( ) : ( )}

{partner.name}

{getStatusBadge(partner.status)} {partner.type === 'external' ? '外部系统' : '内部系统'}

{partner.description}

创建于 {partner.createdAt} 最后调用 {partner.statistics.lastCallTime}
togglePartnerStatus(partner.id)} />
{/* 权限和配额 */}
{/* 权限 */}

接口权限

togglePermission(partner.id, 'dataIngest')} > 数据流入 togglePermission(partner.id, 'dataQuery')} > 数据查询 togglePermission(partner.id, 'tagEnrich')} > 标签完善 togglePermission(partner.id, 'batchProcess')} > 批量处理
{/* 统计 */}

调用统计

今日 {partner.statistics.todayCalls.toLocaleString()} /{partner.quotaConfig.dailyLimit.toLocaleString()}
本月 {partner.statistics.monthCalls.toLocaleString()}
累计 {partner.statistics.totalCalls.toLocaleString()}
))}
{/* API端点 */} {/* 分类筛选 */}
{['ingest', 'query', 'tag', 'batch'].map(cat => { const count = OPEN_API_ENDPOINTS.filter(e => e.category === cat).length return ( ) })}
{/* 端点列表 */}
{filteredEndpoints.map(endpoint => (
{endpoint.method} {endpoint.path}
{getCategoryIcon(endpoint.category)} {getCategoryName(endpoint.category)}

{endpoint.name}

{endpoint.description}

{/* 参数 */} {endpoint.params && endpoint.params.length > 0 && (

参数

{endpoint.params.map((param, i) => (
{param.name} {param.type} {param.required && 必填} {param.desc}
))}
)} {/* 示例展开 */} {selectedEndpoint?.id === endpoint.id && (

请求示例

                            {endpoint.example.request}
                          

响应示例

                            {endpoint.example.response}
                          
)}
))}
{/* 调用日志 */}
最近调用记录
时间 接入方 端点 方法 状态 耗时 大小 {callLogs.map(log => ( {log.timestamp} {log.partnerName} {log.endpoint} {log.method} {getStatusBadge(log.status)} {log.responseTime}ms {(log.requestSize / 1024).toFixed(1)}KB / {(log.responseSize / 1024).toFixed(1)}KB ))}
{/* 添加接入方弹窗 */} 添加接入方 配置第三方系统接入神射手数据中台
setNewPartner({ ...newPartner, name: e.target.value })} />