yemeishu před 4 dny
rodič
revize
790069391e

+ 57 - 16
app/Services/ExamAnalysisService.php

@@ -248,16 +248,35 @@ class ExamAnalysisService
     }
 
     /**
-     * 获取学习分析数据
+     * 获取学习分析数据(修改为本地数据库查询)
      */
     private function getLearningAnalysisData(string $paperId, string $studentId, array $paperData): array
     {
         $analysisData = [];
 
         if (!empty($paperData['analysis_id'])) {
-            $analysis = $this->learningAnalyticsService->getAnalysisResult($paperData['analysis_id']);
-            if (!empty($analysis['data'])) {
-                $analysisData = $analysis['data'];
+            Log::info('ExamAnalysisService: 从本地数据库获取分析数据', [
+                'paper_id' => $paperId,
+                'student_id' => $studentId,
+                'analysis_id' => $paperData['analysis_id']
+            ]);
+
+            $analysisRecord = \DB::table('exam_analysis_results')
+                ->where('id', $paperData['analysis_id'])
+                ->where('student_id', $studentId)
+                ->first();
+
+            if ($analysisRecord && !empty($analysisRecord->analysis_data)) {
+                $analysisData = json_decode($analysisRecord->analysis_data, true);
+                Log::info('ExamAnalysisService: 成功获取本地分析数据', [
+                    'data_size' => strlen($analysisRecord->analysis_data)
+                ]);
+            } else {
+                Log::warning('ExamAnalysisService: 未找到本地分析数据', [
+                    'paper_id' => $paperId,
+                    'student_id' => $studentId,
+                    'analysis_id' => $paperData['analysis_id']
+                ]);
             }
         }
 
@@ -265,29 +284,51 @@ class ExamAnalysisService
     }
 
     /**
-     * 获取掌握度数据
+     * 获取掌握度数据(修改为使用本地方法)
      */
     private function getMasteryData(string $studentId): array
     {
-        $masteryData = [];
-        $masteryResponse = $this->learningAnalyticsService->getStudentMastery($studentId);
-        if (!empty($masteryResponse['data'])) {
-            $masteryData = $this->buildMasterySummary($masteryResponse['data']);
+        try {
+            Log::info('ExamAnalysisService: 获取学生掌握度概览', [
+                'student_id' => $studentId
+            ]);
+            $masteryOverview = $this->learningAnalyticsService->getStudentMasteryOverview($studentId);
+            $masteryData = $masteryOverview['details'] ?? [];
+            Log::info('ExamAnalysisService: 成功获取掌握度数据', [
+                'count' => count($masteryData)
+            ]);
+            return $masteryData;
+        } catch (\Exception $e) {
+            Log::error('ExamAnalysisService: 获取掌握度数据失败', [
+                'student_id' => $studentId,
+                'error' => $e->getMessage()
+            ]);
+            return [];
         }
-        return $masteryData;
     }
 
     /**
-     * 获取学习建议
+     * 获取学习建议(修改为使用本地方法)
      */
     private function getLearningRecommendations(string $studentId): array
     {
-        $recommendations = [];
-        $recommendationResponse = $this->learningAnalyticsService->getLearningRecommendations($studentId);
-        if (!empty($recommendationResponse['data'])) {
-            $recommendations = $recommendationResponse['data'];
+        try {
+            Log::info('ExamAnalysisService: 获取学习路径推荐', [
+                'student_id' => $studentId
+            ]);
+            $learningPaths = $this->learningAnalyticsService->recommendLearningPaths($studentId, 3);
+            $recommendations = $learningPaths['recommendations'] ?? [];
+            Log::info('ExamAnalysisService: 成功获取学习路径推荐', [
+                'count' => count($recommendations)
+            ]);
+            return $recommendations;
+        } catch (\Exception $e) {
+            Log::error('ExamAnalysisService: 获取学习路径推荐失败', [
+                'student_id' => $studentId,
+                'error' => $e->getMessage()
+            ]);
+            return [];
         }
-        return $recommendations;
     }
 
     /**

+ 60 - 13
app/Services/ExamPdfExportService.php

@@ -218,6 +218,7 @@ class ExamPdfExportService
 
     /**
      * 构建分析数据(重构版)
+     * 优先使用本地MySQL数据,减少API依赖
      */
     private function buildAnalysisData(string $paperId, string $studentId): ?array
     {
@@ -241,27 +242,73 @@ class ExamPdfExportService
             'class' => $student?->class_name ?? '未知班级',
         ];
 
-        // 获取分析数据
+        // 【修改】直接从本地数据库获取分析数据(不再调用API)
         $analysisData = [];
         if (!empty($paper->analysis_id)) {
-            $analysis = $this->learningAnalyticsService->getAnalysisResult($paper->analysis_id);
-            if (!empty($analysis['data'])) {
-                $analysisData = $analysis['data'];
+            Log::info('ExamPdfExportService: 从本地数据库获取试卷分析数据', [
+                'paper_id' => $paperId,
+                'student_id' => $studentId,
+                'analysis_id' => $paper->analysis_id
+            ]);
+
+            $analysisRecord = \DB::table('exam_analysis_results')
+                ->where('id', $paper->analysis_id)
+                ->where('student_id', $studentId)
+                ->first();
+
+            if ($analysisRecord && !empty($analysisRecord->analysis_data)) {
+                $analysisData = json_decode($analysisRecord->analysis_data, true);
+                Log::info('ExamPdfExportService: 成功获取本地分析数据', [
+                    'data_size' => strlen($analysisRecord->analysis_data)
+                ]);
+            } else {
+                Log::warning('ExamPdfExportService: 未找到本地分析数据,将使用空数据', [
+                    'paper_id' => $paperId,
+                    'student_id' => $studentId,
+                    'analysis_id' => $paper->analysis_id
+                ]);
             }
+        } else {
+            Log::warning('ExamPdfExportService: 试卷无analysis_id,将使用空分析数据', [
+                'paper_id' => $paperId,
+                'student_id' => $studentId
+            ]);
         }
 
-        // 获取掌握度数据
+        // 【修改】使用本地方法获取掌握度概览(替代API调用)
         $masteryData = [];
-        $masteryResponse = $this->learningAnalyticsService->getStudentMastery($studentId);
-        if (!empty($masteryResponse['data'])) {
-            $masteryData = $masteryResponse['data'];
+        try {
+            Log::info('ExamPdfExportService: 获取学生掌握度概览', [
+                'student_id' => $studentId
+            ]);
+            $masteryOverview = $this->learningAnalyticsService->getStudentMasteryOverview($studentId);
+            $masteryData = $masteryOverview['details'] ?? [];
+            Log::info('ExamPdfExportService: 成功获取掌握度数据', [
+                'count' => count($masteryData)
+            ]);
+        } catch (\Exception $e) {
+            Log::error('ExamPdfExportService: 获取掌握度数据失败', [
+                'student_id' => $studentId,
+                'error' => $e->getMessage()
+            ]);
         }
 
-        // 获取学习建议
+        // 【修改】使用本地方法获取学习路径推荐(替代API调用)
         $recommendations = [];
-        $recommendationResponse = $this->learningAnalyticsService->getLearningRecommendations($studentId);
-        if (!empty($recommendationResponse['data'])) {
-            $recommendations = $recommendationResponse['data'];
+        try {
+            Log::info('ExamPdfExportService: 获取学习路径推荐', [
+                'student_id' => $studentId
+            ]);
+            $learningPaths = $this->learningAnalyticsService->recommendLearningPaths($studentId, 3);
+            $recommendations = $learningPaths['recommendations'] ?? [];
+            Log::info('ExamPdfExportService: 成功获取学习路径推荐', [
+                'count' => count($recommendations)
+            ]);
+        } catch (\Exception $e) {
+            Log::error('ExamPdfExportService: 获取学习路径推荐失败', [
+                'student_id' => $studentId,
+                'error' => $e->getMessage()
+            ]);
         }
 
         // 获取知识点名称映射
@@ -284,7 +331,7 @@ class ExamPdfExportService
             'student' => $studentInfo,
             'questions' => $questions,
             'mastery' => $this->buildMasterySummary($masteryData, $kpNameMap),
-            'insights' => $analysisData['question_results'] ?? [],
+            'insights' => $analysisData['question_analysis'] ?? [], // 使用question_analysis替代question_results
             'recommendations' => $recommendations,
             'analysis_data' => $analysisData,
         ];

+ 136 - 34
app/Services/LearningAnalyticsService.php

@@ -1123,11 +1123,33 @@ class LearningAnalyticsService
 
     /**
      * 获取学生薄弱点列表
+     * 策略:MySQL作为权威数据源,LearningAnalytics API仅作为辅助/缓存
      */
     public function getStudentWeaknesses(string $studentId, int $limit = 10): array
     {
         try {
-            // 使用正确的API路径:/api/v1/student/{student_id}/weak-points
+            // 首先从本地MySQL(权威数据源)获取数据
+            Log::info('从MySQL权威数据源获取学生薄弱点', [
+                'student_id' => $studentId,
+                'source' => 'mysql'
+            ]);
+
+            $localData = $this->getStudentWeaknessesFromMySQL($studentId, $limit);
+
+            if (!empty($localData)) {
+                Log::info('从MySQL权威数据源获取到薄弱点数据', [
+                    'student_id' => $studentId,
+                    'count' => count($localData)
+                ]);
+                return $localData;
+            }
+
+            // 本地无数据时,尝试从LearningAnalytics API获取(作为辅助/缓存)
+            Log::warning('MySQL中无该学生薄弱点数据,尝试从LearningAnalytics API获取', [
+                'student_id' => $studentId,
+                'api_url' => $this->baseUrl . "/api/v1/student/{$studentId}/weak-points"
+            ]);
+
             $response = Http::timeout($this->timeout)
                 ->get($this->baseUrl . "/api/v1/student/{$studentId}/weak-points");
 
@@ -1135,36 +1157,42 @@ class LearningAnalyticsService
                 $data = $response->json('data', []);
                 $weakPoints = $data['weak_points'] ?? [];
 
-                // 转换为统一的格式
-                return array_map(function ($item) use ($studentId) {
-                    return [
-                        'kp_code' => $item['kp'] ?? '',
-                        'kp_name' => $item['kp'] ?? '',
-                        'mastery' => $item['mastery_level'] ?? 0,
-                        'stability' => 0.5, // 默认稳定性
-                        'weakness_level' => 1.0 - ($item['mastery_level'] ?? 0.5),
-                        'practice_count' => $item['practice_count'] ?? 0,
-                        'success_rate' => $item['success_rate'] ?? 0,
-                        'priority' => $item['priority'] ?? '中',
-                        'suggested_questions' => $item['suggested_questions'] ?? 0
-                    ];
-                }, $weakPoints);
+                if (!empty($weakPoints)) {
+                    Log::info('从LearningAnalytics API辅助获取到薄弱点数据', [
+                        'student_id' => $studentId,
+                        'count' => count($weakPoints)
+                    ]);
+
+                    return array_map(function ($item) use ($studentId) {
+                        return [
+                            'kp_code' => $item['kp'] ?? '',
+                            'kp_name' => $item['kp'] ?? '',
+                            'mastery' => $item['mastery_level'] ?? 0,
+                            'stability' => 0.5, // 默认稳定性
+                            'weakness_level' => 1.0 - ($item['mastery_level'] ?? 0.5),
+                            'practice_count' => $item['practice_count'] ?? 0,
+                            'success_rate' => $item['success_rate'] ?? 0,
+                            'priority' => $item['priority'] ?? '中',
+                            'suggested_questions' => $item['suggested_questions'] ?? 0
+                        ];
+                    }, $weakPoints);
+                }
             }
 
-            Log::warning('LearningAnalytics weaknesses API失败,使用本地MySQL数据', [
+            Log::warning('所有数据源均无该学生薄弱点数据', [
                 'student_id' => $studentId,
-                'status' => $response->status()
+                'mysql_count' => count($localData),
+                'api_status' => $response->status() ?? 'timeout'
             ]);
 
-            // API失败时,从MySQL直接查询
-            return $this->getStudentWeaknessesFromMySQL($studentId, $limit);
+            return [];
         } catch (\Exception $e) {
             Log::error('Get Student Weaknesses Error', [
                 'student_id' => $studentId,
                 'error' => $e->getMessage()
             ]);
 
-            // 发生异常时,返回空数组,让前端可以继续使用默认值
+            // 发生异常时,返回空数组
             return [];
         }
     }
@@ -1175,34 +1203,108 @@ class LearningAnalyticsService
     private function getStudentWeaknessesFromMySQL(string $studentId, int $limit = 10): array
     {
         try {
-            $weaknesses = DB::table('student_mastery as sm')
-                ->join('knowledge_points as kp', 'sm.kp', '=', 'kp.kp')
-                ->where('sm.student_id', $studentId)
-                ->where('sm.mastery', '<', 0.7) // 掌握度低于70%视为薄弱点
-                ->orderBy('sm.mastery', 'asc')
+            // 优先从 student_knowledge_mastery 表读取(更完整的掌握度数据)
+            $weaknesses = DB::table('student_knowledge_mastery as skm')
+                ->where('skm.student_id', $studentId)
+                ->where('skm.mastery_level', '<', 0.7) // 掌握度低于70%视为薄弱点
+                ->orderBy('skm.mastery_level', 'asc')
                 ->limit($limit)
                 ->select([
-                    'sm.kp as kp_code',
-                    'kp.cn_name as kp_name',
-                    'sm.mastery',
-                    'sm.stability'
+                    'skm.kp_code',
+                    'skm.mastery_level',
+                    'skm.total_attempts',
+                    'skm.correct_attempts',
+                    'skm.incorrect_attempts',
+                    'skm.confidence_level',
+                    'skm.mastery_trend'
                 ])
                 ->get()
                 ->toArray();
 
+            // 如果student_knowledge_mastery表没有数据,尝试从student_mastery表读取
+            if (empty($weaknesses)) {
+                Log::info('student_knowledge_mastery表无数据,尝试从student_mastery表读取', [
+                    'student_id' => $studentId
+                ]);
+
+                $weaknesses = DB::table('student_mastery as sm')
+                    ->leftJoin('knowledge_points as kp', 'sm.kp', '=', 'kp.kp')
+                    ->where('sm.student_id', $studentId)
+                    ->where('sm.mastery', '<', 0.7) // 掌握度低于70%视为薄弱点
+                    ->orderBy('sm.mastery', 'asc')
+                    ->limit($limit)
+                    ->select([
+                        'sm.kp as kp_code',
+                        'kp.cn_name as kp_name',
+                        'sm.mastery',
+                        'sm.attempts',
+                        'sm.correct'
+                    ])
+                    ->get()
+                    ->toArray();
+
+                // 转换为统一格式
+                return array_map(function ($item) {
+                    $mastery = (float) ($item->mastery ?? 0);
+                    $attempts = (int) ($item->attempts ?? 0);
+                    $correct = (int) ($item->correct ?? 0);
+
+                    return [
+                        'kp_code' => $item->kp_code,
+                        'kp_name' => $item->kp_name ?? $item->kp_code,
+                        'mastery' => $mastery,
+                        'stability' => 0.5, // 默认稳定性
+                        'weakness_level' => 1.0 - $mastery, // 薄弱程度
+                        'practice_count' => $attempts,
+                        'success_rate' => $attempts > 0 ? ($correct / $attempts) : 0,
+                        'priority' => $mastery < 0.3 ? '高' : ($mastery < 0.5 ? '中' : '低'),
+                        'suggested_questions' => max(5, (int)((0.7 - $mastery) * 20)) // 掌握度越低,建议题目越多
+                    ];
+                }, $weaknesses);
+            }
+
+            // 转换student_knowledge_mastery表的数据格式
             return array_map(function ($item) {
+                $mastery = (float) ($item->mastery_level ?? 0);
+                $totalAttempts = (int) ($item->total_attempts ?? 0);
+                $correctAttempts = (int) ($item->correct_attempts ?? 0);
+                $incorrectAttempts = (int) ($item->incorrect_attempts ?? 0);
+                $confidence = (float) ($item->confidence_level ?? 0.5);
+                $trend = $item->mastery_trend ?? 'stable';
+
+                // 计算成功率
+                $successRate = $totalAttempts > 0 ? ($correctAttempts / $totalAttempts) : 0;
+
+                // 确定优先级
+                $priority = '中';
+                if ($mastery < 0.3) {
+                    $priority = '高';
+                } elseif ($mastery < 0.5) {
+                    $priority = '中';
+                } else {
+                    $priority = '低';
+                }
+
                 return [
                     'kp_code' => $item->kp_code,
-                    'kp_name' => $item->kp_name,
-                    'mastery' => (float) $item->mastery,
-                    'stability' => (float) $item->stability,
-                    'weakness_level' => 1.0 - (float) $item->mastery // 薄弱程度
+                    'kp_name' => $item->kp_code, // 如果没有中文名,使用代码作为名称
+                    'mastery' => $mastery,
+                    'stability' => $confidence,
+                    'weakness_level' => 1.0 - $mastery, // 薄弱程度
+                    'practice_count' => $totalAttempts,
+                    'success_rate' => $successRate,
+                    'priority' => $priority,
+                    'suggested_questions' => max(5, (int)((0.7 - $mastery) * 20)), // 掌握度越低,建议题目越多
+                    'trend' => $trend,
+                    'correct_attempts' => $correctAttempts,
+                    'incorrect_attempts' => $incorrectAttempts
                 ];
             }, $weaknesses);
         } catch (\Exception $e) {
             Log::error('Get Student Weaknesses From MySQL Error', [
                 'student_id' => $studentId,
-                'error' => $e->getMessage()
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
             ]);
             return [];
         }