|
@@ -1022,14 +1022,19 @@ class ExamPdfExportService
|
|
|
$utf8Html = $this->ensureUtf8Html($html);
|
|
$utf8Html = $this->ensureUtf8Html($html);
|
|
|
$written = file_put_contents($tmpHtml, $utf8Html);
|
|
$written = file_put_contents($tmpHtml, $utf8Html);
|
|
|
|
|
|
|
|
- Log::warning('ExamPdfExportService: [调试] HTML文件已创建', [
|
|
|
|
|
|
|
+ Log::debug('ExamPdfExportService: HTML文件已创建', [
|
|
|
'path' => $tmpHtml,
|
|
'path' => $tmpHtml,
|
|
|
'html_length' => strlen($utf8Html),
|
|
'html_length' => strlen($utf8Html),
|
|
|
'written_bytes' => $written,
|
|
'written_bytes' => $written,
|
|
|
- 'file_exists' => file_exists($tmpHtml),
|
|
|
|
|
- 'file_size' => file_exists($tmpHtml) ? filesize($tmpHtml) : 0,
|
|
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
|
|
+ // 【调试】如果启用了HTML保存调试,复制HTML到storage用于分析
|
|
|
|
|
+ if (config('pdf.debug_save_html', false)) {
|
|
|
|
|
+ $debugPath = storage_path('app/debug_pdf_' . date('YmdHis') . '.html');
|
|
|
|
|
+ @copy($tmpHtml, $debugPath);
|
|
|
|
|
+ Log::warning('ExamPdfExportService: [DEBUG] HTML副本已保存', ['path' => $debugPath]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 仅使用Chrome渲染
|
|
// 仅使用Chrome渲染
|
|
|
$chromePdf = $this->renderWithChrome($tmpHtml);
|
|
$chromePdf = $this->renderWithChrome($tmpHtml);
|
|
|
@unlink($tmpHtml);
|
|
@unlink($tmpHtml);
|
|
@@ -1870,6 +1875,7 @@ class ExamPdfExportService
|
|
|
foreach ($paperQuestions as $pq) {
|
|
foreach ($paperQuestions as $pq) {
|
|
|
$questionsData[] = [
|
|
$questionsData[] = [
|
|
|
'id' => $pq->question_bank_id,
|
|
'id' => $pq->question_bank_id,
|
|
|
|
|
+ 'question_number' => $pq->question_number, // 传递原始题号,确保渲染时序号正确
|
|
|
'kp_code' => $pq->knowledge_point,
|
|
'kp_code' => $pq->knowledge_point,
|
|
|
'question_type' => $pq->question_type ?? 'answer',
|
|
'question_type' => $pq->question_type ?? 'answer',
|
|
|
'stem' => $pq->question_text ?? '题目内容缺失',
|
|
'stem' => $pq->question_text ?? '题目内容缺失',
|
|
@@ -1916,6 +1922,17 @@ class ExamPdfExportService
|
|
|
$classified[$type][] = (object) $q;
|
|
$classified[$type][] = (object) $q;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 【调试】记录题目分类情况
|
|
|
|
|
+ Log::debug('buildQuestionsData: 题目分类结果', [
|
|
|
|
|
+ 'total_questions' => count($questionsData),
|
|
|
|
|
+ 'choice_count' => count($classified['choice']),
|
|
|
|
|
+ 'fill_count' => count($classified['fill']),
|
|
|
|
|
+ 'answer_count' => count($classified['answer']),
|
|
|
|
|
+ 'choice_numbers' => array_map(fn($q) => $q->question_number ?? 'N/A', $classified['choice']),
|
|
|
|
|
+ 'fill_numbers' => array_map(fn($q) => $q->question_number ?? 'N/A', $classified['fill']),
|
|
|
|
|
+ 'answer_numbers' => array_map(fn($q) => $q->question_number ?? 'N/A', $classified['answer']),
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
return $classified;
|
|
return $classified;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1996,14 +2013,23 @@ class ExamPdfExportService
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 判断题目类型
|
|
* 判断题目类型
|
|
|
|
|
+ * 【修复】优先使用 question_type 字段,避免选择题因含有()被误判为填空题
|
|
|
*/
|
|
*/
|
|
|
private function determineQuestionType(array $question): string
|
|
private function determineQuestionType(array $question): string
|
|
|
{
|
|
{
|
|
|
|
|
+ // 1. 【优先】根据已有类型字段判断(最可靠的来源)
|
|
|
|
|
+ if (!empty($question['question_type'])) {
|
|
|
|
|
+ $type = strtolower(trim($question['question_type']));
|
|
|
|
|
+ if (in_array($type, ['choice', '选择题'])) return 'choice';
|
|
|
|
|
+ if (in_array($type, ['fill', '填空题'])) return 'fill';
|
|
|
|
|
+ if (in_array($type, ['answer', '解答题'])) return 'answer';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 如果没有明确类型,则根据题干内容推断
|
|
|
$stem = $question['stem'] ?? $question['content'] ?? '';
|
|
$stem = $question['stem'] ?? $question['content'] ?? '';
|
|
|
- $tags = $question['tags'] ?? '';
|
|
|
|
|
|
|
|
|
|
- // 根据题干内容判断选择题
|
|
|
|
|
if (is_string($stem)) {
|
|
if (is_string($stem)) {
|
|
|
|
|
+ // 检查是否有选项模式 A. B. C. D.
|
|
|
$hasOptionA = preg_match('/\bA\s*[\.\、\:]/', $stem) || preg_match('/\(A\)/', $stem) || preg_match('/^A[\.\s]/', $stem);
|
|
$hasOptionA = preg_match('/\bA\s*[\.\、\:]/', $stem) || preg_match('/\(A\)/', $stem) || preg_match('/^A[\.\s]/', $stem);
|
|
|
$hasOptionB = preg_match('/\bB\s*[\.\、\:]/', $stem) || preg_match('/\(B\)/', $stem) || preg_match('/^B[\.\s]/', $stem);
|
|
$hasOptionB = preg_match('/\bB\s*[\.\、\:]/', $stem) || preg_match('/\(B\)/', $stem) || preg_match('/^B[\.\s]/', $stem);
|
|
|
$hasOptionC = preg_match('/\bC\s*[\.\、\:]/', $stem) || preg_match('/\(C\)/', $stem) || preg_match('/^C[\.\s]/', $stem);
|
|
$hasOptionC = preg_match('/\bC\s*[\.\、\:]/', $stem) || preg_match('/\(C\)/', $stem) || preg_match('/^C[\.\s]/', $stem);
|
|
@@ -2014,21 +2040,13 @@ class ExamPdfExportService
|
|
|
return 'choice';
|
|
return 'choice';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 检查是否有填空标记
|
|
|
|
|
|
|
+ // 检查是否有填空标记(但要排除选择题的括号)
|
|
|
if (preg_match('/(\s*)|\(\s*\)/', $stem)) {
|
|
if (preg_match('/(\s*)|\(\s*\)/', $stem)) {
|
|
|
return 'fill';
|
|
return 'fill';
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 根据已有类型字段判断
|
|
|
|
|
- if (!empty($question['question_type'])) {
|
|
|
|
|
- $type = strtolower(trim($question['question_type']));
|
|
|
|
|
- if (in_array($type, ['choice', '选择题'])) return 'choice';
|
|
|
|
|
- if (in_array($type, ['fill', '填空题'])) return 'fill';
|
|
|
|
|
- if (in_array($type, ['answer', '解答题'])) return 'answer';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 默认返回解答题
|
|
|
|
|
|
|
+ // 3. 默认返回解答题
|
|
|
return 'answer';
|
|
return 'answer';
|
|
|
}
|
|
}
|
|
|
|
|
|