浏览代码

refine v3 analysis report structure and mastery signals

Simplify v3 report output by removing per-question blocks, align mastery source with snapshot current_mastery, and sharpen module-level learning path/status presentation for clearer intervention guidance.

Made-with: Cursor
yemeishu 3 周之前
父节点
当前提交
45b89c68f2
共有 2 个文件被更改,包括 183 次插入535 次删除
  1. 88 126
      app/Services/ExamPdfExportService.php
  2. 95 409
      resources/views/exam-analysis/pdf-report-v3.blade.php

+ 88 - 126
app/Services/ExamPdfExportService.php

@@ -322,19 +322,8 @@ class ExamPdfExportService
                 'recommendations_count' => count($templateData['recommendations'] ?? []),
             ]);
 
-            // 【重要】处理题目数据中的图片标签和公式
-            // 将 <image src="..."/> 转换为 <img src="..." />,并处理公式
-            if (! empty($templateData['questions'])) {
-                foreach ($templateData['questions'] as $idx => $question) {
-                    $templateData['questions'][$idx] = MathFormulaProcessor::processQuestionData($question);
-                }
-            }
-            if (! empty($templateData['question_insights'])) {
-                foreach ($templateData['question_insights'] as $idx => $insight) {
-                    $templateData['question_insights'][$idx] = MathFormulaProcessor::processQuestionData($insight);
-                }
-            }
-            $mark('process_formula_data_ms');
+            // V3 学情报告不再渲染逐题错题卡,保留题目元数据用于统计即可。
+            $mark('prepare_report_data_ms');
 
             // 组装V3报告展示数据(模块化)
             $templateData['v3'] = $this->buildAnalysisReportV3Data($templateData);
@@ -417,7 +406,6 @@ class ExamPdfExportService
         $rootCode = (string) ($profile['root_code'] ?? 'M00');
         $moduleCodes = $profile['module_codes'] ?? [];
         $moduleNames = $profile['module_names'] ?? [];
-        $dimensionDefs = $profile['dimension_defs'] ?? [];
 
         $moduleAgg = [];
         foreach ($moduleCodes as $moduleCode) {
@@ -516,50 +504,6 @@ class ExamPdfExportService
             ];
         }
 
-        $moduleMap = [];
-        foreach ($moduleRows as $moduleRow) {
-            $moduleMap[$moduleRow['module_code']] = $moduleRow;
-        }
-
-        $dimensions = [];
-        foreach ($dimensionDefs as $def) {
-            $masteryWeightedSum = 0.0;
-            $weightSum = 0.0;
-            $scoreMax = 0.0;
-            $scoreObtained = 0.0;
-            $kpCount = 0;
-
-            foreach ($def['modules'] as $moduleCode) {
-                $row = $moduleMap[$moduleCode] ?? null;
-                if (! $row) {
-                    continue;
-                }
-                $weight = max(1, (int) ($row['kp_count'] ?? 0));
-                if ($row['mastery_level'] !== null) {
-                    $masteryWeightedSum += ((float) $row['mastery_level']) * $weight;
-                    $weightSum += $weight;
-                }
-                $scoreMax += (float) ($row['exam_max_score'] ?? 0);
-                $scoreObtained += (float) ($row['exam_obtained_score'] ?? 0);
-                $kpCount += (int) ($row['kp_count'] ?? 0);
-            }
-
-            $mastery = $weightSum > 0 ? ($masteryWeightedSum / $weightSum) : null;
-            $score5 = $mastery !== null ? round($mastery * 5, 2) : null;
-            $scoreRate = $scoreMax > 0 ? round($scoreObtained / $scoreMax, 4) : null;
-
-            $dimensions[] = [
-                'id' => $def['id'],
-                'name' => $def['name'],
-                'module_codes' => $def['modules'],
-                'mastery_level' => $mastery !== null ? round($mastery, 4) : null,
-                'score_5' => $score5,
-                'status' => $this->resolveMasteryStatus($mastery),
-                'kp_count' => $kpCount,
-                'exam_score_rate' => $scoreRate,
-            ];
-        }
-
         $allMax = 0.0;
         $allObtained = 0.0;
         foreach ($moduleRows as $moduleRow) {
@@ -569,9 +513,9 @@ class ExamPdfExportService
         $scoreRate = $allMax > 0 ? round($allObtained / $allMax, 4) : null;
         $avgMastery = isset($overallSummary['average_mastery'])
             ? (float) $overallSummary['average_mastery']
-            : $this->averageByKey(array_filter($dimensions, fn ($d) => $d['mastery_level'] !== null), 'mastery_level');
+            : $this->averageByKey(array_filter($moduleRows, fn ($m) => $m['mastery_level'] !== null), 'mastery_level');
 
-        $paths = $this->buildV3LearningPaths($dimensions);
+        $paths = $this->buildV3LearningPaths($moduleRows);
         $difficultyInsight = $this->buildV3DifficultyInsight($templateData);
         $comparisonInsight = $this->buildV3ComparisonInsight(
             $templateData,
@@ -798,23 +742,22 @@ class ExamPdfExportService
             ],
             'radar' => $radar,
             'modules' => $moduleRows,
-            'dimensions' => $dimensions,
             'paths' => $paths,
         ];
     }
 
