新增競賽前台呈現、刪除競賽、修改競賽狀態

This commit is contained in:
2025-09-16 14:57:40 +08:00
parent 1f2fb14bd0
commit b4386dc481
21 changed files with 1714 additions and 127 deletions

View File

@@ -323,7 +323,9 @@ class DatabaseFailover {
async query(sql, params) {
const connection = await this.getConnection();
try {
const [rows] = await connection.execute(sql, params);
// 將 undefined 值轉換為 null避免 MySQL 驅動錯誤
const sanitizedParams = params ? params.map(param => param === undefined ? null : param) : [];
const [rows] = await connection.execute(sql, sanitizedParams);
return rows;
}
finally {
@@ -345,7 +347,9 @@ class DatabaseFailover {
async insert(sql, params) {
const connection = await this.getConnection();
try {
const [result] = await connection.execute(sql, params);
// 將 undefined 值轉換為 null避免 MySQL 驅動錯誤
const sanitizedParams = params ? params.map(param => param === undefined ? null : param) : [];
const [result] = await connection.execute(sql, sanitizedParams);
return result;
}
finally {
@@ -356,7 +360,9 @@ class DatabaseFailover {
async update(sql, params) {
const connection = await this.getConnection();
try {
const [result] = await connection.execute(sql, params);
// 將 undefined 值轉換為 null避免 MySQL 驅動錯誤
const sanitizedParams = params ? params.map(param => param === undefined ? null : param) : [];
const [result] = await connection.execute(sql, sanitizedParams);
return result;
}
finally {
@@ -367,7 +373,9 @@ class DatabaseFailover {
async delete(sql, params) {
const connection = await this.getConnection();
try {
const [result] = await connection.execute(sql, params);
// 將 undefined 值轉換為 null避免 MySQL 驅動錯誤
const sanitizedParams = params ? params.map(param => param === undefined ? null : param) : [];
const [result] = await connection.execute(sql, sanitizedParams);
return result;
}
finally {

View File

@@ -137,6 +137,53 @@ class DatabaseSyncFixed {
}
}
// 智能雙寫更新
async smartDualUpdate(tableName, id, updates) {
const result = {
success: false,
masterSuccess: false,
slaveSuccess: false
};
try {
// 先獲取主機的記錄
const masterRecord = await this.getMasterRecord(tableName, id);
if (!masterRecord) {
throw new Error('主機記錄不存在');
}
// 獲取備機的記錄 ID通過名稱匹配
const slaveId = await this.getSlaveRecordIdByName(tableName, masterRecord.name);
if (!slaveId) {
throw new Error('備機記錄不存在');
}
// 同時更新主機和備機
const masterPromise = this.updateMasterRecord(tableName, id, updates);
const slavePromise = this.updateSlaveRecord(tableName, slaveId, updates);
const [masterResult, slaveResult] = await Promise.allSettled([masterPromise, slavePromise]);
result.masterSuccess = masterResult.status === 'fulfilled';
result.slaveSuccess = slaveResult.status === 'fulfilled';
result.success = result.masterSuccess || result.slaveSuccess;
if (masterResult.status === 'rejected') {
result.masterError = masterResult.reason instanceof Error ? masterResult.reason.message : '主機更新失敗';
}
if (slaveResult.status === 'rejected') {
result.slaveError = slaveResult.reason instanceof Error ? slaveResult.reason.message : '備機更新失敗';
}
} catch (error) {
result.masterError = error instanceof Error ? error.message : '雙寫更新執行失敗';
}
return result;
}
// 智能雙寫關聯表 - 使用對應的競賽 ID
async smartDualInsertRelation(relationTable, masterCompetitionId, slaveCompetitionId, relationData, relationIdField) {
const result = {
@@ -370,6 +417,200 @@ class DatabaseSyncFixed {
}
}
// 獲取主機記錄
async getMasterRecord(tableName, id) {
if (!this.masterPool) return null;
const connection = await this.masterPool.getConnection();
try {
const [rows] = await connection.execute(`SELECT * FROM ${tableName} WHERE id = ?`, [id]);
return rows[0] || null;
} finally {
connection.release();
}
}
// 根據名稱獲取備機記錄 ID
async getSlaveRecordIdByName(tableName, name) {
if (!this.slavePool) return null;
const connection = await this.slavePool.getConnection();
try {
const [rows] = await connection.execute(`SELECT id FROM ${tableName} WHERE name = ? ORDER BY created_at DESC LIMIT 1`, [name]);
const result = rows[0]?.id || null;
return result && typeof result !== 'string' ? String(result) : result;
} finally {
connection.release();
}
}
// 更新主機記錄
async updateMasterRecord(tableName, id, updates) {
if (!this.masterPool) return;
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
if (fields.length === 0) return;
const setClause = fields.map(field => `${field} = ?`).join(', ');
const values = fields.map(field => updates[field]);
const connection = await this.masterPool.getConnection();
try {
const sql = `UPDATE ${tableName} SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
await connection.execute(sql, [...values, id]);
} finally {
connection.release();
}
}
// 更新備機記錄
async updateSlaveRecord(tableName, id, updates) {
if (!this.slavePool) return;
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
if (fields.length === 0) return;
const setClause = fields.map(field => `${field} = ?`).join(', ');
const values = fields.map(field => updates[field]);
const connection = await this.slavePool.getConnection();
try {
const sql = `UPDATE ${tableName} SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
await connection.execute(sql, [...values, id]);
} finally {
connection.release();
}
}
// 清除所有競賽的當前狀態
async clearAllCurrentCompetitions() {
const result = {
success: false,
masterSuccess: false,
slaveSuccess: false
};
try {
// 同時清除主機和備機的所有當前競賽狀態
const masterPromise = this.clearMasterCurrentCompetitions();
const slavePromise = this.clearSlaveCurrentCompetitions();
const [masterResult, slaveResult] = await Promise.allSettled([masterPromise, slavePromise]);
result.masterSuccess = masterResult.status === 'fulfilled';
result.slaveSuccess = slaveResult.status === 'fulfilled';
result.success = result.masterSuccess || result.slaveSuccess;
if (masterResult.status === 'rejected') {
result.masterError = masterResult.reason instanceof Error ? masterResult.reason.message : '主機清除失敗';
}
if (slaveResult.status === 'rejected') {
result.slaveError = slaveResult.reason instanceof Error ? slaveResult.reason.message : '備機清除失敗';
}
} catch (error) {
result.masterError = error instanceof Error ? error.message : '清除所有當前競賽執行失敗';
}
return result;
}
// 清除主機的所有當前競賽狀態
async clearMasterCurrentCompetitions() {
if (!this.masterPool) return;
const connection = await this.masterPool.getConnection();
try {
await connection.execute('UPDATE competitions SET is_current = FALSE');
} finally {
connection.release();
}
}
// 清除備機的所有當前競賽狀態
async clearSlaveCurrentCompetitions() {
if (!this.slavePool) return;
const connection = await this.slavePool.getConnection();
try {
await connection.execute('UPDATE competitions SET is_current = FALSE');
} finally {
connection.release();
}
}
// 智能雙寫刪除
async smartDualDelete(tableName, id, idField = 'id') {
const result = {
success: false,
masterSuccess: false,
slaveSuccess: false
};
try {
// 獲取備機對應的 ID
let slaveId = null;
if (tableName === 'competitions') {
// 對於競賽表,先獲取競賽名稱
const masterRecord = await this.getMasterRecord(tableName, id);
if (masterRecord) {
slaveId = await this.getSlaveRecordIdByName(tableName, masterRecord.name);
}
} else {
// 對於關聯表,直接使用主機 ID
slaveId = id;
}
// 同時刪除主機和備機的記錄
const masterPromise = this.deleteFromMaster(tableName, id, idField);
const slavePromise = slaveId ? this.deleteFromSlave(tableName, slaveId, idField) : Promise.resolve();
const [masterResult, slaveResult] = await Promise.allSettled([masterPromise, slavePromise]);
result.masterSuccess = masterResult.status === 'fulfilled';
result.slaveSuccess = slaveResult.status === 'fulfilled';
result.success = result.masterSuccess || result.slaveSuccess;
if (masterResult.status === 'rejected') {
result.masterError = masterResult.reason instanceof Error ? masterResult.reason.message : '主機刪除失敗';
}
if (slaveResult.status === 'rejected') {
result.slaveError = slaveResult.reason instanceof Error ? slaveResult.reason.message : '備機刪除失敗';
}
} catch (error) {
result.masterError = error instanceof Error ? error.message : '雙寫刪除執行失敗';
}
return result;
}
// 從主機刪除記錄
async deleteFromMaster(tableName, id, idField = 'id') {
if (!this.masterPool) return;
const connection = await this.masterPool.getConnection();
try {
const sql = `DELETE FROM ${tableName} WHERE ${idField} = ?`;
await connection.execute(sql, [id]);
} finally {
connection.release();
}
}
// 從備機刪除記錄
async deleteFromSlave(tableName, id, idField = 'id') {
if (!this.slavePool) return;
const connection = await this.slavePool.getConnection();
try {
const sql = `DELETE FROM ${tableName} WHERE ${idField} = ?`;
await connection.execute(sql, [id]);
} finally {
connection.release();
}
}
// 清理資源
async close() {
if (this.masterPool) {

View File

@@ -157,6 +157,53 @@ export class DatabaseSyncFixed {
}
}
// 智能雙寫更新
async smartDualUpdate(tableName: string, id: string, updates: Record<string, any>): Promise<WriteResult> {
const result: WriteResult = {
success: false,
masterSuccess: false,
slaveSuccess: false
};
try {
// 先獲取主機的記錄
const masterRecord = await this.getMasterRecord(tableName, id);
if (!masterRecord) {
throw new Error('主機記錄不存在');
}
// 獲取備機的記錄 ID通過名稱匹配
const slaveId = await this.getSlaveRecordIdByName(tableName, masterRecord.name);
if (!slaveId) {
throw new Error('備機記錄不存在');
}
// 同時更新主機和備機
const masterPromise = this.updateMasterRecord(tableName, id, updates);
const slavePromise = this.updateSlaveRecord(tableName, slaveId, updates);
const [masterResult, slaveResult] = await Promise.allSettled([masterPromise, slavePromise]);
result.masterSuccess = masterResult.status === 'fulfilled';
result.slaveSuccess = slaveResult.status === 'fulfilled';
result.success = result.masterSuccess || result.slaveSuccess;
if (masterResult.status === 'rejected') {
result.masterError = masterResult.reason instanceof Error ? masterResult.reason.message : '主機更新失敗';
}
if (slaveResult.status === 'rejected') {
result.slaveError = slaveResult.reason instanceof Error ? slaveResult.reason.message : '備機更新失敗';
}
} catch (error) {
result.masterError = error instanceof Error ? error.message : '雙寫更新執行失敗';
}
return result;
}
// 智能雙寫關聯表 - 使用對應的競賽 ID
async smartDualInsertRelation(
relationTable: string,
@@ -260,6 +307,71 @@ export class DatabaseSyncFixed {
}
}
// 獲取主機記錄
private async getMasterRecord(tableName: string, id: string): Promise<any> {
if (!this.masterPool) return null;
const connection = await this.masterPool.getConnection();
try {
const [rows] = await connection.execute(`SELECT * FROM ${tableName} WHERE id = ?`, [id]);
return (rows as any[])[0] || null;
} finally {
connection.release();
}
}
// 根據名稱獲取備機記錄 ID
private async getSlaveRecordIdByName(tableName: string, name: string): Promise<string | null> {
if (!this.slavePool) return null;
const connection = await this.slavePool.getConnection();
try {
const [rows] = await connection.execute(`SELECT id FROM ${tableName} WHERE name = ? ORDER BY created_at DESC LIMIT 1`, [name]);
const result = (rows as any[])[0]?.id || null;
return result && typeof result !== 'string' ? String(result) : result;
} finally {
connection.release();
}
}
// 更新主機記錄
private async updateMasterRecord(tableName: string, id: string, updates: Record<string, any>): Promise<void> {
if (!this.masterPool) return;
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
if (fields.length === 0) return;
const setClause = fields.map(field => `${field} = ?`).join(', ');
const values = fields.map(field => updates[field]);
const connection = await this.masterPool.getConnection();
try {
const sql = `UPDATE ${tableName} SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
await connection.execute(sql, [...values, id]);
} finally {
connection.release();
}
}
// 更新備機記錄
private async updateSlaveRecord(tableName: string, id: string, updates: Record<string, any>): Promise<void> {
if (!this.slavePool) return;
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
if (fields.length === 0) return;
const setClause = fields.map(field => `${field} = ?`).join(', ');
const values = fields.map(field => updates[field]);
const connection = await this.slavePool.getConnection();
try {
const sql = `UPDATE ${tableName} SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
await connection.execute(sql, [...values, id]);
} finally {
connection.release();
}
}
// 根據名稱獲取備機競賽 ID
private async getSlaveCompetitionIdByName(name: string): Promise<string | null> {
if (!this.slavePool) return null;
@@ -350,6 +462,135 @@ export class DatabaseSyncFixed {
}
}
// 清除所有競賽的當前狀態
async clearAllCurrentCompetitions(): Promise<WriteResult> {
const result: WriteResult = {
success: false,
masterSuccess: false,
slaveSuccess: false
};
try {
// 同時清除主機和備機的所有當前競賽狀態
const masterPromise = this.clearMasterCurrentCompetitions();
const slavePromise = this.clearSlaveCurrentCompetitions();
const [masterResult, slaveResult] = await Promise.allSettled([masterPromise, slavePromise]);
result.masterSuccess = masterResult.status === 'fulfilled';
result.slaveSuccess = slaveResult.status === 'fulfilled';
result.success = result.masterSuccess || result.slaveSuccess;
if (masterResult.status === 'rejected') {
result.masterError = masterResult.reason instanceof Error ? masterResult.reason.message : '主機清除失敗';
}
if (slaveResult.status === 'rejected') {
result.slaveError = slaveResult.reason instanceof Error ? slaveResult.reason.message : '備機清除失敗';
}
} catch (error) {
result.masterError = error instanceof Error ? error.message : '清除所有當前競賽執行失敗';
}
return result;
}
// 清除主機的所有當前競賽狀態
private async clearMasterCurrentCompetitions(): Promise<void> {
if (!this.masterPool) return;
const connection = await this.masterPool.getConnection();
try {
await connection.execute('UPDATE competitions SET is_current = FALSE');
} finally {
connection.release();
}
}
// 清除備機的所有當前競賽狀態
private async clearSlaveCurrentCompetitions(): Promise<void> {
if (!this.slavePool) return;
const connection = await this.slavePool.getConnection();
try {
await connection.execute('UPDATE competitions SET is_current = FALSE');
} finally {
connection.release();
}
}
// 智能雙寫刪除
async smartDualDelete(tableName: string, id: string, idField: string = 'id'): Promise<WriteResult> {
const result: WriteResult = {
success: false,
masterSuccess: false,
slaveSuccess: false
};
try {
// 獲取備機對應的 ID
let slaveId: string | null = null;
if (tableName === 'competitions') {
// 對於競賽表,先獲取競賽名稱
const masterRecord = await this.getMasterRecord(tableName, id);
if (masterRecord) {
slaveId = await this.getSlaveRecordIdByName(tableName, masterRecord.name);
}
} else {
// 對於關聯表,直接使用主機 ID
slaveId = id;
}
// 同時刪除主機和備機的記錄
const masterPromise = this.deleteFromMaster(tableName, id, idField);
const slavePromise = slaveId ? this.deleteFromSlave(tableName, slaveId, idField) : Promise.resolve();
const [masterResult, slaveResult] = await Promise.allSettled([masterPromise, slavePromise]);
result.masterSuccess = masterResult.status === 'fulfilled';
result.slaveSuccess = slaveResult.status === 'fulfilled';
result.success = result.masterSuccess || result.slaveSuccess;
if (masterResult.status === 'rejected') {
result.masterError = masterResult.reason instanceof Error ? masterResult.reason.message : '主機刪除失敗';
}
if (slaveResult.status === 'rejected') {
result.slaveError = slaveResult.reason instanceof Error ? slaveResult.reason.message : '備機刪除失敗';
}
} catch (error) {
result.masterError = error instanceof Error ? error.message : '雙寫刪除執行失敗';
}
return result;
}
// 從主機刪除記錄
private async deleteFromMaster(tableName: string, id: string, idField: string = 'id'): Promise<void> {
if (!this.masterPool) return;
const connection = await this.masterPool.getConnection();
try {
const sql = `DELETE FROM ${tableName} WHERE ${idField} = ?`;
await connection.execute(sql, [id]);
} finally {
connection.release();
}
}
// 從備機刪除記錄
private async deleteFromSlave(tableName: string, id: string, idField: string = 'id'): Promise<void> {
if (!this.slavePool) return;
const connection = await this.slavePool.getConnection();
try {
const sql = `DELETE FROM ${tableName} WHERE ${idField} = ?`;
await connection.execute(sql, [id]);
} finally {
connection.release();
}
}
// 清理資源
async close(): Promise<void> {
if (this.masterPool) {

View File

@@ -616,7 +616,7 @@ export class UserService {
const competitionStatsSql = `
SELECT
COUNT(*) as total_competitions,
COUNT(CASE WHEN status = 'active' THEN 1 END) as active_competitions
COUNT(CASE WHEN status = 'active' OR status = 'ongoing' THEN 1 END) as active_competitions
FROM competitions
`;
const competitionStats = await this.queryOne(competitionStatsSql);
@@ -1282,13 +1282,135 @@ export class CompetitionService {
// 更新競賽
static async updateCompetition(id: string, updates: Partial<Competition>): Promise<boolean> {
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
const setClause = fields.map(field => `${field} = ?`).join(', ');
const values = fields.map(field => (updates as any)[field]);
const sql = `UPDATE competitions SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
const result = await db.update(sql, [...values, id]);
return result.affectedRows > 0;
try {
const dbSyncFixed = new DatabaseSyncFixed();
// 使用雙寫功能更新競賽
const result = await dbSyncFixed.smartDualUpdate('competitions', id, updates);
if (!result.success) {
console.error('競賽更新失敗:', result.masterError || result.slaveError);
return false;
}
return true;
} catch (error) {
console.error('競賽更新失敗:', error);
return false;
}
}
// 獲取當前競賽
static async getCurrentCompetition(): Promise<any | null> {
try {
const sql = 'SELECT * FROM competitions WHERE is_current = TRUE AND is_active = TRUE LIMIT 1';
const competitions = await db.query<any>(sql);
if (competitions.length > 0) {
const competition = competitions[0];
return await this.getCompetitionWithDetails(competition.id);
}
return null;
} catch (error) {
console.error('獲取當前競賽失敗:', error);
return null;
}
}
// 設置當前競賽
static async setCurrentCompetition(competitionId: string): Promise<boolean> {
try {
const dbSyncFixed = new DatabaseSyncFixed();
// 先清除所有競賽的當前狀態 - 使用直接 SQL 更新
try {
await dbSyncFixed.clearAllCurrentCompetitions();
} catch (error) {
console.error('清除當前競賽狀態失敗:', error);
}
// 設置指定競賽為當前競賽
const setResult = await dbSyncFixed.smartDualUpdate('competitions', competitionId, { is_current: true });
if (!setResult.success) {
console.error('設置當前競賽失敗:', setResult.masterError || setResult.slaveError);
return false;
}
return true;
} catch (error) {
console.error('設置當前競賽失敗:', error);
return false;
}
}
// 清除當前競賽
static async clearCurrentCompetition(): Promise<boolean> {
try {
await db.update('UPDATE competitions SET is_current = FALSE');
return true;
} catch (error) {
console.error('清除當前競賽失敗:', error);
return false;
}
}
// 刪除競賽 - 支援雙寫
static async deleteCompetition(id: string): Promise<boolean> {
try {
const dbSyncFixed = new DatabaseSyncFixed();
// 先獲取競賽信息
const competition = await this.getCompetitionById(id);
if (!competition) {
console.error('競賽不存在');
return false;
}
// 刪除關聯數據
await this.deleteCompetitionRelations(id);
// 使用雙寫功能刪除競賽
const result = await dbSyncFixed.smartDualDelete('competitions', id);
if (!result.success) {
console.error('競賽刪除失敗:', result.masterError || result.slaveError);
return false;
}
return true;
} catch (error) {
console.error('競賽刪除失敗:', error);
return false;
}
}
// 刪除競賽關聯數據
private static async deleteCompetitionRelations(competitionId: string): Promise<void> {
try {
const dbSyncFixed = new DatabaseSyncFixed();
// 刪除所有關聯表數據
const relations = [
'competition_judges',
'competition_teams',
'competition_apps',
'competition_award_types',
'competition_rules'
];
for (const relationTable of relations) {
try {
await dbSyncFixed.smartDualDelete(relationTable, competitionId, 'competition_id');
} catch (error) {
console.error(`刪除關聯表 ${relationTable} 失敗:`, error);
}
}
} catch (error) {
console.error('刪除競賽關聯數據失敗:', error);
}
}
// =====================================================
@@ -1369,16 +1491,50 @@ export class CompetitionService {
}
// 獲取競賽的應用列表
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 getCompetitionApps(competitionId: string, competitionType?: string): Promise<any[]> {
// 先獲取競賽信息
const competition = await this.getCompetitionById(competitionId);
if (!competition) return [];
let apps: any[] = [];
if (competition.type === 'team') {
// 對於團隊競賽,獲取所有參賽團隊的應用
const teams = await this.getCompetitionTeams(competitionId);
if (teams.length > 0) {
// 過濾掉 undefined 或 null 的 team_id 值
const teamIds = teams
.map(t => t.team_id)
.filter(id => id !== undefined && id !== null);
if (teamIds.length > 0) {
const placeholders = teamIds.map(() => '?').join(',');
const sql = `
SELECT a.*, u.name as creator_name, u.department as creator_department, t.name as team_name
FROM apps a
LEFT JOIN users u ON a.creator_id = u.id
LEFT JOIN teams t ON a.team_id = t.id
WHERE a.team_id IN (${placeholders}) AND a.is_active = TRUE
ORDER BY a.created_at ASC
`;
apps = await db.query(sql, teamIds);
}
}
} else {
// 對於個人競賽,從 competition_apps 表獲取
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
`;
apps = await db.query(sql, [competitionId]);
}
return apps;
}
// 為競賽添加團隊
@@ -1621,9 +1777,42 @@ export class CompetitionService {
this.getCompetitionRules(competitionId)
]);
// 根據日期動態計算競賽狀態
const now = new Date();
const startDate = new Date(competition.start_date);
const endDate = new Date(competition.end_date);
let calculatedStatus = competition.status;
// 確保日期比較的準確性,使用 UTC 時間避免時區問題
const nowUTC = new Date(now.getTime() + now.getTimezoneOffset() * 60000);
const startDateUTC = new Date(startDate.getTime() + startDate.getTimezoneOffset() * 60000);
const endDateUTC = new Date(endDate.getTime() + endDate.getTimezoneOffset() * 60000);
console.log('🔍 競賽狀態計算:', {
competitionId,
name: competition.name,
now: nowUTC.toISOString(),
startDate: startDateUTC.toISOString(),
endDate: endDateUTC.toISOString(),
originalStatus: competition.status
});
// 根據實際日期計算狀態
if (nowUTC < startDateUTC) {
calculatedStatus = 'upcoming'; // 即將開始
} else if (nowUTC >= startDateUTC && nowUTC <= endDateUTC) {
calculatedStatus = 'active'; // 進行中
} else if (nowUTC > endDateUTC) {
calculatedStatus = 'completed'; // 已完成
}
console.log('🔍 計算後的狀態:', calculatedStatus);
// 轉換字段名稱以匹配前端期望的格式
return {
...competition,
status: calculatedStatus, // 使用計算後的狀態
startDate: competition.start_date,
endDate: competition.end_date,
evaluationFocus: competition.evaluation_focus,
@@ -2625,7 +2814,7 @@ export class AppService {
const competitionStatsSql = `
SELECT
COUNT(*) as total_competitions,
COUNT(CASE WHEN status = 'active' THEN 1 END) as active_competitions
COUNT(CASE WHEN status = 'active' OR status = 'ongoing' THEN 1 END) as active_competitions
FROM competitions
`;
const competitionStats = await this.queryOne(competitionStatsSql);