StudentAnalysis.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. namespace App\Filament\Pages;
  3. use App\Services\KnowledgeGraphService;
  4. use App\Services\KnowledgeMasteryService;
  5. use App\Services\MasteryCalculator;
  6. use BackedEnum;
  7. use Filament\Pages\Page;
  8. use UnitEnum;
  9. class StudentAnalysis extends Page
  10. {
  11. protected static ?string $title = '学生掌握度分析';
  12. protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-chart-bar';
  13. protected static ?string $navigationLabel = '学生分析';
  14. protected static string|UnitEnum|null $navigationGroup = '其他';
  15. protected static ?int $navigationSort = 24;
  16. protected string $view = 'filament.pages.student-analysis-simple';
  17. /**
  18. * 禁用导航显示(与"学生仪表板"功能重复)
  19. */
  20. public static function shouldRegisterNavigation(): bool
  21. {
  22. return false;
  23. }
  24. // 当前选中的学生
  25. public ?string $selectedStudentId = null;
  26. public array $studentInfo = [];
  27. public array $masteryData = [];
  28. public array $weaknesses = [];
  29. public array $skills = [];
  30. public array $learningPath = [];
  31. /**
  32. * 获取所有学生列表
  33. */
  34. public function students(): array
  35. {
  36. return \App\Models\Student::all()->toArray();
  37. }
  38. public function updatedSelectedStudentId($value)
  39. {
  40. if ($value) {
  41. $this->loadAnalysisData();
  42. } else {
  43. $this->reset(['studentInfo', 'masteryData', 'weaknesses', 'skills', 'learningPath']);
  44. }
  45. }
  46. public function loadAnalysisData()
  47. {
  48. if (!$this->selectedStudentId) {
  49. return;
  50. }
  51. // 使用本地的KnowledgeMasteryService
  52. $masteryService = app(KnowledgeMasteryService::class);
  53. $masteryCalculator = app(MasteryCalculator::class);
  54. // 1. 获取学生掌握度数据
  55. $stats = $masteryService->getStats($this->selectedStudentId);
  56. $this->studentInfo = $stats['success'] ? $stats['data'] : [];
  57. // 2. 获取掌握度概览
  58. $overview = $masteryCalculator->getStudentMasteryOverview($this->selectedStudentId);
  59. // 3. 获取薄弱点列表(掌握度 < 0.5)
  60. $this->weaknesses = array_filter($overview['details'] ?? [], function($item) {
  61. return floatval($item->mastery_level ?? 0) < 0.5;
  62. });
  63. // 4. 获取技能熟练度(从掌握度数据转换)
  64. $this->skills = $this->getSkillsData($overview);
  65. // 5. 获取学习路径建议(基于薄弱点)
  66. $this->learningPath = $this->generateLearningPath($this->weaknesses);
  67. // 6. 获取知识点掌握度详情
  68. $this->masteryData = $overview['details'] ?? [];
  69. }
  70. /**
  71. * 从掌握度数据转换技能数据
  72. */
  73. private function getSkillsData(array $overview): array
  74. {
  75. $skills = [];
  76. foreach ($overview['details'] ?? [] as $detail) {
  77. $skills[] = [
  78. 'skill_name' => $detail->kp_code,
  79. 'proficiency' => floatval($detail->mastery_level ?? 0),
  80. 'mastery_level' => floatval($detail->mastery_level ?? 0),
  81. ];
  82. }
  83. return $skills;
  84. }
  85. /**
  86. * 基于薄弱点生成学习路径
  87. */
  88. private function generateLearningPath(array $weaknesses): array
  89. {
  90. $path = [];
  91. foreach (array_slice($weaknesses, 0, 5) as $weakness) {
  92. $path[] = [
  93. 'kp_code' => $weakness->kp_code ?? '',
  94. 'recommendation' => '建议重点练习此知识点',
  95. 'priority' => 'high',
  96. 'estimated_hours' => 2,
  97. ];
  98. }
  99. return $path;
  100. }
  101. private function getMasteryDetails(string $studentId): array
  102. {
  103. try {
  104. // 从MySQL直接查询学生掌握度详情
  105. $db = app('db');
  106. $db->connection('remote_mysql');
  107. $masteryRecords = \Illuminate\Support\Facades\DB::connection('remote_mysql')
  108. ->table('student_mastery as sm')
  109. ->join('knowledge_points as kp', 'sm.kp', '=', 'kp.kp')
  110. ->where('sm.student_id', $studentId)
  111. ->select([
  112. 'sm.kp as kp_code',
  113. 'kp.cn_name as kp_name',
  114. 'sm.mastery',
  115. 'sm.stability',
  116. 'sm.update_time'
  117. ])
  118. ->orderBy('sm.mastery', 'asc')
  119. ->limit(50)
  120. ->get()
  121. ->toArray();
  122. return array_map(function ($record) {
  123. return [
  124. 'kp_code' => $record->kp_code,
  125. 'kp_name' => $record->kp_name,
  126. 'mastery' => (float) $record->mastery,
  127. 'stability' => (float) $record->stability,
  128. 'update_time' => $record->update_time,
  129. 'mastery_level' => $this->getMasteryLevel((float) $record->mastery),
  130. 'weakness_score' => 1.0 - (float) $record->mastery
  131. ];
  132. }, $masteryRecords);
  133. } catch (\Exception $e) {
  134. \Illuminate\Support\Facades\Log::error('获取掌握度详情失败', [
  135. 'student_id' => $studentId,
  136. 'error' => $e->getMessage()
  137. ]);
  138. return [];
  139. }
  140. }
  141. private function getMasteryLevel(float $mastery): string
  142. {
  143. if ($mastery >= 0.9) return '优秀';
  144. if ($mastery >= 0.8) return '良好';
  145. if ($mastery >= 0.7) return '中等';
  146. if ($mastery >= 0.6) return '及格';
  147. return '需提升';
  148. }
  149. public function getMasteryColor(float $mastery): string
  150. {
  151. if ($mastery >= 0.9) return '#10b981'; // emerald-500
  152. if ($mastery >= 0.8) return '#34d399'; // emerald-400
  153. if ($mastery >= 0.7) return '#fbbf24'; // amber-400
  154. if ($mastery >= 0.6) return '#fb923c'; // orange-400
  155. return '#ef4444'; // red-500
  156. }
  157. public function getMasteryBgColor(float $mastery): string
  158. {
  159. if ($mastery >= 0.9) return 'bg-emerald-100';
  160. if ($mastery >= 0.8) return 'bg-emerald-50';
  161. if ($mastery >= 0.7) return 'bg-amber-100';
  162. if ($mastery >= 0.6) return 'bg-orange-100';
  163. return 'bg-red-100';
  164. }
  165. public function generateStudyPlan()
  166. {
  167. if (!$this->selectedStudentId || empty($this->weaknesses)) {
  168. return;
  169. }
  170. // TODO: 调用LearningAnalytics服务生成学习计划
  171. // 这里可以调用专门的API来生成个性化学习计划
  172. return redirect()->route('filament.admin.auth.learning-plan', [
  173. 'student_id' => $this->selectedStudentId
  174. ]);
  175. }
  176. public function exportAnalysis()
  177. {
  178. if (!$this->selectedStudentId) {
  179. return;
  180. }
  181. // TODO: 导出分析报告为PDF或Excel
  182. // 可以调用专门的导出服务
  183. }
  184. }