-    private function buildV3LearningPaths(array $dimensions): array
+    private function buildV3LearningPaths(array $moduleRows): array
     {
         $items = [];
-        foreach ($dimensions as $dimension) {
-            if (! isset($dimension['mastery_level']) || $dimension['mastery_level'] === null) {
+        foreach ($moduleRows as $moduleRow) {
+            if (! isset($moduleRow['mastery_level']) || $moduleRow['mastery_level'] === null) {
                 continue;
             }
             $items[] = [
-                'name' => (string) ($dimension['name'] ?? ''),
-                'mastery_level' => (float) $dimension['mastery_level'],
-                'score_5' => (float) ($dimension['score_5'] ?? 0),
-                'status' => $dimension['status'] ?? '一般',
+                'name' => (string) ($moduleRow['module_name'] ?? ''),
+                'module_code' => (string) ($moduleRow['module_code'] ?? ''),
+                'mastery_level' => (float) $moduleRow['mastery_level'],
+                'status' => $moduleRow['status'] ?? '未学习',
             ];
         }
 
@@ -822,19 +765,9 @@ class ExamPdfExportService
         $lowToHigh = $items;
         $highToLow = array_reverse($items);
 
-        $keep = array_values(array_filter($highToLow, fn ($i) => $i['mastery_level'] >= 0.75));
-        $boost = array_values(array_filter($lowToHigh, fn ($i) => $i['mastery_level'] >= 0.5 && $i['mastery_level'] < 0.75));
-        $key = array_values(array_filter($lowToHigh, fn ($i) => $i['mastery_level'] < 0.5));
-
-        if (empty($keep) && ! empty($highToLow)) {
-            $keep[] = $highToLow[0];
-        }
-        if (empty($boost) && count($lowToHigh) > 1) {
-            $boost[] = $lowToHigh[(int) floor((count($lowToHigh) - 1) / 2)];
-        }
-        if (empty($key) && ! empty($lowToHigh)) {
-            $key[] = $lowToHigh[0];
-        }
+        $keep = array_values(array_filter($highToLow, fn ($i) => $i['mastery_level'] >= 0.85));
+        $boost = array_values(array_filter($lowToHigh, fn ($i) => $i['mastery_level'] >= 0.6 && $i['mastery_level'] < 0.85));
+        $key = array_values(array_filter($lowToHigh, fn ($i) => $i['mastery_level'] < 0.6));
 
         return [
             'keep' => array_slice($keep, 0, 2),
@@ -846,16 +779,16 @@ class ExamPdfExportService
     private function resolveMasteryStatus(?float $mastery): string
     {
         if ($mastery === null) {
-            return '暂无';
+            return '未学习';
         }
-        if ($mastery >= 0.8) {
-            return '良好';
+        if ($mastery >= 0.85) {
+            return '已掌握';
         }
         if ($mastery >= 0.6) {
-            return '一般';
+            return '薄弱';
         }
 
-        return '薄弱';
+        return '未入门';
     }
 
     private function resolveOverallPerformanceLabel(?float $avgMastery): string
@@ -1542,20 +1475,11 @@ class ExamPdfExportService
                 'S10_000' => '概率统计',
                 'S11_000' => '复数',
             ];
-            $dimensionDefs = [];
-            foreach ($moduleNames as $code => $name) {
-                $dimensionDefs[] = [
-                    'id' => strtolower(str_replace('_', '', $code)),
-                    'name' => $name,
-                    'modules' => [$code],
-                ];
-            }
 
             return [
                 'root_code' => 'S000_000',
                 'module_codes' => array_keys($moduleNames),
                 'module_names' => $moduleNames,
-                'dimension_defs' => $dimensionDefs,
             ];
         }
 
@@ -1571,14 +1495,6 @@ class ExamPdfExportService
                 'M06' => '统计与概率',
                 'M07' => '函数',
             ],
-            'dimension_defs' => [
-                ['id' => 'number_expr', 'name' => '数与式', 'modules' => ['M01']],
-                ['id' => 'equation', 'name' => '方程与不等式', 'modules' => ['M02']],
-                ['id' => 'function', 'name' => '函数', 'modules' => ['M07']],
-                ['id' => 'shape_change', 'name' => '图形的变化', 'modules' => ['M04']],
-                ['id' => 'shape_property', 'name' => '图形的性质', 'modules' => ['M03', 'M05']],
-                ['id' => 'stat_prob', 'name' => '统计与概率', 'modules' => ['M06']],
-            ],
         ];
     }
 
@@ -1610,11 +1526,6 @@ class ExamPdfExportService
         return $map;
     }
 
