Browse Source

增加添加题目的功能

yemeishu 1 tháng trước cách đây
mục cha
commit
86dc8f3f63

+ 92 - 1
app/Filament/Pages/QuestionManagement.php

@@ -387,10 +387,24 @@ class QuestionManagement extends Page
                         $this->refreshData();
                     } else {
                         $error = $taskStatus['error_message'] ?? '未知错误';
+
+                        // 分析错误类型并提供解决建议
+                        $suggestion = $this->getErrorSuggestion($error);
+
                         Notification::make()
                             ->title('生成失败')
-                            ->body($error)
+                            ->body($error . ($suggestion ? "\n\n建议:{$suggestion}" : ''))
                             ->danger()
+                            ->persistent()  // 让通知持续显示,用户需要手动关闭
+                            ->actions([
+                                \Filament\Notifications\Actions\Action::make('retry')
+                                    ->label('重试')
+                                    ->color('primary')
+                                    ->emit('retryGeneration', [$this->generateKpCode, $this->selectedSkills, $this->questionCount]),
+                                \Filament\Notifications\Actions\Action::make('dismiss')
+                                    ->label('关闭')
+                                    ->color('gray'),
+                            ])
                             ->send();
                     }
 
@@ -599,6 +613,83 @@ class QuestionManagement extends Page
         return $pages;
     }
 
+    /**
+     * 重置缓存数据
+     */
+    public function resetCache(): void
+    {
+        // 重置分页参数
+        $this->currentPage = 1;
+        $this->search = null;
+        $this->selectedKpCode = null;
+        $selectedDifficulty = null;
+
+        // 重置生成的题目缓存数据
+        $this->resetQuestionsCache();
+
+        // 强制刷新Computed属性
+        unset($this->questions);
+        unset($this->meta);
+    }
+
+    /**
+     * 重置题目缓存
+     */
+    private function resetQuestionsCache(): void
+    {
+        // 这里可以根据需要添加具体的缓存清理逻辑
+        // 例如:清除任何本地存储的缓存、Redis缓存等
+        // 目前简单重置计算属性即可
+    }
+
+    /**
+     * 获取错误解决建议
+     */
+    private function getErrorSuggestion(string $error): string
+    {
+        if (str_contains($error, 'peer closed connection') || str_contains($error, 'incomplete chunked read')) {
+            return '网络连接不稳定,系统已自动重试。如果问题持续,请稍后再试或减少生成题目数量。';
+        }
+
+        if (str_contains($error, 'Authentication Fails') || str_contains($error, 'invalid api key')) {
+            return 'API密钥认证失败,请检查DeepSeek API密钥配置。';
+        }
+
+        if (str_contains($error, 'timeout') || str_contains($error, '超时')) {
+            return '请求超时,建议减少题目数量或稍后再试。';
+        }
+
+        if (str_contains($error, 'rate limit') || str_contains($error, 'too many requests')) {
+            return 'API调用频率限制,请稍后再试。';
+        }
+
+        if (str_contains($error, 'insufficient quota') || str_contains($error, 'balance')) {
+            return 'API账户余额不足,请充值后重试。';
+        }
+
+        return '请检查网络连接或稍后再试,如问题持续请联系技术支持。';
+    }
+
+    /**
+     * 重试生成
+     */
+    #[On('retryGeneration')]
+    public function retryGeneration(string $kpCode, array $skills, int $count): void
+    {
+        $this->generateKpCode = $kpCode;
+        $this->selectedSkills = $skills;
+        $this->questionCount = min($count, 50); // 重试时限制题目数量
+
+        Notification::make()
+            ->title('准备重试')
+            ->body("正在为知识点 {$kpCode} 重新生成 {$this->questionCount} 道题目...")
+            ->info()
+            ->send();
+
+        // 延迟2秒后开始重试,避免频率限制
+        $this->js('setTimeout(() => { $wire.dispatch("execute-generate") }, 2000)');
+    }
+
     /**
      * 头部操作按钮已在视图中直接添加
      */

+ 1 - 1
app/Services/KnowledgeGraphService.php

@@ -12,7 +12,7 @@ class KnowledgeGraphService
     public function __construct()
     {
         // 从配置文件读取base_url,如果没有配置则使用容器服务名
-        $this->baseUrl = config('services.knowledge_api.base_url', env('KNOWLEDGE_API_BASE', env('KNOWLEDGE_API_BASE_URL', 'http://api-knowledge:5011')));
+        $this->baseUrl = config('services.knowledge_api.base_url', env('KNOWLEDGE_API_BASE', env('KNOWLEDGE_API_BASE_URL', 'http://localhost:5011')));
         $this->baseUrl = rtrim($this->baseUrl, '/');
     }
 

+ 1 - 0
bootstrap/app.php

@@ -7,6 +7,7 @@ use Illuminate\Foundation\Configuration\Middleware;
 return Application::configure(basePath: dirname(__DIR__))
     ->withRouting(
         web: __DIR__.'/../routes/web.php',
+        api: __DIR__.'/../routes/api.php',
         commands: __DIR__.'/../routes/console.php',
         health: '/up',
     )

