新增 競賽建立、評審建立、團隊建立
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
// =====================================================
|
||||
|
||||
import { db } from '../database';
|
||||
import { dbSync } from '../database-sync';
|
||||
const { DatabaseSyncFixed } = require('../database-sync-fixed.js');
|
||||
import bcrypt from 'bcryptjs';
|
||||
import crypto from 'crypto';
|
||||
import type {
|
||||
@@ -822,6 +824,28 @@ export class UserService {
|
||||
// 評審服務
|
||||
// =====================================================
|
||||
export class JudgeService {
|
||||
// 安全解析 expertise 字段
|
||||
private static parseExpertise(expertise: any): string[] {
|
||||
if (!expertise) return [];
|
||||
|
||||
// 如果已經是數組,直接返回
|
||||
if (Array.isArray(expertise)) return expertise;
|
||||
|
||||
// 如果是字符串
|
||||
if (typeof expertise === 'string') {
|
||||
// 嘗試解析為 JSON
|
||||
try {
|
||||
const parsed = JSON.parse(expertise);
|
||||
if (Array.isArray(parsed)) return parsed;
|
||||
} catch (e) {
|
||||
// 如果 JSON 解析失敗,嘗試按逗號分割
|
||||
return expertise.split(',').map(item => item.trim()).filter(item => item);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// 創建評審
|
||||
static async createJudge(judgeData: Omit<Judge, 'id' | 'created_at' | 'updated_at'>): Promise<Judge> {
|
||||
const sql = `
|
||||
@@ -843,37 +867,43 @@ export class JudgeService {
|
||||
|
||||
// 根據姓名獲取評審
|
||||
static async getJudgeByName(name: string): Promise<Judge | null> {
|
||||
const sql = 'SELECT * FROM judges WHERE name = ? AND is_active = TRUE';
|
||||
const sql = 'SELECT * FROM judges WHERE name = ?';
|
||||
const result = await db.queryOne<Judge>(sql, [name]);
|
||||
if (result) {
|
||||
result.expertise = JSON.parse(result.expertise as any);
|
||||
result.expertise = this.parseExpertise(result.expertise as any);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 根據ID獲取評審
|
||||
static async getJudgeById(id: string): Promise<Judge | null> {
|
||||
const sql = 'SELECT * FROM judges WHERE id = ? AND is_active = TRUE';
|
||||
const sql = 'SELECT * FROM judges WHERE id = ?';
|
||||
const result = await db.queryOne<Judge>(sql, [id]);
|
||||
if (result) {
|
||||
result.expertise = JSON.parse(result.expertise as any);
|
||||
result.expertise = this.parseExpertise(result.expertise as any);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 獲取所有評審
|
||||
static async getAllJudges(): Promise<Judge[]> {
|
||||
const sql = 'SELECT * FROM judges WHERE is_active = TRUE ORDER BY created_at DESC';
|
||||
const sql = 'SELECT * FROM judges ORDER BY created_at DESC';
|
||||
const results = await db.query<Judge>(sql);
|
||||
return results.map(judge => ({
|
||||
...judge,
|
||||
expertise: JSON.parse(judge.expertise as any)
|
||||
expertise: this.parseExpertise(judge.expertise as any)
|
||||
}));
|
||||
}
|
||||
|
||||
// 更新評審
|
||||
static async updateJudge(id: string, updates: Partial<Judge>): Promise<boolean> {
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
|
||||
|
||||
if (fields.length === 0) {
|
||||
console.log('沒有字段需要更新');
|
||||
return true; // 沒有需要更新的字段,視為成功
|
||||
}
|
||||
|
||||
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
||||
const values = fields.map(field => {
|
||||
if (field === 'expertise') {
|
||||
@@ -883,9 +913,274 @@ export class JudgeService {
|
||||
});
|
||||
|
||||
const sql = `UPDATE judges SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
|
||||
console.log('執行 SQL:', sql);
|
||||
console.log('參數:', [...values, id]);
|
||||
|
||||
const result = await db.update(sql, [...values, id]);
|
||||
console.log('更新結果:', result);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 刪除評審(硬刪除)
|
||||
static async deleteJudge(id: string): Promise<boolean> {
|
||||
try {
|
||||
const sql = 'DELETE FROM judges WHERE id = ?';
|
||||
const result = await db.delete(sql, [id]);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('刪除評審錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 團隊服務
|
||||
// =====================================================
|
||||
export class TeamService {
|
||||
// 創建團隊
|
||||
static async createTeam(teamData: {
|
||||
name: string;
|
||||
leader_id: string;
|
||||
department: string;
|
||||
contact_email: string;
|
||||
description?: string;
|
||||
}): Promise<string> {
|
||||
const id = `t${Date.now()}${Math.random().toString(36).substr(2, 9)}`;
|
||||
const sql = `
|
||||
INSERT INTO teams (id, name, leader_id, department, contact_email, description)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
const params = [
|
||||
id,
|
||||
teamData.name,
|
||||
teamData.leader_id,
|
||||
teamData.department,
|
||||
teamData.contact_email,
|
||||
teamData.description || null
|
||||
];
|
||||
|
||||
const result = await db.insert(sql, params);
|
||||
console.log('團隊創建結果:', result);
|
||||
return id;
|
||||
}
|
||||
|
||||
// 獲取所有團隊
|
||||
static async getAllTeams(): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT t.*,
|
||||
u.name as leader_name,
|
||||
u.phone as leader_phone,
|
||||
COUNT(tm.id) as member_count
|
||||
FROM teams t
|
||||
LEFT JOIN users u ON t.leader_id = u.id
|
||||
LEFT JOIN team_members tm ON t.id = tm.team_id
|
||||
WHERE t.is_active = TRUE
|
||||
GROUP BY t.id
|
||||
ORDER BY t.created_at DESC
|
||||
`;
|
||||
const results = await db.query(sql);
|
||||
return results;
|
||||
}
|
||||
|
||||
// 根據 ID 獲取團隊
|
||||
static async getTeamById(id: string): Promise<any | null> {
|
||||
const sql = `
|
||||
SELECT t.*,
|
||||
u.name as leader_name,
|
||||
u.phone as leader_phone
|
||||
FROM teams t
|
||||
LEFT JOIN users u ON t.leader_id = u.id
|
||||
WHERE t.id = ? AND t.is_active = TRUE
|
||||
`;
|
||||
const results = await db.query(sql, [id]);
|
||||
return results.length > 0 ? results[0] : null;
|
||||
}
|
||||
|
||||
// 根據名稱獲取團隊
|
||||
static async getTeamByName(name: string): Promise<any | null> {
|
||||
const sql = `
|
||||
SELECT t.*,
|
||||
u.name as leader_name,
|
||||
u.phone as leader_phone
|
||||
FROM teams t
|
||||
LEFT JOIN users u ON t.leader_id = u.id
|
||||
WHERE t.name = ? AND t.is_active = TRUE
|
||||
`;
|
||||
const results = await db.query(sql, [name]);
|
||||
return results.length > 0 ? results[0] : null;
|
||||
}
|
||||
|
||||
// 更新團隊
|
||||
static async updateTeam(id: string, updates: Partial<{
|
||||
name: string;
|
||||
leader_id: string;
|
||||
department: string;
|
||||
contact_email: string;
|
||||
description: string;
|
||||
total_likes: number;
|
||||
}>): Promise<boolean> {
|
||||
const fields = Object.keys(updates).filter(key => key !== 'id' && key !== 'created_at');
|
||||
|
||||
if (fields.length === 0) {
|
||||
console.log('沒有字段需要更新');
|
||||
return true;
|
||||
}
|
||||
|
||||
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
||||
const values = fields.map(field => updates[field as keyof typeof updates]);
|
||||
values.push(id);
|
||||
|
||||
const sql = `UPDATE teams SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
|
||||
|
||||
try {
|
||||
const result = await db.update(sql, values);
|
||||
console.log('團隊更新結果:', result);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('更新團隊錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 刪除團隊(軟刪除)
|
||||
static async deleteTeam(id: string): Promise<boolean> {
|
||||
try {
|
||||
const sql = 'UPDATE teams SET is_active = FALSE, updated_at = CURRENT_TIMESTAMP WHERE id = ?';
|
||||
const result = await db.update(sql, [id]);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('刪除團隊錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 硬刪除團隊
|
||||
static async hardDeleteTeam(id: string): Promise<boolean> {
|
||||
try {
|
||||
const sql = 'DELETE FROM teams WHERE id = ?';
|
||||
const result = await db.delete(sql, [id]);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('硬刪除團隊錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加團隊成員
|
||||
static async addTeamMember(teamId: string, userId: string, role: string = 'member'): Promise<boolean> {
|
||||
const id = `tm${Date.now()}${Math.random().toString(36).substr(2, 9)}`;
|
||||
const sql = `
|
||||
INSERT INTO team_members (id, team_id, user_id, role)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`;
|
||||
const params = [id, teamId, userId, role];
|
||||
|
||||
try {
|
||||
const result = await db.insert(sql, params);
|
||||
console.log('團隊成員添加結果:', result);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('添加團隊成員錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 獲取團隊成員
|
||||
static async getTeamMembers(teamId: string): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT tm.*, u.name, u.department, u.email
|
||||
FROM team_members tm
|
||||
LEFT JOIN users u ON tm.user_id = u.id
|
||||
WHERE tm.team_id = ? AND u.status = 'active'
|
||||
ORDER BY tm.joined_at ASC
|
||||
`;
|
||||
const results = await db.query(sql, [teamId]);
|
||||
return results;
|
||||
}
|
||||
|
||||
// 移除團隊成員
|
||||
static async removeTeamMember(teamId: string, userId: string): Promise<boolean> {
|
||||
try {
|
||||
const sql = 'DELETE FROM team_members WHERE team_id = ? AND user_id = ?';
|
||||
const result = await db.delete(sql, [teamId, userId]);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('移除團隊成員錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新團隊成員角色
|
||||
static async updateTeamMemberRole(teamId: string, userId: string, role: string): Promise<boolean> {
|
||||
try {
|
||||
const sql = 'UPDATE team_members SET role = ? WHERE team_id = ? AND user_id = ?';
|
||||
const result = await db.update(sql, [role, teamId, userId]);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('更新團隊成員角色錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 綁定應用到團隊
|
||||
static async bindAppToTeam(teamId: string, appId: string): Promise<boolean> {
|
||||
try {
|
||||
const sql = 'UPDATE apps SET team_id = ? WHERE id = ? AND is_active = TRUE';
|
||||
const result = await db.update(sql, [teamId, appId]);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('綁定應用到團隊錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 解除應用與團隊的綁定
|
||||
static async unbindAppFromTeam(appId: string): Promise<boolean> {
|
||||
try {
|
||||
const sql = 'UPDATE apps SET team_id = NULL WHERE id = ?';
|
||||
const result = await db.update(sql, [appId]);
|
||||
return result.affectedRows > 0;
|
||||
} catch (error) {
|
||||
console.error('解除應用與團隊綁定錯誤:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 獲取團隊的應用列表
|
||||
static async getTeamApps(teamId: string): Promise<any[]> {
|
||||
console.log('🔍 TeamService.getTeamApps 被調用, teamId:', teamId);
|
||||
const sql = `
|
||||
SELECT id, name, description, category, type, icon, icon_color, app_url, likes_count, views_count, rating
|
||||
FROM apps
|
||||
WHERE team_id = ? AND is_active = TRUE
|
||||
ORDER BY created_at DESC
|
||||
`;
|
||||
console.log('📝 getTeamApps SQL:', sql);
|
||||
console.log('📝 getTeamApps 參數:', [teamId]);
|
||||
const results = await db.query(sql, [teamId]);
|
||||
console.log('📊 getTeamApps 結果:', results.length, '個應用');
|
||||
return results;
|
||||
}
|
||||
|
||||
// 獲取團隊統計
|
||||
static async getTeamStats(): Promise<any> {
|
||||
const sql = `
|
||||
SELECT
|
||||
COUNT(*) as totalTeams,
|
||||
COUNT(CASE WHEN is_active = TRUE THEN 1 END) as activeTeams,
|
||||
COUNT(CASE WHEN is_active = FALSE THEN 1 END) as inactiveTeams,
|
||||
AVG(member_count) as avgMembersPerTeam
|
||||
FROM (
|
||||
SELECT t.id, t.is_active, COUNT(tm.id) as member_count
|
||||
FROM teams t
|
||||
LEFT JOIN team_members tm ON t.id = tm.team_id
|
||||
GROUP BY t.id
|
||||
) as team_stats
|
||||
`;
|
||||
const results = await db.query(sql);
|
||||
return results[0] || { totalTeams: 0, activeTeams: 0, inactiveTeams: 0, avgMembersPerTeam: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
@@ -894,26 +1189,48 @@ export class JudgeService {
|
||||
export class CompetitionService {
|
||||
// 創建競賽
|
||||
static async createCompetition(competitionData: Omit<Competition, 'id' | 'created_at' | 'updated_at'>): Promise<Competition> {
|
||||
const sql = `
|
||||
INSERT INTO competitions (id, name, year, month, start_date, end_date, status, description, type, evaluation_focus, max_team_size, is_active)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
const params = [
|
||||
competitionData.name,
|
||||
competitionData.year,
|
||||
competitionData.month,
|
||||
competitionData.start_date,
|
||||
competitionData.end_date,
|
||||
competitionData.status,
|
||||
competitionData.description || null,
|
||||
competitionData.type,
|
||||
competitionData.evaluation_focus || null,
|
||||
competitionData.max_team_size || null,
|
||||
competitionData.is_active
|
||||
];
|
||||
// 使用智能雙寫,每個資料庫生成自己的 ID
|
||||
const data = {
|
||||
name: competitionData.name,
|
||||
year: competitionData.year,
|
||||
month: competitionData.month,
|
||||
start_date: competitionData.start_date,
|
||||
end_date: competitionData.end_date,
|
||||
status: competitionData.status,
|
||||
description: competitionData.description || null,
|
||||
type: competitionData.type,
|
||||
evaluation_focus: competitionData.evaluation_focus || null,
|
||||
max_team_size: competitionData.max_team_size || null,
|
||||
is_active: competitionData.is_active,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date()
|
||||
};
|
||||
|
||||
await db.insert(sql, params);
|
||||
return await this.getCompetitionByName(competitionData.name) as Competition;
|
||||
const dbSyncFixed = new DatabaseSyncFixed();
|
||||
const result = await dbSyncFixed.smartDualInsert('competitions', data);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(`競賽創建失敗: 主機${result.masterError || '成功'}, 備機${result.slaveError || '成功'}`);
|
||||
}
|
||||
|
||||
// 返回主機的競賽記錄(如果主機成功)
|
||||
if (result.masterSuccess) {
|
||||
const competition = await this.getCompetitionById(result.masterId!) as Competition;
|
||||
// 添加備機 ID 到競賽對象中,用於關聯表寫入
|
||||
(competition as any).slaveId = result.slaveId;
|
||||
return competition;
|
||||
} else {
|
||||
// 如果主機失敗但備機成功,從備機獲取
|
||||
const competition = await this.getCompetitionById(result.slaveId!) as Competition;
|
||||
(competition as any).slaveId = result.slaveId;
|
||||
return competition;
|
||||
}
|
||||
}
|
||||
|
||||
// 根據 ID 獲取競賽
|
||||
static async getCompetitionById(id: string): Promise<Competition | null> {
|
||||
const sql = 'SELECT * FROM competitions WHERE id = ?';
|
||||
return await db.queryOne<Competition>(sql, [id]);
|
||||
}
|
||||
|
||||
// 根據名稱獲取競賽
|
||||
@@ -922,12 +1239,28 @@ export class CompetitionService {
|
||||
return await db.queryOne<Competition>(sql, [name]);
|
||||
}
|
||||
|
||||
// 根據ID獲取競賽
|
||||
static async getCompetitionById(id: string): Promise<Competition | null> {
|
||||
const sql = 'SELECT * FROM competitions WHERE id = ? AND is_active = TRUE';
|
||||
return await db.queryOne<Competition>(sql, [id]);
|
||||
// 根據名稱獲取備機競賽 ID
|
||||
static async getSlaveCompetitionIdByName(name: string): Promise<string | null> {
|
||||
try {
|
||||
const dbSyncFixed = new DatabaseSyncFixed();
|
||||
const slavePool = (dbSyncFixed as any).slavePool;
|
||||
|
||||
if (!slavePool) return null;
|
||||
|
||||
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;
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('獲取備機競賽 ID 失敗:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 獲取所有競賽
|
||||
static async getAllCompetitions(): Promise<Competition[]> {
|
||||
const sql = 'SELECT * FROM competitions WHERE is_active = TRUE ORDER BY year DESC, month DESC';
|
||||
@@ -950,6 +1283,283 @@ export class CompetitionService {
|
||||
const result = await db.update(sql, [...values, id]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// 競賽關聯數據管理方法
|
||||
// =====================================================
|
||||
|
||||
// 獲取競賽的評審列表
|
||||
static async getCompetitionJudges(competitionId: string): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT j.*, cj.assigned_at
|
||||
FROM competition_judges cj
|
||||
JOIN judges j ON cj.judge_id = j.id
|
||||
WHERE cj.competition_id = ? AND j.is_active = TRUE
|
||||
ORDER BY cj.assigned_at ASC
|
||||
`;
|
||||
return await db.query(sql, [competitionId]);
|
||||
}
|
||||
|
||||
// 為競賽添加評審
|
||||
static async addCompetitionJudges(competitionId: string, judgeIds: string[]): Promise<boolean> {
|
||||
try {
|
||||
const dbSyncFixed = new DatabaseSyncFixed();
|
||||
|
||||
// 先刪除現有的評審關聯
|
||||
await db.delete('DELETE FROM competition_judges WHERE competition_id = ?', [competitionId]);
|
||||
|
||||
// 添加新的評審關聯
|
||||
if (judgeIds.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 = judgeIds.map(judgeId => ({ judge_id: judgeId }));
|
||||
const result = await dbSyncFixed.smartDualInsertRelation(
|
||||
'competition_judges',
|
||||
competitionId,
|
||||
slaveCompetitionId,
|
||||
relationData,
|
||||
'judge_id'
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('添加競賽評審失敗:', result.masterError || result.slaveError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('添加競賽評審失敗:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 從競賽中移除評審
|
||||
static async removeCompetitionJudge(competitionId: string, judgeId: string): Promise<boolean> {
|
||||
const sql = 'DELETE FROM competition_judges WHERE competition_id = ? AND judge_id = ?';
|
||||
const result = await db.delete(sql, [competitionId, judgeId]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 獲取競賽的團隊列表
|
||||
static async getCompetitionTeams(competitionId: string): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT t.*, ct.registered_at, u.name as leader_name, u.phone as leader_phone
|
||||
FROM competition_teams ct
|
||||
JOIN teams t ON ct.team_id = t.id
|
||||
LEFT JOIN users u ON t.leader_id = u.id
|
||||
WHERE ct.competition_id = ? AND t.is_active = TRUE
|
||||
ORDER BY ct.registered_at ASC
|
||||
`;
|
||||
return await db.query(sql, [competitionId]);
|
||||
}
|
||||
|
||||
// 為競賽添加團隊
|
||||
static async addCompetitionTeams(competitionId: string, teamIds: string[]): Promise<boolean> {
|
||||
try {
|
||||
const dbSyncFixed = new DatabaseSyncFixed();
|
||||
|
||||
// 先刪除現有的團隊關聯
|
||||
await db.delete('DELETE FROM competition_teams WHERE competition_id = ?', [competitionId]);
|
||||
|
||||
// 添加新的團隊關聯
|
||||
if (teamIds.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 = teamIds.map(teamId => ({ team_id: teamId }));
|
||||
const result = await dbSyncFixed.smartDualInsertRelation(
|
||||
'competition_teams',
|
||||
competitionId,
|
||||
slaveCompetitionId,
|
||||
relationData,
|
||||
'team_id'
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('添加競賽團隊失敗:', result.masterError || result.slaveError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('添加競賽團隊失敗:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 從競賽中移除團隊
|
||||
static async removeCompetitionTeam(competitionId: string, teamId: string): Promise<boolean> {
|
||||
const sql = 'DELETE FROM competition_teams WHERE competition_id = ? AND team_id = ?';
|
||||
const result = await db.delete(sql, [competitionId, teamId]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 獲取競賽的獎項類型列表
|
||||
static async getCompetitionAwardTypes(competitionId: string): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT cat.*
|
||||
FROM competition_award_types cat
|
||||
WHERE cat.competition_id = ?
|
||||
ORDER BY cat.order_index ASC, cat.created_at ASC
|
||||
`;
|
||||
return await db.query(sql, [competitionId]);
|
||||
}
|
||||
|
||||
// 為競賽添加獎項類型
|
||||
static async addCompetitionAwardTypes(competitionId: string, awardTypes: any[]): Promise<boolean> {
|
||||
try {
|
||||
const dbSyncFixed = new DatabaseSyncFixed();
|
||||
|
||||
// 先刪除現有的獎項類型
|
||||
await db.delete('DELETE FROM competition_award_types WHERE competition_id = ?', [competitionId]);
|
||||
|
||||
// 添加新的獎項類型
|
||||
if (awardTypes.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 = awardTypes.map((awardType, i) => ({
|
||||
name: awardType.name,
|
||||
description: awardType.description || '',
|
||||
icon: awardType.icon || '🏆',
|
||||
color: awardType.color || 'text-yellow-600',
|
||||
order_index: i
|
||||
}));
|
||||
|
||||
const result = await dbSyncFixed.smartDualInsertRelation(
|
||||
'competition_award_types',
|
||||
competitionId,
|
||||
slaveCompetitionId,
|
||||
relationData,
|
||||
'name'
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('添加競賽獎項類型失敗:', result.masterError || result.slaveError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('添加競賽獎項類型失敗:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 從競賽中移除獎項類型
|
||||
static async removeCompetitionAwardType(competitionId: string, awardTypeId: string): Promise<boolean> {
|
||||
const sql = 'DELETE FROM competition_award_types WHERE competition_id = ? AND id = ?';
|
||||
const result = await db.delete(sql, [competitionId, awardTypeId]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 獲取競賽的評分規則列表
|
||||
static async getCompetitionRules(competitionId: string): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT cr.*
|
||||
FROM competition_rules cr
|
||||
WHERE cr.competition_id = ?
|
||||
ORDER BY cr.order_index ASC, cr.created_at ASC
|
||||
`;
|
||||
return await db.query(sql, [competitionId]);
|
||||
}
|
||||
|
||||
// 為競賽添加評分規則
|
||||
static async addCompetitionRules(competitionId: string, rules: any[]): Promise<boolean> {
|
||||
try {
|
||||
const dbSyncFixed = new DatabaseSyncFixed();
|
||||
|
||||
// 先刪除現有的評分規則
|
||||
await db.delete('DELETE FROM competition_rules WHERE competition_id = ?', [competitionId]);
|
||||
|
||||
// 添加新的評分規則
|
||||
if (rules.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 = rules.map((rule, i) => ({
|
||||
name: rule.name,
|
||||
description: rule.description || '',
|
||||
weight: rule.weight || 0,
|
||||
order_index: i
|
||||
}));
|
||||
|
||||
const result = await dbSyncFixed.smartDualInsertRelation(
|
||||
'competition_rules',
|
||||
competitionId,
|
||||
slaveCompetitionId,
|
||||
relationData,
|
||||
'name'
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('添加競賽評分規則失敗:', result.masterError || result.slaveError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('添加競賽評分規則失敗:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 從競賽中移除評分規則
|
||||
static async removeCompetitionRule(competitionId: string, ruleId: string): Promise<boolean> {
|
||||
const sql = 'DELETE FROM competition_rules WHERE competition_id = ? AND id = ?';
|
||||
const result = await db.delete(sql, [competitionId, ruleId]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 獲取競賽的完整信息(包含所有關聯數據)
|
||||
static async getCompetitionWithDetails(competitionId: string): Promise<any> {
|
||||
const competition = await this.getCompetitionById(competitionId);
|
||||
if (!competition) return null;
|
||||
|
||||
const [judges, teams, awardTypes, rules] = await Promise.all([
|
||||
this.getCompetitionJudges(competitionId),
|
||||
this.getCompetitionTeams(competitionId),
|
||||
this.getCompetitionAwardTypes(competitionId),
|
||||
this.getCompetitionRules(competitionId)
|
||||
]);
|
||||
|
||||
return {
|
||||
...competition,
|
||||
judges,
|
||||
teams,
|
||||
awardTypes,
|
||||
rules
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
|
Reference in New Issue
Block a user