diff --git a/app/api/__pycache__/futures_analysis.cpython-311.pyc b/app/api/__pycache__/futures_analysis.cpython-311.pyc index 377031d..f684a90 100644 Binary files a/app/api/__pycache__/futures_analysis.cpython-311.pyc and b/app/api/__pycache__/futures_analysis.cpython-311.pyc differ diff --git a/app/api/futures_analysis.py b/app/api/futures_analysis.py index ad75ff7..d64af42 100644 --- a/app/api/futures_analysis.py +++ b/app/api/futures_analysis.py @@ -870,7 +870,7 @@ def get_ai_analysis(symbol: str, force_refresh: bool = False, db: Session = Depe @router.get("/ai-analysis/{symbol}/history") -def get_ai_analysis_history(symbol: str, limit: int = 10, analysis_db: Session = Depends(get_analysis_db)): +def get_ai_analysis_history(symbol: str, limit: int = 20, analysis_db: Session = Depends(get_analysis_db)): """获取AI分析历史记录""" try: records = analysis_db.query(AIAnalysisCache).filter( @@ -885,9 +885,7 @@ def get_ai_analysis_history(symbol: str, limit: int = 10, analysis_db: Session = "id": r.id, "symbol": r.symbol, "analysis_time": r.created_at.isoformat(), - "summary": r.analysis_data.get("summary", ""), - "trading_suggestion": r.analysis_data.get("trading_suggestion", {}), - "confidence": r.analysis_data.get("trading_suggestion", {}).get("confidence", 0) + "analysis_data": r.analysis_data } for r in records] } except Exception as e: @@ -896,3 +894,34 @@ def get_ai_analysis_history(symbol: str, limit: int = 10, analysis_db: Session = "success": False, "error": f"获取历史记录失败: {str(e)}" } + + +@router.get("/ai-analysis/history/{record_id}") +def get_ai_analysis_detail(record_id: int, analysis_db: Session = Depends(get_analysis_db)): + """获取单条AI分析记录详情""" + try: + record = analysis_db.query(AIAnalysisCache).filter( + AIAnalysisCache.id == record_id + ).first() + + if not record: + return { + "success": False, + "error": "记录不存在" + } + + return { + "success": True, + "data": { + "id": record.id, + "symbol": record.symbol, + "analysis_time": record.created_at.isoformat(), + "analysis_data": record.analysis_data + } + } + except Exception as e: + logger.error(f"获取AI分析详情失败: {e}") + return { + "success": False, + "error": f"获取记录详情失败: {str(e)}" + } diff --git a/app/static/futures_analysis.css b/app/static/futures_analysis.css index d60e210..2575a38 100644 --- a/app/static/futures_analysis.css +++ b/app/static/futures_analysis.css @@ -1597,6 +1597,328 @@ body.theme-minimal .sort-select select:hover { border-color: var(--text-muted); } +/* AI历史记录详情弹窗样式 */ +.ai-history-detail { + padding: 8px 0; +} + +.detail-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + padding-bottom: 12px; + border-bottom: 1px solid var(--border-color); +} + +.detail-header h4 { + font-size: 18px; + font-weight: 600; + color: var(--text-primary); + display: flex; + align-items: center; + gap: 8px; +} + +.detail-header h4 i { + color: var(--cyan); +} + +.detail-time { + font-size: 12px; + color: var(--text-muted); + display: flex; + align-items: center; + gap: 6px; +} + +.detail-summary { + background: rgba(6, 182, 212, 0.05); + border-left: 3px solid var(--cyan); + padding: 12px 16px; + margin-bottom: 16px; + border-radius: 4px; + display: flex; + gap: 10px; + align-items: flex-start; +} + +.detail-summary i { + color: var(--cyan); + font-size: 14px; + margin-top: 2px; +} + +.detail-summary p { + font-size: 14px; + color: var(--text-secondary); + line-height: 1.6; + flex: 1; +} + +.detail-suggestion { + margin-bottom: 16px; +} + +.suggestion-card { + display: flex; + align-items: center; + gap: 12px; + padding: 16px; + border-radius: 8px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); +} + +.suggestion-card.long { + border-color: var(--green); + background: rgba(16, 185, 129, 0.05); +} + +.suggestion-card.short { + border-color: var(--red); + background: rgba(239, 68, 68, 0.05); +} + +.suggestion-card.neutral { + border-color: var(--amber); + background: rgba(245, 158, 11, 0.05); +} + +.suggestion-card i { + font-size: 24px; +} + +.suggestion-card.long i { + color: var(--green); +} + +.suggestion-card.short i { + color: var(--red); +} + +.suggestion-card.neutral i { + color: var(--amber); +} + +.suggestion-text { + font-size: 20px; + font-weight: 700; + flex: 1; +} + +.suggestion-card.long .suggestion-text { + color: var(--green); +} + +.suggestion-card.short .suggestion-text { + color: var(--red); +} + +.suggestion-card.neutral .suggestion-text { + color: var(--amber); +} + +.confidence-text { + font-size: 14px; + color: var(--text-muted); +} + +.detail-metrics { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 12px; + margin-bottom: 20px; +} + +.metric-card { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 12px; + text-align: center; +} + +.metric-label { + display: block; + font-size: 11px; + color: var(--text-muted); + margin-bottom: 6px; +} + +.metric-value { + display: block; + font-size: 14px; + font-weight: 600; + color: var(--text-primary); +} + +.metric-value.down { + color: var(--red); +} + +.detail-section { + margin-bottom: 20px; +} + +.detail-section h5 { + font-size: 14px; + font-weight: 600; + color: var(--text-secondary); + margin-bottom: 12px; + display: flex; + align-items: center; + gap: 8px; +} + +.detail-section h5 i { + color: var(--cyan); + font-size: 13px; +} + +.four-d-table { + width: 100%; + border-collapse: collapse; + font-size: 12px; +} + +.four-d-table th { + background: var(--bg-secondary); + padding: 8px 10px; + text-align: left; + color: var(--text-muted); + font-weight: 600; + border-bottom: 1px solid var(--border-color); +} + +.four-d-table td { + padding: 10px; + border-bottom: 1px solid rgba(56, 189, 248, 0.05); + color: var(--text-secondary); +} + +.four-d-table tr:hover td { + background: rgba(6, 182, 212, 0.03); +} + +.kdj-diagnosis-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 10px; +} + +.kdj-item { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 6px; + padding: 10px 12px; + display: flex; + flex-direction: column; + gap: 4px; +} + +.kdj-label { + font-size: 11px; + color: var(--text-muted); +} + +.kdj-value { + font-size: 13px; + color: var(--text-secondary); + font-weight: 500; +} + +.pivot-points-grid { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 8px; +} + +.pivot-item { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 6px; + padding: 10px; + text-align: center; + display: flex; + flex-direction: column; + gap: 4px; +} + +.pivot-item span:first-child { + font-size: 11px; + color: var(--text-muted); + font-weight: 600; +} + +.pivot-item strong { + font-size: 14px; + color: var(--text-primary); +} + +.pivot-item.resistance span:first-child { + color: var(--red); +} + +.pivot-item.resistance strong { + color: var(--red); +} + +.pivot-item.support span:first-child { + color: var(--green); +} + +.pivot-item.support strong { + color: var(--green); +} + +.pivot-item.center { + border-color: var(--purple); + background: rgba(139, 92, 246, 0.05); +} + +.pivot-item.center span:first-child { + color: var(--purple); +} + +.pivot-item.center strong { + color: var(--purple); +} + +.warning-list { + list-style: none; + padding: 0; +} + +.warning-list li { + background: rgba(245, 158, 11, 0.05); + border-left: 3px solid var(--amber); + padding: 10px 12px; + margin-bottom: 8px; + border-radius: 4px; + font-size: 12px; + color: var(--text-secondary); + line-height: 1.5; +} + +.warning-list li:last-child { + margin-bottom: 0; +} + +@media (max-width: 768px) { + .detail-metrics { + grid-template-columns: repeat(2, 1fr); + } + + .pivot-points-grid { + grid-template-columns: repeat(3, 1fr); + } + + .kdj-diagnosis-grid { + grid-template-columns: 1fr; + } +} + /* ============================================ AI智能分析样式 ============================================ */ diff --git a/app/static/futures_analysis.js b/app/static/futures_analysis.js index 03b10ab..b5e0ed6 100644 --- a/app/static/futures_analysis.js +++ b/app/static/futures_analysis.js @@ -520,7 +520,7 @@ function updateDetailView(data) { async function loadHistoryList(symbol) { try { - const response = await fetch(`${API_BASE}/analysis/history/${symbol}?limit=10`); + const response = await fetch(`${API_BASE}/ai-analysis/${symbol}/history?limit=20`); const data = await response.json(); if (data.success) { renderHistoryList(data.data); @@ -537,13 +537,18 @@ async function loadAIAnalysis() { const content = document.getElementById('ai-analysis-content'); try { + console.log(`加载合约 ${currentSymbol} 的AI分析...`); const response = await fetch(`${API_BASE}/ai-analysis/${currentSymbol}`); const data = await response.json(); + console.log(`合约 ${currentSymbol} AI分析响应:`, data); + if (data.success && data.data) { + console.log(`合约 ${currentSymbol} 分析数据 - symbol:`, data.data.symbol); currentAIAnalysis = data.data; displayAIAnalysisResult(data.data); } else { + console.log(`合约 ${currentSymbol} 无分析结果`); content.innerHTML = `
${result.summary || '暂无总结'}
+| 周期 | +MACD趋势 | +成交量 | +KDJ状态 | +结论 | +
|---|---|---|---|---|
| ${period} | +${d.macd?.trend || '--'} | +${d.volume?.status || '--'} | +${d.kdj?.status || '--'} | +${d.conclusion || '--'} | +