125 lines
3.2 KiB
PHP
125 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class DocumentVersion extends Model
|
|
{
|
|
protected $fillable = [
|
|
'document_id',
|
|
'version_number',
|
|
'original_filename',
|
|
'stored_path',
|
|
'mime_type',
|
|
'file_size',
|
|
'file_hash',
|
|
'page_count',
|
|
'processing_status',
|
|
'processing_error',
|
|
'processing_started_at',
|
|
'processing_completed_at',
|
|
'is_current',
|
|
'change_notes',
|
|
'uploaded_by',
|
|
];
|
|
|
|
protected $casts = [
|
|
'is_current' => 'boolean',
|
|
'file_size' => 'integer',
|
|
'page_count' => 'integer',
|
|
'processing_started_at' => 'datetime',
|
|
'processing_completed_at' => 'datetime',
|
|
];
|
|
|
|
// Status constants
|
|
const STATUS_PENDING = 'pending';
|
|
const STATUS_PROCESSING = 'processing';
|
|
const STATUS_EXTRACTING = 'extracting';
|
|
const STATUS_CHUNKING = 'chunking';
|
|
const STATUS_EMBEDDING = 'embedding';
|
|
const STATUS_INDEXED = 'indexed';
|
|
const STATUS_FAILED = 'failed';
|
|
const STATUS_EXTRACTION_FAILED = 'extraction_failed';
|
|
|
|
// === Relationships ===
|
|
|
|
public function document(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Document::class);
|
|
}
|
|
|
|
public function chunks(): HasMany
|
|
{
|
|
return $this->hasMany(DocumentChunk::class)->orderBy('chunk_index');
|
|
}
|
|
|
|
public function uploader(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'uploaded_by');
|
|
}
|
|
|
|
// === Scopes ===
|
|
|
|
public function scopeCurrent(Builder $query): Builder
|
|
{
|
|
return $query->where('is_current', true);
|
|
}
|
|
|
|
public function scopeIndexed(Builder $query): Builder
|
|
{
|
|
return $query->where('processing_status', self::STATUS_INDEXED);
|
|
}
|
|
|
|
// === Helpers ===
|
|
|
|
public function getStorageUrl(): string
|
|
{
|
|
return Storage::url($this->stored_path);
|
|
}
|
|
|
|
public function getFileSizeFormattedAttribute(): string
|
|
{
|
|
$bytes = $this->file_size;
|
|
if ($bytes < 1024) return $bytes . ' B';
|
|
if ($bytes < 1048576) return round($bytes / 1024, 1) . ' KB';
|
|
return round($bytes / 1048576, 1) . ' MB';
|
|
}
|
|
|
|
public function isProcessed(): bool
|
|
{
|
|
return $this->processing_status === self::STATUS_INDEXED;
|
|
}
|
|
|
|
public function hasFailed(): bool
|
|
{
|
|
return in_array($this->processing_status, [
|
|
self::STATUS_FAILED,
|
|
self::STATUS_EXTRACTION_FAILED,
|
|
]);
|
|
}
|
|
|
|
public function updateStatus(string $status, ?string $error = null): void
|
|
{
|
|
$data = ['processing_status' => $status];
|
|
|
|
if ($status === self::STATUS_PROCESSING) {
|
|
$data['processing_started_at'] = now();
|
|
}
|
|
|
|
if ($status === self::STATUS_INDEXED) {
|
|
$data['processing_completed_at'] = now();
|
|
}
|
|
|
|
if ($error !== null) {
|
|
$data['processing_error'] = $error;
|
|
}
|
|
|
|
$this->update($data);
|
|
}
|
|
}
|