|
@@ -8,6 +8,7 @@ use App\Models\PaperQuestion;
|
|
|
use App\Services\LearningAnalyticsService;
|
|
use App\Services\LearningAnalyticsService;
|
|
|
use App\Services\ExamPdfExportService;
|
|
use App\Services\ExamPdfExportService;
|
|
|
use App\Services\QuestionBankService;
|
|
use App\Services\QuestionBankService;
|
|
|
|
|
+use App\Services\PaperPayloadService;
|
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\JsonResponse;
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Request;
|
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Http;
|
|
@@ -19,15 +20,18 @@ class IntelligentExamController extends Controller
|
|
|
private LearningAnalyticsService $learningAnalyticsService;
|
|
private LearningAnalyticsService $learningAnalyticsService;
|
|
|
private QuestionBankService $questionBankService;
|
|
private QuestionBankService $questionBankService;
|
|
|
private ExamPdfExportService $pdfExportService;
|
|
private ExamPdfExportService $pdfExportService;
|
|
|
|
|
+ private PaperPayloadService $paperPayloadService;
|
|
|
|
|
|
|
|
public function __construct(
|
|
public function __construct(
|
|
|
LearningAnalyticsService $learningAnalyticsService,
|
|
LearningAnalyticsService $learningAnalyticsService,
|
|
|
QuestionBankService $questionBankService,
|
|
QuestionBankService $questionBankService,
|
|
|
- ExamPdfExportService $pdfExportService
|
|
|
|
|
|
|
+ ExamPdfExportService $pdfExportService,
|
|
|
|
|
+ PaperPayloadService $paperPayloadService
|
|
|
) {
|
|
) {
|
|
|
$this->learningAnalyticsService = $learningAnalyticsService;
|
|
$this->learningAnalyticsService = $learningAnalyticsService;
|
|
|
$this->questionBankService = $questionBankService;
|
|
$this->questionBankService = $questionBankService;
|
|
|
$this->pdfExportService = $pdfExportService;
|
|
$this->pdfExportService = $pdfExportService;
|
|
|
|
|
+ $this->paperPayloadService = $paperPayloadService;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -126,10 +130,13 @@ class IntelligentExamController extends Controller
|
|
|
$taskId = $this->createAsyncTask($paperId, $data);
|
|
$taskId = $this->createAsyncTask($paperId, $data);
|
|
|
|
|
|
|
|
// 生成识别码
|
|
// 生成识别码
|
|
|
- $codes = $this->generatePaperCodes($paperId);
|
|
|
|
|
|
|
+ $codes = $this->paperPayloadService->generatePaperCodes($paperId);
|
|
|
|
|
|
|
|
// 立即返回完整的试卷数据(不等待PDF生成)
|
|
// 立即返回完整的试卷数据(不等待PDF生成)
|
|
|
- $examContent = $this->buildCompleteExamContent($paperId);
|
|
|
|
|
|
|
+ $paperModel = Paper::with('questions')->find($paperId);
|
|
|
|
|
+ $examContent = $paperModel
|
|
|
|
|
+ ? $this->paperPayloadService->buildExamContent($paperModel)
|
|
|
|
|
+ : [];
|
|
|
$payload = [
|
|
$payload = [
|
|
|
'success' => true,
|
|
'success' => true,
|
|
|
'message' => '智能试卷创建成功,PDF正在后台生成...',
|
|
'message' => '智能试卷创建成功,PDF正在后台生成...',
|
|
@@ -271,7 +278,10 @@ class IntelligentExamController extends Controller
|
|
|
?? route('filament.admin.auth.intelligent-exam.pdf', ['paper_id' => $paperId, 'answer' => 'true']);
|
|
?? route('filament.admin.auth.intelligent-exam.pdf', ['paper_id' => $paperId, 'answer' => 'true']);
|
|
|
|
|
|
|
|
// 构建完整的试卷内容
|
|
// 构建完整的试卷内容
|
|
|
- $examContent = $this->buildCompleteExamContent($paperId);
|
|
|
|
|
|
|
+ $paperModel = Paper::with('questions')->find($paperId);
|
|
|
|
|
+ $examContent = $paperModel
|
|
|
|
|
+ ? $this->paperPayloadService->buildExamContent($paperModel)
|
|
|
|
|
+ : [];
|
|
|
|
|
|
|
|
// 更新任务状态为完成
|
|
// 更新任务状态为完成
|
|
|
$this->updateTaskStatus($taskId, [
|
|
$this->updateTaskStatus($taskId, [
|
|
@@ -552,99 +562,11 @@ class IntelligentExamController extends Controller
|
|
|
private function buildCompleteExamContent(string $paperId): array
|
|
private function buildCompleteExamContent(string $paperId): array
|
|
|
{
|
|
{
|
|
|
$paper = Paper::with('questions')->find($paperId);
|
|
$paper = Paper::with('questions')->find($paperId);
|
|
|
- $questions = $paper ? $paper->questions : collect();
|
|
|
|
|
-
|
|
|
|
|
- // 生成13位识别码
|
|
|
|
|
- $codes = $this->generatePaperCodes($paperId);
|
|
|
|
|
-
|
|
|
|
|
- return [
|
|
|
|
|
- // 试卷基本信息
|
|
|
|
|
- 'paper_info' => [
|
|
|
|
|
- 'paper_id' => $paperId,
|
|
|
|
|
- 'paper_name' => $paper?->paper_name ?? '',
|
|
|
|
|
- 'student_id' => $paper?->student_id ?? '',
|
|
|
|
|
- 'teacher_id' => $paper?->teacher_id ?? '',
|
|
|
|
|
- 'total_questions' => $questions->count(),
|
|
|
|
|
- 'total_score' => $paper?->total_score ?? 0,
|
|
|
|
|
- 'difficulty_category' => $paper?->difficulty_category ?? '基础',
|
|
|
|
|
- 'created_at' => $paper?->created_at?->toISOString(),
|
|
|
|
|
- 'updated_at' => $paper?->updated_at?->toISOString(),
|
|
|
|
|
- // 识别码
|
|
|
|
|
- 'exam_code' => $codes['exam_code'], // 试卷识别码 (1+12位)
|
|
|
|
|
- 'grading_code' => $codes['grading_code'], // 判卷识别码 (2+12位)
|
|
|
|
|
- 'paper_id_num' => $codes['paper_id_num'], // 12位数字ID
|
|
|
|
|
- ],
|
|
|
|
|
-
|
|
|
|
|
- // 完整题目信息
|
|
|
|
|
- 'questions' => $questions->map(function (PaperQuestion $q) {
|
|
|
|
|
- // 构建选择题选项(如果适用)
|
|
|
|
|
- $options = [];
|
|
|
|
|
- if ($q->question_type === 'choice') {
|
|
|
|
|
- // 从题目文本中提取选项
|
|
|
|
|
- $questionText = $q->question_text ?? '';
|
|
|
|
|
- preg_match_all('/([A-D])\s*[\.\、\:]\s*([^A-D]+?)(?=[A-D]\s*[\.\、\:]|$)/u', $questionText, $matches, PREG_SET_ORDER);
|
|
|
|
|
- foreach ($matches as $match) {
|
|
|
|
|
- $options[] = [
|
|
|
|
|
- 'label' => $match[1],
|
|
|
|
|
- 'content' => trim($match[2]),
|
|
|
|
|
- ];
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!$paper) {
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return [
|
|
|
|
|
- // 基本信息
|
|
|
|
|
- 'question_number' => $q->question_number,
|
|
|
|
|
- 'question_id' => $q->question_id,
|
|
|
|
|
- 'question_bank_id' => $q->question_bank_id,
|
|
|
|
|
- 'question_type' => $q->question_type,
|
|
|
|
|
- 'knowledge_point' => $q->knowledge_point,
|
|
|
|
|
- 'difficulty' => $q->difficulty,
|
|
|
|
|
- 'score' => $q->score,
|
|
|
|
|
- 'estimated_time' => $q->estimated_time,
|
|
|
|
|
-
|
|
|
|
|
- // 题目内容
|
|
|
|
|
- 'stem' => $q->question_text ?? '',
|
|
|
|
|
- 'options' => $options,
|
|
|
|
|
-
|
|
|
|
|
- // 答案和解析
|
|
|
|
|
- 'correct_answer' => $q->correct_answer ?? '',
|
|
|
|
|
- 'solution' => $q->solution ?? '',
|
|
|
|
|
-
|
|
|
|
|
- // 元数据
|
|
|
|
|
- 'student_answer' => $q->student_answer,
|
|
|
|
|
- 'is_correct' => $q->is_correct,
|
|
|
|
|
- 'score_obtained' => $q->score_obtained,
|
|
|
|
|
- 'score_ratio' => $q->score_ratio,
|
|
|
|
|
- 'teacher_comment' => $q->teacher_comment,
|
|
|
|
|
- 'graded_at' => $q->graded_at?->toISOString(),
|
|
|
|
|
- 'graded_by' => $q->graded_by,
|
|
|
|
|
-
|
|
|
|
|
- // 题目属性
|
|
|
|
|
- 'metadata' => [
|
|
|
|
|
- 'has_solution' => !empty($q->solution),
|
|
|
|
|
- 'is_choice' => $q->question_type === 'choice',
|
|
|
|
|
- 'is_fill' => $q->question_type === 'fill',
|
|
|
|
|
- 'is_answer' => $q->question_type === 'answer',
|
|
|
|
|
- 'difficulty_label' => $this->getDifficultyLabel($q->difficulty),
|
|
|
|
|
- 'question_type_label' => $this->getQuestionTypeLabel($q->question_type),
|
|
|
|
|
- ],
|
|
|
|
|
- ];
|
|
|
|
|
- })->toArray(),
|
|
|
|
|
-
|
|
|
|
|
- // 统计信息
|
|
|
|
|
- 'statistics' => [
|
|
|
|
|
- 'type_distribution' => $this->getTypeDistribution($questions),
|
|
|
|
|
- 'difficulty_distribution' => $this->getDifficultyDistribution($questions),
|
|
|
|
|
- 'knowledge_point_distribution' => $this->getKnowledgePointDistribution($questions),
|
|
|
|
|
- 'total_score' => $questions->sum('score'),
|
|
|
|
|
- 'average_difficulty' => $questions->avg('difficulty'),
|
|
|
|
|
- 'total_estimated_time' => $questions->sum('estimated_time'),
|
|
|
|
|
- ],
|
|
|
|
|
-
|
|
|
|
|
- // 知识点和技能标签
|
|
|
|
|
- 'knowledge_points' => $questions->pluck('knowledge_point')->unique()->filter()->values()->toArray(),
|
|
|
|
|
- 'skills' => $this->extractSkillsFromQuestions($questions),
|
|
|
|
|
- ];
|
|
|
|
|
|
|
+ return $this->paperPayloadService->buildExamContent($paper);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|