#!/usr/bin/env node // ===================================================== // 視圖遷移腳本 // ===================================================== const mysql = require('mysql2/promise'); // 資料庫配置 const dbConfig = { host: process.env.DB_HOST || 'mysql.theaken.com', port: parseInt(process.env.DB_PORT || '33306'), user: process.env.DB_USER || 'AI_Platform', password: process.env.DB_PASSWORD || 'Aa123456', database: process.env.DB_NAME || 'db_AI_Platform', charset: 'utf8mb4', timezone: '+08:00', acquireTimeout: 60000, timeout: 60000, reconnect: true, connectionLimit: 10, queueLimit: 0, }; async function migrateViews() { let connection; try { console.log('🚀 開始視圖遷移...'); // 創建連接 connection = await mysql.createConnection({ ...dbConfig, multipleStatements: true }); console.log('✅ 資料庫連接成功'); // 視圖 SQL const viewSQL = ` -- 用戶統計視圖 CREATE VIEW user_statistics AS SELECT u.id, u.name, u.email, u.department, u.role, u.join_date, u.total_likes, u.total_views, COUNT(DISTINCT f.app_id) as favorite_count, COUNT(DISTINCT l.app_id) as liked_apps_count, COUNT(DISTINCT v.app_id) as viewed_apps_count, AVG(r.rating) as average_rating_given, COUNT(DISTINCT t.id) as teams_joined, COUNT(DISTINCT CASE WHEN t.leader_id = u.id THEN t.id END) as teams_led FROM users u LEFT JOIN user_favorites f ON u.id = f.user_id LEFT JOIN user_likes l ON u.id = l.user_id LEFT JOIN user_views v ON u.id = v.user_id LEFT JOIN user_ratings r ON u.id = r.user_id LEFT JOIN team_members tm ON u.id = tm.user_id LEFT JOIN teams t ON tm.team_id = t.id WHERE u.is_active = TRUE GROUP BY u.id; -- 應用統計視圖 CREATE VIEW app_statistics AS SELECT a.id, a.name, a.description, a.category, a.type, a.likes_count, a.views_count, a.rating, u.name as creator_name, u.department as creator_department, t.name as team_name, COUNT(DISTINCT f.user_id) as favorite_users_count, COUNT(DISTINCT l.user_id) as liked_users_count, COUNT(DISTINCT v.user_id) as viewed_users_count, AVG(ajs.total_score) as average_judge_score, COUNT(DISTINCT ajs.judge_id) as judge_count FROM apps a LEFT JOIN users u ON a.creator_id = u.id LEFT JOIN teams t ON a.team_id = t.id LEFT JOIN user_favorites f ON a.id = f.app_id LEFT JOIN user_likes l ON a.id = l.app_id LEFT JOIN user_views v ON a.id = v.app_id LEFT JOIN app_judge_scores ajs ON a.id = ajs.app_id WHERE a.is_active = TRUE GROUP BY a.id; -- 競賽統計視圖 CREATE VIEW competition_statistics AS SELECT c.id, c.name, c.year, c.month, c.type, c.status, COUNT(DISTINCT cj.judge_id) as judge_count, COUNT(DISTINCT ca.app_id) as app_count, COUNT(DISTINCT ct.team_id) as team_count, COUNT(DISTINCT cp.proposal_id) as proposal_count, COUNT(DISTINCT aw.id) as award_count FROM competitions c LEFT JOIN competition_judges cj ON c.id = cj.competition_id LEFT JOIN competition_apps ca ON c.id = ca.competition_id LEFT JOIN competition_teams ct ON c.id = ct.competition_id LEFT JOIN competition_proposals cp ON c.id = cp.competition_id LEFT JOIN awards aw ON c.id = aw.competition_id WHERE c.is_active = TRUE GROUP BY c.id; `; console.log('⚡ 執行視圖 SQL...'); // 分別執行每個視圖 const views = [ { name: 'user_statistics', sql: `CREATE VIEW user_statistics AS SELECT u.id, u.name, u.email, u.department, u.role, u.join_date, u.total_likes, u.total_views, COUNT(DISTINCT f.app_id) as favorite_count, COUNT(DISTINCT l.app_id) as liked_apps_count, COUNT(DISTINCT v.app_id) as viewed_apps_count, AVG(r.rating) as average_rating_given, COUNT(DISTINCT t.id) as teams_joined, COUNT(DISTINCT CASE WHEN t.leader_id = u.id THEN t.id END) as teams_led FROM users u LEFT JOIN user_favorites f ON u.id = f.user_id LEFT JOIN user_likes l ON u.id = l.user_id LEFT JOIN user_views v ON u.id = v.user_id LEFT JOIN user_ratings r ON u.id = r.user_id LEFT JOIN team_members tm ON u.id = tm.user_id LEFT JOIN teams t ON tm.team_id = t.id WHERE u.is_active = TRUE GROUP BY u.id` }, { name: 'app_statistics', sql: `CREATE VIEW app_statistics AS SELECT a.id, a.name, a.description, a.category, a.type, a.likes_count, a.views_count, a.rating, u.name as creator_name, u.department as creator_department, t.name as team_name, COUNT(DISTINCT f.user_id) as favorite_users_count, COUNT(DISTINCT l.user_id) as liked_users_count, COUNT(DISTINCT v.user_id) as viewed_users_count, AVG(ajs.total_score) as average_judge_score, COUNT(DISTINCT ajs.judge_id) as judge_count FROM apps a LEFT JOIN users u ON a.creator_id = u.id LEFT JOIN teams t ON a.team_id = t.id LEFT JOIN user_favorites f ON a.id = f.app_id LEFT JOIN user_likes l ON a.id = l.app_id LEFT JOIN user_views v ON a.id = v.app_id LEFT JOIN app_judge_scores ajs ON a.id = ajs.app_id WHERE a.is_active = TRUE GROUP BY a.id` }, { name: 'competition_statistics', sql: `CREATE VIEW competition_statistics AS SELECT c.id, c.name, c.year, c.month, c.type, c.status, COUNT(DISTINCT cj.judge_id) as judge_count, COUNT(DISTINCT ca.app_id) as app_count, COUNT(DISTINCT ct.team_id) as team_count, COUNT(DISTINCT cp.proposal_id) as proposal_count, COUNT(DISTINCT aw.id) as award_count FROM competitions c LEFT JOIN competition_judges cj ON c.id = cj.competition_id LEFT JOIN competition_apps ca ON c.id = ca.competition_id LEFT JOIN competition_teams ct ON c.id = ct.competition_id LEFT JOIN competition_proposals cp ON c.id = cp.competition_id LEFT JOIN awards aw ON c.id = aw.competition_id WHERE c.is_active = TRUE GROUP BY c.id` } ]; for (const view of views) { try { await connection.execute(view.sql); console.log(`✅ 創建視圖: ${view.name}`); } catch (error) { if (error.code === 'ER_TABLE_EXISTS') { console.log(`⚠️ 視圖已存在: ${view.name}`); } else { throw error; } } } console.log('✅ 視圖創建成功!'); // 檢查視圖 console.log('🔍 檢查視圖...'); const [viewList] = await connection.execute('SHOW FULL TABLES WHERE Table_type = "VIEW"'); console.log(`📈 共創建了 ${viewList.length} 個視圖:`); viewList.forEach((view, index) => { const viewName = Object.values(view)[0]; console.log(` ${index + 1}. ${viewName}`); }); console.log('🎉 視圖遷移完成!'); } catch (error) { console.error('❌ 視圖遷移失敗:', error.message); console.error('詳細錯誤:', error); process.exit(1); } finally { if (connection) { await connection.end(); console.log('🔌 資料庫連接已關閉'); } } } // 執行遷移 if (require.main === module) { migrateViews().catch(console.error); } module.exports = { migrateViews };