完成競賽編輯功能
This commit is contained in:
@@ -321,6 +321,9 @@ class DatabaseSyncFixed {
|
||||
|
||||
const connection = await this.slavePool.getConnection();
|
||||
try {
|
||||
// 先刪除現有的關聯數據
|
||||
await connection.execute(`DELETE FROM ${relationTable} WHERE competition_id = ?`, [competitionId]);
|
||||
|
||||
for (const data of relationData) {
|
||||
const [uuidResult] = await connection.execute('SELECT UUID() as id');
|
||||
const relationId = uuidResult[0].id;
|
||||
|
@@ -161,6 +161,7 @@ export class DatabaseSyncFixed {
|
||||
async smartDualInsertRelation(
|
||||
relationTable: string,
|
||||
competitionId: string,
|
||||
slaveCompetitionId: string,
|
||||
relationData: any[],
|
||||
relationIdField: string
|
||||
): Promise<WriteResult> {
|
||||
@@ -171,14 +172,27 @@ export class DatabaseSyncFixed {
|
||||
};
|
||||
|
||||
try {
|
||||
// 先獲取主機和備機的競賽 ID 對應關係
|
||||
console.log(`🔍 smartDualInsertRelation 開始執行`);
|
||||
console.log(` 表名: ${relationTable}`);
|
||||
console.log(` 主機競賽 ID: ${competitionId}`);
|
||||
console.log(` 備機競賽 ID: ${slaveCompetitionId}`);
|
||||
console.log(` 關聯數據:`, relationData);
|
||||
console.log(` 關聯字段: ${relationIdField}`);
|
||||
|
||||
// 先獲取主機的競賽 ID
|
||||
const masterCompetitionId = await this.getMasterCompetitionId(competitionId);
|
||||
const slaveCompetitionId = await this.getSlaveCompetitionId(competitionId);
|
||||
|
||||
if (!masterCompetitionId || !slaveCompetitionId) {
|
||||
throw new Error('找不到對應的競賽 ID');
|
||||
}
|
||||
|
||||
console.log(`🔍 關聯雙寫開始: ${relationTable}`);
|
||||
console.log(` 主機競賽 ID: ${masterCompetitionId}`);
|
||||
console.log(` 備機競賽 ID: ${slaveCompetitionId}`);
|
||||
console.log(` 關聯數據數量: ${relationData.length}`);
|
||||
console.log(` 主機競賽存在: ${!!masterCompetitionId}`);
|
||||
console.log(` 備機競賽存在: ${!!slaveCompetitionId}`);
|
||||
|
||||
// 同時寫入關聯數據
|
||||
const masterPromise = this.insertRelationsToMaster(relationTable, masterCompetitionId, relationData, relationIdField);
|
||||
const slavePromise = this.insertRelationsToSlave(relationTable, slaveCompetitionId, relationData, relationIdField);
|
||||
@@ -191,9 +205,11 @@ export class DatabaseSyncFixed {
|
||||
|
||||
if (masterResult.status === 'rejected') {
|
||||
result.masterError = masterResult.reason instanceof Error ? masterResult.reason.message : '主機關聯寫入失敗';
|
||||
console.error(`❌ 主機關聯寫入失敗:`, masterResult.reason);
|
||||
}
|
||||
if (slaveResult.status === 'rejected') {
|
||||
result.slaveError = slaveResult.reason instanceof Error ? slaveResult.reason.message : '備機關聯寫入失敗';
|
||||
console.error(`❌ 備機關聯寫入失敗:`, slaveResult.reason);
|
||||
}
|
||||
|
||||
console.log(`📝 關聯雙寫結果: 主機${result.masterSuccess ? '✅' : '❌'} 備機${result.slaveSuccess ? '✅' : '❌'}`);
|
||||
@@ -231,6 +247,44 @@ export class DatabaseSyncFixed {
|
||||
}
|
||||
}
|
||||
|
||||
// 獲取競賽信息
|
||||
private async getCompetitionById(competitionId: string): Promise<any> {
|
||||
if (!this.masterPool) return null;
|
||||
|
||||
const connection = await this.masterPool.getConnection();
|
||||
try {
|
||||
const [rows] = await connection.execute('SELECT * FROM competitions WHERE id = ?', [competitionId]);
|
||||
return (rows as any[])[0] || null;
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
}
|
||||
|
||||
// 根據名稱獲取備機競賽 ID
|
||||
private async getSlaveCompetitionIdByName(name: string): Promise<string | null> {
|
||||
if (!this.slavePool) return null;
|
||||
|
||||
console.log(`🔍 getSlaveCompetitionIdByName 調用,名稱:`, name);
|
||||
|
||||
const connection = await this.slavePool.getConnection();
|
||||
try {
|
||||
const [rows] = await connection.execute('SELECT id FROM competitions WHERE name = ? ORDER BY created_at DESC LIMIT 1', [name]);
|
||||
console.log(`🔍 備機查詢結果:`, rows);
|
||||
const result = (rows as any[])[0]?.id || null;
|
||||
console.log(`🔍 備機競賽 ID 結果:`, result, typeof result);
|
||||
|
||||
// 確保返回的是字符串
|
||||
if (result && typeof result !== 'string') {
|
||||
console.log(`⚠️ 備機競賽 ID 不是字符串,轉換為字符串:`, String(result));
|
||||
return String(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
}
|
||||
|
||||
// 寫入主機關聯表
|
||||
private async insertRelationsToMaster(
|
||||
relationTable: string,
|
||||
@@ -263,15 +317,34 @@ export class DatabaseSyncFixed {
|
||||
): Promise<void> {
|
||||
if (!this.slavePool) return;
|
||||
|
||||
console.log(`🔍 備機關聯寫入開始: ${relationTable}`);
|
||||
console.log(` 備機競賽 ID: ${competitionId}`);
|
||||
console.log(` 關聯數據:`, relationData);
|
||||
console.log(` 關聯字段: ${relationIdField}`);
|
||||
|
||||
const connection = await this.slavePool.getConnection();
|
||||
try {
|
||||
for (const data of relationData) {
|
||||
const [uuidResult] = await connection.execute('SELECT UUID() as id');
|
||||
const relationId = (uuidResult as any)[0].id;
|
||||
|
||||
console.log(`🔍 準備插入關聯數據:`, {
|
||||
relationId,
|
||||
competitionId,
|
||||
relationField: relationIdField,
|
||||
relationValue: data[relationIdField]
|
||||
});
|
||||
|
||||
const sql = `INSERT INTO ${relationTable} (id, competition_id, ${relationIdField}) VALUES (?, ?, ?)`;
|
||||
console.log(`🔍 執行 SQL:`, sql);
|
||||
console.log(`🔍 參數:`, [relationId, competitionId, data[relationIdField]]);
|
||||
|
||||
await connection.execute(sql, [relationId, competitionId, data[relationIdField]]);
|
||||
console.log(`✅ 備機關聯數據插入成功`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ 備機關聯寫入失敗:`, error);
|
||||
throw error;
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
|
@@ -1250,7 +1250,14 @@ export class CompetitionService {
|
||||
const connection = await slavePool.getConnection();
|
||||
try {
|
||||
const [rows] = await connection.execute('SELECT id FROM competitions WHERE name = ? ORDER BY created_at DESC LIMIT 1', [name]);
|
||||
return (rows as any[])[0]?.id || null;
|
||||
const result = (rows as any[])[0]?.id || null;
|
||||
|
||||
// 確保返回的是字符串
|
||||
if (result && typeof result !== 'string') {
|
||||
return String(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
@@ -1361,6 +1368,19 @@ export class CompetitionService {
|
||||
return await db.query(sql, [competitionId]);
|
||||
}
|
||||
|
||||
// 獲取競賽的應用列表
|
||||
static async getCompetitionApps(competitionId: string): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT a.*, ca.submitted_at, u.name as creator_name, u.department as creator_department
|
||||
FROM competition_apps ca
|
||||
JOIN apps a ON ca.app_id = a.id
|
||||
LEFT JOIN users u ON a.creator_id = u.id
|
||||
WHERE ca.competition_id = ? AND a.is_active = TRUE
|
||||
ORDER BY ca.submitted_at ASC
|
||||
`;
|
||||
return await db.query(sql, [competitionId]);
|
||||
}
|
||||
|
||||
// 為競賽添加團隊
|
||||
static async addCompetitionTeams(competitionId: string, teamIds: string[]): Promise<boolean> {
|
||||
try {
|
||||
@@ -1409,6 +1429,54 @@ export class CompetitionService {
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 為競賽添加應用
|
||||
static async addCompetitionApps(competitionId: string, appIds: string[]): Promise<boolean> {
|
||||
try {
|
||||
const dbSyncFixed = new DatabaseSyncFixed();
|
||||
|
||||
// 先刪除現有的應用關聯
|
||||
await db.delete('DELETE FROM competition_apps WHERE competition_id = ?', [competitionId]);
|
||||
|
||||
// 添加新的應用關聯
|
||||
if (appIds.length > 0) {
|
||||
// 獲取備機的競賽 ID - 通過名稱查找
|
||||
const competition = await this.getCompetitionById(competitionId);
|
||||
const slaveCompetitionId = await this.getSlaveCompetitionIdByName(competition?.name || '');
|
||||
|
||||
if (!slaveCompetitionId) {
|
||||
console.error('找不到備機競賽 ID');
|
||||
return false;
|
||||
}
|
||||
|
||||
const relationData = appIds.map(appId => ({ app_id: appId }));
|
||||
const result = await dbSyncFixed.smartDualInsertRelation(
|
||||
'competition_apps',
|
||||
competitionId,
|
||||
slaveCompetitionId,
|
||||
relationData,
|
||||
'app_id'
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('添加競賽應用失敗:', result.masterError || result.slaveError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('添加競賽應用失敗:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 從競賽中移除應用
|
||||
static async removeCompetitionApp(competitionId: string, appId: string): Promise<boolean> {
|
||||
const sql = 'DELETE FROM competition_apps WHERE competition_id = ? AND app_id = ?';
|
||||
const result = await db.delete(sql, [competitionId, appId]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 獲取競賽的獎項類型列表
|
||||
static async getCompetitionAwardTypes(competitionId: string): Promise<any[]> {
|
||||
const sql = `
|
||||
@@ -1545,17 +1613,27 @@ export class CompetitionService {
|
||||
const competition = await this.getCompetitionById(competitionId);
|
||||
if (!competition) return null;
|
||||
|
||||
const [judges, teams, awardTypes, rules] = await Promise.all([
|
||||
const [judges, teams, apps, awardTypes, rules] = await Promise.all([
|
||||
this.getCompetitionJudges(competitionId),
|
||||
this.getCompetitionTeams(competitionId),
|
||||
this.getCompetitionApps(competitionId),
|
||||
this.getCompetitionAwardTypes(competitionId),
|
||||
this.getCompetitionRules(competitionId)
|
||||
]);
|
||||
|
||||
// 轉換字段名稱以匹配前端期望的格式
|
||||
return {
|
||||
...competition,
|
||||
startDate: competition.start_date,
|
||||
endDate: competition.end_date,
|
||||
evaluationFocus: competition.evaluation_focus,
|
||||
maxTeamSize: competition.max_team_size,
|
||||
isActive: competition.is_active,
|
||||
createdAt: competition.created_at,
|
||||
updatedAt: competition.updated_at,
|
||||
judges,
|
||||
teams,
|
||||
apps,
|
||||
awardTypes,
|
||||
rules
|
||||
};
|
||||
|
Reference in New Issue
Block a user