A more comprehensive large model analysis setup, front-end interface optimization.
Showing
3 changed files
with
341 additions
and
35 deletions
| @@ -14,9 +14,26 @@ class AIAnalyzer: | @@ -14,9 +14,26 @@ class AIAnalyzer: | ||
| 14 | 14 | ||
| 15 | openai.api_key = self.api_key | 15 | openai.api_key = self.api_key |
| 16 | 16 | ||
| 17 | - # 系统提示词,限制AI的输出格式 | ||
| 18 | - self.system_prompt = """你是一个专业的舆情分析助手。你的任务是分析每条消息的情感倾向、关键词和潜在影响。 | ||
| 19 | -请严格按照以下JSON格式返回分析结果: | 17 | + # 不同深度的分析提示词 |
| 18 | + self.prompt_templates = { | ||
| 19 | + 'basic': """你是一个专业的舆情分析助手。请对每条消息进行基础的情感分析。 | ||
| 20 | +请按以下JSON格式返回: | ||
| 21 | +{ | ||
| 22 | + "analysis_results": [ | ||
| 23 | + { | ||
| 24 | + "message_id": "消息ID", | ||
| 25 | + "sentiment": "情感倾向 (积极/消极/中性)", | ||
| 26 | + "sentiment_score": "情感分数 (0-1)", | ||
| 27 | + "keywords": ["关键词1", "关键词2"], | ||
| 28 | + "key_points": "简要概述", | ||
| 29 | + "influence_analysis": "基础影响分析", | ||
| 30 | + "risk_level": "风险等级 (低/中/高)", | ||
| 31 | + "timestamp": "分析时间戳" | ||
| 32 | + } | ||
| 33 | + ] | ||
| 34 | +}""", | ||
| 35 | + 'standard': """你是一个专业的舆情分析助手。请对每条消息进行标准深度的分析。 | ||
| 36 | +请按以下JSON格式返回: | ||
| 20 | { | 37 | { |
| 21 | "analysis_results": [ | 38 | "analysis_results": [ |
| 22 | { | 39 | { |
| @@ -30,41 +47,69 @@ class AIAnalyzer: | @@ -30,41 +47,69 @@ class AIAnalyzer: | ||
| 30 | "timestamp": "分析时间戳" | 47 | "timestamp": "分析时间戳" |
| 31 | } | 48 | } |
| 32 | ] | 49 | ] |
| 33 | -} | ||
| 34 | -请确保每个字段都有值,并保持JSON格式的一致性。""" | 50 | +}""", |
| 51 | + 'deep': """你是一个专业的舆情分析助手。请对每条消息进行深度分析。 | ||
| 52 | +请按以下JSON格式返回: | ||
| 53 | +{ | ||
| 54 | + "analysis_results": [ | ||
| 55 | + { | ||
| 56 | + "message_id": "消息ID", | ||
| 57 | + "sentiment": "情感倾向 (积极/消极/中性)", | ||
| 58 | + "sentiment_score": "情感分数 (0-1)", | ||
| 59 | + "keywords": ["关键词1", "关键词2", "关键词3", "关键词4", "关键词5"], | ||
| 60 | + "key_points": "详细的核心观点分析", | ||
| 61 | + "influence_analysis": "深度影响分析,包括短期和长期影响", | ||
| 62 | + "risk_factors": ["风险因素1", "风险因素2", "风险因素3"], | ||
| 63 | + "risk_level": "风险等级 (低/中/高)", | ||
| 64 | + "suggestions": ["建议1", "建议2", "建议3"], | ||
| 65 | + "timestamp": "分析时间戳" | ||
| 66 | + } | ||
| 67 | + ] | ||
| 68 | +}""" | ||
| 69 | + } | ||
| 35 | 70 | ||
| 36 | - async def analyze_messages(self, messages: List[Dict]) -> List[Dict]: | 71 | + async def analyze_messages(self, messages: List[Dict], batch_size: int = 50, |
| 72 | + model_type: str = "gpt-3.5-turbo", | ||
| 73 | + analysis_depth: str = "standard") -> List[Dict]: | ||
| 37 | """分析一批消息并返回分析结果""" | 74 | """分析一批消息并返回分析结果""" |
| 38 | try: | 75 | try: |
| 39 | - # 构建输入消息 | 76 | + all_results = [] |
| 77 | + | ||
| 78 | + # 分批处理消息 | ||
| 79 | + for i in range(0, len(messages), batch_size): | ||
| 80 | + batch = messages[i:i + batch_size] | ||
| 40 | formatted_messages = [] | 81 | formatted_messages = [] |
| 41 | - for msg in messages: | 82 | + for msg in batch: |
| 42 | formatted_messages.append(f"消息ID: {msg['id']}\n内容: {msg['content']}") | 83 | formatted_messages.append(f"消息ID: {msg['id']}\n内容: {msg['content']}") |
| 43 | 84 | ||
| 44 | messages_text = "\n---\n".join(formatted_messages) | 85 | messages_text = "\n---\n".join(formatted_messages) |
| 45 | 86 | ||
| 87 | + # 获取对应深度的提示词 | ||
| 88 | + system_prompt = self.prompt_templates.get(analysis_depth, self.prompt_templates['standard']) | ||
| 89 | + | ||
| 46 | # 调用OpenAI API | 90 | # 调用OpenAI API |
| 47 | response = await openai.ChatCompletion.acreate( | 91 | response = await openai.ChatCompletion.acreate( |
| 48 | - model="gpt-3.5-turbo", | 92 | + model=model_type, |
| 49 | messages=[ | 93 | messages=[ |
| 50 | - {"role": "system", "content": self.system_prompt}, | 94 | + {"role": "system", "content": system_prompt}, |
| 51 | {"role": "user", "content": f"请分析以下消息:\n{messages_text}"} | 95 | {"role": "user", "content": f"请分析以下消息:\n{messages_text}"} |
| 52 | ], | 96 | ], |
| 53 | temperature=0.3, # 降低随机性 | 97 | temperature=0.3, # 降低随机性 |
| 54 | - max_tokens=2000, | 98 | + max_tokens=2000 if analysis_depth != 'deep' else 3000, |
| 55 | n=1 | 99 | n=1 |
| 56 | ) | 100 | ) |
| 57 | 101 | ||
| 58 | - # 解析返回结果 | ||
| 59 | try: | 102 | try: |
| 60 | result = json.loads(response.choices[0].message.content) | 103 | result = json.loads(response.choices[0].message.content) |
| 61 | - # 验证结果格式 | ||
| 62 | - if not isinstance(result, dict) or 'analysis_results' not in result: | ||
| 63 | - raise ValueError("AI返回格式不正确") | ||
| 64 | - return result['analysis_results'] | ||
| 65 | - except json.JSONDecodeError: | ||
| 66 | - logging.error("AI返回结果解析失败") | ||
| 67 | - return [] | 104 | + if isinstance(result, dict) and 'analysis_results' in result: |
| 105 | + all_results.extend(result['analysis_results']) | ||
| 106 | + else: | ||
| 107 | + logging.error(f"API返回格式不正确: {response.choices[0].message.content}") | ||
| 108 | + except json.JSONDecodeError as e: | ||
| 109 | + logging.error(f"JSON解析失败: {e}") | ||
| 110 | + continue | ||
| 111 | + | ||
| 112 | + return all_results | ||
| 68 | 113 | ||
| 69 | except Exception as e: | 114 | except Exception as e: |
| 70 | logging.error(f"AI分析过程出错: {e}") | 115 | logging.error(f"AI分析过程出错: {e}") |
| @@ -72,7 +117,7 @@ class AIAnalyzer: | @@ -72,7 +117,7 @@ class AIAnalyzer: | ||
| 72 | 117 | ||
| 73 | def format_analysis_for_display(self, analysis: Dict) -> Dict: | 118 | def format_analysis_for_display(self, analysis: Dict) -> Dict: |
| 74 | """将分析结果格式化为前端显示格式""" | 119 | """将分析结果格式化为前端显示格式""" |
| 75 | - return { | 120 | + base_result = { |
| 76 | 'id': analysis['message_id'], | 121 | 'id': analysis['message_id'], |
| 77 | 'sentiment': analysis['sentiment'], | 122 | 'sentiment': analysis['sentiment'], |
| 78 | 'sentiment_score': f"{float(analysis['sentiment_score']):.2%}", | 123 | 'sentiment_score': f"{float(analysis['sentiment_score']):.2%}", |
| @@ -85,5 +130,14 @@ class AIAnalyzer: | @@ -85,5 +130,14 @@ class AIAnalyzer: | ||
| 85 | ).strftime('%Y-%m-%d %H:%M:%S') | 130 | ).strftime('%Y-%m-%d %H:%M:%S') |
| 86 | } | 131 | } |
| 87 | 132 | ||
| 133 | + # 如果是深度分析,添加额外信息 | ||
| 134 | + if 'risk_factors' in analysis: | ||
| 135 | + base_result.update({ | ||
| 136 | + 'risk_factors': analysis['risk_factors'], | ||
| 137 | + 'suggestions': analysis['suggestions'] | ||
| 138 | + }) | ||
| 139 | + | ||
| 140 | + return base_result | ||
| 141 | + | ||
| 88 | # 创建全局AI分析器实例 | 142 | # 创建全局AI分析器实例 |
| 89 | ai_analyzer = AIAnalyzer() | 143 | ai_analyzer = AIAnalyzer() |
| @@ -308,11 +308,33 @@ def articleChar(id): | @@ -308,11 +308,33 @@ def articleChar(id): | ||
| 308 | @pb.route('/api/analyze_messages', methods=['POST']) | 308 | @pb.route('/api/analyze_messages', methods=['POST']) |
| 309 | async def analyze_messages(): | 309 | async def analyze_messages(): |
| 310 | try: | 310 | try: |
| 311 | - # 获取最近50条消息 | ||
| 312 | - messages = getRecentMessages(50) # 需要实现这个函数 | 311 | + # 获取请求参数 |
| 312 | + data = request.get_json() | ||
| 313 | + batch_size = data.get('batch_size', 50) | ||
| 314 | + model_type = data.get('model_type', 'gpt-3.5-turbo') | ||
| 315 | + analysis_depth = data.get('analysis_depth', 'standard') | ||
| 316 | + | ||
| 317 | + # 获取最近的消息 | ||
| 318 | + messages = getRecentMessages(batch_size) | ||
| 319 | + if not messages: | ||
| 320 | + return jsonify({ | ||
| 321 | + 'success': False, | ||
| 322 | + 'error': '没有找到需要分析的消息' | ||
| 323 | + }), 404 | ||
| 313 | 324 | ||
| 314 | # 调用AI进行分析 | 325 | # 调用AI进行分析 |
| 315 | - analysis_results = await ai_analyzer.analyze_messages(messages) | 326 | + analysis_results = await ai_analyzer.analyze_messages( |
| 327 | + messages=messages, | ||
| 328 | + batch_size=batch_size, | ||
| 329 | + model_type=model_type, | ||
| 330 | + analysis_depth=analysis_depth | ||
| 331 | + ) | ||
| 332 | + | ||
| 333 | + if not analysis_results: | ||
| 334 | + return jsonify({ | ||
| 335 | + 'success': False, | ||
| 336 | + 'error': '分析过程中出现错误' | ||
| 337 | + }), 500 | ||
| 316 | 338 | ||
| 317 | # 保存到数据库 | 339 | # 保存到数据库 |
| 318 | with Session(engine) as session: | 340 | with Session(engine) as session: |
| @@ -337,7 +359,14 @@ async def analyze_messages(): | @@ -337,7 +359,14 @@ async def analyze_messages(): | ||
| 337 | 359 | ||
| 338 | return jsonify({ | 360 | return jsonify({ |
| 339 | 'success': True, | 361 | 'success': True, |
| 340 | - 'data': display_results | 362 | + 'data': display_results, |
| 363 | + 'meta': { | ||
| 364 | + 'total_messages': len(messages), | ||
| 365 | + 'analyzed_messages': len(analysis_results), | ||
| 366 | + 'batch_size': batch_size, | ||
| 367 | + 'model_type': model_type, | ||
| 368 | + 'analysis_depth': analysis_depth | ||
| 369 | + } | ||
| 341 | }) | 370 | }) |
| 342 | 371 | ||
| 343 | except Exception as e: | 372 | except Exception as e: |
| @@ -454,11 +454,74 @@ | @@ -454,11 +454,74 @@ | ||
| 454 | <div class="header-title"> | 454 | <div class="header-title"> |
| 455 | <h4 class="card-title">AI深度分析</h4> | 455 | <h4 class="card-title">AI深度分析</h4> |
| 456 | </div> | 456 | </div> |
| 457 | - <button class="btn btn-primary" onclick="requestAIAnalysis()"> | ||
| 458 | - 开始AI分析 | 457 | + <div class="analysis-controls"> |
| 458 | + <!-- 批量处理设置 --> | ||
| 459 | + <div class="d-flex align-items-center"> | ||
| 460 | + <div class="form-group mx-2 mb-0"> | ||
| 461 | + <select id="batchSize" class="form-control form-control-sm"> | ||
| 462 | + <option value="10">每批10条</option> | ||
| 463 | + <option value="20">每批20条</option> | ||
| 464 | + <option value="50" selected>每批50条</option> | ||
| 465 | + <option value="100">每批100条</option> | ||
| 466 | + </select> | ||
| 467 | + </div> | ||
| 468 | + <div class="form-group mx-2 mb-0"> | ||
| 469 | + <select id="modelType" class="form-control form-control-sm"> | ||
| 470 | + <option value="gpt-3.5-turbo" selected>GPT-3.5</option> | ||
| 471 | + <option value="gpt-4">GPT-4</option> | ||
| 472 | + </select> | ||
| 473 | + </div> | ||
| 474 | + <div class="form-group mx-2 mb-0"> | ||
| 475 | + <select id="analysisDepth" class="form-control form-control-sm"> | ||
| 476 | + <option value="basic">基础分析</option> | ||
| 477 | + <option value="standard" selected>标准分析</option> | ||
| 478 | + <option value="deep">深度分析</option> | ||
| 479 | + </select> | ||
| 480 | + </div> | ||
| 481 | + <div class="custom-control custom-switch mx-2"> | ||
| 482 | + <input type="checkbox" class="custom-control-input" id="autoUpdate"> | ||
| 483 | + <label class="custom-control-label" for="autoUpdate">自动更新</label> | ||
| 484 | + </div> | ||
| 485 | + <button class="btn btn-primary btn-sm" onclick="requestAIAnalysis()"> | ||
| 486 | + 开始分析 | ||
| 459 | </button> | 487 | </button> |
| 460 | </div> | 488 | </div> |
| 489 | + </div> | ||
| 490 | + </div> | ||
| 461 | <div class="card-body"> | 491 | <div class="card-body"> |
| 492 | + <!-- 进度显示 --> | ||
| 493 | + <div id="analysis-progress" class="mb-3" style="display: none;"> | ||
| 494 | + <div class="progress"> | ||
| 495 | + <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div> | ||
| 496 | + </div> | ||
| 497 | + <small class="text-muted mt-1 d-block">正在分析: <span id="progress-text">0/0</span></small> | ||
| 498 | + </div> | ||
| 499 | + | ||
| 500 | + <!-- 分析结果过滤器 --> | ||
| 501 | + <div class="mb-3"> | ||
| 502 | + <div class="d-flex align-items-center"> | ||
| 503 | + <div class="form-group mb-0 mr-2"> | ||
| 504 | + <input type="text" class="form-control form-control-sm" id="keywordFilter" placeholder="按关键词过滤"> | ||
| 505 | + </div> | ||
| 506 | + <div class="form-group mb-0 mr-2"> | ||
| 507 | + <select class="form-control form-control-sm" id="sentimentFilter"> | ||
| 508 | + <option value="">全部情感</option> | ||
| 509 | + <option value="积极">积极</option> | ||
| 510 | + <option value="消极">消极</option> | ||
| 511 | + <option value="中性">中性</option> | ||
| 512 | + </select> | ||
| 513 | + </div> | ||
| 514 | + <div class="form-group mb-0"> | ||
| 515 | + <select class="form-control form-control-sm" id="riskFilter"> | ||
| 516 | + <option value="">全部风险等级</option> | ||
| 517 | + <option value="高">高风险</option> | ||
| 518 | + <option value="中">中风险</option> | ||
| 519 | + <option value="低">低风险</option> | ||
| 520 | + </select> | ||
| 521 | + </div> | ||
| 522 | + </div> | ||
| 523 | + </div> | ||
| 524 | + | ||
| 462 | <div id="ai-analysis-results" class="analysis-container"> | 525 | <div id="ai-analysis-results" class="analysis-container"> |
| 463 | <!-- 分析结果将在这里动态显示 --> | 526 | <!-- 分析结果将在这里动态显示 --> |
| 464 | </div> | 527 | </div> |
| @@ -467,7 +530,7 @@ | @@ -467,7 +530,7 @@ | ||
| 467 | </div> | 530 | </div> |
| 468 | </div> | 531 | </div> |
| 469 | 532 | ||
| 470 | -<!-- 添加必要的CSS样式 --> | 533 | +<!-- 更新CSS样式 --> |
| 471 | <style> | 534 | <style> |
| 472 | .analysis-container { | 535 | .analysis-container { |
| 473 | max-height: 600px; | 536 | max-height: 600px; |
| @@ -481,10 +544,12 @@ | @@ -481,10 +544,12 @@ | ||
| 481 | margin-bottom: 15px; | 544 | margin-bottom: 15px; |
| 482 | background-color: #fff; | 545 | background-color: #fff; |
| 483 | box-shadow: 0 2px 4px rgba(0,0,0,0.1); | 546 | box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
| 547 | + transition: all 0.3s ease; | ||
| 484 | } | 548 | } |
| 485 | 549 | ||
| 486 | .analysis-card:hover { | 550 | .analysis-card:hover { |
| 487 | box-shadow: 0 4px 8px rgba(0,0,0,0.15); | 551 | box-shadow: 0 4px 8px rgba(0,0,0,0.15); |
| 552 | + transform: translateY(-2px); | ||
| 488 | } | 553 | } |
| 489 | 554 | ||
| 490 | .risk-level { | 555 | .risk-level { |
| @@ -521,32 +586,167 @@ | @@ -521,32 +586,167 @@ | ||
| 521 | padding: 4px 8px; | 586 | padding: 4px 8px; |
| 522 | border-radius: 16px; | 587 | border-radius: 16px; |
| 523 | font-size: 0.9em; | 588 | font-size: 0.9em; |
| 589 | + transition: all 0.2s ease; | ||
| 590 | +} | ||
| 591 | + | ||
| 592 | +.keyword-tag:hover { | ||
| 593 | + background-color: #1976d2; | ||
| 594 | + color: #fff; | ||
| 595 | +} | ||
| 596 | + | ||
| 597 | +.sentiment-score { | ||
| 598 | + display: inline-block; | ||
| 599 | + padding: 2px 6px; | ||
| 600 | + border-radius: 4px; | ||
| 601 | + font-size: 0.9em; | ||
| 602 | + margin-left: 8px; | ||
| 603 | +} | ||
| 604 | + | ||
| 605 | +.sentiment-positive { | ||
| 606 | + background-color: #e8f5e9; | ||
| 607 | + color: #2e7d32; | ||
| 608 | +} | ||
| 609 | + | ||
| 610 | +.sentiment-negative { | ||
| 611 | + background-color: #ffebee; | ||
| 612 | + color: #c62828; | ||
| 613 | +} | ||
| 614 | + | ||
| 615 | +.sentiment-neutral { | ||
| 616 | + background-color: #f5f5f5; | ||
| 617 | + color: #616161; | ||
| 618 | +} | ||
| 619 | + | ||
| 620 | +.progress { | ||
| 621 | + height: 0.5rem; | ||
| 622 | +} | ||
| 623 | + | ||
| 624 | +.analysis-controls { | ||
| 625 | + display: flex; | ||
| 626 | + align-items: center; | ||
| 627 | + flex-wrap: wrap; | ||
| 628 | + gap: 10px; | ||
| 629 | +} | ||
| 630 | + | ||
| 631 | +@media (max-width: 768px) { | ||
| 632 | + .analysis-controls { | ||
| 633 | + flex-direction: column; | ||
| 634 | + align-items: stretch; | ||
| 635 | + } | ||
| 524 | } | 636 | } |
| 525 | </style> | 637 | </style> |
| 526 | 638 | ||
| 527 | -<!-- 添加必要的JavaScript代码 --> | 639 | +<!-- 更新JavaScript代码 --> |
| 528 | <script> | 640 | <script> |
| 641 | +let currentAnalysis = { | ||
| 642 | + inProgress: false, | ||
| 643 | + totalMessages: 0, | ||
| 644 | + processedMessages: 0, | ||
| 645 | + autoUpdateInterval: null | ||
| 646 | +}; | ||
| 647 | + | ||
| 529 | async function requestAIAnalysis() { | 648 | async function requestAIAnalysis() { |
| 649 | + if (currentAnalysis.inProgress) { | ||
| 650 | + return; | ||
| 651 | + } | ||
| 652 | + | ||
| 530 | try { | 653 | try { |
| 654 | + currentAnalysis.inProgress = true; | ||
| 655 | + showProgress(); | ||
| 656 | + | ||
| 657 | + const batchSize = parseInt(document.getElementById('batchSize').value); | ||
| 658 | + const modelType = document.getElementById('modelType').value; | ||
| 659 | + const analysisDepth = document.getElementById('analysisDepth').value; | ||
| 660 | + | ||
| 531 | const response = await fetch('/page/api/analyze_messages', { | 661 | const response = await fetch('/page/api/analyze_messages', { |
| 532 | method: 'POST', | 662 | method: 'POST', |
| 533 | headers: { | 663 | headers: { |
| 534 | 'Content-Type': 'application/json' | 664 | 'Content-Type': 'application/json' |
| 535 | - } | 665 | + }, |
| 666 | + body: JSON.stringify({ | ||
| 667 | + batch_size: batchSize, | ||
| 668 | + model_type: modelType, | ||
| 669 | + analysis_depth: analysisDepth | ||
| 670 | + }) | ||
| 536 | }); | 671 | }); |
| 537 | 672 | ||
| 538 | const result = await response.json(); | 673 | const result = await response.json(); |
| 539 | if (result.success) { | 674 | if (result.success) { |
| 540 | displayAnalysisResults(result.data); | 675 | displayAnalysisResults(result.data); |
| 676 | + setupAutoUpdate(); | ||
| 541 | } else { | 677 | } else { |
| 542 | - alert('分析失败: ' + result.error); | 678 | + showError(result.error); |
| 543 | } | 679 | } |
| 544 | } catch (error) { | 680 | } catch (error) { |
| 545 | console.error('AI分析请求失败:', error); | 681 | console.error('AI分析请求失败:', error); |
| 546 | - alert('请求失败,请稍后重试'); | 682 | + showError('请求失败,请稍后重试'); |
| 683 | + } finally { | ||
| 684 | + currentAnalysis.inProgress = false; | ||
| 685 | + hideProgress(); | ||
| 547 | } | 686 | } |
| 548 | } | 687 | } |
| 549 | 688 | ||
| 689 | +function showProgress() { | ||
| 690 | + const progressDiv = document.getElementById('analysis-progress'); | ||
| 691 | + progressDiv.style.display = 'block'; | ||
| 692 | + updateProgress(0, 100); | ||
| 693 | +} | ||
| 694 | + | ||
| 695 | +function hideProgress() { | ||
| 696 | + const progressDiv = document.getElementById('analysis-progress'); | ||
| 697 | + progressDiv.style.display = 'none'; | ||
| 698 | +} | ||
| 699 | + | ||
| 700 | +function updateProgress(current, total) { | ||
| 701 | + const progressBar = document.querySelector('.progress-bar'); | ||
| 702 | + const progressText = document.getElementById('progress-text'); | ||
| 703 | + const percentage = (current / total) * 100; | ||
| 704 | + | ||
| 705 | + progressBar.style.width = `${percentage}%`; | ||
| 706 | + progressText.textContent = `${current}/${total}`; | ||
| 707 | +} | ||
| 708 | + | ||
| 709 | +function setupAutoUpdate() { | ||
| 710 | + const autoUpdate = document.getElementById('autoUpdate').checked; | ||
| 711 | + if (autoUpdate && !currentAnalysis.autoUpdateInterval) { | ||
| 712 | + currentAnalysis.autoUpdateInterval = setInterval(requestAIAnalysis, 300000); // 5分钟更新一次 | ||
| 713 | + } else if (!autoUpdate && currentAnalysis.autoUpdateInterval) { | ||
| 714 | + clearInterval(currentAnalysis.autoUpdateInterval); | ||
| 715 | + currentAnalysis.autoUpdateInterval = null; | ||
| 716 | + } | ||
| 717 | +} | ||
| 718 | + | ||
| 719 | +function filterResults() { | ||
| 720 | + const keyword = document.getElementById('keywordFilter').value.toLowerCase(); | ||
| 721 | + const sentiment = document.getElementById('sentimentFilter').value; | ||
| 722 | + const risk = document.getElementById('riskFilter').value; | ||
| 723 | + | ||
| 724 | + const cards = document.querySelectorAll('.analysis-card'); | ||
| 725 | + cards.forEach(card => { | ||
| 726 | + let show = true; | ||
| 727 | + | ||
| 728 | + // 关键词过滤 | ||
| 729 | + if (keyword) { | ||
| 730 | + const content = card.textContent.toLowerCase(); | ||
| 731 | + show = show && content.includes(keyword); | ||
| 732 | + } | ||
| 733 | + | ||
| 734 | + // 情感过滤 | ||
| 735 | + if (sentiment) { | ||
| 736 | + const cardSentiment = card.querySelector('.sentiment-text').textContent; | ||
| 737 | + show = show && cardSentiment.includes(sentiment); | ||
| 738 | + } | ||
| 739 | + | ||
| 740 | + // 风险等级过滤 | ||
| 741 | + if (risk) { | ||
| 742 | + const cardRisk = card.querySelector('.risk-level').textContent; | ||
| 743 | + show = show && cardRisk.includes(risk); | ||
| 744 | + } | ||
| 745 | + | ||
| 746 | + card.style.display = show ? 'block' : 'none'; | ||
| 747 | + }); | ||
| 748 | +} | ||
| 749 | + | ||
| 550 | function displayAnalysisResults(results) { | 750 | function displayAnalysisResults(results) { |
| 551 | const container = document.getElementById('ai-analysis-results'); | 751 | const container = document.getElementById('ai-analysis-results'); |
| 552 | container.innerHTML = ''; // 清空现有结果 | 752 | container.innerHTML = ''; // 清空现有结果 |
| @@ -559,6 +759,10 @@ function displayAnalysisResults(results) { | @@ -559,6 +759,10 @@ function displayAnalysisResults(results) { | ||
| 559 | analysis.risk_level === '高' ? 'risk-high' : | 759 | analysis.risk_level === '高' ? 'risk-high' : |
| 560 | analysis.risk_level === '中' ? 'risk-medium' : 'risk-low'; | 760 | analysis.risk_level === '中' ? 'risk-medium' : 'risk-low'; |
| 561 | 761 | ||
| 762 | + const sentimentClass = | ||
| 763 | + analysis.sentiment === '积极' ? 'sentiment-positive' : | ||
| 764 | + analysis.sentiment === '消极' ? 'sentiment-negative' : 'sentiment-neutral'; | ||
| 765 | + | ||
| 562 | card.innerHTML = ` | 766 | card.innerHTML = ` |
| 563 | <div class="d-flex justify-content-between align-items-center"> | 767 | <div class="d-flex justify-content-between align-items-center"> |
| 564 | <h5 class="mb-2">消息ID: ${analysis.id}</h5> | 768 | <h5 class="mb-2">消息ID: ${analysis.id}</h5> |
| @@ -567,8 +771,9 @@ function displayAnalysisResults(results) { | @@ -567,8 +771,9 @@ function displayAnalysisResults(results) { | ||
| 567 | </span> | 771 | </span> |
| 568 | </div> | 772 | </div> |
| 569 | <div class="mb-2"> | 773 | <div class="mb-2"> |
| 570 | - <strong>情感倾向:</strong> ${analysis.sentiment} | ||
| 571 | - <span class="ml-2">(${analysis.sentiment_score})</span> | 774 | + <strong>情感倾向:</strong> |
| 775 | + <span class="sentiment-text">${analysis.sentiment}</span> | ||
| 776 | + <span class="sentiment-score ${sentimentClass}">${analysis.sentiment_score}</span> | ||
| 572 | </div> | 777 | </div> |
| 573 | <div class="keywords-container"> | 778 | <div class="keywords-container"> |
| 574 | ${analysis.keywords.split(',').map(keyword => | 779 | ${analysis.keywords.split(',').map(keyword => |
| @@ -590,10 +795,28 @@ function displayAnalysisResults(results) { | @@ -590,10 +795,28 @@ function displayAnalysisResults(results) { | ||
| 590 | 795 | ||
| 591 | container.appendChild(card); | 796 | container.appendChild(card); |
| 592 | }); | 797 | }); |
| 798 | + | ||
| 799 | + // 应用当前的过滤器 | ||
| 800 | + filterResults(); | ||
| 593 | } | 801 | } |
| 594 | 802 | ||
| 595 | -// 页面加载完成后自动请求一次AI分析 | ||
| 596 | -document.addEventListener('DOMContentLoaded', requestAIAnalysis); | 803 | +function showError(message) { |
| 804 | + alert(message); | ||
| 805 | +} | ||
| 806 | + | ||
| 807 | +// 设置事件监听器 | ||
| 808 | +document.addEventListener('DOMContentLoaded', () => { | ||
| 809 | + // 自动更新切换 | ||
| 810 | + document.getElementById('autoUpdate').addEventListener('change', setupAutoUpdate); | ||
| 811 | + | ||
| 812 | + // 过滤器变化监听 | ||
| 813 | + document.getElementById('keywordFilter').addEventListener('input', filterResults); | ||
| 814 | + document.getElementById('sentimentFilter').addEventListener('change', filterResults); | ||
| 815 | + document.getElementById('riskFilter').addEventListener('change', filterResults); | ||
| 816 | + | ||
| 817 | + // 首次加载时请求分析 | ||
| 818 | + requestAIAnalysis(); | ||
| 819 | +}); | ||
| 597 | </script> | 820 | </script> |
| 598 | 821 | ||
| 599 | {% endblock %} | 822 | {% endblock %} |
-
Please register or login to post a comment