Explorar el Código

修改 api 路径的问题

yemeishu hace 3 semanas
padre
commit
549b3c47ab

+ 58 - 39
app/Filament/Pages/QuestionGeneration.php

@@ -10,6 +10,7 @@ use Filament\Pages\Page;
 use UnitEnum;
 use Livewire\Attributes\Computed;
 use Illuminate\Support\Facades\Session;
+use Illuminate\Support\Facades\Http;
 
 class QuestionGeneration extends Page
 {
@@ -148,70 +149,88 @@ class QuestionGeneration extends Page
         $this->currentTaskId = null;
 
         try {
-            // 增加PHP脚本执行时间到120秒,给足够时间启动异步任务
-            set_time_limit(120);
-
             $service = app(QuestionBankService::class);
             $callbackUrl = route('api.questions.callback');
 
-            \Log::info("[QuestionGen] 开始生成,callback URL: " . $callbackUrl);
+            \Log::info("[QuestionGen] 开始异步生成,callback URL: " . $callbackUrl);
 
-            $result = $service->generateIntelligentQuestions([
+            // 异步请求生成题目
+            $params = [
                 'kp_code' => $this->generateKpCode,
                 'skills' => $this->selectedSkills,
                 'count' => $this->questionCount,
                 'difficulty' => $this->generateDifficulty,
                 'type' => $this->generateType,
                 'prompt_template' => $this->promptTemplate ?? null
-            ], $callbackUrl);
+            ];
 
-            if ($result['success'] ?? false) {
-                $this->currentTaskId = $result['task_id'] ?? null;
+            // 添加回调 URL
+            $params['callback_url'] = $callbackUrl;
 
-                \Log::info("[QuestionGen] ✅ 任务已创建: {$this->currentTaskId},准备跳转到题库管理");
-                \Log::info("[QuestionGen] 完整结果: " . json_encode($result));
+            // 获取 base URL(通过公共方法或配置)
+            $baseUrl = config('services.question_bank.base_url', env('QUESTION_BANK_API_BASE', 'http://localhost:5015'));
+            $baseUrl = rtrim($baseUrl, '/');
+            if (!str_ends_with($baseUrl, '/api')) {
+                $baseUrl .= '/api';
+            }
 
-                // 准备跳转URL(不传递task_id参数)
+            // 直接发送异步请求,不使用队列
+            try {
+                // 先立即跳转,避免阻塞
                 $redirectUrl = "/admin/question-management";
-                $taskId = $this->currentTaskId ?? 'unknown';
-                \Log::info("[QuestionGen] ✅ 准备跳转到: {$redirectUrl}");
-                \Log::info("[QuestionGen] ✅ 准备传递的taskId: {$taskId}");
-
-                // 使用简单可靠的Livewire dispatch事件
-                // 注意:不要设置 isGenerating = false,让状态栏继续显示
-                \Log::info("[QuestionGen] ✅ 即将分发跳转事件", [
-                    'url' => $redirectUrl,
-                    'taskId' => $taskId
+                $this->js("window.location.href = '{$redirectUrl}';");
+
+                // 使用 stream 方式快速发送请求
+                $ch = curl_init();
+                curl_setopt($ch, CURLOPT_URL, $baseUrl . '/ai/generate-intelligent-questions');
+                curl_setopt($ch, CURLOPT_POST, true);
+                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
+                curl_setopt($ch, CURLOPT_HTTPHEADER, [
+                    'Content-Type: application/json',
+                    'Accept: application/json'
+                ]);
+                curl_setopt($ch, CURLOPT_TIMEOUT, 1); // 1秒超时,只确保请求发出
+                curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
+                curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
+                curl_setopt($ch, CURLOPT_FORBID_REUSE, true);
+                curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
+
+                // 异步执行,不等待响应
+                curl_exec($ch);
+                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+                curl_close($ch);
+
+                \Log::info("[QuestionGen] 异步请求已发送", [
+                    'http_code' => $httpCode
                 ]);
 
-                // 使用最简单的事件名称和参数
-                $this->dispatch('redirect-now', url: $redirectUrl, taskId: $taskId);
-                \Log::info("[QuestionGen] ✅ 事件已分发");
-
-                // 使用Livewire的JavaScript方法执行直接跳转
-                $this->js("console.log('[QuestionGen] 直接JS跳转启动');
-                alert('✅ 任务已启动\\n任务 ID: {$taskId}\\n正在跳转到题库管理页面...');
-                setTimeout(function() {
-                    console.log('[QuestionGen] 直接跳转执行:', '{$redirectUrl}');
-                    window.location.href = '{$redirectUrl}';
-                }, 1000);");
-
-                // 明确返回,避免执行后面的代码
-                \Log::info("[QuestionGen] ✅ 即将返回");
-                return;
-            } else {
+                return; // 立即返回
+
+            } catch (\Exception $e) {
                 $this->isGenerating = false;
+                \Log::error("[QuestionGen] 发送异步请求失败", [
+                    'error' => $e->getMessage()
+                ]);
+
                 Notification::make()
-                    ->title('创建任务失败')
-                    ->body($result['message'] ?? '未知错误')
+                    ->title('❌ 请求发送失败')
+                    ->body('请检查网络连接并重试')
                     ->danger()
                     ->send();
             }
+        } catch (\Illuminate\Http\Client\ConnectionException $e) {
+            $this->isGenerating = false;
+            \Log::error("[QuestionGen] 连接异常: " . $e->getMessage());
+            Notification::make()
+                ->title('连接AI服务失败')
+                ->body('请检查AI服务是否正常运行')
+                ->danger()
+                ->send();
         } catch (\Exception $e) {
             $this->isGenerating = false;
             \Log::error("[QuestionGen] 生成异常: " . $e->getMessage());
             Notification::make()
-                ->title('生成异常')
+                ->title('请求异常')
                 ->body($e->getMessage())
                 ->danger()
                 ->send();

+ 3 - 19
app/Filament/Pages/QuestionManagement.php

@@ -32,9 +32,6 @@ class QuestionManagement extends Page
     public bool $showDetailModal = false;
     public array $editing = [];
 
-    // ✅ 用于存储从URL参数获取的任务ID
-    public ?string $pendingTaskId = null;
-
 
     #[Computed(cache: false)]
     public function questions(): array
@@ -81,10 +78,12 @@ class QuestionManagement extends Page
     // ✅ 检查待处理的回调任务(简化版)
     public function mount(): void
     {
-        // 检查是否有从其他页面跳转带来的通知
+        // 检查是否有从回调生成的通知
         $notification = Session::get('notification');
         if ($notification) {
             $color = $notification['color'] ?? 'info';
+
+            // 使用 persistent 确保通知持续显示
             Notification::make()
                 ->title($notification['title'] ?? '通知')
                 ->body($notification['body'] ?? '')
@@ -92,23 +91,8 @@ class QuestionManagement extends Page
                 ->persistent()
                 ->send();
         }
-
-        // 从 request 中获取 task_id 参数(来自 URL 或缓存)
-        $taskId = request()->get('task_id');
-        if ($taskId) {
-            $this->pendingTaskId = $taskId;
-
-            // 简化:只记录task_id,前端通过JS定期检查回调状态
-            Notification::make()
-                ->title('📋 任务已创建')
-                ->body("任务 ID: {$taskId}\n等待后台生成完成,请稍候...")
-                ->info()
-                ->persistent()
-                ->send();
-        }
     }
 
-
     public function deleteQuestion(string $questionCode): void
     {
         try {

+ 30 - 0
app/Http/Controllers/NotificationController.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Http\Request;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Session;
+
+class NotificationController extends Controller
+{
+    /**
+     * 检查是否有新的通知
+     */
+    public function checkNotifications(Request $request): JsonResponse
+    {
+        $notification = Session::get('notification');
+
+        if ($notification) {
+            // 清除session中的通知,避免重复显示
+            Session::forget('notification');
+
+            return response()->json([
+                'hasNotification' => true,
+                'notification' => $notification
+            ]);
+        }
+
+        return response()->json(['hasNotification' => false]);
+    }
+}

+ 131 - 78
app/Services/QuestionServiceApi.php

@@ -7,6 +7,7 @@ use Illuminate\Http\Client\RequestException;
 use Illuminate\Support\Collection;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\Http;
+use Illuminate\Support\Facades\Log;
 
 class QuestionServiceApi
 {
@@ -25,47 +26,71 @@ class QuestionServiceApi
      */
     public function listQuestions(int $page = 1, int $perPage = 50, array $filters = []): array
     {
-        $cacheKey = sprintf(
-            'questions-list-%d-%d-%s',
-            $page,
-            $perPage,
-            md5(json_encode($filters))
-        );
-
-        return Cache::remember(
-            $cacheKey,
-            now()->addSeconds($this->cacheTtl),
-            function () use ($page, $perPage, $filters): array {
-                $query = array_filter([
-                    'page' => $page,
-                    'per_page' => $perPage,
-                    'kp_code' => $filters['kp_code'] ?? null,
-                    'difficulty' => $filters['difficulty'] ?? null,
-                    'type' => $filters['type'] ?? null,
-                    'skill' => $filters['skill'] ?? null,
-                    'search' => $filters['search'] ?? null,
-                ], fn ($value) => filled($value));
-
-                $response = $this->request('GET', '/questions', $query);
-
-                // 处理数学公式
-                $data = $response['data'] ?? [];
-                foreach ($data as &$question) {
-                    // 使用数学公式处理器处理题目数据
-                    $question = MathFormulaProcessor::processQuestionData($question);
-                }
+        // 移除缓存,直接请求最新数据
+        $query = array_filter([
+            'page' => $page,
+            'per_page' => $perPage,
+            'kp_code' => $filters['kp_code'] ?? null,
+            'difficulty' => $filters['difficulty'] ?? null,
+            'type' => $filters['type'] ?? null,
+            'skill' => $filters['skill'] ?? null,
+            'search' => $filters['search'] ?? null,
+        ], fn ($value) => filled($value));
 
+        // 使用更短的超时时间避免阻塞
+        try {
+            $response = Http::timeout(5) // 5秒超时
+                ->get($this->baseUrl . '/questions', $query);
+
+            if (!$response->successful()) {
+                Log::warning('题目列表API调用失败', [
+                    'status' => $response->status(),
+                    'url' => $this->baseUrl . '/questions'
+                ]);
                 return [
-                    'data' => $data,
-                    'meta' => $response['meta'] ?? [
+                    'data' => [],
+                    'meta' => [
                         'page' => $page,
                         'per_page' => $perPage,
-                        'total' => is_array($response) ? count($response) : 0,
-                        'total_pages' => 1,
-                    ],
+                        'total' => 0,
+                        'total_pages' => 0,
+                    ]
                 ];
             }
-        );
+
+            $response = $response->json();
+        } catch (\Illuminate\Http\Client\ConnectionException $e) {
+            Log::error('题目列表API连接超时', [
+                'error' => $e->getMessage()
+            ]);
+            // 返回空数据,避免页面卡死
+            return [
+                'data' => [],
+                'meta' => [
+                    'page' => $page,
+                    'per_page' => $perPage,
+                    'total' => 0,
+                    'total_pages' => 0,
+                ]
+            ];
+        }
+
+        // 处理数学公式
+        $data = $response['data'] ?? [];
+        foreach ($data as &$question) {
+            // 使用数学公式处理器处理题目数据
+            $question = MathFormulaProcessor::processQuestionData($question);
+        }
+
+        return [
+            'data' => $data,
+            'meta' => $response['meta'] ?? [
+                'page' => $page,
+                'per_page' => $perPage,
+                'total' => is_array($response) ? count($response) : 0,
+                'total_pages' => 1,
+            ],
+        ];
     }
 
     /**
@@ -73,22 +98,42 @@ class QuestionServiceApi
      */
     public function getStatistics(): array
     {
-        $cacheKey = 'question-statistics';
-
-        return Cache::remember(
-            $cacheKey,
-            now()->addSeconds($this->cacheTtl),
-            function (): array {
-                $response = $this->request('GET', '/questions/statistics');
+        // 移除缓存,直接请求最新数据
+        try {
+            $response = Http::timeout(5) // 5秒超时
+                ->get($this->baseUrl . '/questions/statistics');
 
-                return $response ?? [
+            if (!$response->successful()) {
+                Log::warning('统计API调用失败', [
+                    'status' => $response->status()
+                ]);
+                return [
                     'total' => 0,
                     'by_difficulty' => [],
                     'by_kp' => [],
                     'by_source' => [],
                 ];
             }
-        );
+
+            $response = $response->json();
+        } catch (\Illuminate\Http\Client\ConnectionException $e) {
+            Log::error('统计API连接超时', [
+                'error' => $e->getMessage()
+            ]);
+            return [
+                'total' => 0,
+                'by_difficulty' => [],
+                'by_kp' => [],
+                'by_source' => [],
+            ];
+        }
+
+        return $response ?? [
+            'total' => 0,
+            'by_difficulty' => [],
+            'by_kp' => [],
+            'by_source' => [],
+        ];
     }
 
     /**
@@ -235,44 +280,52 @@ class QuestionServiceApi
      */
     public function getKnowledgePointOptions(): array
     {
-        try {
-            // 使用新的知识图谱API
-            $knowledgeApiBase = config('services.knowledge_api.base_url', 'http://localhost:5011');
-            $response = Http::timeout(10)
-                ->get($knowledgeApiBase . '/graph/export');
-
-            if ($response->successful()) {
-                $data = $response->json();
-                $nodes = $data['nodes'] ?? [];
-
-                // 转换为键值对格式
-                $options = [];
-                foreach ($nodes as $node) {
-                    $code = $node['kp_code'] ?? null;
-                    $name = $node['cn_name'] ?? null;
-                    
-                    if ($code && $name) {
-                        $options[$code] = $name;
+        // 使用缓存来避免重复请求
+        $cacheKey = 'knowledge-point-options';
+        return Cache::remember(
+            $cacheKey,
+            now()->addHours(1), // 缓存1小时
+            function () {
+                try {
+                    // 使用新的知识图谱API
+                    $knowledgeApiBase = config('services.knowledge_api.base_url', 'http://localhost:5011');
+                    $response = Http::timeout(30) // 增加超时时间到30秒
+                        ->get($knowledgeApiBase . '/graph/export');
+
+                    if ($response->successful()) {
+                        $data = $response->json();
+                        $nodes = $data['nodes'] ?? [];
+
+                        // 转换为键值对格式
+                        $options = [];
+                        foreach ($nodes as $node) {
+                            $code = $node['kp_code'] ?? null;
+                            $name = $node['cn_name'] ?? null;
+
+                            if ($code && $name) {
+                                $options[$code] = $name;
+                            }
+                        }
+
+                        // 按代码排序
+                        ksort($options);
+
+                        \Log::info('成功获取知识点选项', ['count' => count($options)]);
+                        return $options;
                     }
-                }
 
-                // 按代码排序
-                ksort($options);
+                    \Log::warning('知识图谱API调用失败', [
+                        'status' => $response->status(),
+                        'url' => $knowledgeApiBase . '/graph/export'
+                    ]);
+                } catch (\Exception $e) {
+                    \Log::error('Failed to get knowledge points: ' . $e->getMessage());
+                }
 
-                \Log::info('成功获取知识点选项', ['count' => count($options)]);
-                return $options;
+                // 返回空数组作为fallback
+                return [];
             }
-
-            \Log::warning('知识图谱API调用失败', [
-                'status' => $response->status(),
-                'url' => $knowledgeApiBase . '/graph/export'
-            ]);
-        } catch (\Exception $e) {
-            \Log::error('Failed to get knowledge points: ' . $e->getMessage());
-        }
-
-        // 返回空数组作为fallback
-        return [];
+        );
     }
 
     /**

+ 4 - 4
resources/views/filament/pages/question-generation.blade.php

@@ -180,12 +180,12 @@ function handleGenerateWithRedirect() {
         button.innerHTML = '<svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg><span class="text-white font-semibold">生成中...</span>';
     }
 
-    // 2秒后自动跳转(不管Livewire是否成功)
+    // 0.5秒后自动跳转(不管Livewire是否成功)
     setTimeout(() => {
-        console.log('[QuestionGen] 2秒后自动跳转到题库管理页面');
-        alert('✅ 生成任务已启动\n正在跳转到题库管理页面查看...');
+        console.log('[QuestionGen] 0.5秒后自动跳转到题库管理页面');
+        // 直接跳转,不传递task_id
         window.location.href = '/admin/question-management';
-    }, 2000);
+    }, 500);
 }
 
 // 将函数暴露到全局作用域

+ 41 - 2
resources/views/filament/pages/question-management-simple.blade.php

@@ -230,6 +230,45 @@
             </div>
         </div>
     @endif
-</div>
 
-</x-filament-panels::page>
+    </x-filament-panels::page>
+
+<script>
+document.addEventListener('livewire:init', () => {
+    // 定期检查通知
+    let checkCount = 0;
+    const maxChecks = 30; // 最多检查30次(15秒)
+
+    function checkForNotifications() {
+        checkCount++;
+
+        // 使用 fetch 检查是否有新的通知
+        fetch('/admin/question-management/check-notifications', {
+            method: 'GET',
+            headers: {
+                'X-Requested-With': 'XMLHttpRequest',
+                'Accept': 'application/json',
+            }
+        })
+        .then(response => response.json())
+        .then(data => {
+            if (data.hasNotification && data.notification) {
+                // 刷新页面以显示通知
+                window.location.reload();
+            } else if (checkCount < maxChecks) {
+                // 继续检查
+                setTimeout(checkForNotifications, 500);
+            }
+        })
+        .catch(error => {
+            console.error('检查通知失败:', error);
+            if (checkCount < maxChecks) {
+                setTimeout(checkForNotifications, 500);
+            }
+        });
+    }
+
+    // 页面加载后开始检查
+    setTimeout(checkForNotifications, 1000);
+});
+</script>

+ 33 - 17
routes/api.php

@@ -23,30 +23,46 @@ Route::post('/questions/callback', function () {
             return response()->json(['error' => 'Invalid callback data'], 400);
         }
 
-        // ✅ 同时存储到session和缓存中,确保前端可以访问
-        session(['question_gen_callback_' . $data['task_id'] => $data]);
+        // 处理回调数据并存储通知到session
+        if ($data['status'] === 'completed') {
+            $result = $data['result'] ?? [];
+            $total = $result['total'] ?? $data['total'] ?? ($result['saved'] ?? 0);
+            $kpCode = $result['kp_code'] ?? $data['kp_code'] ?? '';
+
+            // 将成功通知存储到session,供下次页面刷新时显示
+            session()->flash('notification', [
+                'type' => 'success',
+                'title' => '✅ 题目生成完成',
+                'body' => "任务 ID: {$data['task_id']}\n生成题目: {$total} 道" . ($kpCode ? "\n知识点: {$kpCode}" : ''),
+                'color' => 'success'
+            ]);
 
-        // ✅ 使用缓存存储回调结果,供前端查询(保留30秒)
-        cache([$data['task_id'] => $data], now()->addSeconds(30));
+            Log::info("题目生成成功通知已存储", [
+                'task_id' => $data['task_id'],
+                'total' => $total,
+                'kp_code' => $kpCode
+            ]);
 
-        // ✅ 使用事件替代轮询 - 直接分发事件给前端
-        if ($data['status'] === 'completed') {
-            QuestionGenerationCompleted::dispatch(
-                $data['task_id'],
-                $data['kp_code'] ?? '',
-                $data['total'] ?? 0
-            );
         } elseif ($data['status'] === 'failed') {
-            QuestionGenerationFailed::dispatch(
-                $data['task_id'],
-                $data['kp_code'] ?? '',
-                $data['error'] ?? '未知错误'
-            );
+            $error = $data['error'] ?? '未知错误';
+
+            // 将失败通知存储到session
+            session()->flash('notification', [
+                'type' => 'error',
+                'title' => '❌ 题目生成失败',
+                'body' => "任务 ID: {$data['task_id']}\n错误: {$error}",
+                'color' => 'danger'
+            ]);
+
+            Log::error("题目生成失败通知已存储", [
+                'task_id' => $data['task_id'],
+                'error' => $error
+            ]);
         }
 
         return response()->json([
             'success' => true,
-            'message' => 'Callback received',
+            'message' => 'Callback received and notification stored',
             'status' => $data['status']
         ]);
     } catch (\Exception $e) {

+ 3 - 1
routes/web.php

@@ -1,6 +1,7 @@
 <?php
 
 use Illuminate\Support\Facades\Route;
+use App\Http\Controllers\NotificationController;
 
 Route::get('/', function () {
     return redirect()->route('filament.admin.pages.dashboard');
@@ -13,4 +14,5 @@ Route::get('/test-case', function() { return view('test-case'); });
 Route::view('/knowledge-mindmap-public', 'public.knowledge-mindmap');
 Route::get('/admin/intelligent-exam/pdf/{paper_id}', [\App\Http\Controllers\ExamPdfController::class, 'show'])->name('filament.admin.auth.intelligent-exam.pdf');
 
-
+// 检查通知的路由
+Route::get('/admin/question-management/check-notifications', [NotificationController::class, 'checkNotifications']);