- GuiderailModelController/Service/Resource: 가이드레일/케이스/하단마감재 통합 CRUD - item_category 필터 (GUIDERAIL_MODEL/SHUTTERBOX_MODEL/BOTTOMBAR_MODEL) - BendingItemResource: legacy_bending_num 노출 추가 - ApiKeyMiddleware: guiderail-models, files 화이트리스트 추가 - Swagger: BendingItemApi, GuiderailModelApi 문서 (케이스/하단마감재 필드 포함) - 마이그레이션 커맨드 5개: GuiderailImportLegacy, BendingProductImportLegacy, BendingImportImages, BendingModelImportImages, BendingModelImportAssemblyImages - 데이터: GR 20건 + SB 30건 + BB 10건 + 이미지 473건 R2 업로드
201 lines
7.2 KiB
PHP
201 lines
7.2 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Attributes\AsCommand;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/**
|
|
* chandj shutterbox(케이스) + bottombar(하단마감재) → SAM items 임포트
|
|
*/
|
|
#[AsCommand(name: 'bending-product:import-legacy', description: 'chandj 케이스/하단마감재 모델 → SAM items 임포트')]
|
|
class BendingProductImportLegacy extends Command
|
|
{
|
|
protected $signature = 'bending-product:import-legacy
|
|
{--tenant_id=287 : Target tenant ID}
|
|
{--dry-run : 실제 저장 없이 미리보기}';
|
|
|
|
public function handle(): int
|
|
{
|
|
$tenantId = (int) $this->option('tenant_id');
|
|
$dryRun = $this->option('dry-run');
|
|
|
|
$this->info('=== 케이스/하단마감재 모델 → SAM 임포트 ===');
|
|
$this->info('Mode: '.($dryRun ? 'DRY-RUN' : 'LIVE'));
|
|
$this->newLine();
|
|
|
|
// 케이스 (shutterbox)
|
|
$this->info('--- 케이스 (shutterbox) ---');
|
|
$cases = DB::connection('chandj')->table('shutterbox')->whereNull('is_deleted')->get();
|
|
$this->info("chandj shutterbox: {$cases->count()}건");
|
|
$caseCreated = $this->importItems($cases, 'SHUTTERBOX_MODEL', $tenantId, $dryRun);
|
|
|
|
$this->newLine();
|
|
|
|
// 하단마감재 (bottombar)
|
|
$this->info('--- 하단마감재 (bottombar) ---');
|
|
$bars = DB::connection('chandj')->table('bottombar')->whereNull('is_deleted')->get();
|
|
$this->info("chandj bottombar: {$bars->count()}건");
|
|
$barCreated = $this->importItems($bars, 'BOTTOMBAR_MODEL', $tenantId, $dryRun);
|
|
|
|
$this->newLine();
|
|
$this->info("결과: 케이스 {$caseCreated}건 + 하단마감재 {$barCreated}건");
|
|
if ($dryRun) {
|
|
$this->info('🔍 DRY-RUN 완료.');
|
|
}
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function importItems($rows, string $category, int $tenantId, bool $dryRun): int
|
|
{
|
|
$created = 0;
|
|
$skipped = 0;
|
|
|
|
foreach ($rows as $row) {
|
|
$code = $this->buildCode($row, $category);
|
|
$name = $this->buildName($row, $category);
|
|
|
|
$existing = DB::table('items')
|
|
->where('tenant_id', $tenantId)
|
|
->where('code', $code)
|
|
->whereNull('deleted_at')
|
|
->first();
|
|
|
|
if ($existing) {
|
|
$skipped++;
|
|
|
|
continue;
|
|
}
|
|
|
|
$components = $this->convertComponents(json_decode($row->bending_components ?? '[]', true) ?: []);
|
|
$materialSummary = json_decode($row->material_summary ?? '{}', true) ?: [];
|
|
|
|
$options = $this->buildOptions($row, $category, $components, $materialSummary);
|
|
|
|
if (! $dryRun) {
|
|
DB::table('items')->insert([
|
|
'tenant_id' => $tenantId,
|
|
'code' => $code,
|
|
'name' => $name,
|
|
'item_type' => 'FG',
|
|
'item_category' => $category,
|
|
'unit' => 'SET',
|
|
'options' => json_encode($options, JSON_UNESCAPED_UNICODE),
|
|
'is_active' => true,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
$created++;
|
|
$this->line(" ✅ {$code} ({$name}) — 부품 ".count($components).'개');
|
|
}
|
|
|
|
$this->info(" 생성: {$created}건 | 스킵: {$skipped}건");
|
|
|
|
return $created;
|
|
}
|
|
|
|
private function buildCode(object $row, string $category): string
|
|
{
|
|
if ($category === 'SHUTTERBOX_MODEL') {
|
|
$size = ($row->box_width ?? '').
|
|
'*'.($row->box_height ?? '');
|
|
$exit = match ($row->exit_direction ?? '') {
|
|
'양면 점검구' => '양면',
|
|
'밑면 점검구' => '밑면',
|
|
'후면 점검구' => '후면',
|
|
default => $row->exit_direction ?? '',
|
|
};
|
|
|
|
return "SB-{$size}-{$exit}";
|
|
}
|
|
|
|
// BOTTOMBAR_MODEL
|
|
$model = $row->model_name ?? 'UNKNOWN';
|
|
$finish = str_contains($row->finishing_type ?? '', 'SUS') ? 'SUS' : 'EGI';
|
|
|
|
return "BB-{$model}-{$finish}";
|
|
}
|
|
|
|
private function buildName(object $row, string $category): string
|
|
{
|
|
if ($category === 'SHUTTERBOX_MODEL') {
|
|
return "케이스 {$row->box_width}*{$row->box_height} {$row->exit_direction}";
|
|
}
|
|
|
|
return "하단마감재 {$row->model_name} {$row->firstitem}";
|
|
}
|
|
|
|
private function buildOptions(object $row, string $category, array $components, array $materialSummary): array
|
|
{
|
|
$base = [
|
|
'author' => $row->author ?? null,
|
|
'registration_date' => $row->registration_date ?? null,
|
|
'search_keyword' => $row->search_keyword ?? null,
|
|
'memo' => $row->remark ?? null,
|
|
'components' => $components,
|
|
'material_summary' => $materialSummary,
|
|
'source' => 'chandj_'.(strtolower($category)),
|
|
'legacy_num' => $row->num,
|
|
];
|
|
|
|
if ($category === 'SHUTTERBOX_MODEL') {
|
|
return array_merge($base, [
|
|
'box_width' => (int) ($row->box_width ?? 0),
|
|
'box_height' => (int) ($row->box_height ?? 0),
|
|
'exit_direction' => $row->exit_direction ?? null,
|
|
'front_bottom_width' => $row->front_bottom_width ?? null,
|
|
'rail_width' => $row->rail_width ?? null,
|
|
]);
|
|
}
|
|
|
|
// BOTTOMBAR_MODEL
|
|
return array_merge($base, [
|
|
'model_name' => $row->model_name ?? null,
|
|
'item_sep' => $row->firstitem ?? null,
|
|
'model_UA' => $row->model_UA ?? null,
|
|
'finishing_type' => $row->finishing_type ?? null,
|
|
'bar_width' => $row->bar_width ?? null,
|
|
'bar_height' => $row->bar_height ?? null,
|
|
]);
|
|
}
|
|
|
|
private function convertComponents(array $legacyComps): array
|
|
{
|
|
return array_map(function ($c, $idx) {
|
|
$inputs = $c['inputList'] ?? [];
|
|
$rates = $c['bendingrateList'] ?? [];
|
|
$sums = $c['sumList'] ?? [];
|
|
$colors = $c['colorList'] ?? [];
|
|
$angles = $c['AList'] ?? [];
|
|
|
|
$bendingData = [];
|
|
for ($i = 0; $i < count($inputs); $i++) {
|
|
$bendingData[] = [
|
|
'no' => $i + 1,
|
|
'input' => (float) ($inputs[$i] ?? 0),
|
|
'rate' => (string) ($rates[$i] ?? ''),
|
|
'sum' => (float) ($sums[$i] ?? 0),
|
|
'color' => (bool) ($colors[$i] ?? false),
|
|
'aAngle' => (bool) ($angles[$i] ?? false),
|
|
];
|
|
}
|
|
|
|
$lastSum = ! empty($sums) ? (float) end($sums) : ($c['widthsum'] ?? 0);
|
|
|
|
return [
|
|
'orderNumber' => $idx + 1,
|
|
'itemName' => $c['itemName'] ?? '',
|
|
'material' => $c['material'] ?? '',
|
|
'quantity' => (int) ($c['quantity'] ?? 1),
|
|
'width_sum' => (float) $lastSum,
|
|
'bendingData' => $bendingData,
|
|
'legacy_bending_num' => $c['source_num'] ?? $c['num'] ?? null,
|
|
];
|
|
}, $legacyComps, array_keys($legacyComps));
|
|
}
|
|
}
|