feat: 품목(Item) 더미 데이터 시더 추가
- DummyItemSeeder.php 생성: 10,000개 품목 더미 데이터 - FG(완제품) 2,000개, PT(부품) 3,000개 - SM(부자재) 2,000개, RM(원자재) 2,000개, CS(소모품) 1,000개 - 1,000개씩 배치 삽입으로 성능 최적화 - 타입별 속성(attributes) 자동 생성 - DummyDataSeeder에 품목 시더 등록 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
159
database/seeders/Dummy/DummyItemSeeder.php
Normal file
159
database/seeders/Dummy/DummyItemSeeder.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\Dummy;
|
||||
|
||||
use App\Models\Items\Item;
|
||||
use Database\Seeders\DummyDataSeeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class DummyItemSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* 타입별 생성 수량
|
||||
*/
|
||||
private const TYPE_COUNTS = [
|
||||
'FG' => 2000, // 완제품
|
||||
'PT' => 3000, // 부품
|
||||
'SM' => 2000, // 부자재
|
||||
'RM' => 2000, // 원자재
|
||||
'CS' => 1000, // 소모품
|
||||
];
|
||||
|
||||
/**
|
||||
* 타입별 코드 접두사
|
||||
*/
|
||||
private const TYPE_PREFIXES = [
|
||||
'FG' => 'FG',
|
||||
'PT' => 'PT',
|
||||
'SM' => 'SM',
|
||||
'RM' => 'RM',
|
||||
'CS' => 'CS',
|
||||
];
|
||||
|
||||
/**
|
||||
* 타입별 품목명 패턴
|
||||
*/
|
||||
private const TYPE_NAMES = [
|
||||
'FG' => ['스크린', '블라인드', '커튼', '차양막', '롤스크린', '버티칼', '우드블라인드', '허니콤', '듀오롤', '콤비블라인드'],
|
||||
'PT' => ['모터', '브라켓', '가이드레일', '샤프트', '베어링', '기어박스', '풀리', '벨트', '스프링', '힌지'],
|
||||
'SM' => ['나사', '볼트', '너트', '와셔', '리벳', '클립', '고정핀', '스토퍼', '캡', '부싱'],
|
||||
'RM' => ['알루미늄', '스틸', 'PVC', '폴리에스터', '면', '폴리카보네이트', 'ABS', '나일론', '실리콘', '고무'],
|
||||
'CS' => ['포장재', '라벨', '테이프', '박스', '비닐', '완충재', '스티커', '인쇄물', '매뉴얼', '보증서'],
|
||||
];
|
||||
|
||||
/**
|
||||
* 단위 목록
|
||||
*/
|
||||
private const UNITS = ['EA', 'SET', 'M', 'MM', 'KG', 'G', 'L', 'ML', 'BOX', 'ROLL'];
|
||||
|
||||
/**
|
||||
* 색상 목록
|
||||
*/
|
||||
private const COLORS = ['화이트', '아이보리', '베이지', '그레이', '블랙', '브라운', '네이비', '카키', '버건디', '실버'];
|
||||
|
||||
/**
|
||||
* 규격 목록
|
||||
*/
|
||||
private const SPECS = ['소형', '중형', '대형', '특대형', 'A타입', 'B타입', 'C타입', '표준', '고급', '프리미엄'];
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = DummyDataSeeder::TENANT_ID;
|
||||
$userId = DummyDataSeeder::USER_ID;
|
||||
|
||||
// 기존 데이터 확인
|
||||
$existing = Item::where('tenant_id', $tenantId)->count();
|
||||
if ($existing >= 10000) {
|
||||
$this->command->info(' ⚠ items: 이미 '.$existing.'개 존재 (스킵)');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->command->info(' 🔄 items: 10,000개 생성 중...');
|
||||
|
||||
$totalCount = 0;
|
||||
$now = now();
|
||||
|
||||
foreach (self::TYPE_COUNTS as $type => $count) {
|
||||
$prefix = self::TYPE_PREFIXES[$type];
|
||||
$names = self::TYPE_NAMES[$type];
|
||||
$items = [];
|
||||
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
$code = sprintf('%s-%06d', $prefix, $i);
|
||||
$baseName = $names[array_rand($names)];
|
||||
$color = self::COLORS[array_rand(self::COLORS)];
|
||||
$spec = self::SPECS[array_rand(self::SPECS)];
|
||||
$name = "{$baseName} {$color} {$spec}";
|
||||
|
||||
$items[] = [
|
||||
'tenant_id' => $tenantId,
|
||||
'item_type' => $type,
|
||||
'code' => $code,
|
||||
'name' => $name,
|
||||
'unit' => self::UNITS[array_rand(self::UNITS)],
|
||||
'category_id' => null,
|
||||
'bom' => null,
|
||||
'attributes' => json_encode($this->generateAttributes($type)),
|
||||
'attributes_archive' => null,
|
||||
'options' => json_encode(['color' => $color, 'spec' => $spec]),
|
||||
'description' => "{$type} 타입 품목 - {$name}",
|
||||
'is_active' => rand(1, 100) <= 95, // 95% 활성
|
||||
'created_by' => $userId,
|
||||
'updated_by' => $userId,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
];
|
||||
|
||||
// 1000개씩 배치 삽입
|
||||
if (count($items) >= 1000) {
|
||||
DB::table('items')->insert($items);
|
||||
$totalCount += count($items);
|
||||
$items = [];
|
||||
$this->command->info(" → {$totalCount}개 완료...");
|
||||
}
|
||||
}
|
||||
|
||||
// 남은 데이터 삽입
|
||||
if (! empty($items)) {
|
||||
DB::table('items')->insert($items);
|
||||
$totalCount += count($items);
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info(' ✓ items: '.$totalCount.'건 생성 완료');
|
||||
}
|
||||
|
||||
/**
|
||||
* 타입별 속성 생성
|
||||
*/
|
||||
private function generateAttributes(string $type): array
|
||||
{
|
||||
$base = [
|
||||
'weight' => rand(10, 10000) / 100, // 0.1 ~ 100 kg
|
||||
'width' => rand(10, 3000), // 10 ~ 3000 mm
|
||||
'height' => rand(10, 3000), // 10 ~ 3000 mm
|
||||
];
|
||||
|
||||
return match ($type) {
|
||||
'FG' => array_merge($base, [
|
||||
'warranty_months' => rand(6, 36),
|
||||
'lead_time_days' => rand(3, 30),
|
||||
]),
|
||||
'PT' => array_merge($base, [
|
||||
'material' => ['알루미늄', '스틸', 'ABS', 'PVC'][array_rand(['알루미늄', '스틸', 'ABS', 'PVC'])],
|
||||
'tolerance' => '±'.rand(1, 10) / 10 .'mm',
|
||||
]),
|
||||
'SM', 'RM' => array_merge($base, [
|
||||
'min_order_qty' => rand(1, 100) * 10,
|
||||
'supplier' => ['국내', '중국', '베트남', '일본'][array_rand(['국내', '중국', '베트남', '일본'])],
|
||||
]),
|
||||
'CS' => [
|
||||
'expiry_months' => rand(6, 24),
|
||||
'storage' => ['상온', '냉장', '냉동'][array_rand(['상온', '냉장', '냉동'])],
|
||||
],
|
||||
default => $base,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,14 @@ public function run(): void
|
||||
ApprovalTestDataSeeder::class,
|
||||
]);
|
||||
|
||||
// 5. 기타 데이터
|
||||
// 5. 품목 데이터
|
||||
$this->command->info('');
|
||||
$this->command->info('📦 품목 데이터 생성...');
|
||||
$this->call([
|
||||
Dummy\DummyItemSeeder::class,
|
||||
]);
|
||||
|
||||
// 6. 기타 데이터
|
||||
$this->command->info('');
|
||||
$this->command->info('📋 기타 데이터 생성...');
|
||||
$this->call([
|
||||
@@ -94,9 +101,10 @@ public function run(): void
|
||||
['결재', 'approval_forms', '3'],
|
||||
['결재', 'approvals', '20'],
|
||||
['결재', 'approval_steps', '~40'],
|
||||
['품목', 'items', '10,000'],
|
||||
['기타', 'popups', '8'],
|
||||
['기타', 'payments', '13'],
|
||||
['', '총계', '~1,010'],
|
||||
['', '총계', '~11,010'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user