learningAnalyticsBase = rtrim( $learningAnalyticsBase ?: config('services.learning_analytics.url', env('LEARNING_ANALYTICS_API_BASE', 'http://localhost:5016')), '/' ); $this->knowledgeServiceBase = rtrim( $knowledgeServiceBase ?: config('services.knowledge_service.url', env('KNOWLEDGE_SERVICE_API_BASE', 'http://localhost:5011')), '/' ); $this->timeout = $timeout ?? (int) config('services.learning_analytics.timeout', 20); } /** * 获取学生知识点掌握情况统计 * * @param string $studentId 学生ID * @return array */ public function getStats(string $studentId): array { try { $response = Http::timeout($this->timeout) ->get($this->learningAnalyticsBase . '/api/knowledge-mastery/stats/' . $studentId); if ($response->successful()) { $body = $response->json(); // 丰富知识点名称 $body = $this->enrichWithKnowledgePointNames($body); // 添加知识图谱总数统计 $graphStats = $this->getKnowledgeGraphStats(); $body['graph_total_knowledge_points'] = $graphStats['total'] ?? 0; Log::info('KnowledgeMasteryService::getStats', ['student_id' => $studentId]); return [ 'success' => true, 'data' => $body, ]; } Log::warning('Knowledge mastery stats request failed', [ 'student_id' => $studentId, 'status' => $response->status(), 'body' => $response->body(), ]); return [ 'success' => false, 'error' => '获取知识点掌握情况失败: ' . $response->status(), ]; } catch (\Throwable $e) { Log::error('Knowledge mastery stats exception', [ 'student_id' => $studentId, 'error' => $e->getMessage(), ]); return [ 'success' => false, 'error' => '获取知识点掌握情况异常: ' . $e->getMessage(), ]; } } /** * 丰富知识点名称 */ private function enrichWithKnowledgePointNames(array $data): array { if (empty($data['details'])) { return $data; } // 收集所有kp_code $kpCodes = array_column($data['details'], 'kp_code'); if (empty($kpCodes)) { return $data; } // 批量获取知识点名称 $kpNames = $this->getKnowledgePointNames($kpCodes); // 丰富details数据 foreach ($data['details'] as &$detail) { $kpCode = $detail['kp_code'] ?? null; if ($kpCode && isset($kpNames[$kpCode])) { $detail['kp_name'] = $kpNames[$kpCode]; } else { $detail['kp_name'] = $kpCode; // fallback to code } } return $data; } /** * 批量获取知识点名称 */ private function getKnowledgePointNames(array $kpCodes): array { $result = []; foreach ($kpCodes as $kpCode) { $name = $this->getKnowledgePointName($kpCode); if ($name) { $result[$kpCode] = $name; } } return $result; } /** * 获取单个知识点名称(带缓存) */ private function getKnowledgePointName(string $kpCode): ?string { $cacheKey = "kp_name_{$kpCode}"; return Cache::remember($cacheKey, 3600, function () use ($kpCode) { try { $response = Http::timeout(5) ->get($this->knowledgeServiceBase . '/knowledge-points/' . $kpCode); if ($response->successful()) { $data = $response->json(); return $data['cn_name'] ?? $data['en_name'] ?? null; } } catch (\Throwable $e) { Log::debug('Failed to get knowledge point name', [ 'kp_code' => $kpCode, 'error' => $e->getMessage(), ]); } return null; }); } /** * 获取知识图谱统计信息(带缓存) */ public function getKnowledgeGraphStats(): array { return Cache::remember('knowledge_graph_stats', 3600, function () { try { $response = Http::timeout(10) ->get($this->knowledgeServiceBase . '/knowledge-points/'); if ($response->successful()) { $data = $response->json(); $items = $data['data'] ?? $data ?? []; return [ 'total' => count($items), 'updated_at' => now()->toISOString(), ]; } } catch (\Throwable $e) { Log::error('Failed to get knowledge graph stats', [ 'error' => $e->getMessage(), ]); } return ['total' => 0]; }); } /** * 获取学生知识点图谱数据 * * @param string $studentId 学生ID * @param string|null $examId 考试ID(可选,不指定则返回最新快照) * @return array */ public function getGraph(string $studentId, ?string $examId = null): array { try { $query = array_filter(['exam_id' => $examId], fn($v) => filled($v)); $response = Http::timeout($this->timeout) ->get($this->learningAnalyticsBase . '/api/knowledge-mastery/graph/' . $studentId, $query); if ($response->successful()) { $body = $response->json(); Log::info('KnowledgeMasteryService::getGraph', ['student_id' => $studentId, 'exam_id' => $examId]); return [ 'success' => true, 'data' => $body, ]; } Log::warning('Knowledge graph request failed', [ 'student_id' => $studentId, 'exam_id' => $examId, 'status' => $response->status(), 'body' => $response->body(), ]); return [ 'success' => false, 'error' => '获取知识点图谱失败: ' . $response->status(), ]; } catch (\Throwable $e) { Log::error('Knowledge graph exception', [ 'student_id' => $studentId, 'exam_id' => $examId, 'error' => $e->getMessage(), ]); return [ 'success' => false, 'error' => '获取知识点图谱异常: ' . $e->getMessage(), ]; } } /** * 获取学生知识点图谱快照列表 * * @param string $studentId 学生ID * @param int $limit 返回数量限制 * @return array */ public function getGraphSnapshots(string $studentId, int $limit = 10): array { try { $response = Http::timeout($this->timeout) ->get($this->learningAnalyticsBase . '/api/knowledge-mastery/graph/snapshots/' . $studentId, [ 'limit' => $limit, ]); if ($response->successful()) { $body = $response->json(); Log::info('KnowledgeMasteryService::getGraphSnapshots', ['student_id' => $studentId, 'limit' => $limit]); return [ 'success' => true, 'data' => $body, ]; } Log::warning('Knowledge graph snapshots request failed', [ 'student_id' => $studentId, 'limit' => $limit, 'status' => $response->status(), 'body' => $response->body(), ]); return [ 'success' => false, 'error' => '获取知识点图谱快照列表失败: ' . $response->status(), ]; } catch (\Throwable $e) { Log::error('Knowledge graph snapshots exception', [ 'student_id' => $studentId, 'limit' => $limit, 'error' => $e->getMessage(), ]); return [ 'success' => false, 'error' => '获取知识点图谱快照列表异常: ' . $e->getMessage(), ]; } } /** * 获取学生知识点掌握摘要(简化版) * * @param string $studentId 学生ID * @return array */ public function getSummary(string $studentId): array { $stats = $this->getStats($studentId); if (!$stats['success']) { return $stats; } $data = $stats['data']; return [ 'success' => true, 'data' => [ 'student_id' => $data['student_id'] ?? $studentId, 'total' => $data['total_knowledge_points'] ?? 0, 'mastered' => $data['mastered_knowledge_points'] ?? 0, 'unmastered' => $data['unmastered_knowledge_points'] ?? 0, 'mastery_rate' => $data['mastery_rate'] ?? 0.0, 'mastery_percentage' => round(($data['mastery_rate'] ?? 0) * 100, 1) . '%', 'graph_total' => $data['graph_total_knowledge_points'] ?? 0, ], ]; } /** * 创建知识点掌握度快照 * * @param string $studentId 学生ID * @param string $snapshotType 快照类型 (exam/report/manual/scheduled) * @param string|null $sourceId 来源ID * @param string|null $sourceName 来源名称 * @param string|null $notes 备注 * @return array */ public function createSnapshot( string $studentId, string $snapshotType = 'report', ?string $sourceId = null, ?string $sourceName = null, ?string $notes = null ): array { try { $response = Http::timeout($this->timeout) ->post($this->learningAnalyticsBase . '/api/knowledge-mastery/snapshot/' . $studentId, [ 'snapshot_type' => $snapshotType, 'source_id' => $sourceId, 'source_name' => $sourceName, 'notes' => $notes, ]); if ($response->successful()) { $body = $response->json(); Log::info('KnowledgeMasteryService::createSnapshot', [ 'student_id' => $studentId, 'snapshot_type' => $snapshotType, 'snapshot_id' => $body['data']['snapshot_id'] ?? null, ]); return [ 'success' => true, 'data' => $body['data'] ?? $body, ]; } Log::warning('Create knowledge mastery snapshot failed', [ 'student_id' => $studentId, 'status' => $response->status(), 'body' => $response->body(), ]); return [ 'success' => false, 'error' => '创建知识点掌握度快照失败: ' . $response->status(), ]; } catch (\Throwable $e) { Log::error('Create knowledge mastery snapshot exception', [ 'student_id' => $studentId, 'error' => $e->getMessage(), ]); return [ 'success' => false, 'error' => '创建知识点掌握度快照异常: ' . $e->getMessage(), ]; } } }