id()->comment('ID'); $table->foreignId('tenant_id')->constrained()->cascadeOnDelete()->comment('테넌트 ID'); $table->foreignId('template_id')->constrained('document_templates')->comment('템플릿 ID'); // 문서 정보 $table->string('document_no', 50)->comment('문서번호'); $table->string('title', 255)->comment('문서 제목'); $table->enum('status', ['DRAFT', 'PENDING', 'APPROVED', 'REJECTED', 'CANCELLED']) ->default('DRAFT')->comment('상태 (DRAFT:임시저장, PENDING:결재중, APPROVED:승인, REJECTED:반려, CANCELLED:취소)'); // 연결 정보 (다형성) $table->string('linkable_type', 100)->nullable()->comment('연결 모델 타입'); $table->unsignedBigInteger('linkable_id')->nullable()->comment('연결 모델 ID'); // 결재 정보 $table->timestamp('submitted_at')->nullable()->comment('결재 요청일'); $table->timestamp('completed_at')->nullable()->comment('결재 완료일'); // 감사 컬럼 $table->foreignId('created_by')->nullable()->constrained('users')->comment('생성자 ID'); $table->foreignId('updated_by')->nullable()->constrained('users')->comment('수정자 ID'); $table->foreignId('deleted_by')->nullable()->constrained('users')->comment('삭제자 ID'); $table->timestamps(); $table->softDeletes(); // 인덱스 $table->index(['tenant_id', 'status']); $table->index('document_no'); $table->index(['linkable_type', 'linkable_id']); }); // 문서 결재 Schema::create('document_approvals', function (Blueprint $table) { $table->id()->comment('ID'); $table->foreignId('document_id')->constrained()->cascadeOnDelete()->comment('문서 ID'); $table->foreignId('user_id')->constrained()->comment('결재자 ID'); $table->unsignedTinyInteger('step')->default(1)->comment('결재 순서'); $table->string('role', 50)->comment('역할 (작성/검토/승인)'); $table->enum('status', ['PENDING', 'APPROVED', 'REJECTED']) ->default('PENDING')->comment('상태 (PENDING:대기, APPROVED:승인, REJECTED:반려)'); $table->text('comment')->nullable()->comment('결재 의견'); $table->timestamp('acted_at')->nullable()->comment('결재 처리일'); // 감사 컬럼 $table->foreignId('created_by')->nullable()->constrained('users')->comment('생성자 ID'); $table->foreignId('updated_by')->nullable()->constrained('users')->comment('수정자 ID'); $table->timestamps(); // 인덱스 $table->index(['document_id', 'step']); $table->index(['user_id', 'status']); }); // 문서 데이터 (EAV 패턴) Schema::create('document_data', function (Blueprint $table) { $table->id()->comment('ID'); $table->foreignId('document_id')->constrained()->cascadeOnDelete()->comment('문서 ID'); $table->unsignedBigInteger('section_id')->nullable()->comment('섹션 ID (document_template_sections 참조)'); $table->unsignedBigInteger('column_id')->nullable()->comment('컬럼 ID (document_template_columns 참조)'); $table->unsignedSmallInteger('row_index')->default(0)->comment('행 인덱스 (테이블 데이터용)'); $table->string('field_key', 100)->comment('필드 키'); $table->text('field_value')->nullable()->comment('필드 값'); $table->timestamps(); // 인덱스 $table->index(['document_id', 'section_id']); $table->index(['document_id', 'field_key']); }); // 문서 첨부파일 Schema::create('document_attachments', function (Blueprint $table) { $table->id()->comment('ID'); $table->foreignId('document_id')->constrained()->cascadeOnDelete()->comment('문서 ID'); $table->foreignId('file_id')->constrained('files')->comment('파일 ID'); $table->string('attachment_type', 50)->default('general')->comment('첨부 유형 (general, signature, image 등)'); $table->string('description', 255)->nullable()->comment('설명'); // 감사 컬럼 $table->foreignId('created_by')->nullable()->constrained('users')->comment('생성자 ID'); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('document_attachments'); Schema::dropIfExists('document_data'); Schema::dropIfExists('document_approvals'); Schema::dropIfExists('documents'); } };