| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- <?php
- namespace App\Models;
- use Illuminate\Database\Eloquent\Factories\HasFactory;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Database\Eloquent\Relations\HasMany;
- class MarkdownImport extends Model
- {
- use HasFactory;
- protected $fillable = [
- 'file_name',
- 'original_markdown',
- 'parsed_json',
- 'source_type',
- 'source_name',
- 'status',
- 'error_message',
- 'progress_stage',
- 'progress_message',
- 'progress_current',
- 'progress_total',
- 'progress_updated_at',
- 'processing_started_at',
- 'processing_finished_at',
- ];
- protected $casts = [
- 'created_at' => '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',
- };
- }
- public function parseFilename(): array
- {
- if (empty($this->file_name)) {
- return [];
- }
- $base = pathinfo((string) $this->file_name, PATHINFO_FILENAME);
- $parts = array_map('trim', explode('_', $base));
- if (count($parts) < 4) {
- return [];
- }
- $series = $parts[0] ?? null;
- $grade = isset($parts[1]) && is_numeric($parts[1]) ? (int) $parts[1] : null;
- $termFlag = isset($parts[2]) && is_numeric($parts[2]) ? (int) $parts[2] : null;
- $subject = $parts[3] ?? null;
- $name = trim(implode('_', array_slice($parts, 4)));
- $term = match ($termFlag) {
- 1 => '上册',
- 2 => '下册',
- 0 => '上下册',
- default => null,
- };
- return [
- 'series' => $series,
- 'grade' => $grade,
- 'term' => $term,
- 'subject' => $subject,
- 'name' => $name !== '' ? $name : $base,
- ];
- }
- }
|