// ===================================================== // 評分管理 API // ===================================================== import { NextRequest, NextResponse } from 'next/server'; import { ScoringService } from '@/lib/services/database-service'; // 獲取競賽評分記錄 export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const competitionId = searchParams.get('competitionId'); const judgeId = searchParams.get('judgeId'); const status = searchParams.get('status'); const search = searchParams.get('search'); if (!competitionId) { return NextResponse.json({ success: false, message: '缺少競賽ID', error: 'competitionId 參數是必需的' }, { status: 400 }); } let scores; if (judgeId) { // 獲取特定評審的評分記錄 scores = await ScoringService.getJudgeScores(judgeId, competitionId); } else { // 獲取競賽的所有評分記錄 scores = await ScoringService.getCompetitionScores(competitionId); } // 狀態篩選 if (status && status !== 'all') { if (status === 'completed') { scores = scores.filter(score => score.total_score > 0); } else if (status === 'pending') { scores = scores.filter(score => !score.total_score || score.total_score === 0); } } // 搜尋篩選 if (search) { const searchLower = search.toLowerCase(); scores = scores.filter(score => score.judge_name?.toLowerCase().includes(searchLower) || score.app_name?.toLowerCase().includes(searchLower) || score.creator_name?.toLowerCase().includes(searchLower) ); } // 獲取評分統計 const stats = await ScoringService.getCompetitionScoreStats(competitionId); // 處理評分數據,將 score_details 轉換為前端期望的格式 const processedScores = scores.map((score: any) => { // 解析 score_details 字符串 let scoreDetails: Record = {}; if (score.score_details) { const details = score.score_details.split(','); details.forEach((detail: string) => { const [ruleName, scoreValue] = detail.split(':'); if (ruleName && scoreValue) { scoreDetails[ruleName] = parseInt(scoreValue); } }); } // 映射到前端期望的字段 return { ...score, innovation_score: scoreDetails['創新性'] || scoreDetails['innovation'] || 0, technical_score: scoreDetails['技術性'] || scoreDetails['technical'] || 0, usability_score: scoreDetails['實用性'] || scoreDetails['usability'] || 0, presentation_score: scoreDetails['展示效果'] || scoreDetails['presentation'] || 0, impact_score: scoreDetails['影響力'] || scoreDetails['impact'] || 0, }; }); return NextResponse.json({ success: true, message: '評分記錄獲取成功', data: { scores: processedScores, stats, total: processedScores.length } }); } catch (error) { console.error('獲取評分記錄失敗:', error); return NextResponse.json({ success: false, message: '獲取評分記錄失敗', error: error instanceof Error ? error.message : '未知錯誤' }, { status: 500 }); } } // 處理評分的輔助函數 async function processScoringWithCompetitionId(participantId: string, judgeId: string, scores: any, comments: string, competitionId: string, isEdit: boolean = false, recordId: string | null = null) { const rules = await ScoringService.getCompetitionRules(competitionId); if (!rules || rules.length === 0) { return NextResponse.json({ success: false, message: '競賽評分規則未設置', error: '請先設置競賽評分規則' }, { status: 400 }); } // 驗證評分格式(基於實際的競賽規則) const providedScores = Object.keys(scores).filter(key => scores[key] > 0); const invalidScores = providedScores.filter(score => scores[score] < 1 || scores[score] > 10); if (invalidScores.length > 0) { return NextResponse.json({ success: false, message: '評分格式無效', error: `無效的評分項目: ${invalidScores.join(', ')}` }, { status: 400 }); } if (providedScores.length === 0) { return NextResponse.json({ success: false, message: '評分格式無效', error: '至少需要提供一個評分項目' }, { status: 400 }); } // 計算總分(基於權重,轉換為100分制) let totalScore = 0; let totalWeight = 0; rules.forEach((rule: any) => { const score = scores[rule.name]; if (score && score > 0) { totalScore += score * (rule.weight / 100); totalWeight += rule.weight; } }); // 如果總權重為0,使用平均分 if (totalWeight === 0) { const validScores = Object.values(scores).filter(score => score > 0); totalScore = validScores.length > 0 ? validScores.reduce((sum, score) => sum + score, 0) / validScores.length : 0; } // 轉換為100分制(10分制 * 10 = 100分制) totalScore = totalScore * 10; // 將自定義評分映射到標準字段 const validScoreData: any = { judge_id: judgeId, app_id: participantId, competition_id: competitionId, scores: scores, // 傳遞原始評分數據 total_score: totalScore, comments: comments || null, isEdit: isEdit || false, recordId: recordId || null }; // 按順序將自定義評分映射到標準字段 const standardFields = ['innovation_score', 'technical_score', 'usability_score', 'presentation_score', 'impact_score']; const customScores = Object.entries(scores).filter(([key, value]) => value > 0); customScores.forEach(([customKey, score], index) => { if (index < standardFields.length) { validScoreData[standardFields[index]] = score; } }); const result = await ScoringService.submitAppScore(validScoreData); return NextResponse.json({ success: true, message: '評分提交成功', data: result }); } // 提交評分 export async function POST(request: NextRequest) { try { const body = await request.json(); console.log('🔍 API 接收到的請求數據:', JSON.stringify(body, null, 2)); const { judgeId, participantId, participantType, scores, comments, competitionId, isEdit, recordId } = body; console.log('🔍 解析後的參數:'); console.log('judgeId:', judgeId, typeof judgeId); console.log('participantId:', participantId, typeof participantId); console.log('participantType:', participantType, typeof participantType); console.log('scores:', scores, typeof scores); console.log('competitionId:', competitionId, typeof competitionId); console.log('isEdit:', isEdit, typeof isEdit); console.log('recordId:', recordId, typeof recordId); // 驗證必填欄位 if (!judgeId || !participantId || !participantType || !scores) { console.log('❌ 缺少必填欄位驗證失敗'); return NextResponse.json({ success: false, message: '缺少必填欄位', error: 'judgeId, participantId, participantType, scores 為必填欄位' }, { status: 400 }); } // 驗證評分類型 if (!['app', 'proposal', 'team'].includes(participantType)) { return NextResponse.json({ success: false, message: '無效的參賽者類型', error: 'participantType 必須是 "app"、"proposal" 或 "team"' }, { status: 400 }); } let result; if (participantType === 'app') { // 獲取競賽規則來驗證評分格式 let finalCompetitionId = await ScoringService.getCompetitionIdByAppId(participantId); if (!finalCompetitionId) { // 如果找不到競賽關聯,嘗試通過其他方式獲取競賽ID console.log('⚠️ 找不到APP的競賽關聯,嘗試其他方式...'); // 檢查是否有其他方式獲取競賽ID(例如通過請求參數) if (competitionId) { console.log('✅ 使用參數中的競賽ID:', competitionId); finalCompetitionId = competitionId; } else { return NextResponse.json({ success: false, message: '找不到對應的競賽', error: 'APP未註冊到任何競賽中,請先在競賽管理中將APP添加到競賽' }, { status: 400 }); } } const rules = await ScoringService.getCompetitionRules(finalCompetitionId); if (!rules || rules.length === 0) { return NextResponse.json({ success: false, message: '競賽評分規則未設置', error: '請先設置競賽評分規則' }, { status: 400 }); } // 驗證評分格式(基於實際的競賽規則) const providedScores = Object.keys(scores).filter(key => scores[key] > 0); const invalidScores = providedScores.filter(score => scores[score] < 1 || scores[score] > 10); if (invalidScores.length > 0) { return NextResponse.json({ success: false, message: '評分格式無效', error: `無效的評分項目: ${invalidScores.join(', ')}` }, { status: 400 }); } if (providedScores.length === 0) { return NextResponse.json({ success: false, message: '評分格式無效', error: '至少需要提供一個評分項目' }, { status: 400 }); } // 計算總分(基於權重,轉換為100分制) let totalScore = 0; let totalWeight = 0; rules.forEach((rule: any) => { const score = scores[rule.name]; if (score && score > 0) { totalScore += score * (rule.weight / 100); totalWeight += rule.weight; } }); // 如果總權重為0,使用平均分 if (totalWeight === 0) { const validScores = Object.values(scores).filter(score => score > 0); totalScore = validScores.length > 0 ? validScores.reduce((sum, score) => sum + score, 0) / validScores.length : 0; } // 轉換為100分制(10分制 * 10 = 100分制) totalScore = totalScore * 10; // 使用新的基於競賽規則的評分系統 const validScoreData = { judge_id: judgeId, app_id: participantId, competition_id: finalCompetitionId, scores: scores, // 直接使用原始評分數據 total_score: totalScore, comments: comments || null, isEdit: isEdit || false, recordId: recordId || null }; result = await ScoringService.submitAppScore(validScoreData); } else if (participantType === 'proposal') { // 驗證提案評分格式 const requiredScores = ['problem_identification_score', 'solution_feasibility_score', 'innovation_score', 'impact_score', 'presentation_score']; const missingScores = requiredScores.filter(score => !(score in scores) || scores[score] < 1 || scores[score] > 10); if (missingScores.length > 0) { return NextResponse.json({ success: false, message: '評分格式無效', error: `缺少或無效的評分項目: ${missingScores.join(', ')}` }, { status: 400 }); } // 計算總分 const totalScore = ( scores.problem_identification_score + scores.solution_feasibility_score + scores.innovation_score + scores.impact_score + scores.presentation_score ) / 5; result = await ScoringService.submitProposalScore({ judge_id: judgeId, proposal_id: participantId, problem_identification_score: scores.problem_identification_score, solution_feasibility_score: scores.solution_feasibility_score, innovation_score: scores.innovation_score, impact_score: scores.impact_score, presentation_score: scores.presentation_score, total_score: totalScore, comments: comments || null }); } else if (participantType === 'team') { // 驗證團隊評分格式 const requiredScores = ['innovation_score', 'technical_score', 'usability_score', 'presentation_score', 'impact_score']; const missingScores = requiredScores.filter(score => !(score in scores) || scores[score] < 1 || scores[score] > 10); if (missingScores.length > 0) { return NextResponse.json({ success: false, message: '評分格式無效', error: `缺少或無效的評分項目: ${missingScores.join(', ')}` }, { status: 400 }); } // 計算總分 const totalScore = ( scores.innovation_score + scores.technical_score + scores.usability_score + scores.presentation_score + scores.impact_score ) / 5; // 團隊評分使用應用評分表 result = await ScoringService.submitTeamScore({ judge_id: judgeId, teamId: participantId, innovation_score: scores.innovation_score, technical_score: scores.technical_score, usability_score: scores.usability_score, presentation_score: scores.presentation_score, impact_score: scores.impact_score, total_score: totalScore, comments: comments || null }); } return NextResponse.json({ success: true, message: '評分提交成功', data: result }); } catch (error) { console.error('提交評分失敗:', error); return NextResponse.json({ success: false, message: '提交評分失敗', error: error instanceof Error ? error.message : '未知錯誤' }, { status: 500 }); } }