Files
sam-api/app/Console/Commands/BendingModelCopyImages.php

146 lines
4.8 KiB
PHP
Raw Normal View History

<?php
namespace App\Console\Commands;
use App\Models\BendingItem;
use App\Models\BendingModel;
use App\Models\Commons\File;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
/**
* 모델 component별 이미지 복사 (기초관리 원본 독립 복사본)
*
* component.source_num bending_items.legacy_bending_id 원본 이미지
* R2에 복사 file 레코드 component.image_file_id 업데이트
*
* 실행: php artisan bending:model-copy-images [--dry-run] [--tenant_id=287]
*/
class BendingModelCopyImages extends Command
{
protected $signature = 'bending:model-copy-images
{--tenant_id=287 : 테넌트 ID}
{--dry-run : 미리보기}';
protected $description = '모델 component별 이미지를 기초관리에서 복사';
public function handle(): int
{
$tenantId = (int) $this->option('tenant_id');
$dryRun = $this->option('dry-run');
// bending_items의 legacy_bending_id → 이미지 파일 매핑
$itemImageMap = [];
$items = BendingItem::where('tenant_id', $tenantId)
->whereNotNull('legacy_bending_id')
->get();
foreach ($items as $bi) {
$file = File::where('document_type', 'bending_item')
->where('document_id', $bi->id)
->where('field_key', 'bending_diagram')
->whereNull('deleted_at')
->first();
if ($file) {
$itemImageMap[$bi->legacy_bending_id] = $file;
}
}
$this->info("기초관리 이미지 매핑: " . count($itemImageMap) . "");
$models = BendingModel::where('tenant_id', $tenantId)
->whereNotNull('components')
->get();
$copied = 0;
$skipped = 0;
$noSource = 0;
foreach ($models as $model) {
$components = $model->components;
if (empty($components)) {
continue;
}
$updated = false;
foreach ($components as $idx => &$comp) {
// 이미 image_file_id가 있으면 skip
if (! empty($comp['image_file_id'])) {
$skipped++;
continue;
}
// source_num으로 기초관리 이미지 찾기
$sourceNum = $comp['num'] ?? $comp['source_num'] ?? null;
if (! $sourceNum) {
$noSource++;
continue;
}
$sourceFile = $itemImageMap[(int) $sourceNum] ?? null;
if (! $sourceFile || ! $sourceFile->file_path) {
$noSource++;
continue;
}
if ($dryRun) {
$this->line(" [DRY] model#{$model->id} comp[{$idx}] ← bending#{$sourceNum} file#{$sourceFile->id}");
$copied++;
continue;
}
// R2에서 파일 복사
try {
$newFile = $this->copyFile($sourceFile, $model->id, $tenantId);
$comp['image_file_id'] = $newFile->id;
$updated = true;
$copied++;
} catch (\Throwable $e) {
$this->warn(" ⚠️ 복사 실패: model#{$model->id} comp[{$idx}] — {$e->getMessage()}");
}
}
unset($comp);
if ($updated && ! $dryRun) {
$model->components = $components;
$model->save();
}
}
$this->newLine();
$this->info("완료: 복사 {$copied}건, 스킵 {$skipped}건, 소스없음 {$noSource}");
return 0;
}
private function copyFile(File $source, int $modelId, int $tenantId): File
{
$extension = pathinfo($source->stored_name, PATHINFO_EXTENSION);
$storedName = bin2hex(random_bytes(8)) . '.' . $extension;
$directory = sprintf('%d/bending/model-parts/%s/%s', $tenantId, date('Y'), date('m'));
$newPath = $directory . '/' . $storedName;
// R2 파일 복사
$content = Storage::disk('r2')->get($source->file_path);
Storage::disk('r2')->put($newPath, $content);
// 새 파일 레코드 생성
return File::create([
'tenant_id' => $tenantId,
'display_name' => $source->display_name,
'stored_name' => $storedName,
'file_path' => $newPath,
'file_size' => $source->file_size,
'mime_type' => $source->mime_type,
'file_type' => 'image',
'field_key' => 'component_image',
'document_id' => $modelId,
'document_type' => 'bending_model',
'is_temp' => false,
'uploaded_by' => 1,
'created_by' => 1,
]);
}
}