Files
sam-manage/database/seeders/InterviewQuestionMasterSeeder.php
김보곤 f74bd8960b feat: [interview] 인터뷰 시나리오 고도화 Phase 1 구현
- InterviewProject/Attachment/Knowledge 모델 3개 신규
- 기존 모델 확장 (Question, Answer, Session, Category)
- 서비스 확장: 프로젝트 CRUD, 첨부파일, 지식 관리
- 컨트롤러 확장: 프로젝트/첨부/지식 API 엔드포인트
- 라우트 20개 추가 (프로젝트, 첨부, 지식)
- InterviewQuestionMasterSeeder: 8개 도메인 80개 질문
- UI 확장: 프로젝트 모드/기존 모드 전환
  - 프로젝트 선택 바, 상태 바, 도메인 사이드바
  - 탭 구조 (질문편집/인터뷰/첨부파일/추출지식)
  - 구조화 답변 입력 (테이블, 수식, 다중선택 등)
  - 첨부파일 업로드/관리
  - 지식 수동 추가/검증/필터링
2026-02-28 20:03:14 +09:00

327 lines
24 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Database\Seeders;
use App\Models\Interview\InterviewCategory;
use App\Models\Interview\InterviewQuestion;
use App\Models\Interview\InterviewTemplate;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
/**
* 인터뷰 질문 마스터 데이터 시더
*
* 8개 도메인 × 세부 질문 (총 80개)
* 이 시더는 프로젝트 생성 시 질문 템플릿으로 활용된다.
*
* 사용법:
* docker exec sam-mng-1 php artisan db:seed --class=InterviewQuestionMasterSeeder
*
* 주의: 기존 인터뷰 질문/카테고리와 충돌하지 않도록
* interview_project_id = NULL인 마스터 데이터로 생성한다.
*/
class InterviewQuestionMasterSeeder extends Seeder
{
public function run(): void
{
DB::transaction(function () {
$tenantId = 1;
$userId = 1;
$domains = $this->getDomainData();
foreach ($domains as $domainKey => $domain) {
$categorySortOrder = InterviewCategory::max('sort_order') ?? 0;
$category = InterviewCategory::create([
'tenant_id' => $tenantId,
'interview_project_id' => null,
'name' => $domain['name'],
'description' => $domain['description'],
'domain' => $domainKey,
'sort_order' => $categorySortOrder + 1,
'is_active' => true,
'created_by' => $userId,
'updated_by' => $userId,
]);
foreach ($domain['templates'] as $tplIndex => $tpl) {
$template = InterviewTemplate::create([
'tenant_id' => $tenantId,
'interview_category_id' => $category->id,
'name' => $tpl['name'],
'sort_order' => $tplIndex + 1,
'is_active' => true,
'created_by' => $userId,
'updated_by' => $userId,
]);
foreach ($tpl['questions'] as $qIndex => $q) {
InterviewQuestion::create([
'tenant_id' => $tenantId,
'interview_template_id' => $template->id,
'question_text' => $q['text'],
'question_type' => $q['type'],
'options' => $q['options'] ?? null,
'ai_hint' => $q['ai_hint'] ?? null,
'expected_format' => $q['expected_format'] ?? null,
'depends_on' => $q['depends_on'] ?? null,
'domain' => $domainKey,
'is_required' => $q['is_required'] ?? false,
'sort_order' => $qIndex + 1,
'is_active' => true,
'created_by' => $userId,
'updated_by' => $userId,
]);
}
}
}
});
}
private function getDomainData(): array
{
return [
// ================================================================
// Domain 1: 제품 분류 체계
// ================================================================
'product_classification' => [
'name' => '제품 분류 체계',
'description' => '제품 카테고리, 모델 코드, 분류 기준 파악',
'templates' => [
[
'name' => '제품 카테고리 구조',
'questions' => [
['text' => '귀사의 주요 제품군을 모두 나열해주세요', 'type' => 'text', 'ai_hint' => '쉼표 구분으로 제품군 나열', 'is_required' => true],
['text' => '각 제품군의 하위 모델명과 코드 체계를 알려주세요', 'type' => 'table_input', 'options' => ['columns' => ['모델코드', '모델명', '비고']], 'ai_hint' => '코드-이름 매핑 테이블'],
['text' => '제품을 분류하는 기준은 무엇인가요? (소재, 용도, 크기 등)', 'type' => 'multi_select', 'options' => ['choices' => ['소재별', '용도별', '크기별', '설치방식별', '인증여부별']]],
['text' => '인증(인정) 제품과 비인증 제품의 구분이 있나요?', 'type' => 'select', 'options' => ['choices' => ['있음', '없음']]],
['text' => '인증 제품의 경우 구성이 고정되나요?', 'type' => 'checkbox', 'depends_on' => ['question_index' => 3, 'value' => '있음']],
['text' => '카테고리별 제품 수는 대략 몇 개인가요?', 'type' => 'number', 'expected_format' => '개'],
['text' => '제품 코드 명명 규칙을 설명해주세요 (예: KSS01의 의미)', 'type' => 'text', 'ai_hint' => '코드 체계의 각 자릿수 의미'],
['text' => '기존 시스템(ERP/엑셀)에서 사용하는 제품 분류 방식을 캡처하여 업로드해주세요', 'type' => 'file_upload'],
],
],
[
'name' => '설치 유형별 분류',
'questions' => [
['text' => '설치 유형(벽면형, 측면형, 혼합형 등)에 따라 견적이 달라지나요?', 'type' => 'select', 'options' => ['choices' => ['예, 크게 달라짐', '약간 달라짐', '달라지지 않음']]],
['text' => '각 설치 유형별로 어떤 부품이 달라지나요?', 'type' => 'table_input', 'options' => ['columns' => ['설치유형', '추가부품', '제외부품', '비고']]],
['text' => '설치 유형에 따른 추가 비용 항목이 있나요?', 'type' => 'text'],
],
],
],
],
// ================================================================
// Domain 2: BOM 구조
// ================================================================
'bom_structure' => [
'name' => 'BOM 구조',
'description' => '완제품-부품 관계, 부품 카테고리, BOM 레벨',
'templates' => [
[
'name' => '완제품-부품 관계',
'questions' => [
['text' => '대표 제품 1개의 완제품→부품 구성을 트리로 그려주세요', 'type' => 'bom_tree', 'ai_hint' => '최상위 제품부터 하위 부품까지 트리 구조', 'is_required' => true],
['text' => '모든 제품에 공통으로 들어가는 부품은 무엇인가요?', 'type' => 'multi_select', 'options' => ['choices' => ['가이드레일', '케이스', '모터', '제어기', '브라켓', '볼트/너트']], 'ai_hint' => '직접 입력 가능'],
['text' => '제품별로 선택적(옵션)인 부품은 무엇인가요?', 'type' => 'table_input', 'options' => ['columns' => ['제품명', '옵션부품', '적용조건']]],
['text' => 'BOM이 현재 엑셀로 관리되고 있나요? 파일을 업로드해주세요', 'type' => 'file_upload'],
['text' => '하위 부품의 단계(레벨)는 최대 몇 단계인가요?', 'type' => 'number', 'expected_format' => '단계'],
['text' => '부품 수량이 고정인 것과 계산이 필요한 것을 구분해주세요', 'type' => 'table_input', 'options' => ['columns' => ['부품명', '고정/계산', '고정수량 또는 계산식']]],
],
],
[
'name' => '부품 카테고리',
'questions' => [
['text' => '부품을 카테고리로 분류하면 어떻게 나눠지나요? (본체, 절곡품, 전동부, 부자재 등)', 'type' => 'text', 'ai_hint' => '부품 분류 체계'],
['text' => '각 카테고리에 속하는 부품 목록을 정리해주세요', 'type' => 'table_input', 'options' => ['columns' => ['카테고리', '부품명', '규격']]],
['text' => '외주 구매 부품과 자체 제작 부품의 구분이 있나요?', 'type' => 'select', 'options' => ['choices' => ['있음', '없음']]],
['text' => '부자재(볼트, 너트, 패킹 등)는 별도 관리하나요?', 'type' => 'checkbox'],
],
],
],
],
// ================================================================
// Domain 3: 치수/변수 계산
// ================================================================
'dimension_formula' => [
'name' => '치수/변수 계산',
'description' => '오픈 사이즈→제작 사이즈 변환, 파생 변수 계산',
'templates' => [
[
'name' => '오픈 사이즈 → 제작 사이즈',
'questions' => [
['text' => '고객이 입력하는 기본 치수 항목은 무엇인가요? (폭, 높이, 깊이 등)', 'type' => 'multi_select', 'options' => ['choices' => ['폭(W)', '높이(H)', '깊이(D)', '두께(T)', '지름(Ø)']], 'is_required' => true],
['text' => '오픈 사이즈에서 제작 사이즈로 변환할 때 더하는 마진값은?', 'type' => 'formula_input', 'ai_hint' => '예: W1 = W0 + 120, H1 = H0 + 50', 'expected_format' => 'mm'],
['text' => '제품 카테고리별로 마진값이 다른가요?', 'type' => 'table_input', 'options' => ['columns' => ['제품카테고리', 'W 마진(mm)', 'H 마진(mm)', '비고']]],
['text' => '면적(㎡) 계산 공식을 알려주세요', 'type' => 'formula_input', 'ai_hint' => '예: area = W1 * H1 / 1000000', 'expected_format' => '㎡'],
['text' => '중량(kg) 계산 공식을 알려주세요', 'type' => 'formula_input', 'ai_hint' => '예: weight = area * 단위중량(kg/㎡)', 'expected_format' => 'kg'],
['text' => '기타 파생 변수가 있나요? (예: 분할 개수, 절곡 길이 등)', 'type' => 'table_input', 'options' => ['columns' => ['변수명', '계산식', '단위', '비고']]],
['text' => '치수 계산에 사용하는 엑셀 수식을 캡처해주세요', 'type' => 'file_upload'],
],
],
[
'name' => '변수 의존 관계',
'questions' => [
['text' => '변수 간 의존 관계를 설명해주세요 (A는 B와 C로 계산)', 'type' => 'text', 'ai_hint' => '계산 순서와 변수 의존성'],
['text' => '계산 순서가 중요한 변수가 있나요?', 'type' => 'text'],
['text' => '단위는 mm, m, kg 중 어떤 것을 기본으로 사용하나요?', 'type' => 'select', 'options' => ['choices' => ['mm', 'm', 'cm', '혼용']]],
],
],
],
],
// ================================================================
// Domain 4: 부품 구성 상세
// ================================================================
'component_config' => [
'name' => '부품 구성 상세',
'description' => '주요 부품별 규격, 선택 기준, 특수 구성',
'templates' => [
[
'name' => '주요 부품별 상세',
'questions' => [
['text' => '가이드레일의 표준 길이 규격은? (예: 1219, 2438, 3305mm)', 'type' => 'table_input', 'options' => ['columns' => ['규격코드', '길이(mm)', '비고']]],
['text' => '가이드레일 길이 조합 규칙은? (어떤 길이를 몇 개 사용?)', 'type' => 'text', 'ai_hint' => '높이에 따른 가이드레일 조합 로직'],
['text' => '케이스(하우징) 크기별 규격과 부속품 차이를 설명해주세요', 'type' => 'table_input', 'options' => ['columns' => ['케이스규격', '적용조건', '부속품']]],
['text' => '모터 용량 종류와 선택 기준은? (무게별? 면적별?)', 'type' => 'table_input', 'options' => ['columns' => ['모터용량', '적용범위(최소)', '적용범위(최대)', '단위']], 'ai_hint' => '무게/면적 범위별 모터 매핑'],
['text' => '모터 전압 옵션은? (380V, 220V 등)', 'type' => 'multi_select', 'options' => ['choices' => ['380V', '220V', '110V', 'DC 24V']]],
['text' => '제어기 종류와 선택 기준은? (노출형/매립형 등)', 'type' => 'table_input', 'options' => ['columns' => ['제어기유형', '적용조건', '비고']]],
['text' => '절곡품(판재 가공) 목록과 각각의 치수 결정 방식은?', 'type' => 'table_input', 'options' => ['columns' => ['절곡품명', '치수결정방식', '재질', '두께(mm)']]],
['text' => '부자재(볼트, 너트, 패킹 등) 목록과 수량 결정 방식은?', 'type' => 'table_input', 'options' => ['columns' => ['부자재명', '규격', '수량결정방식', '기본수량']]],
],
],
[
'name' => '특수 구성',
'questions' => [
['text' => '연기차단재 등 특수 부품이 있나요? 적용 조건은?', 'type' => 'text'],
['text' => '보강재(샤프트, 파이프, 앵글 등) 사용 조건은?', 'type' => 'table_input', 'options' => ['columns' => ['보강재명', '규격', '적용조건', '수량']]],
['text' => '고객 요청에 따라 추가/제외되는 옵션 부품은?', 'type' => 'table_input', 'options' => ['columns' => ['옵션부품', '추가/제외', '추가비용', '비고']]],
],
],
],
],
// ================================================================
// Domain 5: 단가 체계
// ================================================================
'pricing_structure' => [
'name' => '단가 체계',
'description' => '단가 관리 방식, 계산 방식, 마진/LOSS율',
'templates' => [
[
'name' => '단가 관리 방식',
'questions' => [
['text' => '부품별 단가를 어디서 관리하나요? (엑셀, ERP, 구두 등)', 'type' => 'select', 'options' => ['choices' => ['엑셀', 'ERP 시스템', '구두/경험', '기타']]],
['text' => '단가표 파일을 업로드해주세요', 'type' => 'file_upload'],
['text' => '단가 변경 주기는? (월/분기/연 등)', 'type' => 'select', 'options' => ['choices' => ['수시', '월 단위', '분기 단위', '반기 단위', '연 단위']]],
['text' => '단가에 포함되는 항목은? (재료비만? 가공비 포함?)', 'type' => 'multi_select', 'options' => ['choices' => ['재료비', '가공비', '운송비', '설치비', '마진']]],
['text' => '고객별/거래처별 차등 단가가 있나요?', 'type' => 'select', 'options' => ['choices' => ['있음 (등급별)', '있음 (거래처별)', '없음 (일괄 동일)']]],
['text' => 'LOSS율(손실률)을 적용하나요? 적용 방식은?', 'type' => 'formula_input', 'ai_hint' => '예: 실제수량 = 계산수량 × (1 + LOSS율)', 'expected_format' => '%'],
['text' => '마진율 설정 방식은? (일괄? 품목별?)', 'type' => 'select', 'options' => ['choices' => ['일괄 마진율', '품목별 마진율', '카테고리별 마진율', '고객별 마진율']]],
],
],
[
'name' => '단가 계산 방식',
'questions' => [
['text' => '면적 기반 단가 품목은? (원/㎡)', 'type' => 'table_input', 'options' => ['columns' => ['품목명', '단가(원/㎡)', '비고']], 'expected_format' => '원/㎡'],
['text' => '중량 기반 단가 품목은? (원/kg)', 'type' => 'table_input', 'options' => ['columns' => ['품목명', '단가(원/kg)', '비고']], 'expected_format' => '원/kg'],
['text' => '수량 기반 단가 품목은? (원/EA)', 'type' => 'table_input', 'options' => ['columns' => ['품목명', '단가(원/EA)', '비고']], 'expected_format' => '원/EA'],
['text' => '길이 기반 단가 품목은? (원/m)', 'type' => 'table_input', 'options' => ['columns' => ['품목명', '단가(원/m)', '비고']], 'expected_format' => '원/m'],
['text' => '기타 특수 단가 계산 방식이 있나요?', 'type' => 'text'],
],
],
],
],
// ================================================================
// Domain 6: 수량 수식
// ================================================================
'quantity_formula' => [
'name' => '수량 수식',
'description' => '부품별 수량 결정 규칙, 계산식, 검증',
'templates' => [
[
'name' => '수량 결정 규칙',
'questions' => [
['text' => '고정 수량 부품 목록 (항상 1개, 2개 등)', 'type' => 'table_input', 'options' => ['columns' => ['부품명', '고정수량', '비고']]],
['text' => '치수 기반 수량 계산 부품과 수식', 'type' => 'formula_input', 'ai_hint' => '예: 슬랫수량 = CEIL(H1 / 슬랫피치)'],
['text' => '면적 기반 수량 계산 부품과 수식', 'type' => 'formula_input', 'ai_hint' => '예: 스크린수량 = area / 기준면적'],
['text' => '중량 기반 수량 계산 부품과 수식', 'type' => 'formula_input'],
['text' => '올림/내림/반올림 규칙이 있는 계산은?', 'type' => 'table_input', 'options' => ['columns' => ['계산항목', '올림/내림/반올림', '소수점자릿수']]],
['text' => '여유 수량(LOSS) 적용 품목과 비율은?', 'type' => 'table_input', 'options' => ['columns' => ['품목명', 'LOSS율(%)', '비고']]],
],
],
[
'name' => '수식 검증',
'questions' => [
['text' => '실제 견적서에서 수량 계산 예시를 보여주세요 (W=3000, H=2500일 때)', 'type' => 'table_input', 'options' => ['columns' => ['부품명', '수식', '계산결과', '단위']], 'is_required' => true],
['text' => '수식에 사용하는 함수가 있나요? (SUM, CEIL, ROUND 등)', 'type' => 'multi_select', 'options' => ['choices' => ['CEIL (올림)', 'FLOOR (내림)', 'ROUND (반올림)', 'MAX', 'MIN', 'IF 조건문', 'SUM']]],
['text' => '조건에 따라 수식이 달라지는 경우가 있나요?', 'type' => 'text', 'ai_hint' => '예: 폭이 3000 초과이면 분할 계산'],
],
],
],
],
// ================================================================
// Domain 7: 조건부 로직
// ================================================================
'conditional_logic' => [
'name' => '조건부 로직',
'description' => '범위/매핑 기반 부품 자동 선택 규칙',
'templates' => [
[
'name' => '범위 기반 선택',
'questions' => [
['text' => '무게 범위별 모터 용량 선택표를 작성해주세요', 'type' => 'price_table', 'options' => ['columns' => ['범위 시작(kg)', '범위 끝(kg)', '모터용량', '비고']], 'is_required' => true],
['text' => '크기 범위별 부품 자동 선택 규칙이 있나요?', 'type' => 'table_input', 'options' => ['columns' => ['조건(변수)', '범위', '선택부품', '비고']]],
['text' => '브라켓 크기 결정 기준은?', 'type' => 'table_input', 'options' => ['columns' => ['조건', '범위', '브라켓 규격']]],
],
],
[
'name' => '매핑 기반 선택',
'questions' => [
['text' => '제품 모델 → 기본 부품 세트 매핑표', 'type' => 'table_input', 'options' => ['columns' => ['제품모델', '기본부품1', '기본부품2', '기본부품3']]],
['text' => '설치 유형 → 추가 부품 매핑표', 'type' => 'table_input', 'options' => ['columns' => ['설치유형', '추가부품', '수량', '비고']]],
['text' => '제어기 유형 → 부속품 매핑표', 'type' => 'table_input', 'options' => ['columns' => ['제어기유형', '부속품1', '부속품2', '부속품3']]],
['text' => '기타 조건부 자동 선택 규칙', 'type' => 'text', 'ai_hint' => '위 항목에 해당하지 않는 조건-결과 매핑'],
],
],
],
],
// ================================================================
// Domain 8: 견적서 양식
// ================================================================
'quote_format' => [
'name' => '견적서 양식',
'description' => '출력 양식, 항목 그룹, 소계/합계 구조',
'templates' => [
[
'name' => '출력 양식',
'questions' => [
['text' => '현재 사용 중인 견적서 양식을 업로드해주세요', 'type' => 'file_upload', 'is_required' => true],
['text' => '견적서에 표시되는 항목 그룹은? (재료비, 노무비, 설치비 등)', 'type' => 'multi_select', 'options' => ['choices' => ['재료비', '노무비', '경비', '설치비', '운반비', '이윤', '부가세']]],
['text' => '소계/합계 계산 구조를 설명해주세요', 'type' => 'text', 'ai_hint' => '항목 그룹별 소계와 최종 합계의 관계'],
['text' => '할인 적용 방식은? (일괄? 항목별?)', 'type' => 'select', 'options' => ['choices' => ['일괄 할인', '항목별 할인', '할인 없음', '협의 할인']]],
['text' => '부가세 표시 방식은? (별도? 포함?)', 'type' => 'select', 'options' => ['choices' => ['별도 표시', '포함 표시', '선택 가능']]],
['text' => '견적서에 표시하지 않는 내부 관리 항목은?', 'type' => 'text'],
['text' => '견적 번호 체계를 알려주세요', 'type' => 'text', 'ai_hint' => '예: Q-2026-001 형식'],
],
],
[
'name' => '특수 요구사항',
'questions' => [
['text' => '산출내역서(세부 내역)를 별도로 제공하나요?', 'type' => 'checkbox'],
['text' => '위치별(층/부호) 개별 산출이 필요한가요?', 'type' => 'checkbox'],
['text' => '일괄 산출(여러 위치 합산)을 사용하나요?', 'type' => 'checkbox'],
],
],
],
],
];
}
}