// 근태 관리 테이블 생성 스크립트 const mysql = require('mysql2/promise'); async function createAttendanceTables() { let connection; try { // 로컬 MySQL 연결 (기본 설정) connection = await mysql.createConnection({ host: 'localhost', user: 'root', password: '', // 비밀번호가 있다면 여기에 입력 database: 'hyungi' }); console.log('✅ MySQL 연결 성공'); // 1. 근로 유형 테이블 생성 console.log('📋 근로 유형 테이블 생성 중...'); await connection.execute(` CREATE TABLE IF NOT EXISTS work_attendance_types ( id INT PRIMARY KEY AUTO_INCREMENT, type_code VARCHAR(20) NOT NULL UNIQUE COMMENT '근로 유형 코드', type_name VARCHAR(50) NOT NULL COMMENT '근로 유형명', description TEXT COMMENT '설명', is_active BOOLEAN DEFAULT TRUE COMMENT '활성 상태', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) COMMENT='근로 유형 관리 테이블' `); // 2. 휴가 유형 테이블 생성 console.log('🏖️ 휴가 유형 테이블 생성 중...'); await connection.execute(` CREATE TABLE IF NOT EXISTS vacation_types ( id INT PRIMARY KEY AUTO_INCREMENT, type_code VARCHAR(20) NOT NULL UNIQUE COMMENT '휴가 유형 코드', type_name VARCHAR(50) NOT NULL COMMENT '휴가 유형명', hours_deduction DECIMAL(4,2) NOT NULL COMMENT '차감 시간', description TEXT COMMENT '설명', is_active BOOLEAN DEFAULT TRUE COMMENT '활성 상태', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) COMMENT='휴가 유형 관리 테이블' `); // 3. 일일 근태 기록 테이블 생성 console.log('📊 일일 근태 기록 테이블 생성 중...'); await connection.execute(` CREATE TABLE IF NOT EXISTS daily_attendance_records ( id INT PRIMARY KEY AUTO_INCREMENT, record_date DATE NOT NULL COMMENT '기록 날짜', worker_id INT NOT NULL COMMENT '작업자 ID', total_work_hours DECIMAL(4,2) DEFAULT 0 COMMENT '총 작업 시간', attendance_type_id INT COMMENT '근로 유형 ID', vacation_type_id INT NULL COMMENT '휴가 유형 ID', is_vacation_processed BOOLEAN DEFAULT FALSE COMMENT '휴가 처리 여부', overtime_approved BOOLEAN DEFAULT FALSE COMMENT '초과근무 승인 여부', overtime_approved_by INT NULL COMMENT '초과근무 승인자 ID', overtime_approved_at TIMESTAMP NULL COMMENT '초과근무 승인 시간', status ENUM('incomplete', 'partial', 'complete', 'overtime', 'vacation', 'error') DEFAULT 'incomplete' COMMENT '상태', notes TEXT COMMENT '비고', created_by INT NOT NULL DEFAULT 1 COMMENT '생성자 ID', updated_by INT NULL COMMENT '수정자 ID', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY unique_worker_date (worker_id, record_date), INDEX idx_record_date (record_date), INDEX idx_worker_date (worker_id, record_date), INDEX idx_status (status) ) COMMENT='일일 근태 기록 테이블' `); // 4. 작업자 휴가 잔여 관리 테이블 생성 console.log('📅 휴가 잔여 관리 테이블 생성 중...'); await connection.execute(` CREATE TABLE IF NOT EXISTS worker_vacation_balance ( id INT PRIMARY KEY AUTO_INCREMENT, worker_id INT NOT NULL COMMENT '작업자 ID', year YEAR NOT NULL COMMENT '연도', total_annual_leave DECIMAL(4,2) DEFAULT 15.0 COMMENT '연간 총 연차 (일)', used_annual_leave DECIMAL(4,2) DEFAULT 0 COMMENT '사용한 연차 (일)', remaining_annual_leave DECIMAL(4,2) GENERATED ALWAYS AS (total_annual_leave - used_annual_leave) STORED COMMENT '잔여 연차 (일)', notes TEXT COMMENT '비고', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY unique_worker_year (worker_id, year), INDEX idx_worker_year (worker_id, year) ) COMMENT='작업자별 휴가 잔여 관리 테이블' `); // 5. 기본 데이터 삽입 console.log('📝 기본 데이터 삽입 중...'); // 근로 유형 기본 데이터 await connection.execute(` INSERT IGNORE INTO work_attendance_types (type_code, type_name, description) VALUES ('REGULAR', '정시근로', '8시간 정규 근무'), ('OVERTIME', '연장근로', '8시간 초과 근무'), ('PARTIAL', '부분근로', '8시간 미만 근무'), ('VACATION', '휴가근로', '휴가와 함께하는 부분 근무') `); // 휴가 유형 기본 데이터 await connection.execute(` INSERT IGNORE INTO vacation_types (type_code, type_name, hours_deduction, description) VALUES ('ANNUAL_FULL', '연차', 8.0, '하루 전체 연차'), ('ANNUAL_HALF', '반차', 4.0, '반일 연차'), ('ANNUAL_QUARTER', '반반차', 2.0, '1/4일 연차'), ('SICK_FULL', '병가', 8.0, '하루 전체 병가'), ('SICK_HALF', '반일병가', 4.0, '반일 병가'), ('PERSONAL_FULL', '개인사유', 8.0, '개인사유로 인한 휴가'), ('PERSONAL_HALF', '반일개인사유', 4.0, '반일 개인사유 휴가') `); // 6. 휴가 전용 작업 유형 추가 console.log('🏖️ 휴가 전용 작업 유형 추가 중...'); await connection.execute(` INSERT IGNORE INTO work_types (name, description, is_active) VALUES ('휴가', '연차, 반차, 병가 등 휴가 처리용', TRUE) `); // 7. daily_work_reports 테이블에 근태 기록 연결 컬럼 추가 (이미 있으면 무시) try { await connection.execute(` ALTER TABLE daily_work_reports ADD COLUMN attendance_record_id INT NULL COMMENT '근태 기록 ID' AFTER updated_by `); console.log('✅ daily_work_reports 테이블에 attendance_record_id 컬럼 추가됨'); } catch (error) { if (error.code !== 'ER_DUP_FIELDNAME') { console.log('⚠️ attendance_record_id 컬럼 추가 실패:', error.message); } else { console.log('✅ attendance_record_id 컬럼이 이미 존재함'); } } // 8. 인덱스 추가 try { await connection.execute(`CREATE INDEX idx_attendance_record ON daily_work_reports(attendance_record_id)`); console.log('✅ attendance_record_id 인덱스 추가됨'); } catch (error) { console.log('⚠️ 인덱스 추가 실패 (이미 존재할 수 있음):', error.message); } console.log('🎉 근태 관리 DB 설정 완료!'); console.log(''); console.log('📋 생성된 테이블:'); console.log(' - work_attendance_types (근로 유형)'); console.log(' - vacation_types (휴가 유형)'); console.log(' - daily_attendance_records (일일 근태 기록)'); console.log(' - worker_vacation_balance (휴가 잔여 관리)'); console.log(''); console.log('✅ 기본 데이터도 모두 삽입되었습니다.'); } catch (error) { console.error('❌ DB 설정 중 오류 발생:', error); // 다른 연결 정보로 시도 if (error.code === 'ECONNREFUSED' || error.code === 'ER_ACCESS_DENIED_ERROR') { console.log(''); console.log('💡 다른 DB 연결 정보를 시도해보세요:'); console.log(' - host: localhost 또는 127.0.0.1'); console.log(' - port: 3306 (기본값)'); console.log(' - user: root 또는 다른 사용자'); console.log(' - password: 설정된 비밀번호'); console.log(' - database: hyungi'); } throw error; } finally { if (connection) { await connection.end(); } } } // 직접 실행 if (require.main === module) { createAttendanceTables() .then(() => { console.log('✅ 설정 완료'); process.exit(0); }) .catch((error) => { console.error('❌ 설정 실패:', error); process.exit(1); }); } module.exports = { createAttendanceTables };