Files
cunkebao_v3/Cunkebao/app/api/docs/scenarios/page.tsx

406 lines
16 KiB
TypeScript
Raw Normal View History

2025-06-19 15:14:41 +08:00
"use client"
import { useState } from "react"
import { ChevronLeft, Copy, Check, Info } from "lucide-react"
import { Button } from "@/components/ui/button"
import { useRouter } from "next/navigation"
import { useToast } from "@/components/ui/use-toast"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { getApiGuideForScenario } from "@/docs/api-guide"
import { Badge } from "@/components/ui/badge"
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'https://yishi.com'
export default function ApiDocPage({ params }: { params: { channel: string; id: string } }) {
const router = useRouter()
const { toast } = useToast()
const [copiedExample, setCopiedExample] = useState<string | null>(null)
const apiGuide = getApiGuideForScenario(params.id, params.channel)
// 假设 fullUrl 和 apiKey 可通过 props 或接口获取,这里用演示值
const [apiKey] = useState("naxf1-82h2f-vdwcm-rrhpm-q9hd1")
const [fullUrl] = useState("/v1/api/scenarios")
const testUrl = fullUrl.startsWith("http") ? fullUrl : `${API_BASE_URL}${fullUrl}`
const copyToClipboard = (text: string, exampleId: string) => {
navigator.clipboard.writeText(text)
setCopiedExample(exampleId)
toast({
title: "已复制代码",
description: "代码示例已复制到剪贴板",
})
setTimeout(() => {
setCopiedExample(null)
}, 2000)
}
return (
<div className="min-h-screen bg-gray-50">
<header className="sticky top-0 z-10 bg-white border-b">
<div className="flex items-center justify-between h-14 px-4">
<div className="flex items-center">
<Button variant="ghost" size="icon" onClick={() => router.back()}>
<ChevronLeft className="h-5 w-5" />
</Button>
<h1 className="ml-2 text-lg font-medium"></h1>
</div>
</div>
</header>
<div className="container mx-auto py-6 px-4 max-w-4xl">
<Card className="mb-6">
<CardHeader>
<CardTitle></CardTitle>
<CardDescription>使 <b>apiKey</b> </CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center gap-2">
<Info className="h-4 w-4 text-blue-500" />
<p className="text-sm text-gray-700">
</p>
</div>
<div className="rounded-md bg-amber-50 p-4 border border-amber-200">
<p className="text-sm text-amber-800">
<strong></strong> API密钥使
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="mb-6">
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2 text-sm text-gray-700">
<div>
<b></b> <span className="font-mono">sign</span>
</div>
<div>
<b></b> sign apiKey<b></b>& <span className="font-mono">MD5</span> apiKey <span className="font-mono">MD5</span> sign
</div>
<div>
<b></b>
<ol className="list-decimal list-inside ml-4">
<li> sign apiKey namephonetimestampsourceremarktags</li>
<li><b></b> value1value2value3...</li>
<li> MD5 </li>
<li> apiKey </li>
<li> MD5 sign</li>
</ol>
</div>
<div>
<b></b>
<pre className="bg-gray-50 rounded p-2 text-xs overflow-auto">
{`参数:
name=
phone=18888888888
timestamp=1700000000
apiKey=naxf1-82h2f-vdwcm-rrhpm-q9hd1
apiKey
188888888881700000000
MD5
md5(188888888881700000000) = 123456abcdef...
apiKey
123456abcdef...naxf1-82h2f-vdwcm-rrhpm-q9hd1
MD5
sign=md5(123456abcdef...naxf1-82h2f-vdwcm-rrhpm-q9hd1)`}
</pre>
</div>
<div className="text-xs text-amber-700 mt-2"> sign apiKey URL </div>
</div>
</CardContent>
</Card>
<Card className="mb-6">
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="mb-2">
<span className="font-mono text-sm">{testUrl}</span>
</div>
<div className="text-xs text-gray-500">
<div>: <b>phone</b> (), <b>timestamp</b> (), <b>apiKey</b> ()</div>
<div>: <b>name</b> (), <b>source</b> (), <b>remark</b> (), <b>tags</b> ()</div>
<div>: <b>POST</b> <b>GET</b></div>
</div>
</CardContent>
</Card>
<Card className="mb-6">
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gray-50 rounded-md p-3 text-xs">
<div><b>phone</b> (string, ): </div>
<div><b>timestamp</b> (int, ): 1700000000 Math.floor(Date.now() / 1000) </div>
<div><b>apiKey</b> (string, ): </div>
<div><b>name</b> (string, ): </div>
<div><b>source</b> (string, ): </div>
<div><b>remark</b> (string, ): </div>
<div><b>tags</b> (string, ): </div>
</div>
</CardContent>
</Card>
<Card className="mb-6">
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<pre className="bg-gray-50 rounded-md p-3 text-xs overflow-auto">
{`{
"code": 200,
"msg": "导入成功",
"data": {
"customerId": "123456"
}
}`}
</pre>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<Tabs defaultValue="curl">
<TabsList className="mb-4">
<TabsTrigger value="curl">cURL</TabsTrigger>
<TabsTrigger value="python">Python</TabsTrigger>
<TabsTrigger value="node">Node.js</TabsTrigger>
<TabsTrigger value="php">PHP</TabsTrigger>
<TabsTrigger value="java">Java</TabsTrigger>
</TabsList>
<TabsContent value="curl">
<pre className="bg-gray-50 p-4 rounded-md overflow-auto text-xs">{`curl -X POST 'http://yishi.com/v1/plan/api/scenariosz' \
-d "phone=18888888888" \
-d "timestamp=1700000000" \
-d "name=张三" \
-d "apiKey=naxf1-82h2f-vdwcm-rrhpm-q9hd1" \
-d "sign=请用签名算法生成"`}</pre>
</TabsContent>
<TabsContent value="python">
<pre className="bg-gray-50 p-4 rounded-md overflow-auto text-xs">{`import hashlib
import time
import requests
def gen_sign(params, api_key):
data = {k: v for k, v in params.items() if k not in ('sign', 'apiKey')}
s = ''.join([str(data[k]) for k in sorted(data)])
first = hashlib.md5(s.encode('utf-8')).hexdigest()
return hashlib.md5((first + api_key).encode('utf-8')).hexdigest()
api_key = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1'
params = {
'phone': '18888888888',
'timestamp': int(time.time()),
'name': '张三',
}
params['apiKey'] = api_key
params['sign'] = gen_sign(params, api_key)
resp = requests.post('http://yishi.com/v1/plan/api/scenariosz', data=params)
print(resp.json())`}</pre>
</TabsContent>
<TabsContent value="node">
<pre className="bg-gray-50 p-4 rounded-md overflow-auto text-xs">{`const axios = require('axios');
const crypto = require('crypto');
function genSign(params, apiKey) {
const data = {...params};
delete data.sign;
delete data.apiKey;
const keys = Object.keys(data).sort();
let str = '';
keys.forEach(k => { str += data[k]; });
const first = crypto.createHash('md5').update(str).digest('hex');
return crypto.createHash('md5').update(first + apiKey).digest('hex');
}
const apiKey = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1';
const params = {
phone: '18888888888',
timestamp: Math.floor(Date.now() / 1000),
name: '张三',
};
params.apiKey = apiKey;
params.sign = genSign(params, apiKey);
axios.post('http://yishi.com/v1/plan/api/scenariosz', params)
.then(res => console.log(res.data));`}</pre>
</TabsContent>
<TabsContent value="php">
<pre className="bg-gray-50 p-4 rounded-md overflow-auto text-xs">{`<?php
function md5_sign($params, $apiKey) {
unset($params['sign']);
unset($params['apiKey']);
ksort($params);
$str = '';
foreach ($params as $v) {
$str .= $v;
}
$first = md5($str);
$final = md5($first . $apiKey);
return $final;
}
$params = [
'phone' => '18888888888',
'timestamp' => time(),
'name' => '张三',
// 'source' => '', 'remark' => '', 'tags' => ''
];
$apiKey = 'naxf1-82h2f-vdwcm-rrhpm-q9hd1';
$params['apiKey'] = $apiKey;
$params['sign'] = md5_sign($params, $apiKey);
$url = '${API_BASE_URL}/v1/api/scenarios';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>`}</pre>
</TabsContent>
<TabsContent value="java">
<pre className="bg-gray-50 p-4 rounded-md overflow-auto text-xs">{`import java.security.MessageDigest;
import java.util.*;
import java.net.*;
import java.io.*;
public class ApiSignDemo {
public static String md5(String s) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(s.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : array) sb.append(String.format("%02x", b));
return sb.toString();
}
public static void main(String[] args) throws Exception {
Map<String, String> params = new HashMap<>();
params.put("phone", "18888888888");
params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
params.put("name", "张三");
// params.put("source", ""); params.put("remark", ""); params.put("tags", "");
String apiKey = "naxf1-82h2f-vdwcm-rrhpm-q9hd1";
// 排序并拼接
List<String> keys = new ArrayList<>(params.keySet());
keys.remove("sign");
keys.remove("apiKey");
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
for (String k : keys) {
sb.append(params.get(k));
}
String first = md5(sb.toString());
String sign = md5(first + apiKey);
params.put("apiKey", apiKey);
params.put("sign", sign);
// 发送POST请求
StringBuilder postData = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
if (postData.length() > 0) postData.append("&");
postData.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
postData.append("=");
postData.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
}
URL url = new URL("${API_BASE_URL}/v1/api/scenarios");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(postData.toString().getBytes("UTF-8"));
os.flush(); os.close();
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line; StringBuilder resp = new StringBuilder();
while ((line = in.readLine()) != null) resp.append(line);
in.close();
System.out.println(resp.toString());
}
}`}</pre>
</TabsContent>
</Tabs>
</CardContent>
</Card>
<div className="mt-8">
<h3 className="text-lg font-medium mb-4"></h3>
<Card className="mb-4">
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent>
<ol className="list-decimal list-inside space-y-2 text-sm">
<li></li>
<li>"应用集成" &gt; "外部接口"</li>
<li>"添加新接口"</li>
<li>"X-API-KEY"API密钥</li>
<li>
URL为
<code className="bg-gray-100 px-1 py-0.5 rounded">
{testUrl}
</code>
</li>
<li>name, phone等</li>
<li></li>
</ol>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<h4 className="text-sm font-medium mb-1"></h4>
<p className="text-sm text-gray-700">
X-API-KEY正确无误
</p>
</div>
<div>
<h4 className="text-sm font-medium mb-1"></h4>
<p className="text-sm text-gray-700">
</p>
</div>
<div>
<h4 className="text-sm font-medium mb-1"></h4>
<p className="text-sm text-gray-700">
API密钥每分钟最多可发送30个请求使
</p>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
)
}