修正詳細評審頁面結果
This commit is contained in:
168
app/api/evaluation/[id]/route.ts
Normal file
168
app/api/evaluation/[id]/route.ts
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { EvaluationService } from '@/lib/services/database';
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: NextRequest,
|
||||||
|
{ params }: { params: { id: string } }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const evaluationId = parseInt(params.id);
|
||||||
|
|
||||||
|
if (isNaN(evaluationId)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, error: '無效的評審ID' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`📊 開始獲取評審詳細數據: ID=${evaluationId}`);
|
||||||
|
|
||||||
|
// 獲取評審詳細數據
|
||||||
|
const evaluationWithDetails = await EvaluationService.findWithDetails(evaluationId);
|
||||||
|
|
||||||
|
if (!evaluationWithDetails) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, error: '找不到指定的評審記錄' },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✅ 成功獲取評審數據: 專案=${evaluationWithDetails.project?.title}`);
|
||||||
|
|
||||||
|
// 處理反饋數據,按類型分組
|
||||||
|
const feedbackByType = {
|
||||||
|
criteria: evaluationWithDetails.feedback?.filter(f => f.feedback_type === 'criteria') || [],
|
||||||
|
strength: evaluationWithDetails.feedback?.filter(f => f.feedback_type === 'strength') || [],
|
||||||
|
improvement: evaluationWithDetails.feedback?.filter(f => f.feedback_type === 'improvement') || [],
|
||||||
|
overall: evaluationWithDetails.feedback?.filter(f => f.feedback_type === 'overall') || []
|
||||||
|
};
|
||||||
|
|
||||||
|
// 為每個評分標準獲取對應的 AI 評語、優點和改進建議
|
||||||
|
const criteriaWithFeedback = evaluationWithDetails.scores?.map(score => {
|
||||||
|
const criteriaId = score.criteria_item_id;
|
||||||
|
const criteriaName = score.criteria_item_name || '未知項目';
|
||||||
|
|
||||||
|
// 獲取該評分標準的 AI 評語
|
||||||
|
const criteriaFeedback = feedbackByType.criteria
|
||||||
|
.filter(f => f.criteria_item_id === criteriaId)
|
||||||
|
.map(f => f.content)
|
||||||
|
.filter((content, index, arr) => arr.indexOf(content) === index); // 去重
|
||||||
|
|
||||||
|
// 獲取該評分標準的優點
|
||||||
|
const strengths = feedbackByType.strength
|
||||||
|
.filter(f => f.criteria_item_id === criteriaId)
|
||||||
|
.map(f => f.content)
|
||||||
|
.filter((content, index, arr) => arr.indexOf(content) === index); // 去重
|
||||||
|
|
||||||
|
// 獲取該評分標準的改進建議
|
||||||
|
const improvements = feedbackByType.improvement
|
||||||
|
.filter(f => f.criteria_item_id === criteriaId)
|
||||||
|
.map(f => f.content)
|
||||||
|
.filter((content, index, arr) => arr.indexOf(content) === index); // 去重
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: criteriaName,
|
||||||
|
score: Number(score.score) || 0,
|
||||||
|
maxScore: Number(score.max_score) || 10,
|
||||||
|
weight: Number(score.weight) || 1,
|
||||||
|
weightedScore: Number(score.weighted_score) || 0,
|
||||||
|
percentage: Number(score.percentage) || 0,
|
||||||
|
feedback: criteriaFeedback[0] || '無評語', // 使用第一個評語
|
||||||
|
strengths: strengths,
|
||||||
|
improvements: improvements
|
||||||
|
};
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
// 生成圖表數據
|
||||||
|
const chartData = {
|
||||||
|
// 各項目得分 - 使用 evaluation_scores 的 score 資料
|
||||||
|
barChart: criteriaWithFeedback.map(criteria => ({
|
||||||
|
name: criteria.name,
|
||||||
|
score: criteria.score
|
||||||
|
})),
|
||||||
|
|
||||||
|
// 權重分布 - 使用 evaluation_scores 的 weighted_score 資料
|
||||||
|
pieChart: criteriaWithFeedback.map(criteria => ({
|
||||||
|
name: criteria.name,
|
||||||
|
value: criteria.weightedScore,
|
||||||
|
weight: criteria.weight
|
||||||
|
})),
|
||||||
|
|
||||||
|
// 能力雷達圖 - 使用 evaluation_scores 的 score 資料
|
||||||
|
radarChart: criteriaWithFeedback.map(criteria => ({
|
||||||
|
subject: criteria.name,
|
||||||
|
score: criteria.score,
|
||||||
|
fullMark: criteria.maxScore
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
// 轉換數據格式以符合前端需求
|
||||||
|
const formattedData = {
|
||||||
|
projectTitle: evaluationWithDetails.project?.title || '未知專案',
|
||||||
|
overallScore: Number(evaluationWithDetails.overall_score) || 0,
|
||||||
|
totalPossible: Number(evaluationWithDetails.max_possible_score) || 100,
|
||||||
|
grade: evaluationWithDetails.grade || 'N/A',
|
||||||
|
performanceStatus: evaluationWithDetails.performance_status || 'N/A',
|
||||||
|
recommendedStars: evaluationWithDetails.recommended_stars || 0,
|
||||||
|
analysisDate: evaluationWithDetails.created_at ? new Date(evaluationWithDetails.created_at).toISOString().split('T')[0] : new Date().toISOString().split('T')[0],
|
||||||
|
criteria: criteriaWithFeedback,
|
||||||
|
overview: {
|
||||||
|
excellentItems: evaluationWithDetails.excellent_items || 0,
|
||||||
|
improvementItems: evaluationWithDetails.improvement_items || 0,
|
||||||
|
overallPerformance: Number(evaluationWithDetails.overall_score) || 0
|
||||||
|
},
|
||||||
|
detailedAnalysis: {
|
||||||
|
summary: feedbackByType.overall[0]?.content || '無詳細分析',
|
||||||
|
keyFindings: feedbackByType.overall.map(f => f.content).filter((content, index, arr) => arr.indexOf(content) === index) // 去重
|
||||||
|
},
|
||||||
|
improvementSuggestions: {
|
||||||
|
// 整體改進建議 - evaluation_feedback 的第一筆 overall
|
||||||
|
overallSuggestions: feedbackByType.overall[0]?.content || '無改進建議',
|
||||||
|
|
||||||
|
// 繼續保持的優勢 - evaluation_feedback 的 strength
|
||||||
|
maintainStrengths: feedbackByType.strength
|
||||||
|
.filter(f => f.criteria_item_id) // 只取有 criteria_item_id 的
|
||||||
|
.map(f => ({
|
||||||
|
title: f.criteria_item_name || '優勢',
|
||||||
|
description: f.content || '無描述'
|
||||||
|
}))
|
||||||
|
.filter((item, index, arr) => arr.findIndex(i => i.title === item.title) === index), // 去重
|
||||||
|
|
||||||
|
// 重點改進方向 - evaluation_feedback 的 improvement (除了最後3筆)
|
||||||
|
keyImprovements: feedbackByType.improvement
|
||||||
|
.filter(f => f.criteria_item_id) // 只取有 criteria_item_id 的
|
||||||
|
.slice(0, -3) // 排除最後3筆
|
||||||
|
.map(f => ({
|
||||||
|
title: f.criteria_item_name || '改進項目',
|
||||||
|
description: f.content || '無描述',
|
||||||
|
suggestions: [f.content || '無建議']
|
||||||
|
}))
|
||||||
|
.filter((item, index, arr) => arr.findIndex(i => i.title === item.title) === index), // 去重
|
||||||
|
|
||||||
|
// 下一步行動計劃 - evaluation_feedback 的 improvement 最後3筆
|
||||||
|
actionPlan: feedbackByType.improvement
|
||||||
|
.filter(f => f.criteria_item_id) // 只取有 criteria_item_id 的
|
||||||
|
.slice(-3) // 取最後3筆
|
||||||
|
.map((f, index) => ({
|
||||||
|
phase: `階段 ${index + 1}`,
|
||||||
|
description: f.content || '無描述'
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
chartData: chartData
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`📋 評審數據格式化完成: 專案=${formattedData.projectTitle}, 分數=${formattedData.overallScore}`);
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: formattedData
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 獲取評審詳細數據失敗:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, error: '獲取評審詳細數據失敗' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -260,7 +260,7 @@ export default function HistoryPage() {
|
|||||||
{item.status === "completed" ? (
|
{item.status === "completed" ? (
|
||||||
<>
|
<>
|
||||||
<Button asChild variant="outline" size="sm">
|
<Button asChild variant="outline" size="sm">
|
||||||
<Link href={`/results?id=${item.id}`}>
|
<Link href={`/results?id=${item.evaluation_id}`}>
|
||||||
<Eye className="h-4 w-4" />
|
<Eye className="h-4 w-4" />
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
|
import { useSearchParams } from "next/navigation"
|
||||||
import { Sidebar } from "@/components/sidebar"
|
import { Sidebar } from "@/components/sidebar"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
@@ -128,32 +129,58 @@ export default function ResultsPage() {
|
|||||||
const [activeTab, setActiveTab] = useState("overview")
|
const [activeTab, setActiveTab] = useState("overview")
|
||||||
const [evaluationData, setEvaluationData] = useState(null)
|
const [evaluationData, setEvaluationData] = useState(null)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [error, setError] = useState(null)
|
||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 從 localStorage 獲取評審結果
|
const loadEvaluationData = async () => {
|
||||||
const storedData = localStorage.getItem('evaluationResult')
|
|
||||||
if (storedData) {
|
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(storedData)
|
setIsLoading(true)
|
||||||
setEvaluationData(data)
|
setError(null)
|
||||||
} catch (error) {
|
|
||||||
console.error('解析評審結果失敗:', error)
|
// 檢查是否有 URL 參數中的評審 ID
|
||||||
toast({
|
const evaluationId = searchParams.get('id')
|
||||||
title: "數據錯誤",
|
|
||||||
description: "無法載入評審結果,請重新進行評審",
|
if (evaluationId) {
|
||||||
variant: "destructive",
|
// 從 API 獲取評審數據
|
||||||
})
|
console.log(`📊 從 API 獲取評審數據: ID=${evaluationId}`)
|
||||||
|
const response = await fetch(`/api/evaluation/${evaluationId}`)
|
||||||
|
const result = await response.json()
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
setEvaluationData(result.data)
|
||||||
|
console.log('✅ 成功載入評審數據:', result.data.projectTitle)
|
||||||
|
} else {
|
||||||
|
throw new Error(result.error || '獲取評審數據失敗')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// 回退到從 localStorage 獲取評審結果
|
||||||
|
console.log('📊 從 localStorage 獲取評審結果')
|
||||||
|
const storedData = localStorage.getItem('evaluationResult')
|
||||||
|
if (storedData) {
|
||||||
|
const data = JSON.parse(storedData)
|
||||||
|
setEvaluationData(data)
|
||||||
|
console.log('✅ 成功載入 localStorage 數據')
|
||||||
|
} else {
|
||||||
|
throw new Error('沒有找到評審結果')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('載入評審結果失敗:', error)
|
||||||
|
setError(error.message)
|
||||||
toast({
|
toast({
|
||||||
title: "無評審結果",
|
title: "載入失敗",
|
||||||
description: "請先進行評審以查看結果",
|
description: error.message || "無法載入評審結果,請重新進行評審",
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
})
|
})
|
||||||
}
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}, [toast])
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadEvaluationData()
|
||||||
|
}, [searchParams, toast])
|
||||||
|
|
||||||
// 如果正在載入,顯示載入狀態
|
// 如果正在載入,顯示載入狀態
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@@ -174,8 +201,8 @@ export default function ResultsPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果沒有數據,顯示錯誤狀態
|
// 如果沒有數據或發生錯誤,顯示錯誤狀態
|
||||||
if (!evaluationData) {
|
if (!evaluationData || error) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
@@ -183,10 +210,17 @@ export default function ResultsPage() {
|
|||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<p className="text-muted-foreground mb-4">沒有找到評審結果</p>
|
<p className="text-muted-foreground mb-4">
|
||||||
|
{error ? `載入失敗: ${error}` : '沒有找到評審結果'}
|
||||||
|
</p>
|
||||||
|
<div className="space-x-2">
|
||||||
<Button onClick={() => window.location.href = '/upload'}>
|
<Button onClick={() => window.location.href = '/upload'}>
|
||||||
重新進行評審
|
重新進行評審
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button variant="outline" onClick={() => window.location.href = '/history'}>
|
||||||
|
查看歷史記錄
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -196,7 +230,7 @@ export default function ResultsPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用真實數據或回退到模擬數據
|
// 使用真實數據或回退到模擬數據
|
||||||
const results = evaluationData.evaluation?.fullData || mockResults
|
const results = evaluationData.evaluation?.fullData || evaluationData || mockResults
|
||||||
|
|
||||||
// 計算統計數據 - 基於 criteria_items 的平均分作為閾值
|
// 計算統計數據 - 基於 criteria_items 的平均分作為閾值
|
||||||
const calculateOverview = (criteria: any[]) => {
|
const calculateOverview = (criteria: any[]) => {
|
||||||
|
66
check-feedback-data.js
Normal file
66
check-feedback-data.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
const mysql = require('mysql2/promise');
|
||||||
|
|
||||||
|
const dbConfig = {
|
||||||
|
host: 'mysql.theaken.com',
|
||||||
|
port: 33306,
|
||||||
|
user: 'root',
|
||||||
|
password: 'zh6161168',
|
||||||
|
database: 'db_AI_scoring'
|
||||||
|
};
|
||||||
|
|
||||||
|
async function checkFeedbackData() {
|
||||||
|
let connection;
|
||||||
|
try {
|
||||||
|
console.log('🔗 連接到資料庫...');
|
||||||
|
connection = await mysql.createConnection(dbConfig);
|
||||||
|
console.log('✅ 資料庫連接成功');
|
||||||
|
|
||||||
|
// 檢查 evaluation_feedback 表結構
|
||||||
|
console.log('📋 檢查 evaluation_feedback 表結構...');
|
||||||
|
const [columns] = await connection.execute(`
|
||||||
|
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_NAME = 'evaluation_feedback'
|
||||||
|
ORDER BY ORDINAL_POSITION
|
||||||
|
`);
|
||||||
|
console.log('表結構:', columns);
|
||||||
|
|
||||||
|
// 檢查 evaluation_feedback 數據
|
||||||
|
console.log('📊 檢查 evaluation_feedback 數據...');
|
||||||
|
const [feedbackData] = await connection.execute(`
|
||||||
|
SELECT id, evaluation_id, criteria_item_id, feedback_type, content, sort_order
|
||||||
|
FROM evaluation_feedback
|
||||||
|
WHERE evaluation_id = 2
|
||||||
|
ORDER BY sort_order
|
||||||
|
`);
|
||||||
|
console.log('評審 ID 2 的反饋數據:', feedbackData);
|
||||||
|
|
||||||
|
// 檢查不同 feedback_type 的數據
|
||||||
|
console.log('🔍 檢查不同 feedback_type 的數據...');
|
||||||
|
const [typeStats] = await connection.execute(`
|
||||||
|
SELECT feedback_type, COUNT(*) as count
|
||||||
|
FROM evaluation_feedback
|
||||||
|
GROUP BY feedback_type
|
||||||
|
`);
|
||||||
|
console.log('feedback_type 統計:', typeStats);
|
||||||
|
|
||||||
|
// 檢查 criteria_items 表
|
||||||
|
console.log('📝 檢查 criteria_items 表...');
|
||||||
|
const [criteriaItems] = await connection.execute(`
|
||||||
|
SELECT id, name, description
|
||||||
|
FROM criteria_items
|
||||||
|
ORDER BY id
|
||||||
|
`);
|
||||||
|
console.log('評分標準項目:', criteriaItems);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 錯誤:', error.message);
|
||||||
|
} finally {
|
||||||
|
if (connection) {
|
||||||
|
await connection.end();
|
||||||
|
console.log('🔌 資料庫連接已關閉');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFeedbackData();
|
61
check-strength-improvement.js
Normal file
61
check-strength-improvement.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
const mysql = require('mysql2/promise');
|
||||||
|
|
||||||
|
const dbConfig = {
|
||||||
|
host: 'mysql.theaken.com',
|
||||||
|
port: 33306,
|
||||||
|
user: 'root',
|
||||||
|
password: 'zh6161168',
|
||||||
|
database: 'db_AI_scoring'
|
||||||
|
};
|
||||||
|
|
||||||
|
async function checkStrengthImprovement() {
|
||||||
|
let connection;
|
||||||
|
try {
|
||||||
|
console.log('🔗 連接到資料庫...');
|
||||||
|
connection = await mysql.createConnection(dbConfig);
|
||||||
|
console.log('✅ 資料庫連接成功');
|
||||||
|
|
||||||
|
// 檢查 strength 類型的數據
|
||||||
|
console.log('💪 檢查 strength 類型的數據...');
|
||||||
|
const [strengthData] = await connection.execute(`
|
||||||
|
SELECT ef.id, ef.evaluation_id, ef.criteria_item_id, ef.content, ci.name as criteria_name
|
||||||
|
FROM evaluation_feedback ef
|
||||||
|
LEFT JOIN criteria_items ci ON ef.criteria_item_id = ci.id
|
||||||
|
WHERE ef.evaluation_id = 2 AND ef.feedback_type = 'strength'
|
||||||
|
ORDER BY ef.criteria_item_id, ef.sort_order
|
||||||
|
`);
|
||||||
|
console.log('優點數據:', strengthData);
|
||||||
|
|
||||||
|
// 檢查 improvement 類型的數據
|
||||||
|
console.log('🔧 檢查 improvement 類型的數據...');
|
||||||
|
const [improvementData] = await connection.execute(`
|
||||||
|
SELECT ef.id, ef.evaluation_id, ef.criteria_item_id, ef.content, ci.name as criteria_name
|
||||||
|
FROM evaluation_feedback ef
|
||||||
|
LEFT JOIN criteria_items ci ON ef.criteria_item_id = ci.id
|
||||||
|
WHERE ef.evaluation_id = 2 AND ef.feedback_type = 'improvement'
|
||||||
|
ORDER BY ef.criteria_item_id, ef.sort_order
|
||||||
|
`);
|
||||||
|
console.log('改進建議數據:', improvementData);
|
||||||
|
|
||||||
|
// 檢查 criteria 類型的數據(AI 評語)
|
||||||
|
console.log('💬 檢查 criteria 類型的數據(AI 評語)...');
|
||||||
|
const [criteriaData] = await connection.execute(`
|
||||||
|
SELECT ef.id, ef.evaluation_id, ef.criteria_item_id, ef.content, ci.name as criteria_name
|
||||||
|
FROM evaluation_feedback ef
|
||||||
|
LEFT JOIN criteria_items ci ON ef.criteria_item_id = ci.id
|
||||||
|
WHERE ef.evaluation_id = 2 AND ef.feedback_type = 'criteria'
|
||||||
|
ORDER BY ef.criteria_item_id, ef.sort_order
|
||||||
|
`);
|
||||||
|
console.log('AI 評語數據:', criteriaData);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 錯誤:', error.message);
|
||||||
|
} finally {
|
||||||
|
if (connection) {
|
||||||
|
await connection.end();
|
||||||
|
console.log('🔌 資料庫連接已關閉');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkStrengthImprovement();
|
25
test-api.js
Normal file
25
test-api.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
async function testAPI() {
|
||||||
|
try {
|
||||||
|
console.log('🧪 測試 API 端點...');
|
||||||
|
|
||||||
|
// 測試統計API
|
||||||
|
console.log('📊 測試統計API...');
|
||||||
|
const statsResponse = await fetch('http://localhost:3000/api/history/stats');
|
||||||
|
const statsData = await statsResponse.json();
|
||||||
|
console.log('統計數據:', statsData);
|
||||||
|
|
||||||
|
// 測試評審詳細API
|
||||||
|
console.log('📋 測試評審詳細API...');
|
||||||
|
const detailResponse = await fetch('http://localhost:3000/api/evaluation/2');
|
||||||
|
const detailData = await detailResponse.json();
|
||||||
|
console.log('評審詳細數據:', JSON.stringify(detailData, null, 2));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 測試失敗:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待一下讓服務器啟動
|
||||||
|
setTimeout(testAPI, 3000);
|
74
test-chart-data.js
Normal file
74
test-chart-data.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
function makeRequest(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.get(url, (res) => {
|
||||||
|
let data = '';
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
const jsonData = JSON.parse(data);
|
||||||
|
resolve(jsonData);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testChartData() {
|
||||||
|
try {
|
||||||
|
console.log('🧪 測試圖表數據生成...');
|
||||||
|
|
||||||
|
// 測試評審詳細API
|
||||||
|
console.log('📋 測試評審詳細API (ID=2)...');
|
||||||
|
const detailData = await makeRequest('http://localhost:3000/api/evaluation/2');
|
||||||
|
|
||||||
|
if (detailData.success) {
|
||||||
|
console.log('✅ API 調用成功');
|
||||||
|
console.log('📊 專案標題:', detailData.data.projectTitle);
|
||||||
|
console.log('📊 總分:', detailData.data.overallScore);
|
||||||
|
|
||||||
|
// 檢查圖表數據
|
||||||
|
console.log('\n📊 圖表數據檢查:');
|
||||||
|
|
||||||
|
console.log('\n📈 各項目得分 (Bar Chart):');
|
||||||
|
detailData.data.chartData.barChart.forEach((item, index) => {
|
||||||
|
console.log(` ${index + 1}. ${item.name}: ${item.score}分`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\n🥧 權重分布 (Pie Chart):');
|
||||||
|
detailData.data.chartData.pieChart.forEach((item, index) => {
|
||||||
|
console.log(` ${index + 1}. ${item.name}: 權重=${item.weight}%, 加權分數=${item.value}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\n🎯 能力雷達圖 (Radar Chart):');
|
||||||
|
detailData.data.chartData.radarChart.forEach((item, index) => {
|
||||||
|
console.log(` ${index + 1}. ${item.subject}: ${item.score}/${item.fullMark}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 檢查評分標準詳情
|
||||||
|
console.log('\n📝 評分標準詳情:');
|
||||||
|
detailData.data.criteria.forEach((criteria, index) => {
|
||||||
|
console.log(` ${index + 1}. ${criteria.name}: ${criteria.score}/${criteria.maxScore}`);
|
||||||
|
console.log(` AI 評語: ${criteria.feedback}`);
|
||||||
|
console.log(` 優點: ${criteria.strengths.length > 0 ? criteria.strengths.join(', ') : '無'}`);
|
||||||
|
console.log(` 改進建議: ${criteria.improvements.length > 0 ? criteria.improvements.join(', ') : '無'}`);
|
||||||
|
console.log('');
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('❌ API 調用失敗:', detailData.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 測試失敗:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待一下讓服務器啟動
|
||||||
|
setTimeout(testChartData, 2000);
|
40
test-database.js
Normal file
40
test-database.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const mysql = require('mysql2/promise');
|
||||||
|
|
||||||
|
const dbConfig = {
|
||||||
|
host: 'mysql.theaken.com',
|
||||||
|
port: 33306,
|
||||||
|
user: 'root',
|
||||||
|
password: 'zh6161168',
|
||||||
|
database: 'db_AI_scoring'
|
||||||
|
};
|
||||||
|
|
||||||
|
async function checkData() {
|
||||||
|
let connection;
|
||||||
|
try {
|
||||||
|
console.log('🔗 連接到資料庫...');
|
||||||
|
connection = await mysql.createConnection(dbConfig);
|
||||||
|
console.log('✅ 資料庫連接成功');
|
||||||
|
|
||||||
|
// 檢查評審記錄
|
||||||
|
const [evaluations] = await connection.execute('SELECT id, project_id, overall_score, grade FROM evaluations LIMIT 5');
|
||||||
|
console.log('📊 評審記錄:', evaluations);
|
||||||
|
|
||||||
|
// 檢查專案記錄
|
||||||
|
const [projects] = await connection.execute('SELECT id, title, status FROM projects LIMIT 5');
|
||||||
|
console.log('📋 專案記錄:', projects);
|
||||||
|
|
||||||
|
// 檢查評分記錄
|
||||||
|
const [scores] = await connection.execute('SELECT id, evaluation_id, criteria_item_id, score FROM evaluation_scores LIMIT 5');
|
||||||
|
console.log('🎯 評分記錄:', scores);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 錯誤:', error.message);
|
||||||
|
} finally {
|
||||||
|
if (connection) {
|
||||||
|
await connection.end();
|
||||||
|
console.log('🔌 資料庫連接已關閉');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkData();
|
66
test-fixed-api.js
Normal file
66
test-fixed-api.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
function makeRequest(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.get(url, (res) => {
|
||||||
|
let data = '';
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
const jsonData = JSON.parse(data);
|
||||||
|
resolve(jsonData);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testFixedAPI() {
|
||||||
|
try {
|
||||||
|
console.log('🧪 測試修正後的 API...');
|
||||||
|
|
||||||
|
// 測試評審詳細API
|
||||||
|
console.log('📋 測試評審詳細API (ID=2)...');
|
||||||
|
const detailData = await makeRequest('http://localhost:3000/api/evaluation/2');
|
||||||
|
|
||||||
|
if (detailData.success) {
|
||||||
|
console.log('✅ API 調用成功');
|
||||||
|
console.log('📊 專案標題:', detailData.data.projectTitle);
|
||||||
|
console.log('📊 總分:', detailData.data.overallScore);
|
||||||
|
console.log('📊 等級:', detailData.data.grade);
|
||||||
|
|
||||||
|
console.log('\n📝 評分標準詳情:');
|
||||||
|
detailData.data.criteria.forEach((criteria, index) => {
|
||||||
|
console.log(` ${index + 1}. ${criteria.name}: ${criteria.score}/${criteria.maxScore}`);
|
||||||
|
console.log(` AI 評語: ${criteria.feedback}`);
|
||||||
|
console.log(` 優點: ${criteria.strengths.length > 0 ? criteria.strengths.join(', ') : '無'}`);
|
||||||
|
console.log(` 改進建議: ${criteria.improvements.length > 0 ? criteria.improvements.join(', ') : '無'}`);
|
||||||
|
console.log('');
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('📋 詳細分析:');
|
||||||
|
console.log(' 摘要:', detailData.data.detailedAnalysis.summary);
|
||||||
|
console.log(' 關鍵發現:', detailData.data.detailedAnalysis.keyFindings);
|
||||||
|
|
||||||
|
console.log('\n💡 改進建議:');
|
||||||
|
console.log(' 整體建議:', detailData.data.improvementSuggestions.overallSuggestions);
|
||||||
|
console.log(' 保持優勢:', detailData.data.improvementSuggestions.maintainStrengths);
|
||||||
|
console.log(' 關鍵改進:', detailData.data.improvementSuggestions.keyImprovements);
|
||||||
|
console.log(' 行動計劃:', detailData.data.improvementSuggestions.actionPlan);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('❌ API 調用失敗:', detailData.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 測試失敗:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待一下讓服務器啟動
|
||||||
|
setTimeout(testFixedAPI, 2000);
|
BIN
uploads/projects/24/1758637006045_3d4e2i2yb.pptx
Normal file
BIN
uploads/projects/24/1758637006045_3d4e2i2yb.pptx
Normal file
Binary file not shown.
Reference in New Issue
Block a user