First commit

This commit is contained in:
Saufi
2026-05-18 08:56:23 +08:00
commit fd3d3a4d2b
147 changed files with 22099 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
<?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);
}
}