api-test.html•59.5 kB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>API 测试工具</title>
    <!-- 引入marked.js用于Markdown解析 -->
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: #f5f7fa;
            min-height: 100vh;
            overflow: hidden;
        }
        .container {
            height: 100vh;
            display: flex;
            flex-direction: column;
        }
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px;
            text-align: center;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            z-index: 100;
        }
        .header h1 {
            font-size: 1.8em;
            margin-bottom: 5px;
            font-weight: 600;
        }
        .header p {
            font-size: 0.9em;
            opacity: 0.9;
        }
        .main-content {
            display: flex;
            flex: 1;
            overflow: hidden;
        }
        /* 左侧API列表 */
        .api-sidebar {
            width: 450px;
            background: white;
            border-right: 1px solid #e9ecef;
            overflow-y: auto;
            flex-shrink: 0;
        }
        .api-category {
            border-bottom: 1px solid #e9ecef;
        }
        .category-header {
            background: #f8f9fa;
            padding: 15px 20px;
            font-weight: 600;
            color: #495057;
            border-bottom: 1px solid #e9ecef;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: space-between;
            transition: background-color 0.2s;
        }
        .category-header:hover {
            background: #e9ecef;
        }
        .category-header.active {
            background: #007bff;
            color: white;
        }
        .category-icon {
            font-size: 1.2em;
            margin-right: 10px;
        }
        .collapse-icon {
            transition: transform 0.3s;
        }
        .collapse-icon.collapsed {
            transform: rotate(-90deg);
        }
        .api-list {
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.3s ease;
        }
        .api-list.show {
            max-height: 1000px;
        }
        .api-item {
            padding: 12px 20px;
            border-bottom: 1px solid #f1f3f4;
            cursor: pointer;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .api-item:hover {
            background: #f8f9fa;
            padding-left: 25px;
        }
        .api-item.active {
            background: #e3f2fd;
            border-left: 4px solid #2196f3;
            color: #1976d2;
        }
        .method-badge {
            padding: 2px 6px;
            border-radius: 3px;
            font-size: 0.7em;
            font-weight: bold;
            min-width: 40px;
            text-align: center;
        }
        .method-get {
            background: #4caf50;
            color: white;
        }
        .method-post {
            background: #ff9800;
            color: white;
        }
        .method-delete {
            background: #f44336;
            color: white;
        }
        .api-name {
            font-size: 0.9em;
            font-weight: 500;
        }
        /* 右侧测试区域 */
        .test-area {
            flex: 1;
            display: flex;
            flex-direction: column;
            background: white;
        }
        .test-header {
            padding: 20px;
            border-bottom: 1px solid #e9ecef;
            background: #fafbfc;
        }
        .test-title {
            font-size: 1.4em;
            font-weight: 600;
            color: #2c3e50;
            margin-bottom: 5px;
        }
        .test-description {
            color: #6c757d;
            font-size: 0.95em;
        }
        .test-body {
            flex: 1;
            display: flex;
            overflow: hidden;
        }
        .params-section {
            width: 350px;
            padding: 20px;
            border-right: 1px solid #e9ecef;
            overflow-y: auto;
            background: #fafbfc;
        }
        .params-title {
            font-weight: 600;
            margin-bottom: 15px;
            color: #495057;
        }
        .input-group {
            margin-bottom: 15px;
        }
        .input-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
            color: #495057;
            font-size: 0.9em;
        }
        .input-group input,
        .input-group select,
        .input-group textarea {
            width: 100%;
            padding: 8px 12px;
            border: 1px solid #ced4da;
            border-radius: 4px;
            font-size: 13px;
            transition: border-color 0.2s;
        }
        .input-group input:focus,
        .input-group select:focus,
        .input-group textarea:focus {
            outline: none;
            border-color: #007bff;
            box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
        }
        .input-group textarea {
            resize: vertical;
            min-height: 60px;
        }
        .test-btn {
            background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
            color: white;
            padding: 12px 24px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 600;
            transition: all 0.3s;
            width: 100%;
            margin-top: 10px;
        }
        .test-btn:hover:not(:disabled) {
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
        }
        .test-btn:disabled {
            background: #6c757d;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }
        .results-section {
            flex: 1;
            padding: 20px;
            overflow-y: auto;
        }
        .results-title {
            font-weight: 600;
            margin-bottom: 15px;
            color: #495057;
        }
        .response-container {
            background: #f8f9fa;
            border-radius: 6px;
            overflow: hidden;
            border: 1px solid #dee2e6;
            margin-bottom: 15px;
        }
        .response-header {
            background: #e9ecef;
            padding: 12px 15px;
            font-weight: 600;
            color: #495057;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .response-body {
            background: #2d3748;
            color: #e2e8f0;
            padding: 20px;
            font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
            font-size: 13px;
            white-space: pre-wrap;
            max-height: 500px;
            overflow-y: auto;
            line-height: 1.5;
        }
        /* 响应分离展示样式 */
        .response-section {
            margin-bottom: 15px;
        }
        .response-section h4 {
            margin: 0 0 10px 0;
            padding: 8px 12px;
            background: #e9ecef;
            border-radius: 4px 4px 0 0;
            font-size: 14px;
            font-weight: 600;
            color: #495057;
        }
        .response-content {
            padding: 15px;
            border: 1px solid #dee2e6;
            border-top: none;
            border-radius: 0 0 4px 4px;
            background: white;
        }
        .response-content.url {
            background: #f8f9fa;
            font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
            color: #0366d6;
            word-break: break-all;
            font-size: 13px;
        }
        .response-content.json {
            background: #2d3748;
            color: #e2e8f0;
            font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
            font-size: 13px;
            white-space: pre-wrap;
        }
        .response-content.markdown {
            background: white;
            color: #333;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            line-height: 1.6;
        }
        /* Markdown样式 */
        .markdown-content h1,
        .markdown-content h2,
        .markdown-content h3,
        .markdown-content h4,
        .markdown-content h5,
        .markdown-content h6 {
            margin: 20px 0 10px 0;
            font-weight: 600;
            line-height: 1.25;
        }
        .markdown-content h1 {
            font-size: 2em;
            border-bottom: 1px solid #eaecef;
            padding-bottom: 0.3em;
        }
        .markdown-content h2 {
            font-size: 1.5em;
            border-bottom: 1px solid #eaecef;
            padding-bottom: 0.3em;
        }
        .markdown-content h3 {
            font-size: 1.25em;
        }
        .markdown-content h4 {
            font-size: 1em;
        }
        .markdown-content h5 {
            font-size: 0.875em;
        }
        .markdown-content h6 {
            font-size: 0.85em;
            color: #6a737d;
        }
        .markdown-content p {
            margin: 0 0 16px 0;
        }
        .markdown-content ul,
        .markdown-content ol {
            margin: 0 0 16px 0;
            padding-left: 2em;
        }
        .markdown-content li {
            margin: 0.25em 0;
        }
        .markdown-content table {
            border-collapse: collapse;
            width: 100%;
            margin: 16px 0;
        }
        .markdown-content th,
        .markdown-content td {
            border: 1px solid #dfe2e5;
            padding: 6px 13px;
            text-align: left;
        }
        .markdown-content th {
            background: #f6f8fa;
            font-weight: 600;
        }
        .markdown-content code {
            background: #f6f8fa;
            padding: 0.2em 0.4em;
            border-radius: 3px;
            font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
            font-size: 85%;
        }
        .markdown-content pre {
            background: #f6f8fa;
            padding: 16px;
            border-radius: 6px;
            overflow-x: auto;
            margin: 16px 0;
        }
        .markdown-content pre code {
            background: none;
            padding: 0;
        }
        .markdown-content blockquote {
            border-left: 4px solid #dfe2e5;
            padding: 0 1em;
            margin: 16px 0;
            color: #6a737d;
        }
        .status-badge {
            display: inline-block;
            padding: 4px 8px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 600;
            text-transform: uppercase;
        }
        .status-badge.success {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        .status-badge.error {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        .loading {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid #f3f3f3;
            border-top: 2px solid #007bff;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin-right: 8px;
        }
        @keyframes spin {
            0% {
                transform: rotate(0deg);
            }
            100% {
                transform: rotate(360deg);
            }
        }
        .status-success {
            color: #28a745;
            font-weight: 600;
        }
        .status-error {
            color: #dc3545;
            font-weight: 600;
        }
        .empty-state {
            text-align: center;
            padding: 60px 20px;
            color: #6c757d;
        }
        .empty-state h3 {
            margin-bottom: 10px;
            color: #495057;
        }
        /* 响应式设计 */
        @media (max-width: 768px) {
            .main-content {
                flex-direction: column;
            }
            .api-sidebar {
                width: 100%;
                height: 300px;
            }
            .test-body {
                flex-direction: column;
            }
            .params-section {
                width: 100%;
                border-right: none;
                border-bottom: 1px solid #e9ecef;
            }
        }
        /* JSON语法高亮 */
        .json-key {
            color: #79b8ff;
        }
        .json-string {
            color: #88c999;
        }
        .json-number {
            color: #ffab70;
        }
        .json-boolean {
            color: #ff7b7b;
        }
        .json-null {
            color: #969696;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🚀 API 测试工具</h1>
            <p>测试所有可用的 API 端点</p>
        </div>
        <div class="main-content">
            <!-- 左侧API列表 -->
            <div class="api-sidebar">
                <!-- 股票相关API -->
                <div class="api-category">
                    <div class="category-header" onclick="toggleCategory('stock')">
                        <div>
                            <span class="category-icon">📈</span>
                            股票相关 API
                        </div>
                        <span class="collapse-icon">▼</span>
                    </div>
                    <div class="api-list show" id="stock-apis">
                        <div class="api-item" onclick="selectAPI('stock-quote')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">获取股票行情</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('stock-quotes')">
                            <span class="method-badge method-post">POST</span>
                            <span class="api-name">批量获取行情</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('stock-price')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">股票价格数据</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('stock-fundamental')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">基本面分析</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('stock-news')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">股票新闻</span>
                        </div>
                    </div>
                </div>
                <!-- 宏观数据API -->
                <div class="api-category">
                    <div class="category-header" onclick="toggleCategory('macro')">
                        <div>
                            <span class="category-icon">📊</span>
                            宏观数据 API
                        </div>
                        <span class="collapse-icon">▼</span>
                    </div>
                    <div class="api-list show" id="macro-apis">
                        <div class="api-item" onclick="selectAPI('macro-dashboard')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">智能宏观数据仪表板</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-latest')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">最新宏观数据</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-gdp')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">GDP数据</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-cpi')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">CPI数据</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-ppi')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">PPI数据</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-pmi')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">PMI数据</span>
                        </div>
                    </div>
                </div>
                <!-- 交易日历API -->
                <div class="api-category">
                    <div class="category-header" onclick="toggleCategory('calendar')">
                        <div>
                            <span class="category-icon">📅</span>
                            交易日历 API
                        </div>
                        <span class="collapse-icon">▼</span>
                    </div>
                    <div class="api-list show" id="calendar-apis">
                        <div class="api-item" onclick="selectAPI('calendar-trading-days')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">获取交易日</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('calendar-is-trading-day')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">检查交易日</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('calendar-supported-exchanges')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">支持的交易所</span>
                        </div>
                    </div>
                </div>
                <!-- 系统管理API -->
                <div class="api-category">
                    <div class="category-header" onclick="toggleCategory('system')">
                        <div>
                            <span class="category-icon">⚙️</span>
                            系统管理 API
                        </div>
                        <span class="collapse-icon">▼</span>
                    </div>
                    <div class="api-list show" id="system-apis">
                        <div class="api-item" onclick="selectAPI('macro-health')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">系统健康状态</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-sync-status')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">同步状态</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-sync')">
                            <span class="method-badge method-post">POST</span>
                            <span class="api-name">触发同步</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-cache-stats')">
                            <span class="method-badge method-get">GET</span>
                            <span class="api-name">缓存统计</span>
                        </div>
                        <div class="api-item" onclick="selectAPI('macro-clear-cache')">
                            <span class="method-badge method-delete">DELETE</span>
                            <span class="api-name">清除缓存</span>
                        </div>
                    </div>
                </div>
            </div>
            <!-- 右侧测试区域 -->
            <div class="test-area">
                <div class="test-header">
                    <h2 class="test-title" id="api-title">请选择一个API接口</h2>
                    <p class="test-description" id="api-description">从左侧列表中选择要测试的API接口</p>
                </div>
                <div class="test-body">
                    <div class="params-section">
                        <h3 class="params-title">请求参数</h3>
                        <div id="params-container">
                            <div class="empty-state">
                                <h3>未选择接口</h3>
                                <p>请从左侧列表选择要测试的API接口</p>
                            </div>
                        </div>
                    </div>
                    <div class="results-section">
                        <h3 class="results-title">响应结果</h3>
                        <div id="results-container">
                            <div class="empty-state">
                                <h3>等待测试</h3>
                                <p>选择接口并填写参数后点击测试按钮</p>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <!-- 股票相关API -->
    <div class="api-section">
        <h3 class="section-title">📈 股票相关 API</h3>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                获取股票行情
            </div>
            <div class="api-description">获取股票的实时或近实时行情数据</div>
            <div class="input-group">
                <label>股票代码:</label>
                <input type="text" id="stock-quote-symbol" placeholder="例: 000001, AAPL, 00700" value="000001">
            </div>
            <button class="btn" onclick="testAPI('stock-quote')">测试接口</button>
            <div class="response-container" id="stock-quote-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-post">POST</span>
                批量获取行情
            </div>
            <div class="api-description">批量获取多个股票的行情数据</div>
            <div class="input-group">
                <label>股票代码列表 (JSON格式):</label>
                <textarea id="stock-quotes-symbols"
                    placeholder='{"symbols": ["000001", "000002", "AAPL"]}'>{"symbols": ["000001", "000002"]}</textarea>
            </div>
            <button class="btn" onclick="testAPI('stock-quotes')">测试接口</button>
            <div class="response-container" id="stock-quotes-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                股票价格数据
            </div>
            <div class="api-description">获取股票价格数据和分析报告</div>
            <div class="input-group">
                <label>股票代码:</label>
                <input type="text" id="stock-price-symbol" placeholder="例: 000001" value="000001">
            </div>
            <div class="input-group">
                <label>开始日期:</label>
                <input type="date" id="stock-price-start" value="2024-01-01">
            </div>
            <div class="input-group">
                <label>结束日期:</label>
                <input type="date" id="stock-price-end" value="2024-12-31">
            </div>
            <button class="btn" onclick="testAPI('stock-price')">测试接口</button>
            <div class="response-container" id="stock-price-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                基本面分析
            </div>
            <div class="api-description">获取股票基本面财务报告</div>
            <div class="input-group">
                <label>股票代码:</label>
                <input type="text" id="stock-fundamental-symbol" placeholder="例: 000001" value="000001">
            </div>
            <button class="btn" onclick="testAPI('stock-fundamental')">测试接口</button>
            <div class="response-container" id="stock-fundamental-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                股票新闻
            </div>
            <div class="api-description">获取股票最新新闻</div>
            <div class="input-group">
                <label>股票代码:</label>
                <input type="text" id="stock-news-symbol" placeholder="例: 000001" value="000001">
            </div>
            <div class="input-group">
                <label>查询天数:</label>
                <input type="number" id="stock-news-days" value="30" min="1" max="90">
            </div>
            <button class="btn" onclick="testAPI('stock-news')">测试接口</button>
            <div class="response-container" id="stock-news-response"></div>
        </div>
    </div>
    <!-- 宏观数据API -->
    <div class="api-section">
        <h3 class="section-title">📊 宏观数据 API</h3>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                智能宏观数据仪表板
            </div>
            <div class="api-description">获取所有主要宏观指标的最佳期数数据</div>
            <button class="btn" onclick="testAPI('macro-dashboard')">测试接口</button>
            <div class="response-container" id="macro-dashboard-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                最新宏观数据
            </div>
            <div class="api-description">获取所有宏观指标的最新数据</div>
            <div class="input-group">
                <label>期数:</label>
                <input type="number" id="macro-latest-periods" value="1" min="1" max="50">
            </div>
            <button class="btn" onclick="testAPI('macro-latest')">测试接口</button>
            <div class="response-container" id="macro-latest-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                GDP数据
            </div>
            <div class="api-description">获取GDP数据</div>
            <div class="input-group">
                <label>期数:</label>
                <input type="number" id="macro-gdp-periods" value="4" min="1" max="20">
            </div>
            <button class="btn" onclick="testAPI('macro-gdp')">测试接口</button>
            <div class="response-container" id="macro-gdp-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                CPI数据
            </div>
            <div class="api-description">获取CPI数据</div>
            <div class="input-group">
                <label>期数:</label>
                <input type="number" id="macro-cpi-periods" value="12" min="1" max="50">
            </div>
            <button class="btn" onclick="testAPI('macro-cpi')">测试接口</button>
            <div class="response-container" id="macro-cpi-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                PPI数据
            </div>
            <div class="api-description">获取PPI数据</div>
            <div class="input-group">
                <label>期数:</label>
                <input type="number" id="macro-ppi-periods" value="12" min="1" max="50">
            </div>
            <button class="btn" onclick="testAPI('macro-ppi')">测试接口</button>
            <div class="response-container" id="macro-ppi-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                PMI数据
            </div>
            <div class="api-description">获取PMI数据</div>
            <div class="input-group">
                <label>期数:</label>
                <input type="number" id="macro-pmi-periods" value="12" min="1" max="50">
            </div>
            <button class="btn" onclick="testAPI('macro-pmi')">测试接口</button>
            <div class="response-container" id="macro-pmi-response"></div>
        </div>
    </div>
    <!-- 交易日历API -->
    <div class="api-section">
        <h3 class="section-title">📅 交易日历 API</h3>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                获取交易日
            </div>
            <div class="api-description">获取指定股票的交易日列表</div>
            <div class="input-group">
                <label>股票代码:</label>
                <input type="text" id="calendar-trading-days-symbol" placeholder="例: 000001" value="000001">
            </div>
            <div class="input-group">
                <label>开始日期:</label>
                <input type="date" id="calendar-trading-days-start" value="2024-01-01">
            </div>
            <div class="input-group">
                <label>结束日期:</label>
                <input type="date" id="calendar-trading-days-end" value="2024-01-31">
            </div>
            <button class="btn" onclick="testAPI('calendar-trading-days')">测试接口</button>
            <div class="response-container" id="calendar-trading-days-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                检查交易日
            </div>
            <div class="api-description">检查指定日期是否为交易日</div>
            <div class="input-group">
                <label>股票代码:</label>
                <input type="text" id="calendar-is-trading-day-symbol" placeholder="例: 000001" value="000001">
            </div>
            <div class="input-group">
                <label>检查日期:</label>
                <input type="date" id="calendar-is-trading-day-date" value="2024-01-15">
            </div>
            <button class="btn" onclick="testAPI('calendar-is-trading-day')">测试接口</button>
            <div class="response-container" id="calendar-is-trading-day-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                支持的交易所
            </div>
            <div class="api-description">获取支持的交易所列表</div>
            <button class="btn" onclick="testAPI('calendar-supported-exchanges')">测试接口</button>
            <div class="response-container" id="calendar-supported-exchanges-response"></div>
        </div>
    </div>
    <!-- 系统管理API -->
    <div class="api-section">
        <h3 class="section-title">⚙️ 系统管理 API</h3>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                系统健康状态
            </div>
            <div class="api-description">获取宏观数据服务健康状态</div>
            <button class="btn" onclick="testAPI('macro-health')">测试接口</button>
            <div class="response-container" id="macro-health-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                同步状态
            </div>
            <div class="api-description">获取宏观数据同步状态</div>
            <button class="btn" onclick="testAPI('macro-sync-status')">测试接口</button>
            <div class="response-container" id="macro-sync-status-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-post">POST</span>
                触发同步
            </div>
            <div class="api-description">手动触发宏观数据同步</div>
            <div class="input-group">
                <label>指标名称 (可选):</label>
                <select id="macro-sync-indicator">
                    <option value="">全部指标</option>
                    <option value="gdp">GDP</option>
                    <option value="cpi">CPI</option>
                    <option value="ppi">PPI</option>
                    <option value="pmi">PMI</option>
                    <option value="money_supply">货币供应量</option>
                    <option value="social_financing">社会融资</option>
                    <option value="lpr">LPR</option>
                </select>
            </div>
            <div class="input-group">
                <label>
                    <input type="checkbox" id="macro-sync-force"> 强制同步
                </label>
            </div>
            <button class="btn" onclick="testAPI('macro-sync')">测试接口</button>
            <div class="response-container" id="macro-sync-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-get">GET</span>
                缓存统计
            </div>
            <div class="api-description">获取宏观数据缓存统计信息</div>
            <button class="btn" onclick="testAPI('macro-cache-stats')">测试接口</button>
            <div class="response-container" id="macro-cache-stats-response"></div>
        </div>
        <div class="api-item">
            <div class="api-title">
                <span class="method-badge method-delete">DELETE</span>
                清除缓存
            </div>
            <div class="api-description">清除宏观数据缓存</div>
            <div class="input-group">
                <label>指标名称 (可选):</label>
                <select id="macro-clear-cache-indicator">
                    <option value="">全部缓存</option>
                    <option value="gdp">GDP</option>
                    <option value="cpi">CPI</option>
                    <option value="ppi">PPI</option>
                    <option value="pmi">PMI</option>
                    <option value="money_supply">货币供应量</option>
                    <option value="social_financing">社会融资</option>
                    <option value="lpr">LPR</option>
                </select>
            </div>
            <button class="btn" onclick="testAPI('macro-clear-cache')">测试接口</button>
            <div class="response-container" id="macro-clear-cache-response"></div>
        </div>
    </div>
    </div>
    </div>
    </div>
    <script>
        // 动态获取当前服务器URL
        const SERVER_URL = `${window.location.protocol}//${window.location.host}`;
        // API配置
        const API_CONFIG = {
            'stock-quote': {
                title: '获取股票行情',
                description: '获取股票的实时或近实时行情数据',
                method: 'GET',
                url: '/api/stock/quote',
                params: [
                    { name: 'symbol', label: '股票代码', type: 'text', required: true, placeholder: '例: 000001, AAPL', value: '000001' }
                ]
            },
            'stock-quotes': {
                title: '批量获取行情',
                description: '批量获取多个股票的行情数据',
                method: 'POST',
                url: '/api/stock/quotes',
                params: [
                    { name: 'symbols', label: '股票代码列表', type: 'json', required: true, placeholder: '["000001", "000002"]', value: '["000001", "000002"]' }
                ]
            },
            'stock-price': {
                title: '股票价格数据',
                description: '获取股票价格数据和分析报告',
                method: 'GET',
                url: '/api/stock/price',
                params: [
                    { name: 'symbol', label: '股票代码', type: 'text', required: true, placeholder: '例: 000001', value: '000001' },
                    { name: 'start_date', label: '开始日期', type: 'date', required: true, value: '2024-01-01' },
                    { name: 'end_date', label: '结束日期', type: 'date', required: true, value: '2024-12-31' }
                ]
            },
            'stock-fundamental': {
                title: '基本面分析',
                description: '获取股票基本面财务报告',
                method: 'GET',
                url: '/api/stock/fundamental',
                params: [
                    { name: 'symbol', label: '股票代码', type: 'text', required: true, placeholder: '例: 000001', value: '000001' }
                ]
            },
            'stock-news': {
                title: '股票新闻',
                description: '获取股票最新新闻',
                method: 'GET',
                url: '/api/stock/news',
                params: [
                    { name: 'symbol', label: '股票代码', type: 'text', required: true, placeholder: '例: 000001', value: '000001' },
                    { name: 'days_back', label: '查询天数', type: 'number', required: false, value: '30' }
                ]
            },
            'macro-dashboard': {
                title: '智能宏观数据仪表板',
                description: '获取所有主要宏观指标的最佳期数数据',
                method: 'GET',
                url: '/api/macro/smart-dashboard',
                params: []
            },
            'macro-latest': {
                title: '最新宏观数据',
                description: '获取所有宏观指标的最新数据',
                method: 'GET',
                url: '/api/macro/latest',
                params: [
                    { name: 'periods', label: '期数', type: 'number', required: false, value: '1' }
                ]
            },
            'macro-gdp': {
                title: 'GDP数据',
                description: '获取GDP数据',
                method: 'GET',
                url: '/api/macro/gdp',
                params: [
                    { name: 'periods', label: '期数', type: 'number', required: false, value: '4' }
                ]
            },
            'macro-cpi': {
                title: 'CPI数据',
                description: '获取CPI数据',
                method: 'GET',
                url: '/api/macro/cpi',
                params: [
                    { name: 'periods', label: '期数', type: 'number', required: false, value: '12' }
                ]
            },
            'macro-ppi': {
                title: 'PPI数据',
                description: '获取PPI数据',
                method: 'GET',
                url: '/api/macro/ppi',
                params: [
                    { name: 'periods', label: '期数', type: 'number', required: false, value: '12' }
                ]
            },
            'macro-pmi': {
                title: 'PMI数据',
                description: '获取PMI数据',
                method: 'GET',
                url: '/api/macro/pmi',
                params: [
                    { name: 'periods', label: '期数', type: 'number', required: false, value: '12' }
                ]
            },
            'calendar-trading-days': {
                title: '获取交易日',
                description: '获取指定股票的交易日列表',
                method: 'GET',
                url: '/api/calendar/trading-days',
                params: [
                    { name: 'symbol', label: '股票代码', type: 'text', required: true, placeholder: '例: 000001', value: '000001' },
                    { name: 'start_date', label: '开始日期', type: 'date', required: true, value: '2024-01-01' },
                    { name: 'end_date', label: '结束日期', type: 'date', required: true, value: '2024-01-31' }
                ]
            },
            'calendar-is-trading-day': {
                title: '检查交易日',
                description: '检查指定日期是否为交易日',
                method: 'GET',
                url: '/api/calendar/is-trading-day',
                params: [
                    { name: 'symbol', label: '股票代码', type: 'text', required: true, placeholder: '例: 000001', value: '000001' },
                    { name: 'check_date', label: '检查日期', type: 'date', required: true, value: '2024-01-15' }
                ]
            },
            'calendar-supported-exchanges': {
                title: '支持的交易所',
                description: '获取支持的交易所列表',
                method: 'GET',
                url: '/api/calendar/supported-exchanges',
                params: []
            },
            'macro-health': {
                title: '系统健康状态',
                description: '获取宏观数据服务健康状态',
                method: 'GET',
                url: '/api/macro/health',
                params: []
            },
            'macro-sync-status': {
                title: '同步状态',
                description: '获取宏观数据同步状态',
                method: 'GET',
                url: '/api/macro/sync/status',
                params: []
            },
            'macro-sync': {
                title: '触发同步',
                description: '手动触发宏观数据同步',
                method: 'POST',
                url: '/api/macro/sync',
                params: [
                    {
                        name: 'indicator', label: '指标名称', type: 'select', required: false, options: [
                            { value: '', label: '全部指标' },
                            { value: 'gdp', label: 'GDP' },
                            { value: 'cpi', label: 'CPI' },
                            { value: 'ppi', label: 'PPI' },
                            { value: 'pmi', label: 'PMI' },
                            { value: 'money_supply', label: '货币供应量' },
                            { value: 'social_financing', label: '社会融资' },
                            { value: 'lpr', label: 'LPR' }
                        ]
                    },
                    { name: 'force', label: '强制同步', type: 'checkbox', required: false }
                ]
            },
            'macro-cache-stats': {
                title: '缓存统计',
                description: '获取宏观数据缓存统计信息',
                method: 'GET',
                url: '/api/macro/cache/stats',
                params: []
            },
            'macro-clear-cache': {
                title: '清除缓存',
                description: '清除宏观数据缓存',
                method: 'DELETE',
                url: '/api/macro/cache',
                params: [
                    {
                        name: 'indicator', label: '指标名称', type: 'select', required: false, options: [
                            { value: '', label: '全部缓存' },
                            { value: 'gdp', label: 'GDP' },
                            { value: 'cpi', label: 'CPI' },
                            { value: 'ppi', label: 'PPI' },
                            { value: 'pmi', label: 'PMI' },
                            { value: 'money_supply', label: '货币供应量' },
                            { value: 'social_financing', label: '社会融资' },
                            { value: 'lpr', label: 'LPR' }
                        ]
                    }
                ]
            }
        };
        let currentAPI = null;
        // 切换分类的展开/折叠状态
        function toggleCategory(categoryName) {
            const list = document.getElementById(categoryName + '-apis');
            const icon = event.target.closest('.category-header').querySelector('.collapse-icon');
            if (list.classList.contains('show')) {
                list.classList.remove('show');
                icon.classList.add('collapsed');
            } else {
                list.classList.add('show');
                icon.classList.remove('collapsed');
            }
        }
        // 选择API接口
        function selectAPI(apiKey) {
            const config = API_CONFIG[apiKey];
            if (!config) return;
            // 更新当前API
            currentAPI = apiKey;
            // 更新选中状态
            document.querySelectorAll('.api-item').forEach(item => item.classList.remove('active'));
            event.target.closest('.api-item').classList.add('active');
            // 更新标题和描述
            document.getElementById('api-title').textContent = config.title;
            document.getElementById('api-description').textContent = config.description;
            // 生成参数表单
            generateParamsForm(config);
            // 清空结果区域
            document.getElementById('results-container').innerHTML = `
                <div class="empty-state">
                    <h3>等待测试</h3>
                    <p>填写参数后点击测试按钮</p>
                </div>
            `;
        }
        // 生成参数表单
        function generateParamsForm(config) {
            const container = document.getElementById('params-container');
            if (config.params.length === 0) {
                container.innerHTML = `
                    <p style="color: #6c757d; margin-bottom: 15px;">此接口无需参数</p>
                    <button class="test-btn" onclick="testCurrentAPI()">测试接口</button>
                `;
                return;
            }
            let html = '';
            config.params.forEach(param => {
                html += '<div class="input-group">';
                html += `<label>${param.label}${param.required ? ' *' : ''}:</label>`;
                if (param.type === 'select') {
                    html += `<select id="param-${param.name}">`;
                    param.options.forEach(option => {
                        html += `<option value="${option.value}">${option.label}</option>`;
                    });
                    html += '</select>';
                } else if (param.type === 'checkbox') {
                    html += `<label style="display: flex; align-items: center; margin-top: 5px;">
                        <input type="checkbox" id="param-${param.name}" style="margin-right: 8px;">
                        ${param.label}
                    </label>`;
                } else if (param.type === 'json') {
                    html += `<textarea id="param-${param.name}" placeholder='${param.placeholder || ''}'>${param.value || ''}</textarea>`;
                } else {
                    html += `<input type="${param.type}" id="param-${param.name}" placeholder="${param.placeholder || ''}" value="${param.value || ''}">`;
                }
                html += '</div>';
            });
            html += '<button class="test-btn" onclick="testCurrentAPI()">测试接口</button>';
            container.innerHTML = html;
        }
        // 测试当前API
        async function testCurrentAPI() {
            if (!currentAPI) return;
            const config = API_CONFIG[currentAPI];
            const button = document.querySelector('.test-btn');
            const resultsContainer = document.getElementById('results-container');
            // 获取参数值
            const params = {};
            let hasError = false;
            config.params.forEach(param => {
                const element = document.getElementById(`param-${param.name}`);
                if (!element) return;
                let value;
                if (param.type === 'checkbox') {
                    value = element.checked;
                } else if (param.type === 'json') {
                    try {
                        value = JSON.parse(element.value);
                    } catch (e) {
                        alert(`参数 "${param.label}" 的JSON格式不正确`);
                        hasError = true;
                        return;
                    }
                } else {
                    value = element.value;
                }
                if (param.required && (!value || value === '')) {
                    alert(`请填写必填参数: ${param.label}`);
                    hasError = true;
                    return;
                }
                if (value !== '' && value !== null && value !== undefined) {
                    params[param.name] = value;
                }
            });
            if (hasError) return;
            // 显示加载状态
            button.disabled = true;
            button.innerHTML = '<span class="loading"></span>请求中...';
            resultsContainer.innerHTML = `
                <div class="response-container">
                    <div class="response-header">正在请求...</div>
                    <div class="response-body">等待服务器响应...</div>
                </div>
            `;
            try {
                let url = `${SERVER_URL}${config.url}`;
                let options = {
                    method: config.method,
                    headers: {
                        'Content-Type': 'application/json'
                    }
                };
                // 处理参数
                if (config.method === 'GET' || config.method === 'DELETE') {
                    if (Object.keys(params).length > 0) {
                        const urlParams = new URLSearchParams();
                        Object.entries(params).forEach(([key, value]) => {
                            if (value !== null && value !== undefined && value !== '') {
                                urlParams.append(key, value);
                            }
                        });
                        if (urlParams.toString()) {
                            url += '?' + urlParams.toString();
                        }
                    }
                } else if (config.method === 'POST') {
                    options.body = JSON.stringify(params);
                }
                const startTime = Date.now();
                const response = await fetch(url, options);
                const endTime = Date.now();
                const duration = endTime - startTime;
                const responseText = await response.text();
                let responseData;
                try {
                    responseData = JSON.parse(responseText);
                } catch {
                    responseData = responseText;
                }
                // 显示响应
                const statusClass = response.ok ? 'status-success' : 'status-error';
                // 生成分离显示的HTML
                let responseHtml = '';
                // HTTP状态信息
                responseHtml += `
                    <div class="response-container">
                        <div class="response-header">
                            <span class="${statusClass}">
                                ${response.status} ${response.statusText}
                            </span>
                            <span style="float: right;">${duration}ms</span>
                        </div>
                    </div>
                `;
                // 显示请求URL
                responseHtml += `
                    <div class="response-section">
                        <h4>请求URL</h4>
                        <div class="response-content url">
                            ${config.method} ${url}
                        </div>
                    </div>
                `;
                // 如果响应是JSON对象且包含标准结构,则分离显示
                if (typeof responseData === 'object' && responseData !== null) {
                    // 显示Status
                    if (responseData.status !== undefined) {
                        const statusBadgeClass = responseData.status === 'success' ? 'success' : 'error';
                        responseHtml += `
                            <div class="response-section">
                                <h4>Status</h4>
                                <div class="response-content">
                                    <span class="status-badge ${statusBadgeClass}">${responseData.status}</span>
                                </div>
                            </div>
                        `;
                    }
                    // 显示Message
                    if (responseData.message !== undefined) {
                        responseHtml += `
                            <div class="response-section">
                                <h4>Message</h4>
                                <div class="response-content">
                                    ${responseData.message}
                                </div>
                            </div>
                        `;
                    }
                    // 显示Data
                    if (responseData.data !== undefined) {
                        responseHtml += `
                            <div class="response-section">
                                <h4>Data</h4>
                        `;
                        // 检查data是否为Markdown字符串
                        if (typeof responseData.data === 'string' &&
                            (responseData.data.includes('#') || responseData.data.includes('**') ||
                                responseData.data.includes('##') || responseData.data.includes('###'))) {
                            // 使用marked.js渲染Markdown
                            const markdownHtml = marked.parse(responseData.data);
                            responseHtml += `
                                <div class="response-content markdown">
                                    <div class="markdown-content">${markdownHtml}</div>
                                </div>
                            `;
                        } else {
                            // 显示为JSON
                            const dataStr = typeof responseData.data === 'object'
                                ? JSON.stringify(responseData.data, null, 2)
                                : String(responseData.data);
                            responseHtml += `
                                <div class="response-content json">
                                    ${dataStr}
                                </div>
                            `;
                        }
                        responseHtml += `</div>`;
                    }
                    // 如果有其他字段,显示完整响应
                    const standardFields = ['status', 'message', 'data'];
                    const otherFields = Object.keys(responseData).filter(key => !standardFields.includes(key));
                    if (otherFields.length > 0) {
                        const otherData = {};
                        otherFields.forEach(key => {
                            otherData[key] = responseData[key];
                        });
                        responseHtml += `
                            <div class="response-section">
                                <h4>其他字段</h4>
                                <div class="response-content json">
                                    ${JSON.stringify(otherData, null, 2)}
                                </div>
                            </div>
                        `;
                    }
                } else {
                    // 非标准格式,显示原始响应
                    const formattedResponse = typeof responseData === 'object'
                        ? JSON.stringify(responseData, null, 2)
                        : String(responseData);
                    responseHtml += `
                        <div class="response-section">
                            <h4>响应内容</h4>
                            <div class="response-content json">
                                ${formattedResponse}
                            </div>
                        </div>
                    `;
                }
                resultsContainer.innerHTML = responseHtml;
            } catch (error) {
                resultsContainer.innerHTML = `
                    <div class="response-container">
                        <div class="response-header">
                            <span class="status-error">请求失败</span>
                        </div>
                    </div>
                    <div class="response-section">
                        <h4>错误信息</h4>
                        <div class="response-content">
                            <span class="status-badge error">ERROR</span>
                            <p style="margin-top: 10px;">错误: ${error.message}</p>
                        </div>
                    </div>
                `;
            } finally {
                // 恢复按钮状态
                button.disabled = false;
                button.innerHTML = '测试接口';
            }
        }
        // 页面加载完成后的初始化
        document.addEventListener('DOMContentLoaded', function () {
            console.log('API测试工具已加载,服务器地址:', SERVER_URL);
            // 默认选择第一个API
            selectAPI('stock-quote');
        });
    </script>
</body>
</html>