|
|
@@ -2,6 +2,8 @@
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
+use App\DTO\ExamAnalysisDataDto;
|
|
|
+use App\DTO\ReportPayloadDto;
|
|
|
use App\Models\Paper;
|
|
|
use App\Models\Student;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
@@ -184,6 +186,100 @@ class ExamPdfExportService
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 生成学情分析 PDF
|
|
|
+ */
|
|
|
+ public function generateAnalysisReportPdf(string $paperId, string $studentId, ?string $recordId = null): ?string
|
|
|
+ {
|
|
|
+ if (function_exists('set_time_limit')) {
|
|
|
+ @set_time_limit(240);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Log::info('ExamPdfExportService: 开始生成学情分析PDF', [
|
|
|
+ 'paper_id' => $paperId,
|
|
|
+ 'student_id' => $studentId,
|
|
|
+ 'record_id' => $recordId,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 构建分析数据
|
|
|
+ $analysisData = $this->buildAnalysisData($paperId, $studentId);
|
|
|
+ if (! $analysisData) {
|
|
|
+ Log::warning('ExamPdfExportService: buildAnalysisData返回空数据', [
|
|
|
+ 'paper_id' => $paperId,
|
|
|
+ 'student_id' => $studentId,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Log::info('ExamPdfExportService: buildAnalysisData返回数据', [
|
|
|
+ 'paper_id' => $paperId,
|
|
|
+ 'student_id' => $studentId,
|
|
|
+ 'analysisData_keys' => array_keys($analysisData),
|
|
|
+ 'mastery_count' => count($analysisData['mastery']['items'] ?? []),
|
|
|
+ 'questions_count' => count($analysisData['questions'] ?? []),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 创建DTO
|
|
|
+ $dto = ExamAnalysisDataDto::fromArray($analysisData);
|
|
|
+ $payloadDto = ReportPayloadDto::fromExamAnalysisDataDto($dto);
|
|
|
+
|
|
|
+ // 打印传给模板的数据
|
|
|
+ $templateData = $payloadDto->toArray();
|
|
|
+ Log::info('ExamPdfExportService: 传给模板的数据', [
|
|
|
+ 'paper' => $templateData['paper'] ?? null,
|
|
|
+ 'student' => $templateData['student'] ?? null,
|
|
|
+ 'mastery' => $templateData['mastery'] ?? null,
|
|
|
+ 'parent_mastery_levels' => $templateData['parent_mastery_levels'] ?? null,
|
|
|
+ 'questions_count' => count($templateData['questions'] ?? []),
|
|
|
+ 'insights_count' => count($templateData['question_insights'] ?? []),
|
|
|
+ 'recommendations_count' => count($templateData['recommendations'] ?? []),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 渲染HTML
|
|
|
+ $html = view('exam-analysis.pdf-report', $templateData)->render();
|
|
|
+ if (! $html) {
|
|
|
+ Log::error('ExamPdfExportService: 渲染HTML为空', ['paper_id' => $paperId]);
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成PDF
|
|
|
+ $pdfBinary = $this->buildPdf($html);
|
|
|
+ if (! $pdfBinary) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存PDF
|
|
|
+ $version = time();
|
|
|
+ $path = "analysis_reports/{$paperId}_{$studentId}_{$version}.pdf";
|
|
|
+ $url = $this->pdfStorageService->put($path, $pdfBinary);
|
|
|
+ if (! $url) {
|
|
|
+ Log::error('ExamPdfExportService: 保存学情PDF失败', ['path' => $path]);
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存URL到数据库
|
|
|
+ $this->saveAnalysisPdfUrl($paperId, $studentId, $recordId, $url);
|
|
|
+
|
|
|
+ return $url;
|
|
|
+
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Log::error('ExamPdfExportService: 生成学情分析PDF失败', [
|
|
|
+ 'paper_id' => $paperId,
|
|
|
+ 'student_id' => $studentId,
|
|
|
+ 'record_id' => $recordId,
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ 'exception' => get_class($e),
|
|
|
+ 'trace' => $e->getTraceAsString(),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 生成合并PDF(试卷 + 判卷)
|
|
|
* 先分别生成两个PDF,然后合并
|