feat:인터뷰 시나리오 마이그레이션/모델 추가

- interview_categories, interview_templates, interview_questions 테이블 생성
- interview_sessions, interview_answers 테이블 생성
- InterviewCategory, InterviewTemplate, InterviewQuestion 모델 추가
- InterviewSession, InterviewAnswer 모델 추가
- 멀티테넌트(tenant_id) 지원, 감사 로깅(Auditable) 적용

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-06 21:01:24 +09:00
parent bdf6bcc480
commit 3a62a2a6e6
10 changed files with 408 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
<?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_categories', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
$table->string('name', 100)->comment('카테고리명 (예: 제조-방화셔터)');
$table->text('description')->nullable()->comment('설명');
$table->unsignedInteger('sort_order')->default(0)->comment('정렬순서');
$table->boolean('is_active')->default(true)->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_categories_tenant');
$table->index('sort_order', 'idx_interview_categories_sort');
});
}
public function down(): void
{
Schema::dropIfExists('interview_categories');
}
};

View File

@@ -0,0 +1,35 @@
<?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_templates', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
$table->unsignedBigInteger('interview_category_id')->comment('카테고리 ID');
$table->string('name', 200)->comment('항목명 (예: 견적서 제작)');
$table->text('description')->nullable()->comment('설명');
$table->unsignedInteger('sort_order')->default(0)->comment('정렬순서');
$table->boolean('is_active')->default(true)->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_templates_tenant');
$table->index('interview_category_id', 'idx_interview_templates_category');
$table->index('sort_order', 'idx_interview_templates_sort');
});
}
public function down(): void
{
Schema::dropIfExists('interview_templates');
}
};

View File

@@ -0,0 +1,37 @@
<?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_questions', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
$table->unsignedBigInteger('interview_template_id')->comment('템플릿(항목) ID');
$table->string('question_text', 500)->comment('질문 내용');
$table->string('question_type', 20)->default('checkbox')->comment('질문유형: checkbox/text');
$table->json('options')->nullable()->comment('확장용 옵션');
$table->boolean('is_required')->default(false)->comment('필수여부');
$table->unsignedInteger('sort_order')->default(0)->comment('정렬순서');
$table->boolean('is_active')->default(true)->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_questions_tenant');
$table->index('interview_template_id', 'idx_interview_questions_template');
$table->index('sort_order', 'idx_interview_questions_sort');
});
}
public function down(): void
{
Schema::dropIfExists('interview_questions');
}
};

View File

@@ -0,0 +1,41 @@
<?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_sessions', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
$table->unsignedBigInteger('interview_category_id')->comment('사용한 카테고리 ID');
$table->unsignedBigInteger('interviewer_id')->comment('면담자(매니저) ID');
$table->string('interviewee_name', 100)->nullable()->comment('면담 상대 이름');
$table->string('interviewee_company', 200)->nullable()->comment('면담 상대 회사');
$table->date('interview_date')->comment('면담 일자');
$table->string('status', 20)->default('in_progress')->comment('상태: in_progress/completed');
$table->unsignedInteger('total_questions')->default(0)->comment('총 질문 수');
$table->unsignedInteger('answered_questions')->default(0)->comment('답변 완료 수');
$table->text('memo')->nullable()->comment('메모');
$table->timestamp('completed_at')->nullable()->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', 'interviewer_id'], 'idx_interview_sessions_tenant_interviewer');
$table->index('interview_category_id', 'idx_interview_sessions_category');
$table->index('interview_date', 'idx_interview_sessions_date');
$table->index('status', 'idx_interview_sessions_status');
});
}
public function down(): void
{
Schema::dropIfExists('interview_sessions');
}
};

View File

@@ -0,0 +1,32 @@
<?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_answers', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
$table->unsignedBigInteger('interview_session_id')->comment('세션 ID');
$table->unsignedBigInteger('interview_question_id')->comment('질문 ID');
$table->unsignedBigInteger('interview_template_id')->comment('템플릿 ID (비정규화)');
$table->boolean('is_checked')->default(false)->comment('체크 여부');
$table->text('answer_text')->nullable()->comment('텍스트 답변');
$table->text('memo')->nullable()->comment('개별 메모');
$table->timestamps();
$table->unique(['interview_session_id', 'interview_question_id'], 'uniq_session_question');
$table->index('tenant_id', 'idx_interview_answers_tenant');
$table->index('interview_template_id', 'idx_interview_answers_template');
});
}
public function down(): void
{
Schema::dropIfExists('interview_answers');
}
};