feat: [file] API Resource에서 image_url (presigned URL) 반환

- File 모델에 presignedUrl() 메서드 추가
- GuiderailModelResource: image_url + components[].image_url 반환
- BendingItemResource: image_url 반환
- 소비자(MNG, React)가 별도 처리 없이 R2 직접 로드 가능
This commit is contained in:
2026-03-20 10:22:00 +09:00
parent 9bdb81d8ff
commit 700722d5d2
3 changed files with 64 additions and 6 deletions

View File

@@ -40,6 +40,7 @@ public function toArray(Request $request): array
'registration_date' => $this->getOption('registration_date'),
// 이미지
'image_file_id' => $this->getImageFileId(),
'image_url' => $this->getImageUrl(),
// 추적
'legacy_bending_id' => $this->legacy_bending_id,
'legacy_bending_num' => $this->legacy_bending_id, // MNG2 호환
@@ -60,13 +61,21 @@ public function toArray(Request $request): array
];
}
private function getImageFileId(): ?int
private function getImageFile(): ?\App\Models\Commons\File
{
$file = $this->files()
return $this->files()
->where('field_key', 'bending_diagram')
->orderByDesc('id')
->first();
}
return $file?->id;
private function getImageFileId(): ?int
{
return $this->getImageFile()?->id;
}
private function getImageUrl(): ?string
{
return $this->getImageFile()?->presignedUrl();
}
}

View File

@@ -48,8 +48,9 @@ public function toArray(Request $request): array
'modified_by' => $this->getOption('modified_by'),
// 이미지
'image_file_id' => $this->getImageFileId(),
'image_url' => $this->getImageUrl(),
// 부품 조합
'components' => $components,
'components' => $this->enrichComponentsWithImageUrls($components),
'material_summary' => $materialSummary,
'component_count' => count($components),
// 메타
@@ -58,7 +59,7 @@ public function toArray(Request $request): array
];
}
private function getImageFileId(): ?int
private function getImageFile(): ?\App\Models\Commons\File
{
$file = \App\Models\Commons\File::where('document_id', $this->id)
->where('document_type', 'bending_model')
@@ -74,7 +75,40 @@ private function getImageFileId(): ?int
->first();
}
return $file?->id;
return $file;
}
private function getImageFileId(): ?int
{
return $this->getImageFile()?->id;
}
private function getImageUrl(): ?string
{
return $this->getImageFile()?->presignedUrl();
}
private function enrichComponentsWithImageUrls(array $components): array
{
$fileIds = array_filter(array_column($components, 'image_file_id'));
if (empty($fileIds)) {
return $components;
}
$files = \App\Models\Commons\File::whereIn('id', $fileIds)
->whereNull('deleted_at')
->get()
->keyBy('id');
foreach ($components as &$comp) {
$fileId = $comp['image_file_id'] ?? null;
$comp['image_url'] = $fileId && isset($files[$fileId])
? $files[$fileId]->presignedUrl()
: null;
}
unset($comp);
return $components;
}
private function calcMaterialSummary(array $components): array

View File

@@ -98,6 +98,21 @@ public function fileable()
return $this->morphTo();
}
/**
* R2 Presigned URL 생성 (30분 유효)
*/
public function presignedUrl(int $minutes = 30): ?string
{
if (! $this->file_path) {
return null;
}
return Storage::disk('r2')->temporaryUrl(
$this->file_path,
now()->addMinutes($minutes)
);
}
/**
* Get the full storage path
*/