+ 88 - 1
resources/views/filament/pages/question-management.blade.php

@@ -225,7 +225,14 @@
                                         }
                                         $skillNames = [];
                                         foreach ($skills as $skill) {
-                                            $skillNames[] = $skill['skill_name'] ?? ($skill['skill_code'] ?? 'N/A');
+                                            // 处理不同格式的技能数据
+                                            if (is_string($skill)) {
+                                                // 如果是字符串,直接显示
+                                                $skillNames[] = $skill;
+                                            } elseif (is_array($skill)) {
+                                                // 如果是对象,显示skill_name或skill_code
+                                                $skillNames[] = $skill['skill_name'] ?? $skill['skill_code'] ?? 'N/A';
+                                            }
                                         }
                                         $skillText = implode(', ', array_slice($skillNames, 0, 2));
                                         if (count($skillNames) > 2) {
@@ -530,6 +537,86 @@
                     window.location.reload();
                 }, 500); // 延迟500ms刷新,给用户时间看到成功消息
             });
+
+            // 监听实时回调通知
+            Livewire.on('task-failed', (event) => {
+                const { taskId, error, kpCode } = event[0];
+
+                // 显示详细错误通知
+                const notification = document.createElement('div');
+                notification.className = 'fixed top-4 right-4 bg-red-50 border border-red-200 rounded-lg shadow-lg p-4 max-w-md z-50';
+                notification.innerHTML = `
+                    <div class="flex items-start">
+                        <div class="flex-shrink-0">
+                            <svg class="h-5 w-5 text-red-400" fill="currentColor" viewBox="0 0 20 20">
+                                <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
+                            </svg>
+                        </div>
+                        <div class="ml-3">
+                            <h3 class="text-sm font-medium text-red-800">题目生成失败</h3>
+                            <div class="mt-2 text-sm text-red-700">
+                                <p>任务ID: ${taskId}</p>
+                                <p>知识点: ${kpCode}</p>
+                                <p class="mt-1 font-mono text-xs">${error}</p>
+                            </div>
+                            <div class="mt-3 flex space-x-2">
+                                <button onclick="this.closest('.fixed').remove()" class="text-sm text-red-600 hover:text-red-800 underline">
+                                    关闭
+                                </button>
+                            </div>
+                        </div>
+                    </div>
+                `;
+
+                document.body.appendChild(notification);
+
+                // 5秒后自动移除
+                setTimeout(() => {
+                    if (notification.parentNode) {
+                        notification.parentNode.removeChild(notification);
+                    }
+                }, 10000);
+            });
+
+            // 监听任务成功回调
+            Livewire.on('task-completed', (event) => {
+                const { taskId, kpCode, total } = event[0];
+
+                // 显示成功通知
+                const notification = document.createElement('div');
+                notification.className = 'fixed top-4 right-4 bg-green-50 border border-green-200 rounded-lg shadow-lg p-4 max-w-md z-50';
+                notification.innerHTML = `
+                    <div class="flex items-start">
+                        <div class="flex-shrink-0">
+                            <svg class="h-5 w-5 text-green-400" fill="currentColor" viewBox="0 0 20 20">
+                                <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
+                            </svg>
+                        </div>
+                        <div class="ml-3">
+                            <h3 class="text-sm font-medium text-green-800">题目生成成功</h3>
+                            <div class="mt-2 text-sm text-green-700">
+                                <p>任务ID: ${taskId}</p>
+                                <p>知识点: ${kpCode}</p>
+                                <p>已成功生成 ${total} 道题目</p>
+                            </div>
+                            <div class="mt-3">
+                                <button onclick="location.reload()" class="text-sm text-green-600 hover:text-green-800 underline">
+                                    刷新页面
+                                </button>
+                            </div>
+                        </div>
+                    </div>
+                `;
+
+                document.body.appendChild(notification);
+
+                // 3秒后自动移除
+                setTimeout(() => {
+                    if (notification.parentNode) {
+                        notification.parentNode.removeChild(notification);
+                    }
+                }, 5000);
+            });
         });
     </script>
 </x-filament-panels::page>

+ 15 - 0
routes/api.php

@@ -24,6 +24,21 @@ Route::post('/questions/callback', function () {
         // 存储回调结果到 session 或缓存中,供前端查询
         session(['question_gen_callback_' . $data['task_id'] => $data]);
 
+        // 广播实时通知到前端
+        if ($data['status'] === 'completed') {
+            $kpCode = $data['result']['kp_code'] ?? '未知';
+            $total = $data['result']['total'] ?? 0;
+
+            // 广播成功事件
+            event(new \App\Events\QuestionGenerationCompleted($data['task_id'], $kpCode, $total));
+        } elseif ($data['status'] === 'failed') {
+            $error = $data['error'] ?? '未知错误';
+            $kpCode = $data['result']['kp_code'] ?? '未知';
+
+            // 广播失败事件
+            event(new \App\Events\QuestionGenerationFailed($data['task_id'], $kpCode, $error));
+        }
+
         return response()->json(['success' => true, 'message' => 'Callback received']);
     } catch (\Exception $e) {
         Log::error('Callback processing failed: ' . $e->getMessage());