完成品

This commit is contained in:
2025-09-19 02:58:43 +08:00
parent ffa1e45f63
commit b5e8cce2f3
10 changed files with 926 additions and 16 deletions

34
scripts/clear-database.js Normal file
View File

@@ -0,0 +1,34 @@
const mysql = require('mysql2/promise');
async function clearDatabase() {
const connection = await mysql.createConnection({
host: 'mysql.theaken.com',
port: 33306,
user: 'AI_Platform',
password: 'Aa123456',
database: 'db_AI_Platform'
});
console.log('🗑️ 清空資料庫...');
// 清空所有表(按依賴順序)
const tables = [
'app_judge_scores',
'competition_apps',
'competition_judges',
'apps',
'judges',
'competitions',
'users'
];
for (const table of tables) {
await connection.execute(`DELETE FROM ${table}`);
console.log(`✅ 清空了 ${table}`);
}
await connection.end();
console.log('🎉 資料庫清空完成!');
}
clearDatabase().catch(console.error);

141
scripts/migrate-fixed.js Normal file
View File

@@ -0,0 +1,141 @@
#!/usr/bin/env node
// =====================================================
// 資料庫遷移腳本 (修復版)
// =====================================================
const mysql = require('mysql2/promise');
const fs = require('fs');
const path = require('path');
// 資料庫配置
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',
};
async function runMigration() {
let connection;
try {
console.log('🚀 開始資料庫遷移...');
// 創建連接
connection = await mysql.createConnection({
...dbConfig,
multipleStatements: true
});
console.log('✅ 資料庫連接成功');
// 讀取 SQL 文件
const sqlFile = path.join(__dirname, '..', 'database-schema-simple.sql');
const sqlContent = fs.readFileSync(sqlFile, 'utf8');
console.log('📖 讀取 SQL 文件成功');
// 處理 SQL 內容,正確分割語句
console.log('⚡ 處理 SQL 語句...');
// 先處理觸發器,因為它們包含分號
const triggerRegex = /CREATE TRIGGER[\s\S]*?END;/g;
const triggers = sqlContent.match(triggerRegex) || [];
// 移除觸發器部分,處理其他語句
let remainingSql = sqlContent.replace(triggerRegex, '');
// 分割其他語句
const otherStatements = remainingSql
.split(';')
.map(stmt => stmt.trim())
.filter(stmt => stmt.length > 0 && !stmt.startsWith('--'));
// 合併所有語句
const allStatements = [...otherStatements, ...triggers];
console.log(`📊 共找到 ${allStatements.length} 個語句`);
// 執行語句
for (let i = 0; i < allStatements.length; i++) {
const statement = allStatements[i];
if (statement.trim()) {
try {
// 特殊處理 USE 語句和觸發器
if (statement.toUpperCase().startsWith('USE') ||
statement.toUpperCase().startsWith('CREATE TRIGGER')) {
await connection.query(statement + ';');
} else {
await connection.execute(statement + ';');
}
console.log(`✅ 執行語句 ${i + 1}/${allStatements.length}`);
} catch (error) {
console.error(`❌ 語句 ${i + 1} 執行失敗:`, error.message);
console.error(`語句內容: ${statement.substring(0, 100)}...`);
// 對於某些錯誤,我們可以繼續執行
if (error.message.includes('already exists') ||
error.message.includes('Duplicate entry') ||
error.message.includes('Table') && error.message.includes('already exists')) {
console.log('⚠️ 跳過已存在的項目');
continue;
}
throw error;
}
}
}
console.log('✅ 資料庫結構創建成功!');
// 驗證表是否創建成功
console.log('🔍 驗證表結構...');
const [tables] = await connection.execute('SHOW TABLES');
console.log(`📊 共創建了 ${tables.length} 個表:`);
tables.forEach((table, index) => {
const tableName = Object.values(table)[0];
console.log(` ${index + 1}. ${tableName}`);
});
// 檢查視圖
console.log('🔍 驗證視圖...');
const [views] = await connection.execute('SHOW FULL TABLES WHERE Table_type = "VIEW"');
console.log(`📈 共創建了 ${views.length} 個視圖:`);
views.forEach((view, index) => {
const viewName = Object.values(view)[0];
console.log(` ${index + 1}. ${viewName}`);
});
// 檢查觸發器
console.log('🔍 驗證觸發器...');
const [triggerList] = await connection.execute('SHOW TRIGGERS');
console.log(`⚙️ 共創建了 ${triggerList.length} 個觸發器:`);
triggerList.forEach((trigger, index) => {
console.log(` ${index + 1}. ${trigger.Trigger}`);
});
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) {
runMigration().catch(console.error);
}
module.exports = { runMigration };

View File

@@ -0,0 +1,124 @@
#!/usr/bin/env node
// =====================================================
// 資料庫遷移腳本 (無觸發器版)
// =====================================================
const mysql = require('mysql2/promise');
const fs = require('fs');
const path = require('path');
// 資料庫配置
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',
};
async function runMigration() {
let connection;
try {
console.log('🚀 開始資料庫遷移...');
// 創建連接
connection = await mysql.createConnection({
...dbConfig,
multipleStatements: true
});
console.log('✅ 資料庫連接成功');
// 讀取 SQL 文件
const sqlFile = path.join(__dirname, '..', 'database-schema-simple.sql');
const sqlContent = fs.readFileSync(sqlFile, 'utf8');
console.log('📖 讀取 SQL 文件成功');
// 移除觸發器部分
console.log('⚡ 處理 SQL 語句...');
const triggerRegex = /CREATE TRIGGER[\s\S]*?END;/g;
let sqlWithoutTriggers = sqlContent.replace(triggerRegex, '');
// 分割語句
const statements = sqlWithoutTriggers
.split(';')
.map(stmt => stmt.trim())
.filter(stmt => stmt.length > 0 && !stmt.startsWith('--'));
console.log(`📊 共找到 ${statements.length} 個語句`);
// 執行語句
for (let i = 0; i < statements.length; i++) {
const statement = statements[i];
if (statement.trim()) {
try {
// 特殊處理 USE 語句
if (statement.toUpperCase().startsWith('USE')) {
await connection.query(statement + ';');
} else {
await connection.execute(statement + ';');
}
console.log(`✅ 執行語句 ${i + 1}/${statements.length}`);
} catch (error) {
console.error(`❌ 語句 ${i + 1} 執行失敗:`, error.message);
console.error(`語句內容: ${statement.substring(0, 100)}...`);
// 對於某些錯誤,我們可以繼續執行
if (error.message.includes('already exists') ||
error.message.includes('Duplicate entry') ||
error.message.includes('Table') && error.message.includes('already exists')) {
console.log('⚠️ 跳過已存在的項目');
continue;
}
throw error;
}
}
}
console.log('✅ 資料庫結構創建成功!');
// 驗證表是否創建成功
console.log('🔍 驗證表結構...');
const [tables] = await connection.execute('SHOW TABLES');
console.log(`📊 共創建了 ${tables.length} 個表:`);
tables.forEach((table, index) => {
const tableName = Object.values(table)[0];
console.log(` ${index + 1}. ${tableName}`);
});
// 檢查視圖
console.log('🔍 驗證視圖...');
const [views] = await connection.execute('SHOW FULL TABLES WHERE Table_type = "VIEW"');
console.log(`📈 共創建了 ${views.length} 個視圖:`);
views.forEach((view, index) => {
const viewName = Object.values(view)[0];
console.log(` ${index + 1}. ${viewName}`);
});
console.log('🎉 資料庫遷移完成!');
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) {
runMigration().catch(console.error);
}
module.exports = { runMigration };

View File

@@ -0,0 +1,319 @@
#!/usr/bin/env node
// =====================================================
// 填充示例數據腳本
// =====================================================
const mysql = require('mysql2/promise');
const { v4: uuidv4 } = require('uuid');
// 資料庫配置
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',
};
async function populateSampleData() {
let connection;
try {
console.log('🚀 開始填充示例數據...');
// 創建連接
connection = await mysql.createConnection(dbConfig);
console.log('✅ 資料庫連接成功');
// 1. 創建示例用戶
console.log('👥 創建示例用戶...');
const users = [
{
id: uuidv4(),
name: '張小明',
email: 'zhang.xiaoming@company.com',
password_hash: '$2b$10$example.hash.here', // 示例哈希
department: 'HQBU',
role: 'developer',
join_date: '2024-01-15',
total_likes: 25,
total_views: 150,
is_active: true
},
{
id: uuidv4(),
name: '李美華',
email: 'li.meihua@company.com',
password_hash: '$2b$10$example.hash.here',
department: 'ITBU',
role: 'developer',
join_date: '2024-02-01',
total_likes: 18,
total_views: 120,
is_active: true
},
{
id: uuidv4(),
name: '王大偉',
email: 'wang.dawei@company.com',
password_hash: '$2b$10$example.hash.here',
department: 'MBU1',
role: 'developer',
join_date: '2024-01-20',
total_likes: 32,
total_views: 200,
is_active: true
},
{
id: uuidv4(),
name: '陳小芳',
email: 'chen.xiaofang@company.com',
password_hash: '$2b$10$example.hash.here',
department: 'SBU',
role: 'developer',
join_date: '2024-02-10',
total_likes: 15,
total_views: 90,
is_active: true
},
{
id: uuidv4(),
name: '劉志強',
email: 'liu.zhiqiang@company.com',
password_hash: '$2b$10$example.hash.here',
department: 'HQBU',
role: 'admin',
join_date: '2023-12-01',
total_likes: 5,
total_views: 50,
is_active: true
}
];
for (const user of users) {
await connection.execute(
`INSERT INTO users (id, name, email, password_hash, department, role, join_date, total_likes, total_views, status, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
[user.id, user.name, user.email, user.password_hash, user.department, user.role, user.join_date, user.total_likes, user.total_views, 'active']
);
}
console.log(`✅ 創建了 ${users.length} 個用戶`);
// 2. 創建示例評審
console.log('👨‍⚖️ 創建示例評審...');
const judges = [
{
id: uuidv4(),
name: '王教授',
title: '技術總監',
department: 'ITBU',
expertise: JSON.stringify(['AI', '機器學習', '深度學習']),
is_active: true
},
{
id: uuidv4(),
name: '李博士',
title: '產品經理',
department: 'HQBU',
expertise: JSON.stringify(['產品設計', '用戶體驗', '商業分析']),
is_active: true
},
{
id: uuidv4(),
name: '陳工程師',
title: '資深工程師',
department: 'MBU1',
expertise: JSON.stringify(['軟體開發', '系統架構', '資料庫']),
is_active: true
}
];
for (const judge of judges) {
await connection.execute(
`INSERT INTO judges (id, name, title, department, expertise, is_active, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW())`,
[judge.id, judge.name, judge.title, judge.department, judge.expertise, judge.is_active]
);
}
console.log(`✅ 創建了 ${judges.length} 個評審`);
// 3. 創建示例競賽
console.log('🏆 創建示例競賽...');
const competitions = [
{
id: uuidv4(),
name: '2024年AI創新競賽',
description: '展示最新的AI技術創新成果',
type: 'individual',
year: 2024,
month: 3,
start_date: '2024-03-01',
end_date: '2024-03-31',
status: 'active',
is_current: true,
is_active: true,
evaluation_focus: JSON.stringify(['創新性', '技術性', '實用性']),
max_team_size: 5
},
{
id: uuidv4(),
name: '2024年團隊協作競賽',
description: '團隊協作開發的AI應用',
type: 'team',
year: 2024,
month: 4,
start_date: '2024-04-01',
end_date: '2024-04-30',
status: 'upcoming',
is_current: false,
is_active: true,
evaluation_focus: JSON.stringify(['團隊協作', '技術實現', '創新應用']),
max_team_size: 8
}
];
for (const competition of competitions) {
await connection.execute(
`INSERT INTO competitions (id, name, description, type, year, month, start_date, end_date, status, is_current, is_active, evaluation_focus, max_team_size, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
[competition.id, competition.name, competition.description, competition.type, competition.year, competition.month,
competition.start_date, competition.end_date, competition.status, competition.is_current, competition.is_active,
competition.evaluation_focus, competition.max_team_size]
);
}
console.log(`✅ 創建了 ${competitions.length} 個競賽`);
// 4. 創建示例應用
console.log('📱 創建示例應用...');
const apps = [
{
id: uuidv4(),
name: '智能對話助手',
description: '基於大語言模型的智能對話系統',
creator_id: users[0].id,
team_id: null,
category: '文字處理',
technology_stack: JSON.stringify(['Python', 'OpenAI API', 'React']),
github_url: 'https://github.com/example/chatbot',
demo_url: 'https://demo.example.com/chatbot',
status: 'published',
is_active: true,
total_likes: 25,
total_views: 150
},
{
id: uuidv4(),
name: '圖像生成工具',
description: 'AI驅動的創意圖像生成平台',
creator_id: users[1].id,
team_id: null,
category: '圖像生成',
technology_stack: JSON.stringify(['Python', 'Stable Diffusion', 'FastAPI']),
github_url: 'https://github.com/example/image-gen',
demo_url: 'https://demo.example.com/image-gen',
status: 'published',
is_active: true,
total_likes: 18,
total_views: 120
},
{
id: uuidv4(),
name: '語音識別系統',
description: '高精度多語言語音識別服務',
creator_id: users[2].id,
team_id: null,
category: '語音辨識',
technology_stack: JSON.stringify(['Python', 'Whisper', 'Docker']),
github_url: 'https://github.com/example/speech-recognition',
demo_url: 'https://demo.example.com/speech',
status: 'published',
is_active: true,
total_likes: 32,
total_views: 200
}
];
for (const app of apps) {
await connection.execute(
`INSERT INTO apps (id, name, description, creator_id, team_id, category, type, app_url, likes_count, views_count, is_active, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`,
[app.id, app.name, app.description, app.creator_id, app.team_id, app.category, 'web', app.demo_url, app.total_likes, app.total_views, 1]
);
}
console.log(`✅ 創建了 ${apps.length} 個應用`);
// 5. 關聯競賽和應用
console.log('🔗 關聯競賽和應用...');
const currentCompetition = competitions[0]; // 當前競賽
for (const app of apps) {
await connection.execute(
`INSERT INTO competition_apps (id, competition_id, app_id, submitted_at) VALUES (?, ?, ?, NOW())`,
[uuidv4(), currentCompetition.id, app.id]
);
}
console.log(`✅ 關聯了 ${apps.length} 個應用到當前競賽`);
// 6. 關聯競賽和評審
console.log('🔗 關聯競賽和評審...');
for (const judge of judges) {
await connection.execute(
`INSERT INTO competition_judges (id, competition_id, judge_id, assigned_at) VALUES (?, ?, ?, NOW())`,
[uuidv4(), currentCompetition.id, judge.id]
);
}
console.log(`✅ 關聯了 ${judges.length} 個評審到當前競賽`);
// 7. 創建示例評分
console.log('📊 創建示例評分...');
for (const app of apps) {
for (const judge of judges) {
const scores = {
innovation_score: Math.floor(Math.random() * 5) + 1,
technical_score: Math.floor(Math.random() * 5) + 1,
usability_score: Math.floor(Math.random() * 5) + 1,
presentation_score: Math.floor(Math.random() * 5) + 1,
impact_score: Math.floor(Math.random() * 5) + 1
};
const totalScore = (scores.innovation_score + scores.technical_score + scores.usability_score +
scores.presentation_score + scores.impact_score) / 5;
await connection.execute(
`INSERT INTO app_judge_scores (id, judge_id, app_id, innovation_score, technical_score, usability_score, presentation_score, impact_score, total_score, comments, submitted_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
[uuidv4(), judge.id, app.id, scores.innovation_score, scores.technical_score, scores.usability_score,
scores.presentation_score, scores.impact_score, totalScore, '示例評分']
);
}
}
console.log(`✅ 創建了 ${apps.length * judges.length} 個評分記錄`);
console.log('🎉 示例數據填充完成!');
console.log('\n📊 數據摘要:');
console.log(`- 用戶: ${users.length}`);
console.log(`- 評審: ${judges.length}`);
console.log(`- 競賽: ${competitions.length} 個 (其中 1 個為當前競賽)`);
console.log(`- 應用: ${apps.length}`);
console.log(`- 評分記錄: ${apps.length * judges.length}`);
} catch (error) {
console.error('❌ 填充失敗:', error.message);
console.error('詳細錯誤:', error);
process.exit(1);
} finally {
if (connection) {
await connection.end();
console.log('🔌 資料庫連接已關閉');
}
}
}
// 執行填充
if (require.main === module) {
populateSampleData().catch(console.error);
}
module.exports = { populateSampleData };