整合資料庫、完成登入註冊忘記密碼功能

This commit is contained in:
2025-09-09 12:00:22 +08:00
parent af88c0f037
commit 32b19e9a0f
85 changed files with 11672 additions and 2350 deletions

253
scripts/migrate-views.js Normal file
View File

@@ -0,0 +1,253 @@
#!/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 };