/** * 마이그레이션: 출입 신청 및 안전교육 시스템 * - 방문 목적 타입 테이블 * - 출입 신청 테이블 * - 안전교육 기록 테이블 */ exports.up = async function(knex) { // 1. 방문 목적 타입 테이블 생성 await knex.schema.createTable('visit_purpose_types', function(table) { table.increments('purpose_id').primary().comment('방문 목적 ID'); table.string('purpose_name', 100).notNullable().comment('방문 목적명'); table.integer('display_order').defaultTo(0).comment('표시 순서'); table.boolean('is_active').defaultTo(true).comment('활성 여부'); table.timestamp('created_at').defaultTo(knex.fn.now()); }); // 초기 데이터 삽입 await knex('visit_purpose_types').insert([ { purpose_name: '외주작업', display_order: 1, is_active: true }, { purpose_name: '검사', display_order: 2, is_active: true }, { purpose_name: '견학', display_order: 3, is_active: true }, { purpose_name: '기타', display_order: 99, is_active: true } ]); // 2. 출입 신청 테이블 생성 await knex.schema.createTable('workplace_visit_requests', function(table) { table.increments('request_id').primary().comment('신청 ID'); // 신청자 정보 table.integer('requester_id').notNullable().comment('신청자 user_id'); // 방문자 정보 table.string('visitor_company', 200).notNullable().comment('방문자 소속 (회사명 또는 "일용직")'); table.integer('visitor_count').defaultTo(1).comment('방문 인원'); // 방문 장소 table.integer('category_id').unsigned().notNullable().comment('방문 구역 (공장)'); table.integer('workplace_id').unsigned().notNullable().comment('방문 작업장'); // 방문 일시 table.date('visit_date').notNullable().comment('방문 날짜'); table.time('visit_time').notNullable().comment('방문 시간'); // 방문 목적 table.integer('purpose_id').unsigned().notNullable().comment('방문 목적 ID'); table.text('notes').nullable().comment('비고'); // 상태 관리 table.enum('status', ['pending', 'approved', 'rejected', 'training_completed']) .defaultTo('pending') .comment('신청 상태'); // 승인 정보 table.integer('approved_by').nullable().comment('승인자 user_id'); table.timestamp('approved_at').nullable().comment('승인 시간'); table.text('rejection_reason').nullable().comment('반려 사유'); // 타임스탬프 table.timestamp('created_at').defaultTo(knex.fn.now()); table.timestamp('updated_at').defaultTo(knex.fn.now()); // 외래키 table.foreign('requester_id') .references('user_id') .inTable('users') .onDelete('RESTRICT') .onUpdate('CASCADE'); table.foreign('category_id') .references('category_id') .inTable('workplace_categories') .onDelete('RESTRICT') .onUpdate('CASCADE'); table.foreign('workplace_id') .references('workplace_id') .inTable('workplaces') .onDelete('RESTRICT') .onUpdate('CASCADE'); table.foreign('purpose_id') .references('purpose_id') .inTable('visit_purpose_types') .onDelete('RESTRICT') .onUpdate('CASCADE'); table.foreign('approved_by') .references('user_id') .inTable('users') .onDelete('SET NULL') .onUpdate('CASCADE'); // 인덱스 table.index('visit_date', 'idx_visit_date'); table.index('status', 'idx_status'); table.index(['visit_date', 'status'], 'idx_visit_date_status'); }); // 3. 안전교육 기록 테이블 생성 await knex.schema.createTable('safety_training_records', function(table) { table.increments('training_id').primary().comment('교육 기록 ID'); table.integer('request_id').unsigned().notNullable().comment('출입 신청 ID'); // 교육 진행 정보 table.integer('trainer_id').notNullable().comment('교육 진행자 user_id'); table.date('training_date').notNullable().comment('교육 날짜'); table.time('training_start_time').notNullable().comment('교육 시작 시간'); table.time('training_end_time').nullable().comment('교육 종료 시간'); // 교육 내용 table.text('training_topics').nullable().comment('교육 내용 (JSON 배열)'); // 서명 데이터 (Base64 이미지) table.text('signature_data', 'longtext').nullable().comment('교육 이수자 서명 (Base64 PNG)'); // 완료 정보 table.timestamp('completed_at').nullable().comment('교육 완료 시간'); // 타임스탬프 table.timestamp('created_at').defaultTo(knex.fn.now()); table.timestamp('updated_at').defaultTo(knex.fn.now()); // 외래키 table.foreign('request_id') .references('request_id') .inTable('workplace_visit_requests') .onDelete('CASCADE') .onUpdate('CASCADE'); table.foreign('trainer_id') .references('user_id') .inTable('users') .onDelete('RESTRICT') .onUpdate('CASCADE'); // 인덱스 table.index('training_date', 'idx_training_date'); table.index('request_id', 'idx_request_id'); }); console.log('✅ 출입 신청 및 안전교육 시스템 테이블 생성 완료'); }; exports.down = async function(knex) { // 역순으로 테이블 삭제 await knex.schema.dropTableIfExists('safety_training_records'); await knex.schema.dropTableIfExists('workplace_visit_requests'); await knex.schema.dropTableIfExists('visit_purpose_types'); console.log('✅ 출입 신청 및 안전교육 시스템 테이블 삭제 완료'); };