@php // 提取15位paper_id数字部分作为学案编号 $rawPaperId = $paper['id'] ?? $paper['paper_id'] ?? 'unknown'; preg_match('/paper_(\d{15})/', $rawPaperId, $matches); $reportCode = $matches[1] ?? preg_replace('/[^0-9]/', '', $rawPaperId); $averageMastery = isset($mastery['average']) ? number_format($mastery['average'] * 100, 1) . '%' : '无数据'; // 【修复】从insights中获取AI分析结果(而不是从analysis_data) $questionAnalysis = $insights ?? []; // 生成时间(格式:2026年01月30日 15:04:05) $generateDateTime = now()->format('Y年m月d日 H:i:s'); @endphp 学情报告 - {{ $paper['name'] ?? '试卷' }}

学情分析报告

@php $teacherName = trim((string) ($teacher['name'] ?? '')); $showTeacher = $teacherName !== '' && $teacherName !== '________' && $teacherName !== '未知老师'; @endphp
@if($showTeacher) 老师:{{ $teacherName }} @endif 年级:@formatGrade($student['grade'] ?? '________') @if(!empty($paper['assemble_type_label']) && $paper['assemble_type_label'] !== '未知类型') 类型:{{ $paper['assemble_type_label'] }} @endif 姓名:{{ $student['name'] ?? '________' }} 题目数:{{ $paper['total_questions'] ?? (is_array($questions ?? null) ? count($questions) : '-') }}
本次命中子知识点掌握度
@php // 【修复】过滤掉K-GENERAL等通用知识点,只显示有值的知识点 $filteredMasteryItems = []; if (!empty($mastery['items'])) { foreach ($mastery['items'] as $item) { $kpCode = $item['kp_code'] ?? ''; $masteryLevel = $item['mastery_level'] ?? 0; // 过滤条件:不是通用知识点,且掌握度>0 if (!in_array($kpCode, ['K-GENERAL', 'GENERAL', 'DEFAULT']) && $masteryLevel > 0) { $filteredMasteryItems[] = $item; } } } @endphp @if(!empty($filteredMasteryItems)) @foreach($filteredMasteryItems as $item) @php $pct = min(100, max(0, ($item['mastery_level'] ?? 0) * 100)); $barColor = $pct >= 80 ? '#10b981' : ($pct >= 60 ? '#f59e0b' : '#ef4444'); $delta = $item['mastery_change'] ?? null; // 只有当有变化值时才显示变化信息 $changeText = ''; if ($delta !== null && $delta !== '' && abs($delta) > 0.001) { $changeText = ($delta > 0 ? '↑ ' : '↓ ') . number_format(abs($delta) * 100, 1) . '%'; } @endphp
{{ $item['kp_name'] ?? $item['kp_code'] ?? '未知知识点' }}
{{ number_format($pct, 1) }}% {{ $changeText ? '子节点变化 ' . $changeText : '' }}
@endforeach @else
暂无有效掌握度数据(已过滤通用知识点和零值)
@endif
📊 知识点层级掌握度分析
@php $parentMasteryLevels = $parent_mastery_levels ?? []; $hasParentMastery = !empty($parentMasteryLevels); $childMasteryMap = []; foreach (($filteredMasteryItems ?? []) as $it) { $childMasteryMap[$it['kp_code']] = [ 'level' => floatval($it['mastery_level'] ?? 0), 'delta' => isset($it['mastery_change']) ? floatval($it['mastery_change']) : null, ]; } @endphp @if($hasParentMastery)
父子知识点关系
@foreach($parentMasteryLevels as $parentData) @php $childrenAll = $parentData['children_all'] ?? []; $children = $parentData['children'] ?? []; // 命中子节点 $childCount = count($childrenAll); $hitCount = count($children); $hitAvg = $parentData['children_hit_avg_mastery'] ?? null; $parentPct = number_format(floatval($parentData['mastery_percentage'] ?? 0), 1); $parentLevel = floatval($parentData['mastery_level'] ?? 0); $parentClass = $parentLevel >= 0.8 ? 'high' : ($parentLevel >= 0.6 ? 'mid' : 'low'); $delta = $parentData['mastery_change'] ?? null; $hitNames = array_values(array_map(fn($c) => $c['kp_name'] ?? ($c['kp_code'] ?? ''), $children)); $allChildrenPerfect = !empty($childrenAll) && count(array_filter($childrenAll, function ($c) { return floatval($c['mastery_level'] ?? 0) >= 0.999; })) === count($childrenAll); @endphp
{{ $parentData['kp_name'] ?? $parentData['kp_code'] }}
@if(!empty($childrenAll))
@foreach($childrenAll as $child) @php $isHit = !empty($child['is_hit']); $m = floatval($child['mastery_level'] ?? 0); $badgeClass = $m >= 0.8 ? 'high' : ($m >= 0.6 ? 'mid' : ($m > 0 ? 'low' : 'miss')); $badgeText = number_format($m * 100, 1) . '%'; @endphp
└─ {{ $child['kp_name'] }} {{ $badgeText }} @if($isHit) 本次命中 @endif
@endforeach
@else
无命中子知识点
@endif
{{ $parentData['kp_name'] ?? $parentData['kp_code'] }}
{{ $parentPct }}%
父节点变化: @if($delta !== null) {{ $delta >= 0 ? '↑ ' : '↓ ' }}{{ number_format(abs($delta) * 100, 1) }}% @else - @endif
本次重点子节点: @if($hitCount > 0) @foreach($hitNames as $hitName) @if(!empty(trim($hitName))) {{ $hitName }} @endif @endforeach @else 无 @endif
子节点总数 {{ $childCount }} 个,本次命中 {{ $hitCount }} 个,命中均值 {{ $hitAvg !== null ? number_format(floatval($hitAvg) * 100, 1) . '%' : '-' }}
@endforeach
@php $allParentPerfect = !empty($parentMasteryLevels) && count(array_filter($parentMasteryLevels, function ($p) { return floatval($p['mastery_level'] ?? 0) >= 0.999; })) === count($parentMasteryLevels); $allHitChildrenPerfect = !empty($filteredMasteryItems) && count(array_filter($filteredMasteryItems, function ($it) { return floatval($it['mastery_level'] ?? 0) >= 0.999; })) === count($filteredMasteryItems); $isAllPerfect = $allParentPerfect && $allHitChildrenPerfect; // 1) 优先:本次命中子知识点中的低掌握度(<60%) $hitWeakChildren = []; $hitWeakKeys = []; foreach (($filteredMasteryItems ?? []) as $hitItem) { $level = floatval($hitItem['mastery_level'] ?? 0); $name = trim((string) ($hitItem['kp_name'] ?? $hitItem['kp_code'] ?? '')); if ($name === '' || $level >= 0.6) { continue; } $key = (string) ($hitItem['kp_code'] ?? $name); $hitWeakChildren[$key] = [ 'name' => $name, 'level' => $level, ]; $hitWeakKeys[$key] = true; } $hitWeakChildren = array_values($hitWeakChildren); usort($hitWeakChildren, function ($a, $b) { return $a['level'] <=> $b['level']; }); // 2) 兜底:若命中子知识点都 >=60%,再从其他低掌握度子知识点补 $otherWeakChildren = []; foreach (($parentMasteryLevels ?? []) as $pData) { foreach (($pData['children_all'] ?? []) as $child) { $level = floatval($child['mastery_level'] ?? 0); $name = trim((string) ($child['kp_name'] ?? $child['kp_code'] ?? '')); if ($name === '' || $level >= 0.6) { continue; } $key = ($child['kp_code'] ?? $name); if (isset($hitWeakKeys[$key])) { continue; } $otherWeakChildren[$key] = [ 'name' => $name, 'level' => $level, ]; } } $otherWeakChildren = array_values($otherWeakChildren); usort($otherWeakChildren, function ($a, $b) { return $a['level'] <=> $b['level']; }); // 3) 最终展示:总数不超过5 $globalWeakChildren = []; if (!empty($hitWeakChildren)) { $globalWeakChildren = array_slice($hitWeakChildren, 0, 5); } else { $globalWeakChildren = array_slice($otherWeakChildren, 0, 5); } @endphp
学习建议: @if($isAllPerfect) 本次学案表现非常出色,相关知识点掌握稳定且完整。建议继续进入新的知识点专题学习,优先选择同层级未覆盖内容或更高难度综合题,保持进阶节奏。 @else 建议重点关注掌握度较低的知识点,通过专项练习提升整体学习水平。优先练习掌握度低于60%的知识点。 @endif @if(!empty($globalWeakChildren))
@foreach($globalWeakChildren as $weakChild) {{ $weakChild['name'] }}({{ number_format($weakChild['level'] * 100, 1) }}%) @endforeach
@endif
@else
暂无父节点掌握度数据
当前分析主要基于具体知识点掌握度。完整的层级掌握度分析需要在系统中建立完整的知识点层级关系。
@endif
@php $insightMap = []; foreach (($question_insights ?? []) as $insight) { $no = $insight['question_number'] ?? $insight['question_id'] ?? null; if ($no !== null) { $insightMap[$no] = $insight; } } $analysisWrongMap = []; foreach (($analysis_data['question_analysis'] ?? []) as $qa) { $qid = $qa['question_bank_id'] ?? $qa['question_id'] ?? null; if ($qid === null || $qid === '') { continue; } $rawCorrect = $qa['is_correct'] ?? null; $isWrongFromAnalysis = false; if (is_array($rawCorrect)) { $isWrongFromAnalysis = in_array(0, $rawCorrect, true); } elseif ($rawCorrect !== null) { $isWrongFromAnalysis = !boolval($rawCorrect); } if ($isWrongFromAnalysis) { $analysisWrongMap[(string)$qid] = true; } } $wrongQuestions = []; foreach (($questions ?? []) as $qItem) { $studentAnswerProbe = $qItem['student_answer'] ?? null; $correctAnswerProbe = $qItem['answer'] ?? ($qItem['correct_answer'] ?? null); $isCorrectProbe = $qItem['is_correct'] ?? null; if ($isCorrectProbe === null && !empty($studentAnswerProbe) && !empty($correctAnswerProbe)) { $isCorrectProbe = (trim((string)$studentAnswerProbe) === trim((string)$correctAnswerProbe)) ? 1 : 0; } $normalizedCorrect = $isCorrectProbe; if ($isCorrectProbe !== null) { $normalizedCorrect = is_bool($isCorrectProbe) ? ($isCorrectProbe ? 1 : 0) : intval($isCorrectProbe); } $qidProbe = (string)($qItem['question_bank_id'] ?? $qItem['question_id'] ?? ''); $isWrongByAnalysis = ($qidProbe !== '' && isset($analysisWrongMap[$qidProbe])); if ($normalizedCorrect === 0 || $isWrongByAnalysis) { $wrongQuestions[] = $qItem; } } // 错题知识点聚合统计(同知识点错几题/共几题) $kpStats = []; foreach (($questions ?? []) as $qItem) { $kpName = trim((string)($qItem['knowledge_point_name'] ?? $qItem['knowledge_point'] ?? '未标注知识点')); if ($kpName === '') { $kpName = '未标注知识点'; } if (!isset($kpStats[$kpName])) { $kpStats[$kpName] = ['total' => 0, 'wrong' => 0]; } $kpStats[$kpName]['total']++; } foreach ($wrongQuestions as $qItem) { $kpName = trim((string)($qItem['knowledge_point_name'] ?? $qItem['knowledge_point'] ?? '未标注知识点')); if ($kpName === '') { $kpName = '未标注知识点'; } if (!isset($kpStats[$kpName])) { $kpStats[$kpName] = ['total' => 0, 'wrong' => 0]; } $kpStats[$kpName]['wrong']++; } $kpWrongStats = []; foreach ($kpStats as $kpName => $stat) { if (($stat['wrong'] ?? 0) <= 0) { continue; } $total = max(1, intval($stat['total'] ?? 0)); $wrong = intval($stat['wrong'] ?? 0); $kpWrongStats[] = [ 'kp_name' => $kpName, 'wrong' => $wrong, 'total' => $total, 'rate' => $wrong / $total, ]; } usort($kpWrongStats, function ($a, $b) { if ($a['rate'] === $b['rate']) { return $b['wrong'] <=> $a['wrong']; } return $b['rate'] <=> $a['rate']; }); @endphp @if(!empty($wrongQuestions))
错题列表
@if(!empty($kpWrongStats))
知识点错误率
@foreach($kpWrongStats as $item) {{ $item['kp_name'] }}:{{ $item['wrong'] }}/{{ $item['total'] }}({{ number_format($item['rate'] * 100, 1) }}%) @endforeach
@endif @foreach($wrongQuestions as $q) @php // 【修复】从题目数据中获取学生答案、正确答案和判分结果 $studentAnswer = $q['student_answer'] ?? $q['student_answer'] ?? null; $correctAnswer = $q['answer'] ?? $q['correct_answer'] ?? null; $isCorrect = $q['is_correct'] ?? null; // 1=正确,0=错误,null=未判 // 如果未判分但有学生答案和正确答案,自动比较判断 if ($isCorrect === null && !empty($studentAnswer) && !empty($correctAnswer)) { $isCorrect = (trim($studentAnswer) === trim($correctAnswer)) ? 1 : 0; } // 判分状态显示逻辑 $showStatus = false; $statusText = ''; $statusColor = ''; if (!empty($studentAnswer)) { // 学生有答题,显示判分结果 $showStatus = true; if ($isCorrect === 1) { $statusText = '正确'; $statusColor = '#10b981'; } elseif ($isCorrect === 0) { $statusText = '错误'; $statusColor = '#ef4444'; } } // 没答题的不显示状态 $insight = $insightMap[$q['question_number']] ?? ($insightMap[$q['display_number'] ?? null] ?? []); // 【修复】得分显示:答错显示实际得分,答对显示满分 $fullScore = $insight['full_score'] ?? ($q['score'] ?? null); if ($isCorrect === 1) { // 答对了,显示满分 $score = $fullScore; } elseif ($isCorrect === 0) { // 答错了,显示实际得分(可能为0分或其他分数) $score = $q['score_obtained'] ?? 0; } else { // 未判分或未答题,不显示得分 $score = null; } $analysisRaw = $insight['analysis'] ?? $insight['thinking_process'] ?? $insight['feedback'] ?? $insight['suggestions'] ?? $insight['reason'] ?? ($insight['correct_solution'] ?? null); // 若有下一步建议,追加 if (empty($analysisRaw) && !empty($insight['next_steps'])) { $analysisRaw = '后续建议:' . (is_array($insight['next_steps']) ? implode(';', $insight['next_steps']) : $insight['next_steps']); } $analysis = is_array($analysisRaw) ? json_encode($analysisRaw, JSON_UNESCAPED_UNICODE) : $analysisRaw; if ($analysis === null || $analysis === '') { $analysis = '暂无解题思路,待补充'; } $formatSolutionLikeGrading = function ($text) { if (!is_string($text) || trim($text) === '') { return $text; } $normalized = preg_replace('/\s*;\s*步骤\s*(\d+)/u', ";\n步骤$1", $text); $normalized = preg_replace('/\s*。\s*步骤\s*(\d+)/u', "。\n步骤$1", $normalized); $normalized = preg_replace('/(? '选择题', 'fill' => '填空题', 'answer' => '解答题']; $typeLabel = $typeMap[$q['question_type'] ?? ''] ?? ($q['question_type'] ?? '题型未标注'); $questionText = is_string($q['question_text']) ? $q['question_text'] : json_encode($q['question_text'], JSON_UNESCAPED_UNICODE); $solution = $q['solution'] ?? null; $solution = $formatSolutionLikeGrading($solution); $analysis = $formatSolutionLikeGrading($analysis); @endphp
题号 {{ $q['display_number'] ?? $q['question_number'] }} · {{ $typeLabel }} @php $kpName = $q['knowledge_point_name'] ?? $q['knowledge_point'] ?? null; if (!empty($kpName) && $kpName !== '-' && $kpName !== '未标注') { echo '' . e($kpName) . ''; } @endphp @if($showStatus) {{ $statusText }} @endif
@if($score !== null && $fullScore !== null)
得分 {{ $score }} / {{ $fullScore }}
@endif
{{-- 【新增】学生答案显示(如果有) --}} @if(!empty($studentAnswer))
学生答案
{!! nl2br(e($studentAnswer)) !!}
@endif
{!! $questionText !!}
{{-- 【修复】正确答案显示 --}} @if(!empty($correctAnswer))
正确答案
{!! is_string($correctAnswer) ? $correctAnswer : json_encode($correctAnswer, JSON_UNESCAPED_UNICODE) !!}
@endif {{-- 【修改】解题思路显示(优先显示solution,其次显示analysis) --}} @if(!empty($solution))
解题思路
{!! nl2br(e(is_array($solution) ? json_encode($solution, JSON_UNESCAPED_UNICODE) : (string) $solution)) !!}
@elseif(!empty($analysis) && $analysis !== '暂无解题思路记录')
解题思路
{!! nl2br(e((string) $analysis)) !!}
@endif {{-- 解题步骤(如果有) --}} @if(!empty($steps))
解题步骤
    @foreach($steps as $s)
  1. {!! nl2br(e(is_array($s) ? json_encode($s, JSON_UNESCAPED_UNICODE) : $s)) !!}
  2. @endforeach
@endif
@endforeach
@endif
{{-- 闭合page div --}}