'datetime', 'updated_at' => 'datetime', 'progress_updated_at' => 'datetime', 'processing_started_at' => 'datetime', 'processing_finished_at' => 'datetime', ]; public const STATUS_PENDING = 'pending'; public const STATUS_PARSED = 'parsed'; public const STATUS_REVIEWED = 'reviewed'; public const STATUS_COMPLETED = 'completed'; public const STATUS_PROCESSING = 'processing'; public const STATUS_FAILED = 'failed'; public const STAGE_QUEUED = 'queued'; public const STAGE_SPLITTING = 'splitting'; public const STAGE_AI_PARSING = 'ai_parsing'; public const STAGE_WRITING = 'writing'; public const STAGE_PARSED = 'parsed'; public const STAGE_COMPLETED = 'completed'; public const STAGE_FAILED = 'failed'; public function candidates(): HasMany { return $this->hasMany(PreQuestionCandidate::class, 'import_id'); } public function preQuestions(): HasMany { return $this->hasMany(PreQuestion::class, 'import_id'); } public function getStatusBadgeAttribute(): string { $badges = [ self::STATUS_PENDING => 'gray', self::STATUS_PARSED => 'info', self::STATUS_REVIEWED => 'warning', self::STATUS_COMPLETED => 'success', ]; return $badges[$this->status] ?? 'gray'; } public function getProgressLabelAttribute(): string { $stageLabel = match ($this->progress_stage) { self::STAGE_QUEUED => '已排队', self::STAGE_SPLITTING => '拆题中', self::STAGE_AI_PARSING => 'AI 解析中', self::STAGE_WRITING => '写入候选库', self::STAGE_PARSED => '已解析', self::STAGE_COMPLETED => '已完成', self::STAGE_FAILED => '失败', default => $this->progress_stage ?: '—', }; if (($this->progress_total ?? 0) > 0) { return sprintf( '%s %d/%d', $stageLabel, (int) ($this->progress_current ?? 0), (int) $this->progress_total ); } return $stageLabel; } public function getProgressPercentAttribute(): ?int { $total = (int) ($this->progress_total ?? 0); if ($total <= 0) { return null; } $current = (int) ($this->progress_current ?? 0); return (int) max(0, min(100, round(($current / $total) * 100))); } public function getParsedCountAttribute(): int { if (array_key_exists('parsed_count', $this->attributes)) { return (int) $this->attributes['parsed_count']; } return $this->candidates()->count(); } public function getAcceptedCountAttribute(): int { if (array_key_exists('accepted_count', $this->attributes)) { return (int) $this->attributes['accepted_count']; } return $this->candidates()->where('is_question_candidate', true)->count(); } /** * 获取切分后的候选题目(新的切分格式) */ public function getSplitCandidatesAttribute(): array { if (!$this->parsed_json) { return []; } $data = json_decode($this->parsed_json, true); return $data['candidates'] ?? []; } /** * 获取统计信息 */ public function getSplitStatisticsAttribute(): array { if (!$this->parsed_json) { return []; } $data = json_decode($this->parsed_json, true); return $data['statistics'] ?? []; } /** * 检查是否已完成 */ public function isCompleted(): bool { return $this->status === self::STATUS_COMPLETED; } /** * 检查是否正在处理 */ public function isProcessing(): bool { return $this->status === self::STATUS_PROCESSING; } /** * 检查是否失败 */ public function isFailed(): bool { return $this->status === self::STATUS_FAILED; } /** * 获取状态标签 */ public function getStatusLabelAttribute(): string { return match ($this->status) { self::STATUS_PENDING => '等待处理', self::STATUS_PROCESSING => '处理中', self::STATUS_COMPLETED => '已完成', self::STATUS_FAILED => '处理失败', self::STATUS_PARSED => '已解析', self::STATUS_REVIEWED => '已审核', default => '未知', }; } /** * 获取状态颜色 */ public function getSplitStatusColorAttribute(): string { return match ($this->status) { self::STATUS_PENDING => 'gray', self::STATUS_PROCESSING => 'warning', self::STATUS_COMPLETED => 'success', self::STATUS_FAILED => 'danger', self::STATUS_PARSED => 'info', self::STATUS_REVIEWED => 'primary', default => 'gray', }; } }