feat: [interview] 인터뷰 시나리오 고도화 마이그레이션

- interview_projects 테이블 신규 (회사별 프로젝트)
- interview_attachments 테이블 신규 (첨부파일 + AI 분석)
- interview_knowledge 테이블 신규 (AI 추출 지식)
- interview_categories에 project_id, domain 컬럼 추가
- interview_questions에 ai_hint, expected_format, depends_on, domain 추가
- interview_answers에 answer_data, attachments JSON 추가
- interview_sessions에 project_id, session_type, voice_recording_id 추가
This commit is contained in:
김보곤
2026-02-28 20:02:33 +09:00
parent 93e94901b7
commit 6df1da9e42
5 changed files with 213 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('interview_projects', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
$table->string('company_name', 200)->comment('대상 회사명');
$table->string('company_type', 100)->nullable()->comment('업종 (방화셔터, 블라인드 등)');
$table->string('contact_person', 100)->nullable()->comment('담당자');
$table->string('contact_info', 200)->nullable()->comment('연락처');
$table->enum('status', ['draft', 'interviewing', 'analyzing', 'code_generated', 'deployed'])
->default('draft')->comment('상태');
$table->unsignedBigInteger('target_tenant_id')->nullable()->comment('생성될 테넌트 ID');
$table->json('product_categories')->nullable()->comment('제품 카테고리 목록');
$table->text('summary')->nullable()->comment('AI 생성 요약');
$table->unsignedTinyInteger('progress_percent')->default(0)->comment('전체 진행률');
$table->unsignedBigInteger('created_by')->nullable()->comment('등록자');
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자');
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자');
$table->timestamps();
$table->softDeletes();
$table->index('tenant_id', 'idx_interview_projects_tenant');
$table->index('status', 'idx_interview_projects_status');
});
}
public function down(): void
{
Schema::dropIfExists('interview_projects');
}
};

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('interview_attachments', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
$table->unsignedBigInteger('interview_project_id')->comment('인터뷰 프로젝트 ID');
$table->enum('file_type', ['excel_template', 'pdf_quote', 'sample_bom', 'price_list', 'photo', 'voice', 'other'])
->default('other')->comment('파일 유형');
$table->string('file_name', 255)->comment('파일명');
$table->string('file_path', 500)->comment('저장 경로');
$table->unsignedInteger('file_size')->default(0)->comment('파일 크기 (bytes)');
$table->string('mime_type', 100)->nullable()->comment('MIME 타입');
$table->json('ai_analysis')->nullable()->comment('AI 분석 결과');
$table->enum('ai_analysis_status', ['pending', 'processing', 'completed', 'failed'])
->default('pending')->comment('AI 분석 상태');
$table->text('description')->nullable()->comment('설명');
$table->unsignedBigInteger('created_by')->nullable()->comment('등록자');
$table->timestamps();
$table->index('tenant_id', 'idx_interview_attachments_tenant');
$table->index('interview_project_id', 'idx_interview_attachments_project');
$table->index('file_type', 'idx_interview_attachments_type');
});
}
public function down(): void
{
Schema::dropIfExists('interview_attachments');
}
};

View File

@@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('interview_knowledge', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
$table->unsignedBigInteger('interview_project_id')->comment('인터뷰 프로젝트 ID');
$table->enum('domain', [
'product_classification', 'bom_structure', 'dimension_formula',
'component_config', 'pricing_structure', 'quantity_formula',
'conditional_logic', 'quote_format',
])->comment('도메인 영역');
$table->enum('knowledge_type', ['fact', 'rule', 'formula', 'mapping', 'range', 'table'])
->comment('지식 유형');
$table->string('title', 300)->comment('제목');
$table->json('content')->comment('구조화된 지식 데이터');
$table->enum('source_type', ['interview_answer', 'voice_recording', 'document', 'manual'])
->comment('출처 유형');
$table->unsignedBigInteger('source_id')->nullable()->comment('출처 레코드 ID');
$table->decimal('confidence', 3, 2)->default(0.00)->comment('AI 신뢰도 (0.00~1.00)');
$table->boolean('is_verified')->default(false)->comment('사용자 검증 여부');
$table->unsignedBigInteger('verified_by')->nullable()->comment('검증자');
$table->timestamp('verified_at')->nullable()->comment('검증 일시');
$table->unsignedBigInteger('created_by')->nullable()->comment('등록자');
$table->timestamps();
$table->softDeletes();
$table->index('tenant_id', 'idx_interview_knowledge_tenant');
$table->index('interview_project_id', 'idx_interview_knowledge_project');
$table->index('domain', 'idx_interview_knowledge_domain');
$table->index('is_verified', 'idx_interview_knowledge_verified');
});
}
public function down(): void
{
Schema::dropIfExists('interview_knowledge');
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('interview_categories', function (Blueprint $table) {
$table->unsignedBigInteger('interview_project_id')->nullable()
->after('tenant_id')->comment('프로젝트 연결');
$table->string('domain', 50)->nullable()
->after('description')->comment('도메인 영역 태그');
$table->index('interview_project_id', 'idx_interview_categories_project');
});
}
public function down(): void
{
Schema::table('interview_categories', function (Blueprint $table) {
$table->dropIndex('idx_interview_categories_project');
$table->dropColumn(['interview_project_id', 'domain']);
});
}
};

View File

@@ -0,0 +1,62 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
// interview_questions 확장
Schema::table('interview_questions', function (Blueprint $table) {
$table->text('ai_hint')->nullable()->after('options')->comment('AI 분석 힌트/가이드');
$table->string('expected_format', 100)->nullable()->after('ai_hint')->comment('예상 답변 형식 (mm, 원/kg 등)');
$table->json('depends_on')->nullable()->after('expected_format')->comment('조건부 표시 조건');
$table->string('domain', 50)->nullable()->after('depends_on')->comment('도메인 영역');
});
// question_type 컬럼 확장 (varchar(20) → varchar(50))
Schema::table('interview_questions', function (Blueprint $table) {
$table->string('question_type', 50)->default('checkbox')
->comment('질문유형: checkbox|text|number|select|multi_select|file_upload|formula_input|table_input|bom_tree|price_table|dimension_diagram')
->change();
});
// interview_answers 확장
Schema::table('interview_answers', function (Blueprint $table) {
$table->json('answer_data')->nullable()->after('answer_text')->comment('구조화 답변 (테이블, 수식, BOM 등)');
$table->json('attachments')->nullable()->after('answer_data')->comment('첨부 파일 경로 목록');
});
// interview_sessions 확장
Schema::table('interview_sessions', function (Blueprint $table) {
$table->unsignedBigInteger('interview_project_id')->nullable()
->after('tenant_id')->comment('프로젝트 ID');
$table->string('session_type', 20)->default('checklist')
->after('status')->comment('세션 유형: checklist|structured|voice|review');
$table->unsignedBigInteger('voice_recording_id')->nullable()
->after('session_type')->comment('음성 녹음 ID');
$table->index('interview_project_id', 'idx_interview_sessions_project');
});
}
public function down(): void
{
Schema::table('interview_sessions', function (Blueprint $table) {
$table->dropIndex('idx_interview_sessions_project');
$table->dropColumn(['interview_project_id', 'session_type', 'voice_recording_id']);
});
Schema::table('interview_answers', function (Blueprint $table) {
$table->dropColumn(['answer_data', 'attachments']);
});
Schema::table('interview_questions', function (Blueprint $table) {
$table->string('question_type', 20)->default('checkbox')
->comment('질문유형: checkbox/text')->change();
$table->dropColumn(['ai_hint', 'expected_format', 'depends_on', 'domain']);
});
}
};