-    private function mapKpToJuniorModule(string $kpCode, array $kpMeta): ?string
-    {
-        return $this->mapKpToStageModule($kpCode, $kpMeta, 'M00');
-    }
-
     private function mapKpToStageModule(string $kpCode, array $kpMeta, string $rootCode): ?string
     {
         $code = trim($kpCode);
@@ -2127,6 +2038,7 @@ class ExamPdfExportService
 
         $fullMasteryMap = [];
         $snapshotMasteryData = [];
+        $snapshotCurrentMasteryMap = [];
 
         if (! empty($analysisData['knowledge_point_analysis'])) {
             // 将knowledge_point_analysis转换为buildMasterySummary期望的格式
@@ -2159,21 +2071,46 @@ class ExamPdfExportService
                 $previousMasteryData = [];
                 $snapshotMasteryData = [];
                 if ($lastSnapshot) {
+                    $currentMasteryJson = json_decode((string) ($lastSnapshot->current_mastery ?? ''), true);
+                    if (is_array($currentMasteryJson)) {
+                        foreach ($currentMasteryJson as $kpCode => $level) {
+                            $code = trim((string) $kpCode);
+                            if ($code !== '' && is_numeric($level)) {
+                                $snapshotCurrentMasteryMap[$code] = floatval($level);
+                            }
+                        }
+                    }
+
                     $previousMasteryJson = json_decode($lastSnapshot->mastery_data, true);
-                    foreach ($previousMasteryJson as $kpCode => $data) {
-                        $snapshotMasteryData[$kpCode] = [
-                            'current_mastery' => isset($data['current_mastery']) ? floatval($data['current_mastery']) : null,
-                            'previous_mastery' => isset($data['previous_mastery']) ? floatval($data['previous_mastery']) : null,
-                            'change' => isset($data['change']) ? floatval($data['change']) : null,
-                        ];
-                        $previousMasteryData[$kpCode] = [
-                            'current_mastery' => $data['current_mastery'] ?? 0,
-                            'previous_mastery' => $data['previous_mastery'] ?? null,
-                        ];
+                    if (is_array($previousMasteryJson)) {
+                        foreach ($previousMasteryJson as $kpCode => $data) {
+                            if (! is_array($data)) {
+                                continue;
+                            }
+                            $snapshotMasteryData[$kpCode] = [
+                                'current_mastery' => isset($data['current_mastery']) ? floatval($data['current_mastery']) : null,
+                                'previous_mastery' => isset($data['previous_mastery']) ? floatval($data['previous_mastery']) : null,
+                                'change' => isset($data['change']) ? floatval($data['change']) : null,
+                            ];
+                            $code = trim((string) $kpCode);
+                            if (
+                                $code !== ''
+                                && ! array_key_exists($code, $snapshotCurrentMasteryMap)
+                                && isset($data['current_mastery'])
+                                && is_numeric($data['current_mastery'])
+                            ) {
+                                $snapshotCurrentMasteryMap[$code] = floatval($data['current_mastery']);
+                            }
+                            $previousMasteryData[$kpCode] = [
+                                'current_mastery' => $data['current_mastery'] ?? 0,
+                                'previous_mastery' => $data['previous_mastery'] ?? null,
+                            ];
+                        }
                     }
                     Log::info('ExamPdfExportService: 获取到上一次快照数据', [
                         'snapshot_time' => $lastSnapshot->snapshot_time,
                         'kp_count' => count($previousMasteryData),
+                        'current_mastery_count' => count($snapshotCurrentMasteryMap),
                     ]);
                 }
 
@@ -2320,16 +2257,38 @@ class ExamPdfExportService
                     ->first();
 
                 if ($lastSnapshot) {
+                    $currentMasteryJson = json_decode((string) ($lastSnapshot->current_mastery ?? ''), true);
+                    if (is_array($currentMasteryJson)) {
+                        foreach ($currentMasteryJson as $kpCode => $level) {
+                            $code = trim((string) $kpCode);
+                            if ($code !== '' && is_numeric($level)) {
+                                $snapshotCurrentMasteryMap[$code] = floatval($level);
+                            }
+                        }
+                    }
+
                     $previousMasteryJson = json_decode($lastSnapshot->mastery_data, true);
-                    foreach ($masteryData as &$item) {
-                        $kpCode = $item['kp_code'];
-                        if (isset($previousMasteryJson[$kpCode])) {
-                            $previous = floatval($previousMasteryJson[$kpCode]['previous_mastery'] ?? 0);
-                            $current = floatval($item['mastery_level']);
-                            $item['mastery_change'] = $current - $previous;
+                    if (is_array($previousMasteryJson)) {
+                        foreach ($masteryData as &$item) {
+                            $kpCode = $item['kp_code'];
+                            if (isset($previousMasteryJson[$kpCode]) && is_array($previousMasteryJson[$kpCode])) {
+                                $snapshotNode = $previousMasteryJson[$kpCode];
+                                $code = trim((string) $kpCode);
+                                if (
+                                    $code !== ''
+                                    && ! array_key_exists($code, $snapshotCurrentMasteryMap)
+                                    && isset($snapshotNode['current_mastery'])
+                                    && is_numeric($snapshotNode['current_mastery'])
+                                ) {
+                                    $snapshotCurrentMasteryMap[$code] = floatval($snapshotNode['current_mastery']);
+                                }
+                                $previous = floatval($previousMasteryJson[$kpCode]['previous_mastery'] ?? 0);
+                                $current = floatval($item['mastery_level']);
+                                $item['mastery_change'] = $current - $previous;
+                            }
                         }
+                        unset($item);
                     }
-                    unset($item);
                 }
 
                 Log::info('ExamPdfExportService: 成功获取多层级掌握度数据', [
@@ -2389,7 +2348,10 @@ class ExamPdfExportService
         ]);
 
         // 构建当前学生掌握度映射,供父子影响分析展示使用
-        $masteryMap = !empty($fullMasteryMap) ? $fullMasteryMap : [];
+        // 与 PC 端保持一致:优先使用当前报告/学案对应快照的 current_mastery。
+        $masteryMap = !empty($snapshotCurrentMasteryMap)
+            ? $snapshotCurrentMasteryMap
+            : (!empty($fullMasteryMap) ? $fullMasteryMap : []);
         if (empty($masteryMap)) {
             foreach ($masteryData as $m) {
                 $code = $m['kp_code'] ?? null;

+ 95 - 409
resources/views/exam-analysis/pdf-report-v3.blade.php

@@ -14,7 +14,7 @@
     $scoreTotal = $summary['score_total'] ?? null;
     $scoreRate = $summary['score_rate'] ?? null;
     $averageMastery = $summary['average_mastery'] ?? null;
-    $overallLabel = $summary['overall_label'] ?? '待评估';
+    $examHitKpSet = array_fill_keys(array_map('strval', $exam_hit_kp_codes ?? []), true);
     $difficultySummary = $summary['difficulty'] ?? [];
     $comparisonSummary = $summary['comparison'] ?? [];
     $overallLabelDetail = $summary['overall_label_detail'] ?? [];
@@ -50,42 +50,13 @@
 
     $statusColor = function (string $status): string {
         return match ($status) {
-            '良好' => '#16a34a',
-            '一般' => '#f97316',
-            '薄弱' => '#e11d48',
+            '已掌握' => '#16a34a',
+            '薄弱' => '#f59e0b',
+            '未入门' => '#ef4444',
             default => '#64748b',
         };
     };
 
-    $n = max(1, count($radar));
-    $cx = 210;
-    $cy = 155;
-    $r = 108;
-    $outer = [];
-    $inner = [];
-    for ($i = 0; $i < $n; $i++) {
-        $angle = -M_PI / 2 + (2 * M_PI * $i / $n);
-        $ox = $cx + $r * cos($angle);
-        $oy = $cy + $r * sin($angle);
-        $outer[] = [$ox, $oy];
-
-        $value = isset($radar[$i]['value']) ? (float) $radar[$i]['value'] : 0.0;
-        $ratio = max(0.0, min(1.0, $value / 5));
-        $ix = $cx + $r * $ratio * cos($angle);
-        $iy = $cy + $r * $ratio * sin($angle);
-        $inner[] = [$ix, $iy];
-    }
-    $outerPoints = implode(' ', array_map(fn ($p) => round($p[0], 2).','.round($p[1], 2), $outer));
-    $innerPoints = implode(' ', array_map(fn ($p) => round($p[0], 2).','.round($p[1], 2), $inner));
-
-    $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;
@@ -215,7 +186,7 @@
     };
 
     $clusterCards = [];
-    $allClusterPoints = [];
+    $allStagePoints = [];
     foreach ($radar as $moduleItem) {
         $children = is_array($moduleItem['children'] ?? null) ? $moduleItem['children'] : [];
         $greatMap = [];
@@ -229,8 +200,8 @@
                 $parentCode = trim((string) ($child['parent_code'] ?? ''));
                 $parentName = $parentCode !== '' ? $parentCode : '未分组';
             }
-            $mastery = isset($child['mastery_level']) ? (float) $child['mastery_level'] : null;
-            $status = $childMasteryStatus($mastery);
+            $childMasteryLevel = isset($child['mastery_level']) ? (float) $child['mastery_level'] : null;
+            $status = $childMasteryStatus($childMasteryLevel);
             if (!isset($greatMap[$greatKey])) {
                 $greatMap[$greatKey] = [];
             }
@@ -244,12 +215,20 @@
                 'code' => (string) ($child['code'] ?? ''),
                 'name' => (string) ($child['name'] ?? '未命名知识点'),
                 'path' => (string) ($child['path'] ?? ''),
-                'mastery_level' => $mastery,
+                'mastery_level' => $childMasteryLevel,
                 'change' => isset($child['change']) ? (float) $child['change'] : null,
                 'status' => $status,
                 'color' => $childStatusColor($status),
                 'is_hit' => !empty($child['is_hit']),
             ];
+            $allStagePoints[] = [
+                'code' => (string) ($child['code'] ?? ''),
+                'name' => (string) ($child['name'] ?? '未命名知识点'),
+                'mastery_level' => $childMasteryLevel,
+                'status' => $status,
+                'change' => isset($child['change']) ? (float) $child['change'] : null,
+                'is_hit' => !empty($child['is_hit']),
+            ];
         }
 
         $greatGroups = [];
@@ -329,6 +308,7 @@
             foreach (($great['grand_groups'] ?? []) as $grand) {
                 $gStats = $grand['stats'] ?? ['learned' => 0, 'total' => 0];
                 $clusterCards[] = [
+                    'module_name' => (string) ($moduleItem['name'] ?? '未分组'),
                     'great_name' => $great['great_name'] ?? '未分组',
                     'grand_name' => $grand['grand_name'] ?? '未分组',
                     'parent_groups' => $grand['parent_groups'] ?? [],
@@ -343,21 +323,14 @@
         return (($sb['learned'] ?? 0) <=> ($sa['learned'] ?? 0))
             ?: (($sb['total'] ?? 0) <=> ($sa['total'] ?? 0));
     });
-    foreach ($clusterCards as $card) {
-        foreach (($card['parent_groups'] ?? []) as $pg) {
-            foreach (($pg['points'] ?? []) as $p) {
-                $allClusterPoints[] = $p;
-            }
-        }
-    }
     $kpStatsTotal = [
-        'total' => count($allClusterPoints),
+        'total' => count($allStagePoints),
         'mastered' => 0,
         'weak' => 0,
         'beginner' => 0,
         'unlearned' => 0,
     ];
-    foreach ($allClusterPoints as $p) {
+    foreach ($allStagePoints as $p) {
         $st = (string) ($p['status'] ?? '未学习');
         if ($st === '已掌握') {
             $kpStatsTotal['mastered']++;
@@ -372,10 +345,11 @@
     $moduleRowsWithStatus = array_values(array_filter($modules, function ($m) {
         $status = trim((string) ($m['status'] ?? ''));
         $masteryLevel = $m['mastery_level'] ?? null;
+        $questionCount = (int) ($m['question_count'] ?? 0);
         if ($masteryLevel !== null) {
             return true;
         }
-        return $status !== '' && ! in_array($status, ['暂无', '-', '未涉及'], true);
+        return $questionCount > 0 && $status !== '' && ! in_array($status, ['暂无', '-', '未涉及', '未学习'], true);
     }));
     $pathTagByModuleName = [];
     foreach (['keep' => '保分不错', 'boost' => '需要加强', 'key' => '优先加强'] as $bucket => $tagName) {
@@ -427,8 +401,8 @@
     ];
     $kpQuestionTypeMap = [];
     foreach (($questions ?? []) as $qItem) {
-        $kpName = trim((string) ($qItem['knowledge_point_name'] ?? $qItem['knowledge_point'] ?? ''));
-        if ($kpName === '') {
+        $kpCode = trim((string) ($qItem['knowledge_point'] ?? ''));
+        if ($kpCode === '') {
             continue;
         }
         $rawType = strtolower(trim((string) ($qItem['question_type'] ?? '')));
@@ -436,10 +410,10 @@
         if ($typeLabel === '') {
             $typeLabel = '未知题型';
         }
-        if (! isset($kpQuestionTypeMap[$kpName])) {
-            $kpQuestionTypeMap[$kpName] = [];
+        if (! isset($kpQuestionTypeMap[$kpCode])) {
+            $kpQuestionTypeMap[$kpCode] = [];
         }
-        $kpQuestionTypeMap[$kpName][$typeLabel] = true;
+        $kpQuestionTypeMap[$kpCode][$typeLabel] = true;
     }
     $moduleKpSuggestions = [];
     foreach ($moduleRowsWithStatus as $m) {
@@ -496,8 +470,6 @@
                         return $sa <=> $sb;
                     });
                     $weakest = $unlearned[0];
-                } else {
-                    $weakest = $lowestStarted;
                 }
             }
         } else {
@@ -511,20 +483,33 @@
             }
         }
         if (! is_array($weakest)) {
+            $moduleKpSuggestions[] = [
+                'module_name' => $moduleName,
+                'path_tag' => $pathTagByModuleName[$moduleName] ?? '待观察',
+                'kp_name' => '',
+                'kp_code' => '',
+                'mastery_level' => null,
+                'status' => '当前模块暂无需额外关注知识点',
+                'question_types' => [],
+                'is_empty' => true,
+            ];
             continue;
         }
         $kpName = (string) ($weakest['name'] ?? '');
         if ($kpName === '') {
             continue;
         }
-        $types = array_keys($kpQuestionTypeMap[$kpName] ?? []);
+        $kpCode = (string) ($weakest['code'] ?? '');
+        $types = array_keys($kpQuestionTypeMap[$kpCode] ?? []);
         $moduleKpSuggestions[] = [
             'module_name' => $moduleName,
             'path_tag' => $pathTagByModuleName[$moduleName] ?? '待观察',
             'kp_name' => $kpName,
+            'kp_code' => $kpCode,
             'mastery_level' => $weakest['mastery_level'] ?? null,
             'status' => $childMasteryStatus($weakest['mastery_level'] ?? null),
             'question_types' => $types,
+            'is_empty' => false,
         ];
     }
     $moduleSuggestionByName = [];
@@ -534,15 +519,25 @@
             $moduleSuggestionByName[$name] = $sug;
         }
     }
-    $kpChangeItems = array_values(array_filter($allClusterPoints, function ($p) {
-        $change = $p['change'] ?? null;
-        return $change !== null && is_numeric($change) && !empty($p['is_hit']);
-    }));
-    if (empty($kpChangeItems)) {
-        $kpChangeItems = array_values(array_filter($allClusterPoints, function ($p) {
-            $change = $p['change'] ?? null;
-            return $change !== null && is_numeric($change);
-        }));
+    $kpChangeItems = [];
+    foreach (($mastery['items'] ?? []) as $item) {
+        $code = trim((string) ($item['kp_code'] ?? $item['code'] ?? ''));
+        if ($code !== '' && ! empty($examHitKpSet) && ! isset($examHitKpSet[$code])) {
+            continue;
+        }
+        $level = $item['mastery_level'] ?? null;
+        if ($level === null || ! is_numeric($level)) {
+            continue;
+        }
+        $change = $item['mastery_change'] ?? $item['change'] ?? 0.0;
+        $kpChangeItems[] = [
+            'code' => $code,
+            'name' => (string) ($item['kp_name'] ?? $item['name'] ?? ($code !== '' ? $code : '-')),
+            'mastery_level' => (float) $level,
+            'change' => is_numeric($change) ? (float) $change : 0.0,
+            'status' => $childMasteryStatus((float) $level),
+            'is_hit' => true,
+        ];
     }
     usort($kpChangeItems, function ($a, $b) {
         return abs((float) ($b['change'] ?? 0)) <=> abs((float) ($a['change'] ?? 0));
@@ -648,16 +643,7 @@
             box-shadow: none;
         }
         .overall-meta { margin-top: 8px; font-size: 9px; color: #64748b; line-height: 1.6; white-space: nowrap; }
-        .radar-center { text-align: center; }
-        .legend { margin-top: 8px; font-size: 12px; color: #475569; }
-        .legend span { margin: 0 8px; }
         .dot { display: inline-block; width: 10px; height: 10px; border-radius: 999px; margin-right: 4px; vertical-align: middle; }
-        /* PDF 版优先上下结构,避免左右分栏导致拥挤 */
-        .radar-split { display: block; width: 100%; }
-        .radar-left { width: 100%; text-align: center; }
-        .radar-right { width: 100%; padding-left: 0; margin-top: 10px; }
-        .radar-desc { border: 1px solid #dbeafe; background: #f8fbff; border-radius: 12px; padding: 12px; text-align: left; }
-        .radar-item { display: block; margin: 6px 0; font-size: 12px; }
         .cluster-toolbar {
             margin-bottom: 8px;
             font-size: 12px;
@@ -763,74 +749,6 @@
         .tag { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; color: #334155; background: #e5e7eb; }
         .error-kp-tag { display: inline-block; margin: 0 6px 6px 0; padding: 1px 7px; border-radius: 999px; font-size: 10px; color: #334155; background: #f8fafc; border: 1px solid #d1d5db; }
         .error-kp-tag.high-risk { color: #b91c1c; border-color: #fca5a5; background: #fff; font-weight: 600; }
-        .question-card { border:1px solid #e5e7eb; border-radius:8px; padding:6px 9px; margin-bottom:5px; background:#fff; page-break-inside:auto; break-inside:auto; }
-        .question-block { margin-bottom: 5px; padding: 5px; border-radius: 4px; page-break-inside: auto; break-inside: auto; }
-        .question-card,
-        .question-card .math-content,
-        .question-card .solution-content { font-size: 12px; line-height: 1.7; }
-        .question-card .question-stem svg,
-        .question-card .math-content svg { max-width: 100%; height: auto; display: block; shape-rendering: geometricPrecision; text-rendering: geometricPrecision; }
-        .question-card .question-stem svg text {
-            font-family: "Noto Serif", "Noto Serif CJK SC", "Noto Sans CJK SC", "Noto Sans", "STSongti-SC", "PingFang SC", "Songti SC", serif !important;
-            font-size: 13px !important;
-            font-weight: bold;
-            dominant-baseline: middle;
-            text-anchor: middle;
-        }
-        .question-card .question-stem svg circle,
-        .question-card .question-stem svg line,
-        .question-card .question-stem svg polygon,
-        .question-card .question-stem svg polyline { shape-rendering: geometricPrecision; }
-        .question-card .question-stem img,
-        .question-card .question-main img {
-            display: block;
-            max-width: 220px;
-            max-height: 60mm;
-            width: auto;
-            height: auto;
-            margin: 6px auto;
-            box-sizing: border-box;
-            object-fit: contain;
-            -webkit-print-color-adjust: exact;
-            print-color-adjust: exact;
-            image-rendering: -webkit-optimize-contrast;
-        }
-        .question-card .question-stem .katex { font-size: 1em !important; vertical-align: 0; }
-        .question-card .question-stem .katex-display { margin: 0.35em 0 !important; }
-        .question-card .solution-content img,
-        .question-card .report-answer-meta img {
-            display: block;
-            max-width: 220px;
-            max-height: 60mm;
-            width: auto;
-            height: auto;
-            margin: 6px auto;
-            object-fit: contain;
-            -webkit-print-color-adjust: exact;
-            print-color-adjust: exact;
-        }
-        .solution-content {
-            display: block;
-            line-height: 1.75;
-            white-space: normal;
-            word-break: break-word;
-            overflow-wrap: anywhere;
-            page-break-inside: auto;
-            break-inside: auto;
-        }
-        .report-options { margin-top: 6px; page-break-inside: auto; break-inside: auto; }
-        .report-options.options-grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px 12px; }
-        .report-options.options-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 20px; }
-        .report-options.options-grid-1 { display: grid; grid-template-columns: 1fr; gap: 8px; }
-        .report-options .option { display: flex; align-items: baseline; font-size: 12px; line-height: 1.6; page-break-inside: auto; break-inside: auto; }
-        .report-options .option strong { margin-right: 4px; flex: 0 0 auto; }
-        .report-options .option-value.option-short { white-space: nowrap; }
-        .report-options .option-value.option-long { white-space: normal; word-break: break-word; }
-        .report-options .option p, .report-options .option div { margin: 0; display: inline; }
-        .report-options .option img { max-width: 100%; height: auto; vertical-align: middle; }
-        .report-answer-meta { font-size: 12px; color: #2f2f2f; line-height: 1.75; margin-top: 6px; page-break-inside: auto; break-inside: auto; }
-        .report-answer-meta .answer-line + .answer-line { margin-top: 4px; }
-        .report-answer-meta .solution-content { display: inline; line-height: 1.75; }
         .muted { color: #6b7280; font-size: 12px; }
     </style>
 </head>
@@ -855,7 +773,6 @@
                         暂无得分数据
                     @endif
                 </li>
-                <li>得分率:{{ $scoreRate !== null ? number_format((float) $scoreRate * 100, 1) . '%' : '暂无得分率' }}</li>
                 <li>平均掌握度:{{ $averageMastery !== null ? number_format((float) $averageMastery * 100, 1) . '%' : '暂无掌握度' }}</li>
                 <li>
                     难度匹配:
@@ -928,7 +845,7 @@
             @foreach($clusterCards as $cluster)
                 <div class="cluster-card">
                     <div class="cluster-card-title">
-                        {{ $cluster['grand_name'] }}
+                        {{ $cluster['module_name'] }} / {{ $cluster['grand_name'] }}
                     </div>
                     @if(!empty($cluster['parent_groups']))
                         @foreach($cluster['parent_groups'] as $parent)
@@ -992,7 +909,10 @@
                     @foreach($kpChangeItems as $item)
                         @php
                             $delta = (float) ($item['change'] ?? 0);
-                            $deltaText = ($delta >= 0 ? '+' : '') . number_format($delta * 100, 1) . '%';
+                            $deltaArrow = $delta > 0.0005 ? '↑' : ($delta < -0.0005 ? '↓' : '→');
+                            $deltaText = $deltaArrow === '→'
+                                ? '→'
+                                : ($deltaArrow . number_format(abs($delta) * 100, 1) . '%');
                             $deltaColor = $delta > 0 ? '#16a34a' : ($delta < 0 ? '#dc2626' : '#64748b');
                             $masteryText = isset($item['mastery_level']) && $item['mastery_level'] !== null
                                 ? number_format((float) $item['mastery_level'] * 100, 1) . '%'
@@ -1006,9 +926,25 @@
                     @endforeach
                 </ul>
             @else
-                <div class="muted" style="margin-top:4px;">暂无可用的知识点变化数据</div>
+                <div class="muted" style="margin-top:4px;">
+                    @if(!empty($kpWrongStats))
+                        暂无本学案命中知识点的掌握度数据,以下方知识点错误率作为本学案影响依据。
+                    @else
+                        暂无可用的知识点变化数据
+                    @endif
+                </div>
             @endif
         </div>
+        @if(!empty($kpWrongStats))
+            <div style="margin-bottom:8px; padding:8px 10px; border:1px solid #e5e7eb; border-radius:8px; background:#fff7ed;">
+                <div style="font-size:12px; font-weight:700; color:#9a3412; margin-bottom:6px;">知识点错误率</div>
+                <div style="font-size:12px; color:#475569; line-height:1.7;">
+                    @foreach($kpWrongStats as $item)
+                        <span class="error-kp-tag {{ $item['rate'] > 0.5 ? 'high-risk' : '' }}">{{ $item['kp_name'] }}:{{ $item['wrong'] }}/{{ $item['total'] }}({{ number_format($item['rate'] * 100, 1) }}%)</span>
+                    @endforeach
+                </div>
+            </div>
+        @endif
         <div style="margin-bottom:8px; padding:8px 10px; border:1px solid #e5e7eb; border-radius:8px; background:#fff;">
             <div style="font-size:12px;color:#334155;">
                 本次学案影响模块:
@@ -1032,7 +968,7 @@
                 <th style="width: 18%;">掌握度<br>(学案影响)</th>
                 <th style="width: 12%;">掌握状态</th>
                 <th style="width: 9%;">题目数</th>
-                <th style="width: 11%;">得分</th>
+                <th style="width: 11%;">得分</th>
                 <th style="width: 11%;">路径建议</th>
                 <th style="width: 25%;">关注知识点</th>
             </tr>
@@ -1066,15 +1002,19 @@
                     $focus = $moduleSuggestionByName[$moduleName] ?? null;
                     $focusText = '-';
                     if (is_array($focus)) {
-                        $focusName = (string) ($focus['kp_name'] ?? '');
-                        $focusTypes = !empty($focus['question_types']) ? implode('、', $focus['question_types']) : '';
-                        $focusMastery = isset($focus['mastery_level']) && $focus['mastery_level'] !== null
-                            ? number_format((float) $focus['mastery_level'] * 100, 1) . '%'
-                            : '--';
-                        $focusSuffix = $focusTypes !== '' ? (',' . $focusTypes) : '';
-                        $focusText = $focusName !== ''
-                            ? ($focusName . '(' . $focusMastery . $focusSuffix . ')')
-                            : '-';
+                        if (!empty($focus['is_empty'])) {
+                            $focusText = (string) ($focus['status'] ?? '当前模块暂无需额外关注知识点');
+                        } else {
+                            $focusName = (string) ($focus['kp_name'] ?? '');
+                            $focusTypes = !empty($focus['question_types']) ? implode('、', $focus['question_types']) : '';
+                            $focusMastery = isset($focus['mastery_level']) && $focus['mastery_level'] !== null
+                                ? number_format((float) $focus['mastery_level'] * 100, 1) . '%'
+                                : '--';
+                            $focusSuffix = $focusTypes !== '' ? (',' . $focusTypes) : '';
+                            $focusText = $focusName !== ''
+                                ? ($focusName . '(' . $focusMastery . $focusSuffix . ')')
+                                : '当前模块暂无需额外关注知识点';
+                        }
                     }
                 @endphp
                 <tr>
@@ -1108,260 +1048,6 @@
             </tbody>
         </table>
     </div>
-
-    @if(!empty($wrongQuestions))
-        <div class="section" style="page-break-inside:auto; break-inside:auto;">
-            <div class="section-title">四、这次错题记录</div>
-            @if(!empty($kpWrongStats))
-                <div style="margin-bottom:8px; padding:8px; border:1px solid #e5e7eb; border-radius:6px; background:#f8fafc;">
-                    <div style="font-size:12px; font-weight:600; margin-bottom:6px;">知识点错误率</div>
-                    <div style="font-size:12px; color:#475569; line-height:1.7;">
-                        @foreach($kpWrongStats as $item)
-                            <span class="error-kp-tag {{ $item['rate'] > 0.5 ? 'high-risk' : '' }}">{{ $item['kp_name'] }}:{{ $item['wrong'] }}/{{ $item['total'] }}({{ number_format($item['rate'] * 100, 1) }}%)</span>
-                        @endforeach
-                    </div>
-                </div>
-            @endif
-
-            @foreach($wrongQuestions as $q)
-                @php
-                    $studentAnswer = $q['student_answer'] ?? null;
-                    $correctAnswer = $q['answer'] ?? $q['correct_answer'] ?? null;
-                    $isCorrect = $q['is_correct'] ?? null;
-                    if ($isCorrect === null && !empty($studentAnswer) && !empty($correctAnswer)) {
-                        $isCorrect = (trim($studentAnswer) === trim($correctAnswer)) ? 1 : 0;
-                    }
-
-                    $statusText = '';
-                    $statusColorValue = '';
-                    if ($isCorrect === 1) {
-                        $statusText = '正确';
-                        $statusColorValue = '#10b981';
-                    } elseif ($isCorrect === 0) {
-                        $statusText = '错误';
-                        $statusColorValue = '#ef4444';
-                    }
-                    $showStatus = $statusText !== '';
-
-                    $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) {
-                        $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 = '暂无解题思路,待补充';
-                    }
-                    if (is_string($analysis)) {
-                        $analysis = preg_replace('/^【?\s*解题思路\s*】?\s*[::]?\s*/u', '', $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('/(?<!^)(步骤\s*\d+\s*[::])/u', "\n$1", $normalized);
-                        $normalized = preg_replace('/(?<!^)(第\s*\d+\s*步\s*[::]?)/u', "\n$1", $normalized);
-                        $normalized = preg_replace('/\n{3,}/u', "\n\n", $normalized);
-                        $normalized = preg_replace('/^[\h\x{3000}]+/mu', '', $normalized);
-                        return trim($normalized);
-                    };
-                    $stepsRaw = $insight['steps'] ?? $insight['solution_steps'] ?? $insight['analysis_steps'] ?? null;
-                    $steps = [];
-                    if (is_array($stepsRaw)) {
-                        $steps = $stepsRaw;
-                    } elseif (is_string($stepsRaw) && trim($stepsRaw) !== '') {
-                        $steps = preg_split('/[\r\n]+/', trim($stepsRaw));
-                    }
-                    $typeMap = ['choice' => '选择题', '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;
-                    if (is_string($solution)) {
-                        $solution = preg_replace('/^【?\s*解题思路\s*】?\s*[::]?\s*/u', '', $solution);
-                    }
-                    $solution = $formatSolutionLikeGrading($solution);
-                    $analysis = $formatSolutionLikeGrading($analysis);
-                    $renderLikeGrading = function ($text) {
-                        if (is_array($text)) {
-                            $text = json_encode($text, JSON_UNESCAPED_UNICODE);
-                        }
-                        $text = is_string($text) ? trim($text) : '';
-                        if ($text === '') {
-                            return '';
-                        }
-                        // 兼容题库里常见的转义写法:\$x\$、\$\frac{...}\$
-                        $text = preg_replace('/\\\\\\$/u', '$', $text);
-                        return \App\Services\MathFormulaProcessor::processFormulas($text);
-                    };
-                    $questionTextRendered = $renderLikeGrading($questionText);
-                    $displayCorrectAnswer = is_array($correctAnswer) ? json_encode($correctAnswer, JSON_UNESCAPED_UNICODE) : (string) $correctAnswer;
-                    $questionTypeRaw = strtolower(trim((string) ($q['question_type'] ?? '')));
-                    $isChoiceQuestion = in_array($questionTypeRaw, ['choice', 'multiple_choice', 'single_choice', '选择题', 'select'], true);
-                    $normalizedOptions = [];
-                    $correctAnswerLetters = [];
-                    if ($isChoiceQuestion) {
-                        $rawOptions = $q['options'] ?? [];
-                        if (is_string($rawOptions)) {
-                            $decodedOptions = json_decode($rawOptions, true);
-                            $rawOptions = is_array($decodedOptions) ? $decodedOptions : [];
-                        }
-                        if (is_array($rawOptions)) {
-                            foreach ($rawOptions as $optKey => $optValue) {
-                                $letter = null;
-                                if (is_string($optKey) && preg_match('/([A-H])/i', $optKey, $m)) {
-                                    $letter = strtoupper($m[1]);
-                                } elseif (is_array($optValue)) {
-                                    $candidate = $optValue['label'] ?? $optValue['key'] ?? $optValue['option'] ?? null;
-                                    if (is_string($candidate) && preg_match('/([A-H])/i', $candidate, $m)) {
-                                        $letter = strtoupper($m[1]);
-                                    }
-                                }
-                                if ($letter === null) {
-                                    continue;
-                                }
-                                $content = is_array($optValue) ? ($optValue['content'] ?? $optValue['text'] ?? $optValue['value'] ?? '') : $optValue;
-                                if (!is_string($content)) {
-                                    $content = json_encode($content, JSON_UNESCAPED_UNICODE);
-                                }
-                                $content = trim((string) $content);
-                                if ($content !== '') {
-                                    $normalizedOptions[$letter] = $content;
-                                }
-                            }
-                        }
-                        if (trim((string) $correctAnswer) !== '') {
-                            preg_match_all('/[A-H]/i', strtoupper((string) $correctAnswer), $answerMatches);
-                            $correctAnswerLetters = array_values(array_unique($answerMatches[0] ?? []));
-                        }
-                        if (!empty($normalizedOptions) && !empty($correctAnswerLetters)) {
-                            $mappedAnswers = [];
-                            foreach ($correctAnswerLetters as $letter) {
-                                if (isset($normalizedOptions[$letter])) {
-                                    $mappedAnswers[] = $letter . '. ' . $normalizedOptions[$letter];
-                                }
-                            }
-                            if (!empty($mappedAnswers)) {
-                                $displayCorrectAnswer = implode(';', $mappedAnswers);
-                            }
-                        }
-                    }
-                    $choiceOptionLetters = !empty($normalizedOptions) ? array_keys($normalizedOptions) : [];
-                    sort($choiceOptionLetters);
-                    $choiceLayoutClass = 'options-grid-1';
-                    $layoutDecider = app(\App\Support\OptionLayoutDecider::class);
-                    if (! empty($normalizedOptions) && ! empty($choiceOptionLetters)) {
-                        $optValuesForLayout = [];
-                        foreach ($choiceOptionLetters as $L) {
-                            $optValuesForLayout[] = $normalizedOptions[$L];
-                        }
-                        $layoutMeta = $layoutDecider->decide($optValuesForLayout, 'grading');
-                        $choiceLayoutClass = $layoutMeta['class'] ?? 'options-grid-1';
-                    }
-                @endphp
-                <div class="question-card">
-                    <div style="display:flex; justify-content:space-between; align-items:center; gap:8px; margin-bottom:4px;">
-                        <div style="display:flex; align-items:center; gap:8px; font-weight:600;">
-                            <span class="tag">题号 {{ $q['display_number'] ?? $q['question_number'] }} · {{ $typeLabel }}</span>
-                            @php
-                                $kpName = $q['knowledge_point_name'] ?? $q['knowledge_point'] ?? null;
-                                if (!empty($kpName) && $kpName !== '-' && $kpName !== '未标注') {
-                                    echo '<span class="tag" style="background: #eef2ff; color:#4338ca;">' . e($kpName) . '</span>';
-                                }
-                            @endphp
-                            @if($showStatus)
-                                <span class="tag" style="background: {{ $statusColorValue }}; color:#fff;">{{ $statusText }}</span>
-                            @endif
-                        </div>
-                        @if($score !== null && $fullScore !== null)
-                            <div class="muted">得分 {{ $score }} / {{ $fullScore }}</div>
-                        @endif
-                    </div>
-
-                    <div class="question-stem math-content" style="margin-bottom:6px;">{!! $questionTextRendered !!}</div>
-
-                    @if(!empty($isChoiceQuestion) && !empty($normalizedOptions))
-                        <div class="report-options {{ $choiceLayoutClass }}">
-                            @foreach($choiceOptionLetters as $optLetter)
-                                @php
-                                    $isCorrectOpt = in_array($optLetter, $correctAnswerLetters ?? [], true);
-                                    $rawOpt = (string) ($normalizedOptions[$optLetter] ?? '');
-                                    $normalizedOpt = str_replace('\\dfrac', '\\frac', $rawOpt);
-                                    $normalizedOpt = str_replace('\\displaystyle', '', $normalizedOpt);
-                                    $normalizedOpt = $layoutDecider->normalizeCompactMathForDisplay($normalizedOpt);
-                                    $rawOptPlain = html_entity_decode(strip_tags($rawOpt), ENT_QUOTES | ENT_HTML5, 'UTF-8');
-                                    $rawOptPlain = preg_replace('/\s+/u', '', $rawOptPlain ?? '');
-                                    $isShortOption = mb_strlen((string) $rawOptPlain, 'UTF-8') <= 8;
-                                    $valClass = $isShortOption ? 'option-short' : 'option-long';
-                                    $renderedOpt = $renderLikeGrading($normalizedOpt);
-                                @endphp
-                                <div class="option option-compact">
-                                    <strong>{{ $optLetter }}.</strong>
-                                    <span class="option-value {{ $valClass }}">{!! $renderedOpt !!}</span>
-                                    @if($isCorrectOpt)
-                                        <span style="margin-left:4px; font-size:13px; color:#15803d; font-weight:700;">✅</span>
-                                    @endif
-                                </div>
-                            @endforeach
-                        </div>
-                    @endif
-
-                    @if(!empty($correctAnswer) && (!$isChoiceQuestion || empty($normalizedOptions)))
-                        <div class="question-block" style="background:#f0fdf4; border-left:3px solid #10b981;">
-                            <div style="font-weight:600; font-size:12px; color:#111827; margin-bottom:3px;">正确答案</div>
-                            <div class="math-content" style="line-height:1.7; color:#374151;">
-                                {!! $renderLikeGrading($displayCorrectAnswer) !!}
-                            </div>
-                        </div>
-                    @endif
-
-                    @if(!empty($solution))
-                        <div class="report-answer-meta">
-                            <div class="answer-line">
-                                <strong>解题思路:</strong>
-                                <span class="solution-content">{!! $renderLikeGrading($solution) !!}</span>
-                            </div>
-                        </div>
-                    @elseif(!empty($analysis) && $analysis !== '暂无解题思路记录')
-                        <div class="report-answer-meta">
-                            <div class="answer-line">
-                                <strong>解题思路:</strong>
-                                <span class="solution-content">{!! $renderLikeGrading($analysis) !!}</span>
-                            </div>
-                        </div>
-                    @endif
-
-                    @if(!empty($steps))
-                        <div style="margin-top:6px; font-size:12px;">
-                            <div style="font-weight:600; margin-bottom:3px;">解题步骤</div>
-                            <ol style="margin:0; padding-left:18px;">
-                                @foreach($steps as $s)
-                                    @php
-                                        $stepText = is_array($s) ? json_encode($s, JSON_UNESCAPED_UNICODE) : (string) $s;
-                                    @endphp
-                                    <li style="margin-bottom:2px;">{!! nl2br($renderLikeGrading($stepText)) !!}</li>
-                                @endforeach
-                            </ol>
-                        </div>
-                    @endif
-                </div>
-            @endforeach
-        </div>
-    @endif
 </div>
 <script src="/js/katex.min.js"></script>
 <script src="/js/auto-render.min.js"></script>