mathRecSys = $mathRecSys; } /** * 获取知识图谱数据(格式化后) * * @param string|null $focus 焦点知识点 * @return array */ public function getGraphData(?string $focus = null): array { try { $graphData = $this->mathRecSys->getKnowledgeGraph($focus); return [ 'success' => true, 'nodes' => $this->formatNodes($graphData['nodes'] ?? []), 'edges' => $this->formatEdges($graphData['edges'] ?? []), 'categories' => $this->extractCategories($graphData['nodes'] ?? []), 'metadata' => [ 'total_nodes' => count($graphData['nodes'] ?? []), 'total_edges' => count($graphData['edges'] ?? []), 'focus' => $focus ] ]; } catch (\Exception $e) { \Log::error('获取知识图谱数据失败', ['error' => $e->getMessage()]); return [ 'success' => false, 'nodes' => [], 'edges' => [], 'categories' => [], 'error' => $e->getMessage() ]; } } /** * 获取知识点详情 * * @param string $kpId 知识点ID * @return array */ public function getKnowledgePointDetail(string $kpId): array { try { // 从MathRecSys获取 $detail = $this->mathRecSys->getKnowledgePoint($kpId); // 从本地获取相关题目(这里需要实现) $relatedQuestions = $this->getRelatedQuestions($kpId); return [ 'success' => true, 'data' => array_merge($detail['data'] ?? [], [ 'related_questions' => $relatedQuestions ]) ]; } catch (\Exception $e) { \Log::error('获取知识点详情失败', [ 'kp_id' => $kpId, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * 获取知识点依赖关系 * * @param string $kpId 知识点ID * @return array */ public function getKnowledgePointDependencies(string $kpId): array { try { $graphData = $this->mathRecSys->getKnowledgeGraph($kpId); $dependencies = [ 'prerequisites' => [], // 前置知识点 'advances' => [], // 后续知识点 'contrasts' => [] // 对比知识点 ]; foreach ($graphData['edges'] ?? [] as $edge) { if ($edge['start_uuid'] === $kpId) { // 该知识点指向其他知识点 if ($edge['type'] === 'AdvancesTo') { $dependencies['advances'][] = [ 'target' => $edge['end_uuid'], 'description' => $edge['properties']['description'] ?? '' ]; } elseif ($edge['type'] === 'ContrastsWith') { $dependencies['contrasts'][] = [ 'target' => $edge['end_uuid'], 'description' => $edge['properties']['description'] ?? '' ]; } } elseif ($edge['end_uuid'] === $kpId) { // 其他知识点指向该知识点 if ($edge['type'] === 'Prerequisite') { $dependencies['prerequisites'][] = [ 'source' => $edge['start_uuid'], 'description' => $edge['properties']['description'] ?? '' ]; } } } return [ 'success' => true, 'data' => $dependencies ]; } catch (\Exception $e) { \Log::error('获取知识点依赖关系失败', [ 'kp_id' => $kpId, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * 获取学习路径建议 * * @param string $studentId 学生ID * @param string $targetKp 目标知识点 * @return array */ public function getLearningPath(string $studentId, string $targetKp): array { try { // 获取学生画像 $profile = $this->mathRecSys->getStudentProfile($studentId); // 获取知识图谱 $graphData = $this->mathRecSys->getKnowledgeGraph($targetKp); // 分析前置知识点掌握情况 $prerequisites = []; foreach ($graphData['edges'] ?? [] as $edge) { if ($edge['end_uuid'] === $targetKp && $edge['type'] === 'Prerequisite') { $prereqId = $edge['start_uuid']; $mastery = $this->getMasteryFromProfile($profile, $prereqId); $prerequisites[] = [ 'kp_id' => $prereqId, 'description' => $edge['properties']['description'] ?? '', 'current_mastery' => $mastery, 'is_ready' => $mastery >= 0.6, 'priority' => $this->calculatePriority($mastery) ]; } } // 按优先级排序 usort($prerequisites, function ($a, $b) { return $b['priority'] <=> $a['priority']; }); // 生成学习路径 $learningPath = []; foreach ($prerequisites as $prereq) { if (!$prereq['is_ready']) { $learningPath[] = [ 'type' => 'review', 'kp_id' => $prereq['kp_id'], 'reason' => '需要先掌握前置知识点', 'priority' => $prereq['priority'] ]; } } // 添加目标知识点 $learningPath[] = [ 'type' => 'learn', 'kp_id' => $targetKp, 'reason' => '学习目标知识点', 'priority' => 100 ]; return [ 'success' => true, 'data' => [ 'student_id' => $studentId, 'target_kp' => $targetKp, 'learning_path' => $learningPath, 'total_steps' => count($learningPath), 'estimated_hours' => count($learningPath) * 2 // 假设每个知识点需要2小时 ] ]; } catch (\Exception $e) { \Log::error('获取学习路径失败', [ 'student_id' => $studentId, 'target_kp' => $targetKp, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * 格式化节点数据 * * @param array $nodes 原始节点数据 * @return array */ private function formatNodes(array $nodes): array { return array_map(function ($node) { $props = $node['properties'] ?? []; return [ 'id' => $props['uuid'] ?? $props['kp_id'] ?? uniqid(), 'name' => $props['node_name'] ?? $props['kp_name'] ?? '未知知识点', 'category' => $props['grade'] ?? $props['grade_label'] ?? '未分类', 'description' => $props['description'] ?? '', 'value' => $props['mastery'] ?? 0.6, 'difficulty' => $props['difficulty'] ?? 0.5, 'book' => $props['book'] ?? '', 'chapter' => $props['chapter'] ?? '', 'keywords' => $props['keywords'] ?? '', 'uuid' => $props['uuid'] ?? null, 'kp_id' => $props['kp_id'] ?? null ]; }, $nodes); } /** * 格式化边数据 * * @param array $edges 原始边数据 * @return array */ private function formatEdges(array $edges): array { return array_map(function ($edge) { return [ 'source' => $edge['start_uuid'] ?? $edge['parent_kp'] ?? '', 'target' => $edge['end_uuid'] ?? $edge['child_kp'] ?? '', 'type' => $edge['type'] ?? $edge['relation_type'] ?? 'Prerequisite', 'description' => $edge['properties']['description'] ?? '', 'strength' => $edge['properties']['strength'] ?? 0.8 ]; }, $edges); } /** * 提取分类 * * @param array $nodes 节点数据 * @return array */ private function extractCategories(array $nodes): array { $categories = []; foreach ($nodes as $node) { $props = $node['properties'] ?? []; $category = $props['grade'] ?? $props['grade_label'] ?? '未分类'; if (!in_array($category, $categories)) { $categories[] = $category; } } // 为每个分类分配颜色 return array_map(function ($category, $index) { return [ 'name' => $category, 'color' => $this->getCategoryColor($index) ]; }, $categories, array_keys($categories)); } /** * 获取分类颜色 * * @param int $index 分类索引 * @return string */ private function getCategoryColor(int $index): string { $colors = [ '#3b82f6', // 蓝色 '#10b981', // 绿色 '#f59e0b', // 黄色 '#ef4444', // 红色 '#8b5cf6', // 紫色 '#ec4899', // 粉色 '#06b6d4', // 青色 '#84cc16', // 青柠 ]; return $colors[$index % count($colors)]; } /** * 从画像中获取掌握度 * * @param array $profile 学生画像 * @param string $kpId 知识点ID * @return float */ private function getMasteryFromProfile(array $profile, string $kpId): float { $masteryData = $profile['data']['mastery'] ?? $profile['mastery'] ?? []; foreach ($masteryData as $mastery) { if (($mastery['kp'] ?? $mastery['kp_id'] ?? '') === $kpId) { return floatval($mastery['level'] ?? $mastery['mastery_level'] ?? 0); } } return 0.0; } /** * 计算优先级 * * @param float $mastery 掌握度 * @return int */ private function calculatePriority(float $mastery): int { if ($mastery < 0.3) { return 90; // 急需掌握 } elseif ($mastery < 0.6) { return 70; // 需要复习 } elseif ($mastery < 0.8) { return 50; // 适当巩固 } else { return 10; // 已经掌握 } } /** * 获取相关题目(需要根据实际题库实现) * * @param string $kpId 知识点ID * @return array */ private function getRelatedQuestions(string $kpId): array { // 这里需要实现从本地题库查询相关题目的逻辑 // 暂时返回空数组 return []; } }