feat: [bending] 절곡품 전용 테이블 분리 API

- bending_items 전용 테이블 생성 (items.options → 정규 컬럼 승격)
- bending_models 전용 테이블 생성 (가이드레일/케이스/하단마감재 통합)
- bending_data JSON 통합 (별도 테이블 → bending_items.bending_data 컬럼)
- bending_item_mappings 테이블 DROP (bending_items.code에 흡수)
- BendingItemService/BendingCodeService → BendingItem 모델 전환
- GuiderailModelService component 이미지 자동 복사
- ItemsFileController bending_items/bending_models 폴백 지원
- Swagger 스키마 업데이트
This commit is contained in:
강영보
2026-03-19 19:54:23 +09:00
parent 623298dd82
commit c29090a0b8
32 changed files with 3114 additions and 490 deletions

136
app/Models/BendingItem.php Normal file
View File

@@ -0,0 +1,136 @@
<?php
namespace App\Models;
use App\Models\Commons\File;
use App\Traits\Auditable;
use App\Traits\BelongsToTenant;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* 절곡 기초관리 마스터
*
* code: {제품Code}{종류Code}{YYMMDD} (예: CP260319 = 케이스 점검구)
* bending_data: 전개도 JSON 배열 [{no, input, rate, sum, color, aAngle}]
*/
class BendingItem extends Model
{
use Auditable, BelongsToTenant, SoftDeletes;
protected $table = 'bending_items';
protected $fillable = [
'tenant_id',
'code',
'legacy_code',
'legacy_bending_id',
'item_name',
'item_sep',
'item_bending',
'material',
'item_spec',
'model_name',
'model_UA',
'rail_width',
'exit_direction',
'box_width',
'box_height',
'front_bottom',
'inspection_door',
'length_code',
'length_mm',
'bending_data',
'options',
'is_active',
'created_by',
'updated_by',
];
protected $casts = [
'bending_data' => 'array',
'options' => 'array',
'is_active' => 'boolean',
'rail_width' => 'decimal:2',
'box_width' => 'decimal:2',
'box_height' => 'decimal:2',
'front_bottom' => 'decimal:2',
];
protected $hidden = ['deleted_at'];
// ──────────────────────────────────────────────────────────────
// 관계
// ──────────────────────────────────────────────────────────────
public function files(): HasMany
{
return $this->hasMany(File::class, 'document_id')
->where('document_type', 'bending_item');
}
// ──────────────────────────────────────────────────────────────
// options 헬퍼
// ──────────────────────────────────────────────────────────────
public function getOption(string $key, mixed $default = null): mixed
{
return data_get($this->options, $key, $default);
}
public function setOption(string $key, mixed $value): self
{
$options = $this->options ?? [];
data_set($options, $key, $value);
$this->options = $options;
return $this;
}
// ──────────────────────────────────────────────────────────────
// 계산 accessor
// ──────────────────────────────────────────────────────────────
public function getWidthSumAttribute(): ?float
{
$data = $this->bending_data ?? [];
if (empty($data)) {
return null;
}
$last = end($data);
return isset($last['sum']) ? (float) $last['sum'] : null;
}
public function getBendCountAttribute(): int
{
$data = $this->bending_data ?? [];
return count(array_filter($data, fn ($d) => ($d['rate'] ?? '') !== ''));
}
// ──────────────────────────────────────────────────────────────
// LOT 코드 테이블
// ──────────────────────────────────────────────────────────────
public const PROD_CODES = [
'R' => '가이드레일(벽면형)',
'S' => '가이드레일(측면형)',
'C' => '케이스',
'B' => '하단마감재(스크린)',
'T' => '하단마감재(철재)',
'L' => 'L-Bar',
'G' => '연기차단재',
];
public const SPEC_CODES = [
'R' => ['M' => '본체', 'T' => '본체(철재)', 'C' => 'C형', 'D' => 'D형', 'S' => 'SUS 마감재'],
'S' => ['M' => '본체디딤', 'T' => '본체(철재)', 'C' => 'C형', 'D' => 'D형', 'S' => 'SUS 마감재①', 'U' => 'SUS 마감재②'],
'C' => ['F' => '전면부', 'P' => '점검구', 'L' => '린텔부', 'B' => '후면코너부'],
'B' => ['S' => 'SUS', 'E' => 'EGI'],
'T' => ['S' => 'SUS', 'E' => 'EGI'],
'L' => ['A' => '스크린용'],
'G' => ['I' => '화이바원단'],
];
}