- KyungdongFormulaHandler: product_type 자동 추론(item_category 기반), 철재 주자재 EGI코일로 변경, 조인트바 steel 공통 지원 - FormulaEvaluatorService: FG item_category에서 product_type 자동 판별 - MapItemsToProcesses: 경동 품목-공정 매핑 커맨드 정비 - KyungdongItemMasterSeeder: BOM child_item_id code 기반 재매핑 - ItemsBomController: ghost ID 유효성 검증 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
676 lines
25 KiB
PHP
676 lines
25 KiB
PHP
<?php
|
|
|
|
namespace Database\Seeders\Kyungdong;
|
|
|
|
use Database\Seeders\DummyDataSeeder;
|
|
use Illuminate\Database\Seeder;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/**
|
|
* 경동기업(tenant_id=287) 품목 기준 데이터 배포용 시더
|
|
*
|
|
* DELETE + 재삽입 방식. AUTO_INCREMENT ID 사용으로 환경별 충돌 없음.
|
|
* 종속 테이블은 새 ID로 자동 매핑.
|
|
*
|
|
* 실행: php artisan db:seed --class="Database\Seeders\Kyungdong\KyungdongItemMasterSeeder"
|
|
*/
|
|
class KyungdongItemMasterSeeder extends Seeder
|
|
{
|
|
private int $tenantId;
|
|
|
|
private int $userId;
|
|
|
|
private string $dataPath;
|
|
|
|
/** @var array<int,int> 원본ID → 새ID */
|
|
private array $pageMap = [];
|
|
|
|
private array $sectionMap = [];
|
|
|
|
private array $fieldMap = [];
|
|
|
|
private array $categoryMap = [];
|
|
|
|
/** @var array<string,int> code → 새ID */
|
|
private array $itemMap = [];
|
|
|
|
/** 기대 건수 (검증용) */
|
|
private array $expectedCounts = [];
|
|
|
|
public function run(): void
|
|
{
|
|
$this->tenantId = DummyDataSeeder::TENANT_ID;
|
|
$this->userId = DummyDataSeeder::USER_ID;
|
|
$this->dataPath = database_path('seeders/data/kyungdong');
|
|
|
|
$this->command->info('=== 경동기업 품목 기준 데이터 시더 시작 ===');
|
|
$this->command->info(" tenant_id: {$this->tenantId}");
|
|
$this->command->newLine();
|
|
|
|
DB::transaction(function () {
|
|
$this->cleanup();
|
|
|
|
$this->command->info('--- Phase 1: 독립 테이블 ---');
|
|
$this->seedItemPages();
|
|
$this->seedItemSections();
|
|
$this->seedItemFields();
|
|
$this->seedEntityRelationships();
|
|
$this->updateDisplayConditions();
|
|
|
|
$this->command->info('--- Phase 2: 기반 테이블 ---');
|
|
$this->seedCategories();
|
|
$this->seedItems();
|
|
|
|
$this->command->info('--- Phase 3: 종속 테이블 ---');
|
|
$this->seedItemDetails();
|
|
$this->seedPrices();
|
|
});
|
|
|
|
$this->verify();
|
|
$this->printSummary();
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────
|
|
// CLEANUP
|
|
// ──────────────────────────────────────────────────────────────────
|
|
|
|
private function cleanup(): void
|
|
{
|
|
$this->command->info('CLEANUP: tenant_id='.$this->tenantId.' 데이터 삭제');
|
|
|
|
// FK 역순 삭제
|
|
// prices - tenant_id로 직접 삭제
|
|
$c = DB::table('prices')->where('tenant_id', $this->tenantId)->delete();
|
|
$this->command->info(" prices: {$c}건 삭제");
|
|
|
|
// item_details - items를 통해 삭제
|
|
$itemIds = DB::table('items')->where('tenant_id', $this->tenantId)->pluck('id');
|
|
$c = DB::table('item_details')->whereIn('item_id', $itemIds)->delete();
|
|
$this->command->info(" item_details: {$c}건 삭제");
|
|
|
|
// items
|
|
$c = DB::table('items')->where('tenant_id', $this->tenantId)->delete();
|
|
$this->command->info(" items: {$c}건 삭제");
|
|
|
|
// categories - 자식 먼저 삭제
|
|
$catIds = DB::table('categories')->where('tenant_id', $this->tenantId)->pluck('id');
|
|
// 자식 (parent_id가 있는 것) 먼저
|
|
DB::table('categories')
|
|
->where('tenant_id', $this->tenantId)
|
|
->whereNotNull('parent_id')
|
|
->delete();
|
|
$c = DB::table('categories')->where('tenant_id', $this->tenantId)->delete();
|
|
$this->command->info(' categories: '.count($catIds).'건 삭제');
|
|
|
|
// entity_relationships
|
|
$c = DB::table('entity_relationships')->where('tenant_id', $this->tenantId)->delete();
|
|
$this->command->info(" entity_relationships: {$c}건 삭제");
|
|
|
|
// item_fields
|
|
$c = DB::table('item_fields')->where('tenant_id', $this->tenantId)->delete();
|
|
$this->command->info(" item_fields: {$c}건 삭제");
|
|
|
|
// item_sections
|
|
$c = DB::table('item_sections')->where('tenant_id', $this->tenantId)->delete();
|
|
$this->command->info(" item_sections: {$c}건 삭제");
|
|
|
|
// item_pages
|
|
$c = DB::table('item_pages')->where('tenant_id', $this->tenantId)->delete();
|
|
$this->command->info(" item_pages: {$c}건 삭제");
|
|
|
|
$this->command->newLine();
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────
|
|
// PHASE 1: 독립 테이블
|
|
// ──────────────────────────────────────────────────────────────────
|
|
|
|
private function seedItemPages(): void
|
|
{
|
|
$rows = $this->loadJson('item_pages.json');
|
|
$this->expectedCounts['item_pages'] = count($rows);
|
|
|
|
foreach ($rows as $row) {
|
|
$originalId = $row['_original_id'];
|
|
$data = $this->stripMeta($row);
|
|
$data['tenant_id'] = $this->tenantId;
|
|
$this->setAuditFields($data);
|
|
|
|
$newId = DB::table('item_pages')->insertGetId($data);
|
|
$this->pageMap[$originalId] = $newId;
|
|
}
|
|
|
|
$this->command->info(' item_pages: '.count($rows).'건 삽입');
|
|
}
|
|
|
|
private function seedItemSections(): void
|
|
{
|
|
$rows = $this->loadJson('item_sections.json');
|
|
$this->expectedCounts['item_sections'] = count($rows);
|
|
|
|
foreach ($rows as $row) {
|
|
$originalId = $row['_original_id'];
|
|
$data = $this->stripMeta($row);
|
|
$data['tenant_id'] = $this->tenantId;
|
|
$this->setAuditFields($data);
|
|
|
|
$newId = DB::table('item_sections')->insertGetId($data);
|
|
$this->sectionMap[$originalId] = $newId;
|
|
}
|
|
|
|
$this->command->info(' item_sections: '.count($rows).'건 삽입');
|
|
}
|
|
|
|
private function seedItemFields(): void
|
|
{
|
|
$rows = $this->loadJson('item_fields.json');
|
|
$this->expectedCounts['item_fields'] = count($rows);
|
|
|
|
foreach ($rows as $row) {
|
|
$originalId = $row['_original_id'];
|
|
$data = $this->stripMeta($row);
|
|
$data['tenant_id'] = $this->tenantId;
|
|
$this->setAuditFields($data);
|
|
|
|
$newId = DB::table('item_fields')->insertGetId($data);
|
|
$this->fieldMap[$originalId] = $newId;
|
|
}
|
|
|
|
$this->command->info(' item_fields: '.count($rows).'건 삽입');
|
|
}
|
|
|
|
private function seedEntityRelationships(): void
|
|
{
|
|
$rows = $this->loadJson('entity_relationships.json');
|
|
$this->expectedCounts['entity_relationships'] = count($rows);
|
|
$inserted = 0;
|
|
|
|
foreach ($rows as $row) {
|
|
$data = $this->stripMeta($row);
|
|
$data['tenant_id'] = $this->tenantId;
|
|
$this->setAuditFields($data);
|
|
|
|
// parent_id 매핑
|
|
$data['parent_id'] = $this->mapEntityId($data['parent_type'], $data['parent_id']);
|
|
// child_id 매핑
|
|
$data['child_id'] = $this->mapEntityId($data['child_type'], $data['child_id']);
|
|
|
|
if ($data['parent_id'] === null || $data['child_id'] === null) {
|
|
$this->command->warn(" entity_relationships: 매핑 실패 건 스킵 (parent_type={$row['parent_type']}, child_type={$row['child_type']})");
|
|
|
|
continue;
|
|
}
|
|
|
|
DB::table('entity_relationships')->insert($data);
|
|
$inserted++;
|
|
}
|
|
|
|
$this->expectedCounts['entity_relationships'] = $inserted;
|
|
$this->command->info(" entity_relationships: {$inserted}건 삽입");
|
|
}
|
|
|
|
/**
|
|
* item_fields의 display_condition 내 targetFieldIds를 새 ID로 치환
|
|
*/
|
|
private function updateDisplayConditions(): void
|
|
{
|
|
if (empty($this->fieldMap)) {
|
|
return;
|
|
}
|
|
|
|
$fields = DB::table('item_fields')
|
|
->where('tenant_id', $this->tenantId)
|
|
->whereNotNull('display_condition')
|
|
->get(['id', 'display_condition']);
|
|
|
|
$updated = 0;
|
|
foreach ($fields as $field) {
|
|
$condition = json_decode($field->display_condition, true);
|
|
if (! is_array($condition)) {
|
|
continue;
|
|
}
|
|
|
|
$changed = false;
|
|
$condition = $this->remapDisplayCondition($condition, $changed);
|
|
|
|
if ($changed) {
|
|
DB::table('item_fields')
|
|
->where('id', $field->id)
|
|
->update(['display_condition' => json_encode($condition)]);
|
|
$updated++;
|
|
}
|
|
}
|
|
|
|
if ($updated > 0) {
|
|
$this->command->info(" display_condition 후처리: {$updated}건 업데이트");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* display_condition 내 targetFieldIds 재귀적 치환
|
|
*/
|
|
private function remapDisplayCondition(array $condition, bool &$changed): array
|
|
{
|
|
if (isset($condition['targetFieldIds']) && is_array($condition['targetFieldIds'])) {
|
|
$newIds = [];
|
|
foreach ($condition['targetFieldIds'] as $oldId) {
|
|
if (isset($this->fieldMap[$oldId])) {
|
|
$newIds[] = $this->fieldMap[$oldId];
|
|
$changed = true;
|
|
} else {
|
|
$newIds[] = $oldId;
|
|
}
|
|
}
|
|
$condition['targetFieldIds'] = $newIds;
|
|
}
|
|
|
|
// targetFieldId (단일) 처리
|
|
if (isset($condition['targetFieldId']) && isset($this->fieldMap[$condition['targetFieldId']])) {
|
|
$condition['targetFieldId'] = $this->fieldMap[$condition['targetFieldId']];
|
|
$changed = true;
|
|
}
|
|
|
|
// conditions 배열 재귀 처리
|
|
if (isset($condition['conditions']) && is_array($condition['conditions'])) {
|
|
foreach ($condition['conditions'] as $i => $sub) {
|
|
$condition['conditions'][$i] = $this->remapDisplayCondition($sub, $changed);
|
|
}
|
|
}
|
|
|
|
return $condition;
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────
|
|
// PHASE 2: 기반 테이블
|
|
// ──────────────────────────────────────────────────────────────────
|
|
|
|
private function seedCategories(): void
|
|
{
|
|
$rows = $this->loadJson('categories.json');
|
|
$this->expectedCounts['categories'] = count($rows);
|
|
|
|
// depth 순 삽입을 위해 parent_id 없는 것 먼저
|
|
$roots = array_filter($rows, fn ($r) => empty($r['parent_id']));
|
|
$children = array_filter($rows, fn ($r) => ! empty($r['parent_id']));
|
|
|
|
// 루트 카테고리 삽입
|
|
foreach ($roots as $row) {
|
|
$originalId = $row['_original_id'];
|
|
$data = $this->stripMeta($row);
|
|
$data['tenant_id'] = $this->tenantId;
|
|
$data['parent_id'] = null;
|
|
$this->setAuditFields($data);
|
|
|
|
$newId = DB::table('categories')->insertGetId($data);
|
|
$this->categoryMap[$originalId] = $newId;
|
|
}
|
|
|
|
// 자식 카테고리 삽입 (여러 depth 처리를 위해 최대 5회 반복)
|
|
$remaining = $children;
|
|
for ($pass = 0; $pass < 5 && ! empty($remaining); $pass++) {
|
|
$nextRemaining = [];
|
|
foreach ($remaining as $row) {
|
|
$originalId = $row['_original_id'];
|
|
$parentOriginalId = $row['parent_id'];
|
|
|
|
if (! isset($this->categoryMap[$parentOriginalId])) {
|
|
$nextRemaining[] = $row;
|
|
|
|
continue;
|
|
}
|
|
|
|
$data = $this->stripMeta($row);
|
|
$data['tenant_id'] = $this->tenantId;
|
|
$data['parent_id'] = $this->categoryMap[$parentOriginalId];
|
|
$this->setAuditFields($data);
|
|
|
|
$newId = DB::table('categories')->insertGetId($data);
|
|
$this->categoryMap[$originalId] = $newId;
|
|
}
|
|
$remaining = $nextRemaining;
|
|
}
|
|
|
|
if (! empty($remaining)) {
|
|
$this->command->warn(' categories: '.count($remaining).'건 매핑 실패 (depth 초과)');
|
|
}
|
|
|
|
$this->command->info(' categories: '.count($this->categoryMap).'건 삽입');
|
|
}
|
|
|
|
private function seedItems(): void
|
|
{
|
|
$rows = $this->loadJson('items.json');
|
|
$this->expectedCounts['items'] = count($rows);
|
|
|
|
$chunks = array_chunk($rows, 500);
|
|
$total = 0;
|
|
|
|
foreach ($chunks as $chunk) {
|
|
$insertData = [];
|
|
foreach ($chunk as $row) {
|
|
$data = $this->stripMeta($row);
|
|
$data['tenant_id'] = $this->tenantId;
|
|
$this->setAuditFields($data);
|
|
|
|
// category_id 매핑
|
|
if (! empty($data['category_id']) && isset($this->categoryMap[$data['category_id']])) {
|
|
$data['category_id'] = $this->categoryMap[$data['category_id']];
|
|
}
|
|
|
|
$insertData[] = $data;
|
|
}
|
|
|
|
DB::table('items')->insert($insertData);
|
|
$total += count($insertData);
|
|
}
|
|
|
|
// code로 itemMap 구축
|
|
$this->itemMap = DB::table('items')
|
|
->where('tenant_id', $this->tenantId)
|
|
->whereNull('deleted_at')
|
|
->pluck('id', 'code')
|
|
->toArray();
|
|
|
|
$this->command->info(" items: {$total}건 삽입 (itemMap: ".count($this->itemMap).'건)');
|
|
|
|
// BOM의 child_item_code → child_item_id 변환
|
|
$this->remapBomCodes();
|
|
}
|
|
|
|
/**
|
|
* BOM JSON의 child_item_code를 실제 child_item_id로 변환
|
|
*
|
|
* items.json의 bom 필드가 child_item_code(품목코드) 기반으로 저장되어 있으므로,
|
|
* INSERT 후 itemMap(code→id)을 이용해 child_item_id로 교체한다.
|
|
*/
|
|
private function remapBomCodes(): void
|
|
{
|
|
$bomItems = DB::table('items')
|
|
->where('tenant_id', $this->tenantId)
|
|
->whereNotNull('bom')
|
|
->get(['id', 'code', 'bom']);
|
|
|
|
$updated = 0;
|
|
|
|
foreach ($bomItems as $item) {
|
|
$bom = json_decode($item->bom, true);
|
|
if (! is_array($bom)) {
|
|
continue;
|
|
}
|
|
|
|
$changed = false;
|
|
foreach ($bom as &$entry) {
|
|
// child_item_code → child_item_id 변환
|
|
if (isset($entry['child_item_code'])) {
|
|
$code = $entry['child_item_code'];
|
|
if (isset($this->itemMap[$code])) {
|
|
$entry['child_item_id'] = $this->itemMap[$code];
|
|
unset($entry['child_item_code']);
|
|
$changed = true;
|
|
} else {
|
|
$this->command->warn(" BOM 매핑 실패: {$item->code} → {$code} (품목 미존재)");
|
|
}
|
|
}
|
|
}
|
|
unset($entry);
|
|
|
|
if ($changed) {
|
|
DB::table('items')->where('id', $item->id)
|
|
->update(['bom' => json_encode($bom, JSON_UNESCAPED_UNICODE)]);
|
|
$updated++;
|
|
}
|
|
}
|
|
|
|
$this->command->info(" BOM code→id 매핑: {$updated}건 변환");
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────
|
|
// PHASE 3: 종속 테이블
|
|
// ──────────────────────────────────────────────────────────────────
|
|
|
|
private function seedItemDetails(): void
|
|
{
|
|
$rows = $this->loadJson('item_details.json');
|
|
$this->expectedCounts['item_details'] = count($rows);
|
|
|
|
// items JSON에서 원본 item_id → code 매핑 구축
|
|
$itemsJson = $this->loadJson('items.json');
|
|
$originalIdToCode = [];
|
|
foreach ($itemsJson as $item) {
|
|
$originalIdToCode[$item['_original_id']] = $item['code'];
|
|
}
|
|
|
|
$insertData = [];
|
|
$skipped = 0;
|
|
|
|
foreach ($rows as $row) {
|
|
$data = $this->stripMeta($row);
|
|
$originalItemId = $data['item_id'];
|
|
|
|
// 원본 item_id → code → 새 item_id
|
|
$code = $originalIdToCode[$originalItemId] ?? null;
|
|
if ($code === null || ! isset($this->itemMap[$code])) {
|
|
$skipped++;
|
|
|
|
continue;
|
|
}
|
|
|
|
$data['item_id'] = $this->itemMap[$code];
|
|
$insertData[] = $data;
|
|
}
|
|
|
|
if (! empty($insertData)) {
|
|
foreach (array_chunk($insertData, 500) as $chunk) {
|
|
DB::table('item_details')->insert($chunk);
|
|
}
|
|
}
|
|
|
|
if ($skipped > 0) {
|
|
$this->command->warn(" item_details: {$skipped}건 매핑 실패 스킵");
|
|
}
|
|
|
|
$this->expectedCounts['item_details'] = count($insertData);
|
|
$this->command->info(' item_details: '.count($insertData).'건 삽입');
|
|
}
|
|
|
|
private function seedPrices(): void
|
|
{
|
|
$rows = $this->loadJson('prices.json');
|
|
$this->expectedCounts['prices'] = count($rows);
|
|
|
|
// items JSON에서 원본 item_id → code 매핑 구축
|
|
$itemsJson = $this->loadJson('items.json');
|
|
$originalIdToCode = [];
|
|
foreach ($itemsJson as $item) {
|
|
$originalIdToCode[$item['_original_id']] = $item['code'];
|
|
}
|
|
|
|
$insertData = [];
|
|
$skipped = 0;
|
|
|
|
foreach ($rows as $row) {
|
|
$data = $this->stripMeta($row);
|
|
$data['tenant_id'] = $this->tenantId;
|
|
$this->setAuditFields($data);
|
|
$originalItemId = $data['item_id'];
|
|
|
|
// 원본 item_id → code → 새 item_id
|
|
$code = $originalIdToCode[$originalItemId] ?? null;
|
|
if ($code === null || ! isset($this->itemMap[$code])) {
|
|
$skipped++;
|
|
|
|
continue;
|
|
}
|
|
|
|
$data['item_id'] = $this->itemMap[$code];
|
|
$insertData[] = $data;
|
|
}
|
|
|
|
if (! empty($insertData)) {
|
|
foreach (array_chunk($insertData, 500) as $chunk) {
|
|
DB::table('prices')->insert($chunk);
|
|
}
|
|
}
|
|
|
|
if ($skipped > 0) {
|
|
$this->command->warn(" prices: {$skipped}건 매핑 실패 스킵");
|
|
}
|
|
|
|
$this->expectedCounts['prices'] = count($insertData);
|
|
$this->command->info(' prices: '.count($insertData).'건 삽입');
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────
|
|
// VERIFY & SUMMARY
|
|
// ──────────────────────────────────────────────────────────────────
|
|
|
|
private function verify(): void
|
|
{
|
|
$this->command->newLine();
|
|
$this->command->info('=== 검증 ===');
|
|
|
|
$tables = [
|
|
'item_pages' => fn () => DB::table('item_pages')->where('tenant_id', $this->tenantId)->whereNull('deleted_at')->count(),
|
|
'item_sections' => fn () => DB::table('item_sections')->where('tenant_id', $this->tenantId)->whereNull('deleted_at')->count(),
|
|
'item_fields' => fn () => DB::table('item_fields')->where('tenant_id', $this->tenantId)->whereNull('deleted_at')->count(),
|
|
'entity_relationships' => fn () => DB::table('entity_relationships')->where('tenant_id', $this->tenantId)->count(),
|
|
'categories' => fn () => DB::table('categories')->where('tenant_id', $this->tenantId)->whereNull('deleted_at')->count(),
|
|
'items' => fn () => DB::table('items')->where('tenant_id', $this->tenantId)->whereNull('deleted_at')->count(),
|
|
'item_details' => fn () => DB::table('item_details')->whereIn('item_id', DB::table('items')->where('tenant_id', $this->tenantId)->pluck('id'))->count(),
|
|
'prices' => fn () => DB::table('prices')->where('tenant_id', $this->tenantId)->whereNull('deleted_at')->count(),
|
|
];
|
|
|
|
$allOk = true;
|
|
foreach ($tables as $table => $countFn) {
|
|
$actual = $countFn();
|
|
$expected = $this->expectedCounts[$table] ?? '?';
|
|
$status = ($actual == $expected) ? 'OK' : 'MISMATCH';
|
|
|
|
if ($status === 'MISMATCH') {
|
|
$allOk = false;
|
|
$this->command->error(" {$table}: {$actual}건 (기대: {$expected}) [{$status}]");
|
|
} else {
|
|
$this->command->info(" {$table}: {$actual}건 [{$status}]");
|
|
}
|
|
}
|
|
|
|
// entity_relationships 참조 무결성 검증
|
|
$this->verifyRelationshipIntegrity();
|
|
|
|
if ($allOk) {
|
|
$this->command->newLine();
|
|
$this->command->info('모든 검증 통과!');
|
|
}
|
|
}
|
|
|
|
private function verifyRelationshipIntegrity(): void
|
|
{
|
|
$relationships = DB::table('entity_relationships')
|
|
->where('tenant_id', $this->tenantId)
|
|
->get();
|
|
|
|
$orphans = 0;
|
|
foreach ($relationships as $rel) {
|
|
$parentExists = $this->entityExists($rel->parent_type, $rel->parent_id);
|
|
$childExists = $this->entityExists($rel->child_type, $rel->child_id);
|
|
|
|
if (! $parentExists || ! $childExists) {
|
|
$orphans++;
|
|
}
|
|
}
|
|
|
|
if ($orphans > 0) {
|
|
$this->command->warn(" entity_relationships 참조 무결성: {$orphans}건 고아 레코드");
|
|
} else {
|
|
$this->command->info(' entity_relationships 참조 무결성: OK');
|
|
}
|
|
}
|
|
|
|
private function entityExists(string $type, int $id): bool
|
|
{
|
|
return match ($type) {
|
|
'page' => DB::table('item_pages')->where('id', $id)->exists(),
|
|
'section' => DB::table('item_sections')->where('id', $id)->exists(),
|
|
'field' => DB::table('item_fields')->where('id', $id)->exists(),
|
|
'bom' => DB::table('item_bom_items')->where('id', $id)->exists(),
|
|
default => false,
|
|
};
|
|
}
|
|
|
|
private function printSummary(): void
|
|
{
|
|
$this->command->newLine();
|
|
$this->command->info('=== 경동기업 품목 기준 데이터 시더 완료 ===');
|
|
$this->command->info(' 매핑 현황:');
|
|
$this->command->info(' pageMap: '.count($this->pageMap).'건');
|
|
$this->command->info(' sectionMap: '.count($this->sectionMap).'건');
|
|
$this->command->info(' fieldMap: '.count($this->fieldMap).'건');
|
|
$this->command->info(' categoryMap: '.count($this->categoryMap).'건');
|
|
$this->command->info(' itemMap: '.count($this->itemMap).'건');
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────
|
|
// HELPERS
|
|
// ──────────────────────────────────────────────────────────────────
|
|
|
|
private function loadJson(string $filename): array
|
|
{
|
|
$path = $this->dataPath.'/'.$filename;
|
|
|
|
if (! file_exists($path)) {
|
|
$this->command->error("JSON 파일 없음: {$path}");
|
|
$this->command->error('먼저 php artisan kyungdong:export-item-master 를 실행하세요.');
|
|
|
|
throw new \RuntimeException("JSON 파일 없음: {$path}");
|
|
}
|
|
|
|
return json_decode(file_get_contents($path), true);
|
|
}
|
|
|
|
/**
|
|
* _original_id, id, created_at, updated_at, deleted_at 등 메타 제거
|
|
*/
|
|
private function stripMeta(array $row): array
|
|
{
|
|
unset(
|
|
$row['_original_id'],
|
|
$row['id'],
|
|
$row['created_at'],
|
|
$row['updated_at'],
|
|
$row['deleted_at'],
|
|
);
|
|
|
|
return $row;
|
|
}
|
|
|
|
private function setAuditFields(array &$data): void
|
|
{
|
|
$now = now();
|
|
$data['created_at'] = $now;
|
|
$data['updated_at'] = $now;
|
|
|
|
if (isset($data['created_by'])) {
|
|
$data['created_by'] = $this->userId;
|
|
}
|
|
if (isset($data['updated_by'])) {
|
|
$data['updated_by'] = $this->userId;
|
|
}
|
|
|
|
// deleted_by, deleted_at는 항상 제거
|
|
unset($data['deleted_by'], $data['deleted_at']);
|
|
}
|
|
|
|
/**
|
|
* entity type에 따라 원본 ID를 새 ID로 매핑
|
|
*/
|
|
private function mapEntityId(string $type, int $originalId): ?int
|
|
{
|
|
return match ($type) {
|
|
'page' => $this->pageMap[$originalId] ?? null,
|
|
'section' => $this->sectionMap[$originalId] ?? null,
|
|
'field' => $this->fieldMap[$originalId] ?? null,
|
|
default => $originalId, // bom 등은 그대로
|
|
};
|
|
}
|
|
}
|