Files
cunkebao_v3/Cunkebao/app/api/docs/scenarios/page.tsx
2025-06-19 15:14:41 +08:00

406 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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>
)
}