-
+
+
{sidebarOpen && (
diff --git a/components/chat-bot.tsx b/components/chat-bot.tsx
index 38d540c..0be300f 100644
--- a/components/chat-bot.tsx
+++ b/components/chat-bot.tsx
@@ -25,8 +25,8 @@ interface Message {
quickQuestions?: string[]
}
-const DEEPSEEK_API_KEY = process.env.NEXT_PUBLIC_DEEPSEEK_API_KEY || "sk-30cac9533e5b451fa1e277fe34a7f64b"
-const DEEPSEEK_API_URL = process.env.NEXT_PUBLIC_DEEPSEEK_API_URL || "https://api.deepseek.com/v1/chat/completions"
+const GEMINI_API_KEY = process.env.NEXT_PUBLIC_GEMINI_API_KEY || "AIzaSyAN3pEJr_Vn2xkCidGZAq9eQqsMVvpj8g4"
+const GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent"
const systemPrompt = generateSystemPrompt()
@@ -77,63 +77,62 @@ export function ChatBot() {
.trim()
}
- const callDeepSeekAPI = async (userMessage: string): Promise => {
+ const callGeminiAPI = async (userMessage: string): Promise => {
try {
// 構建對話歷史,只保留最近的幾條對話
const recentMessages = messages
.filter(msg => msg.sender === "user")
.slice(-5) // 只保留最近5條用戶消息
- .map(msg => ({
- role: "user" as const,
- content: msg.text
- }))
+ .map(msg => msg.text)
- const response = await fetch(DEEPSEEK_API_URL, {
+ // 構建完整的對話內容
+ const conversationHistory = recentMessages.length > 0
+ ? `之前的對話:\n${recentMessages.map(msg => `用戶:${msg}`).join('\n')}\n\n`
+ : ''
+
+ const fullPrompt = `${systemPrompt}\n\n${conversationHistory}用戶:${userMessage}`
+
+ const response = await fetch(`${GEMINI_API_URL}?key=${GEMINI_API_KEY}`, {
method: "POST",
headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${DEEPSEEK_API_KEY}`
+ "Content-Type": "application/json"
},
body: JSON.stringify({
- model: "deepseek-chat",
- messages: [
- {
- role: "system",
- content: systemPrompt
- },
- ...recentMessages,
- {
- role: "user",
- content: userMessage
- }
- ],
- max_tokens: 300,
- temperature: 0.7,
- stream: false
+ contents: [{
+ parts: [{
+ text: fullPrompt
+ }]
+ }],
+ generationConfig: {
+ maxOutputTokens: 300,
+ temperature: 0.7,
+ topP: 0.8,
+ topK: 10
+ }
})
})
if (!response.ok) {
const errorText = await response.text()
- console.error("API Error:", response.status, errorText)
+ console.error("Gemini API Error:", response.status, errorText)
throw new Error(`API request failed: ${response.status} - ${errorText}`)
}
const data = await response.json()
- if (!data.choices || !data.choices[0] || !data.choices[0].message) {
- console.error("Invalid API response:", data)
+ if (!data.candidates || !data.candidates[0] || !data.candidates[0].content) {
+ console.error("Invalid Gemini API response:", data)
throw new Error("Invalid API response format")
}
- const rawResponse = data.choices[0].message.content || "抱歉,我現在無法回答您的問題,請稍後再試。"
+ const rawResponse = data.candidates[0].content.parts[0].text || "抱歉,我現在無法回答您的問題,請稍後再試。"
return cleanResponse(rawResponse)
} catch (error) {
- console.error("DeepSeek API error:", error)
+ console.error("Gemini API error:", error)
// 根據錯誤類型提供不同的錯誤信息
if (error instanceof Error) {
- if (error.message.includes('401')) {
+ if (error.message.includes('401') || error.message.includes('403')) {
return "API 密鑰無效,請聯繫管理員檢查配置。"
} else if (error.message.includes('429')) {
return "API 請求過於頻繁,請稍後再試。"
@@ -261,7 +260,7 @@ export function ChatBot() {
setIsLoading(true)
try {
- const aiResponse = await callDeepSeekAPI(text)
+ const aiResponse = await callGeminiAPI(text)
const botMessage: Message = {
id: (Date.now() + 1).toString(),
diff --git a/contexts/competition-context.tsx b/contexts/competition-context.tsx
index cd0f617..f6c8e7e 100644
--- a/contexts/competition-context.tsx
+++ b/contexts/competition-context.tsx
@@ -133,14 +133,20 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
const competitionsData = await competitionsResponse.json()
console.log('📋 競賽API回應:', competitionsData)
- if (competitionsData.success && competitionsData.data) {
- // 確保每個競賽都有judges屬性
- const competitionsWithJudges = competitionsData.data.map((comp: any) => ({
- ...comp,
- judges: comp.judges || []
- }))
- setCompetitions(competitionsWithJudges)
- console.log('✅ 競賽數據載入成功:', competitionsWithJudges.length, '個競賽')
+ if (competitionsData.success) {
+ if (competitionsData.data && competitionsData.data.length > 0) {
+ // 確保每個競賽都有judges屬性
+ const competitionsWithJudges = competitionsData.data.map((comp: any) => ({
+ ...comp,
+ judges: comp.judges || []
+ }))
+ setCompetitions(competitionsWithJudges)
+ console.log('✅ 競賽數據載入成功:', competitionsWithJudges.length, '個競賽')
+ } else {
+ // 沒有競賽資料是正常情況,不報錯
+ setCompetitions([])
+ console.log('ℹ️ 暫無競賽數據')
+ }
} else {
console.error('❌ 競賽數據載入失敗:', competitionsData.message)
// 設置空數組以避免undefined錯誤
@@ -152,14 +158,20 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
const currentData = await currentResponse.json()
console.log('🏆 當前競賽API回應:', currentData)
- if (currentData.success && currentData.data) {
- // 確保當前競賽也有judges屬性
- const currentCompetitionWithJudges = {
- ...currentData.data,
- judges: currentData.data.judges || []
+ if (currentData.success) {
+ if (currentData.data) {
+ // 確保當前競賽也有judges屬性
+ const currentCompetitionWithJudges = {
+ ...currentData.data,
+ judges: currentData.data.judges || []
+ }
+ setCurrentCompetition(currentCompetitionWithJudges)
+ console.log('✅ 當前競賽載入成功:', currentCompetitionWithJudges.name)
+ } else {
+ // 沒有當前競賽是正常情況,不報錯
+ setCurrentCompetition(null)
+ console.log('ℹ️ 暫無當前競賽')
}
- setCurrentCompetition(currentCompetitionWithJudges)
- console.log('✅ 當前競賽載入成功:', currentCompetitionWithJudges.name)
} else {
console.error('❌ 當前競賽載入失敗:', currentData.message)
}
@@ -170,9 +182,14 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
const judgesData = await judgesResponse.json()
console.log('評審API回應:', judgesData)
- if (judgesData.success && judgesData.data) {
- setJudges(judgesData.data)
- console.log('✅ 評審數據載入成功:', judgesData.data.length, '個評審')
+ if (judgesData.success) {
+ if (judgesData.data && judgesData.data.length > 0) {
+ setJudges(judgesData.data)
+ console.log('✅ 評審數據載入成功:', judgesData.data.length, '個評審')
+ } else {
+ setJudges([])
+ console.log('ℹ️ 暫無評審數據')
+ }
} else {
console.error('❌ 評審數據載入失敗:', judgesData.message)
setJudges([])
diff --git a/env.example b/env.example
index 2da76f8..a28f8d4 100644
--- a/env.example
+++ b/env.example
@@ -28,7 +28,12 @@ DB_CONNECTION_TIMEOUT=5000
DB_RETRY_ATTEMPTS=3
DB_RETRY_DELAY=2000
-# DeepSeek API 配置
+# ===== AI API 配置 =====
+# Gemini API 配置 (主要使用)
+NEXT_PUBLIC_GEMINI_API_KEY=AIzaSyAN3pEJr_Vn2xkCidGZAq9eQqsMVvpj8g4
+NEXT_PUBLIC_GEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent
+
+# DeepSeek API 配置 (備用)
NEXT_PUBLIC_DEEPSEEK_API_KEY=your_deepseek_api_key_here
NEXT_PUBLIC_DEEPSEEK_API_URL=https://api.deepseek.com/v1/chat/completions
diff --git a/lib/ai-knowledge-base.ts b/lib/ai-knowledge-base.ts
index a78f9db..b899103 100644
--- a/lib/ai-knowledge-base.ts
+++ b/lib/ai-knowledge-base.ts
@@ -194,13 +194,13 @@ export const platformKnowledge = {
// 常見問題
faq: {
- "忘記密碼怎麼辦": "點擊登入頁面的「忘記密碼」連結,輸入註冊時的電子郵件,系統會發送重設密碼的連結到您的信箱。",
- "如何修改個人資料": "登入後點擊右上角頭像,選擇「個人資料」,即可修改姓名、部門、頭像等信息。",
- "為什麼看不到某些競賽": "可能是因為競賽尚未開始、已結束,或者您沒有參與權限。請聯繫管理員確認。",
- "評分什麼時候會公布": "評分結果會在競賽結束後由管理員統一公布,請關注平台通知。",
- "如何聯繫管理員": "可以通過平台內的通知系統或直接發送郵件給管理員。",
- "作品提交後可以修改嗎": "作品提交後無法修改,請在提交前仔細檢查所有信息。",
- "如何查看我的參賽記錄": "登入後進入「我的競賽」頁面,可以查看所有參賽記錄和狀態。"
+ "忘記密碼怎麼辦": "1. 點擊登入頁面「忘記密碼」\n2. 輸入註冊電子郵件\n3. 檢查信箱重設連結\n4. 按連結重設密碼",
+ "如何修改個人資料": "1. 登入後點擊右上角頭像\n2. 選擇「個人資料」\n3. 修改姓名、部門、頭像\n4. 保存更改",
+ "為什麼看不到某些競賽": "可能原因:\n• 競賽尚未開始\n• 競賽已結束\n• 沒有參與權限\n請聯繫管理員確認",
+ "評分什麼時候會公布": "評分公布時間:\n• 競賽結束後\n• 由管理員統一公布\n• 關注平台通知",
+ "如何聯繫管理員": "聯繫方式:\n• 平台內通知系統\n• 直接發送郵件\n• 查看管理員聯絡資訊",
+ "作品提交後可以修改嗎": "作品提交規則:\n• 提交後無法修改\n• 請提交前仔細檢查\n• 確認所有信息正確",
+ "如何查看我的參賽記錄": "查看步驟:\n1. 登入平台\n2. 進入「我的競賽」\n3. 查看參賽記錄和狀態"
},
// 技術信息
@@ -235,13 +235,15 @@ ${Object.entries(platformKnowledge.modules.backend).map(([key, value]) =>
回答指南:
1. 用友善、專業的語氣回答用戶問題
-2. 提供具體的操作步驟和實用建議
-3. 回答要簡潔明瞭,避免過長的文字
-4. 如果問題涉及具體操作,請提供詳細步驟
-5. 如果不知道答案,請誠實說明並建議聯繫管理員
-6. 不要使用任何 Markdown 格式,只使用純文字回答
-7. 不要使用 **、*、#、- 等符號
-8. 回答長度控制在 200 字以內
+2. 回答要簡潔明瞭,優先使用條列式格式
+3. 操作步驟用數字編號,每個步驟一行
+4. 重要信息用簡短要點列出
+5. 避免長段落文字,多用換行分段
+6. 如果不知道答案,請誠實說明並建議聯繫管理員
+7. 不要使用任何 Markdown 格式,只使用純文字回答
+8. 不要使用 **、*、#、- 等符號
+9. 回答長度控制在 150 字以內
+10. 優先提供實用的操作步驟和要點
常見問題快速回答:
${Object.entries(platformKnowledge.faq).map(([question, answer]) =>
diff --git a/public/ai-cloud.png b/public/ai-cloud.png
new file mode 100644
index 0000000..e6149af
Binary files /dev/null and b/public/ai-cloud.png differ
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..e6149af
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000..753922b
Binary files /dev/null and b/public/logo.png differ