const { getDb } = require('./dbPool'); const fs = require('fs'); const path = require('path'); async function setupAttendanceDB() { try { console.log('๐Ÿš€ ๊ทผํƒœ ๊ด€๋ฆฌ DB ์„ค์ • ์‹œ์ž‘...'); const db = await getDb(); // 1. ๊ทผ๋กœ ์œ ํ˜• ํ…Œ์ด๋ธ” ์ƒ์„ฑ console.log('๐Ÿ“‹ ๊ทผ๋กœ ์œ ํ˜• ํ…Œ์ด๋ธ” ์ƒ์„ฑ ์ค‘...'); await db.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 db.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 db.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 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 db.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 db.execute(` INSERT IGNORE INTO work_attendance_types (type_code, type_name, description) VALUES ('REGULAR', '์ •์‹œ๊ทผ๋กœ', '8์‹œ๊ฐ„ ์ •๊ทœ ๊ทผ๋ฌด'), ('OVERTIME', '์—ฐ์žฅ๊ทผ๋กœ', '8์‹œ๊ฐ„ ์ดˆ๊ณผ ๊ทผ๋ฌด'), ('PARTIAL', '๋ถ€๋ถ„๊ทผ๋กœ', '8์‹œ๊ฐ„ ๋ฏธ๋งŒ ๊ทผ๋ฌด'), ('VACATION', 'ํœด๊ฐ€๊ทผ๋กœ', 'ํœด๊ฐ€์™€ ํ•จ๊ป˜ํ•˜๋Š” ๋ถ€๋ถ„ ๊ทผ๋ฌด') `); // ํœด๊ฐ€ ์œ ํ˜• ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ await db.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 db.execute(` INSERT IGNORE INTO work_types (name, description, is_active) VALUES ('ํœด๊ฐ€', '์—ฐ์ฐจ, ๋ฐ˜์ฐจ, ๋ณ‘๊ฐ€ ๋“ฑ ํœด๊ฐ€ ์ฒ˜๋ฆฌ์šฉ', TRUE) `); // 7. daily_work_reports ํ…Œ์ด๋ธ”์— ๊ทผํƒœ ๊ธฐ๋ก ์—ฐ๊ฒฐ ์ปฌ๋Ÿผ ์ถ”๊ฐ€ (์ด๋ฏธ ์žˆ์œผ๋ฉด ๋ฌด์‹œ) try { await db.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 db.execute(`CREATE INDEX idx_attendance_record ON daily_work_reports(attendance_record_id)`); console.log('โœ… attendance_record_id ์ธ๋ฑ์Šค ์ถ”๊ฐ€๋จ'); } catch (error) { console.log('โš ๏ธ ์ธ๋ฑ์Šค ์ถ”๊ฐ€ ์‹คํŒจ (์ด๋ฏธ ์กด์žฌํ•  ์ˆ˜ ์žˆ์Œ):', error.message); } try { await db.execute(`CREATE INDEX idx_daily_work_reports_worker_date ON daily_work_reports(worker_id, report_date)`); console.log('โœ… worker_date ์ธ๋ฑ์Šค ์ถ”๊ฐ€๋จ'); } 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); throw error; } } // ์ง์ ‘ ์‹คํ–‰ if (require.main === module) { setupAttendanceDB() .then(() => { console.log('โœ… ์„ค์ • ์™„๋ฃŒ'); process.exit(0); }) .catch((error) => { console.error('โŒ ์„ค์ • ์‹คํŒจ:', error); process.exit(1); }); } module.exports = { setupAttendanceDB };