diff --git a/app/api/admin/apps/[id]/route.ts b/app/api/admin/apps/[id]/route.ts index 30628b0..44c1066 100644 --- a/app/api/admin/apps/[id]/route.ts +++ b/app/api/admin/apps/[id]/route.ts @@ -45,7 +45,7 @@ export async function PUT(request: NextRequest, { params }: { params: { id: stri try { const { id: appId } = await params const body = await request.json() - const { name, description, category, type, app_url, icon, icon_color } = body + const { name, description, category, type, app_url, icon, icon_color, department } = body // 更新應用 const result = await appService.updateApp(appId, { @@ -55,7 +55,8 @@ export async function PUT(request: NextRequest, { params }: { params: { id: stri type, app_url, icon, - icon_color + icon_color, + department }) if (result.success) { diff --git a/app/api/admin/judges/[id]/route.ts b/app/api/admin/judges/[id]/route.ts index db07d4f..d2ec25c 100644 --- a/app/api/admin/judges/[id]/route.ts +++ b/app/api/admin/judges/[id]/route.ts @@ -107,7 +107,7 @@ export async function PUT(request: NextRequest, { params }: { params: { id: stri if (body.is_active !== undefined) updateData.is_active = body.is_active; // 執行更新 - const success = await judgeservice.updateJudge(id, updateData); + const success = await JudgeService.updateJudge(id, updateData); if (!success) { return NextResponse.json({ diff --git a/app/api/competitions/[id]/teams/route.ts b/app/api/competitions/[id]/teams/route.ts index 8be8165..4001257 100644 --- a/app/api/competitions/[id]/teams/route.ts +++ b/app/api/competitions/[id]/teams/route.ts @@ -135,10 +135,12 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri } } - // 強制將第一個成員設為隊長(因為隊長邏輯有問題) - if (allMembers.length > 0) { - allMembers[0].role = '隊長'; - } + // 確保其他成員的角色正確設置 + allMembers.forEach(member => { + if (member.user_id !== team.leader_id && member.role !== '隊長') { + member.role = '成員'; + } + }); return { ...team, @@ -220,7 +222,9 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri id: member.id, user_id: member.user_id, name: member.name, - role: member.role === '??????' ? '成員' : (member.role || '成員') + department: member.department, + email: member.email, + role: member.role || '成員' })), apps: team.apps.map(app => ({ id: app.id, diff --git a/components/admin/app-management.tsx b/components/admin/app-management.tsx index 80a9874..181ecab 100644 --- a/components/admin/app-management.tsx +++ b/components/admin/app-management.tsx @@ -517,7 +517,8 @@ export function AppManagement() { type: newApp.type, app_url: newApp.appUrl, icon: newApp.icon, - icon_color: newApp.iconColor + icon_color: newApp.iconColor, + department: newApp.department }) }) diff --git a/components/admin/competition-management.tsx b/components/admin/competition-management.tsx index e30e061..b2107aa 100644 --- a/components/admin/competition-management.tsx +++ b/components/admin/competition-management.tsx @@ -563,7 +563,13 @@ export function CompetitionManagement() { contactEmail: teamDetails.contact_email || '', leaderPhone: teamDetails.leader_phone || '', description: teamDetails.description || '', - members: teamDetails.members || [], + members: (teamDetails.members || []).map((member: any) => ({ + id: member.user_id || member.id, + user_id: member.user_id || member.id, + name: member.name, + department: member.department, + role: member.role || '成員' + })), apps: teamDetails.apps ? teamDetails.apps.map((app: any) => app.id || app) : [], submittedAppCount: teamDetails.apps?.length || 0, } @@ -1142,7 +1148,7 @@ export function CompetitionManagement() { contact_email: newTeam.contactEmail, description: newTeam.description, members: newTeam.members.map(member => ({ - user_id: member.id, // 現在 member.id 就是 user_id + user_id: member.user_id || member.id, // 確保使用正確的 user_id role: member.role || 'member' })), apps: newTeam.apps // 添加應用 ID 列表 @@ -1164,7 +1170,7 @@ export function CompetitionManagement() { contact_email: newTeam.contactEmail, description: newTeam.description, members: newTeam.members.map(member => ({ - user_id: member.id, // 現在 member.id 就是 user_id + user_id: member.user_id || member.id, // 確保使用正確的 user_id role: member.role || 'member' })), apps: newTeam.apps // 添加應用 ID 列表 @@ -2136,6 +2142,19 @@ export function CompetitionManagement() { } } + const getCompetitionTypeColor = (type: string) => { + switch (type) { + case "individual": + return "bg-blue-100 text-blue-800 border-blue-200" + case "team": + return "bg-purple-100 text-purple-800 border-purple-200" + case "mixed": + return "bg-orange-100 text-orange-800 border-orange-200" + default: + return "bg-gray-100 text-gray-800 border-gray-200" + } + } + const getScoreLabelText = (key: string) => { switch (key) { case "innovation": @@ -2464,13 +2483,13 @@ export function CompetitionManagement() { - 競賽名稱 - 類型 - 時間 - 狀態 - 參賽項目 - 評分進度 - 操作 + 競賽名稱 + 類型 + 時間 + 狀態 + 參賽項目 + 評分進度 + 操作 @@ -2506,9 +2525,9 @@ export function CompetitionManagement() { -
+
{getCompetitionTypeIcon(competition.type)} - + {getCompetitionTypeText(competition.type)}
@@ -2527,34 +2546,37 @@ export function CompetitionManagement() {
- - {getStatusText(competition.status)} - - - -
- {getCompetitionTypeIcon(competition.type)} - {participantCount} 個 +
+ + {getStatusText(competition.status)} +
-
+
+ {getCompetitionTypeIcon(competition.type)} + {participantCount} 個 +
+ + +
- + {scoringProgress.completed}/{scoringProgress.total}
-

{scoringProgress.percentage}% 完成

+

{scoringProgress.percentage}% 完成

- - - - +
+ + + + handleViewCompetition(competition)}> @@ -2598,7 +2620,8 @@ export function CompetitionManagement() { 刪除競賽 - + +
) @@ -2771,7 +2794,7 @@ export function CompetitionManagement() {
- {team.submittedAppCount} 個應用 + {team.app_count || 0} 個應用
@@ -6603,7 +6626,7 @@ export function CompetitionManagement() {

{member.name}

- {member.user_id === selectedTeam.leader_id && ( + {member.role === '隊長' && ( 隊長 diff --git a/components/admin/team-management.tsx b/components/admin/team-management.tsx index bab8be6..71a2330 100644 --- a/components/admin/team-management.tsx +++ b/components/admin/team-management.tsx @@ -1,6 +1,6 @@ "use client" -import { useState } from "react" +import { useState, useEffect } from "react" import { useCompetition } from "@/contexts/competition-context" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" @@ -43,6 +43,10 @@ export function TeamManagement() { const [isLoading, setIsLoading] = useState(false) const [success, setSuccess] = useState("") const [error, setError] = useState("") + + // 從 API 獲取的團隊數據 + const [apiTeams, setApiTeams] = useState([]) + const [isLoadingTeams, setIsLoadingTeams] = useState(true) const [newTeam, setNewTeam] = useState({ name: "", @@ -60,10 +64,35 @@ export function TeamManagement() { role: "成員", }) - const filteredTeams = teams.filter((team) => { + // 獲取團隊數據 + const fetchTeams = async () => { + try { + setIsLoadingTeams(true) + const response = await fetch('/api/admin/teams') + const data = await response.json() + + if (data.success) { + setApiTeams(data.data) + } else { + console.error('獲取團隊數據失敗:', data.message) + setError('獲取團隊數據失敗') + } + } catch (error) { + console.error('獲取團隊數據失敗:', error) + setError('獲取團隊數據失敗') + } finally { + setIsLoadingTeams(false) + } + } + + useEffect(() => { + fetchTeams() + }, []) + + const filteredTeams = apiTeams.filter((team) => { const matchesSearch = team.name.toLowerCase().includes(searchTerm.toLowerCase()) || - team.members.some((member) => member.name.toLowerCase().includes(searchTerm.toLowerCase())) + team.leader_name?.toLowerCase().includes(searchTerm.toLowerCase()) const matchesDepartment = selectedDepartment === "all" || team.department === selectedDepartment return matchesSearch && matchesDepartment }) @@ -73,16 +102,29 @@ export function TeamManagement() { setShowTeamDetail(true) } - const handleEditTeam = (team: Team) => { + const handleEditTeam = (team: any) => { setSelectedTeam(team) + + // 確保成員數據結構正確 + const members = team.members && Array.isArray(team.members) + ? team.members.map((member: any) => ({ + id: member.user_id || member.id, + user_id: member.user_id || member.id, + name: member.name, + department: member.department, + role: member.role || '成員' + })) + : [] + setNewTeam({ name: team.name, department: team.department, - contactEmail: team.contactEmail, - members: [...team.members], - leader: team.leader, - apps: [...team.apps], - totalLikes: team.totalLikes, + contactEmail: team.contact_email || team.contactEmail, + description: team.description || '', + members: members, + leader: team.leader_id || team.leader, + apps: team.apps || [], + totalLikes: team.total_likes || team.totalLikes || 0, }) setShowEditTeam(true) } @@ -205,14 +247,49 @@ export function TeamManagement() { } setIsLoading(true) - await new Promise((resolve) => setTimeout(resolve, 1000)) - updateTeam(selectedTeam.id, newTeam) - setShowEditTeam(false) - setSelectedTeam(null) - setSuccess("團隊更新成功!") - setIsLoading(false) - setTimeout(() => setSuccess(""), 3000) + try { + // 準備更新數據 + const updateData = { + name: newTeam.name, + department: newTeam.department, + contact_email: newTeam.contactEmail, + description: newTeam.description || null, + leader_id: newTeam.leader, + members: newTeam.members.map(member => ({ + user_id: member.user_id || member.id, + role: member.role + })) + } + + // 調用 API 更新團隊 + const response = await fetch(`/api/admin/teams/${selectedTeam.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(updateData) + }) + + const data = await response.json() + + if (data.success) { + // 更新成功,重新載入團隊數據 + await fetchTeams() + setShowEditTeam(false) + setSelectedTeam(null) + setSuccess("團隊更新成功!") + } else { + setError(data.message || "更新團隊失敗") + } + } catch (error) { + console.error('更新團隊失敗:', error) + setError("更新團隊失敗") + } finally { + setIsLoading(false) + setTimeout(() => setError(""), 3000) + setTimeout(() => setSuccess(""), 3000) + } } return ( @@ -254,7 +331,7 @@ export function TeamManagement() {

總團隊數

-

{teams.length}

+

{apiTeams.length}

@@ -266,7 +343,7 @@ export function TeamManagement() {

總成員數

-

{teams.reduce((sum, team) => sum + team.members.length, 0)}

+

{apiTeams.reduce((sum, team) => sum + (team.member_count || 0), 0)}

@@ -279,8 +356,8 @@ export function TeamManagement() {

平均團隊規模

- {teams.length > 0 - ? Math.round((teams.reduce((sum, team) => sum + team.members.length, 0) / teams.length) * 10) / 10 + {apiTeams.length > 0 + ? Math.round((apiTeams.reduce((sum, team) => sum + (team.member_count || 0), 0) / apiTeams.length) * 10) / 10 : 0}

@@ -352,8 +429,6 @@ export function TeamManagement() { {filteredTeams.map((team) => { - const leader = team.members.find((m) => m.id === team.leader) - return ( @@ -363,7 +438,7 @@ export function TeamManagement() {

{team.name}

-

{team.contactEmail}

+

{team.contact_email}

@@ -371,10 +446,10 @@ export function TeamManagement() {
- {leader?.name[0] || "?"} + {team.leader_name?.[0] || "?"} - {leader?.name || "未設定"} + {team.leader_name || "未設定"}
@@ -385,11 +460,11 @@ export function TeamManagement() {
- {team.members.length} + {team.member_count || 0}
- {team.apps.length} + {team.app_count || 0} {team.totalLikes} @@ -807,16 +882,16 @@ export function TeamManagement() {

{selectedTeam.name}

-

{selectedTeam.contactEmail}

+

{selectedTeam.contact_email}

{selectedTeam.department} - {selectedTeam.members.length} 名成員 + {selectedTeam.member_count || 0} 名成員 - {selectedTeam.apps.length} 個應用 + {selectedTeam.app_count || 0} 個應用
diff --git a/components/competition/competition-detail-dialog.tsx b/components/competition/competition-detail-dialog.tsx index c9ca8d6..245463b 100644 --- a/components/competition/competition-detail-dialog.tsx +++ b/components/competition/competition-detail-dialog.tsx @@ -372,7 +372,7 @@ export function CompetitionDetailDialog({
@@ -383,7 +383,7 @@ export function CompetitionDetailDialog({

{member.name}

- {member.id === team.leader && ( + {member.role === '隊長' && ( 隊長 diff --git a/components/competition/popularity-rankings.tsx b/components/competition/popularity-rankings.tsx index 594639d..dc38023 100644 --- a/components/competition/popularity-rankings.tsx +++ b/components/competition/popularity-rankings.tsx @@ -453,7 +453,7 @@ export function PopularityRankings() {
{currentTeams.map((team, index) => { const globalRank = startIndex + index + 1 - const leader = team.members.find((m: any) => m.id === team.leader) + const leader = team.members.find((m: any) => m.role === '隊長') return (

{team.name}

-

隊長:{leader?.name}

+

隊長:{leader?.name || '未知隊長'}

團隊賽 @@ -496,7 +496,13 @@ export function PopularityRankings() { {member.name[0]}
{member.name} - ({member.role}) + + {member.role} +
))} {team.members.length > 3 && ( diff --git a/components/competition/team-detail-dialog.tsx b/components/competition/team-detail-dialog.tsx index 117ec15..7527a72 100644 --- a/components/competition/team-detail-dialog.tsx +++ b/components/competition/team-detail-dialog.tsx @@ -71,7 +71,13 @@ const getIconComponent = (iconName: string) => { // App data for team apps - get from team data const getAppDetails = (appId: string, team: any) => { - const appDetail = team.appsDetails?.find((app: any) => app.id === appId); + // 首先嘗試從 appsDetails 獲取 + let appDetail = team.appsDetails?.find((app: any) => app.id === appId); + + // 如果沒有找到,嘗試從 apps 獲取 + if (!appDetail) { + appDetail = team.apps?.find((app: any) => app.id === appId); + } if (appDetail) { return { @@ -311,7 +317,7 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP

{member.name}

- {member.id === team.leader && ( + {member.role === '隊長' && ( 隊長 @@ -334,19 +340,36 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP
- {team.apps.map((appId: string) => { - const app = getAppDetails(appId, team) + {team.apps.map((app: any) => { + // 如果 app 是字符串 ID,使用 getAppDetails 獲取詳情 + // 如果 app 是對象,直接使用 + const appData = typeof app === 'string' ? getAppDetails(app, team) : { + id: app.id, + name: app.name || "未命名應用", + type: app.type || "未知類型", + description: app.description || "無描述", + icon: getIconComponent(app.icon) || Brain, + iconColor: app.icon_color || "from-blue-500 to-purple-500", + author: app.creator_name || "未知作者", + department: app.creator_department || "未知部門", + category: app.category || "未分類", + likes: app.likes_count || 0, + views: app.views_count || 0, + rating: Number(app.rating) || 0, + createdAt: app.created_at + } + // 如果沒有圖標,使用默認的 Brain 圖標 - const IconComponent = app.icon || Brain - const likes = app.likes || 0 - const views = app.views || 0 - const rating = app.rating || 0 + const IconComponent = appData.icon || Brain + const likes = appData.likes || 0 + const views = appData.views || 0 + const rating = appData.rating || 0 return ( handleAppClick(appId)} + onClick={() => handleAppClick(appData.id)} >
@@ -356,16 +379,16 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP

- {app.name} + {appData.name}

- - {app.type} + + {appData.type}
-

{app.description}

+

{appData.description}

@@ -379,7 +402,7 @@ export function TeamDetailDialog({ open, onOpenChange, team }: TeamDetailDialogP
e.stopPropagation()}> 0 ? results[0] : null; @@ -1044,7 +1046,7 @@ export class TeamService extends DatabaseServiceBase { 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 + WHERE t.name = ? AND t.is_active = 1 `; const results = await DatabaseServiceBase.safeQuery(sql, [name]); return results.length > 0 ? results[0] : null; @@ -1058,24 +1060,97 @@ export class TeamService extends DatabaseServiceBase { contact_email: string; description: string; total_likes: number; + members?: Array<{ + user_id: string; + role: string; + }>; + apps?: string[]; }>): Promise { - 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]); + try { + // 更新團隊基本信息 + const teamFields = Object.keys(updates).filter(key => + key !== 'id' && + key !== 'created_at' && + key !== 'members' && + key !== 'apps' + ); + + if (teamFields.length > 0) { + const setClause = teamFields.map(field => `${field} = ?`).join(', '); + const values = teamFields.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 DatabaseServiceBase.safeUpdate(sql, values); - console.log('團隊更新結果:', result); - return result.affectedRows > 0; + const result = await DatabaseServiceBase.safeUpdate(sql, values); + console.log('團隊基本信息更新結果:', result); + + if (result.affectedRows === 0) { + console.log('團隊基本信息更新失敗'); + return false; + } + } + + // 更新團隊成員(如果提供了成員信息) + if (updates.members && Array.isArray(updates.members)) { + try { + // 先刪除現有成員 + console.log('刪除現有團隊成員...'); + await DatabaseServiceBase.safeDelete('DELETE FROM team_members WHERE team_id = ?', [id]); + + // 添加新成員(如果成員列表不為空) + if (updates.members.length > 0) { + console.log('添加新團隊成員...'); + for (const member of updates.members) { + if (member.user_id && member.role) { + console.log(`添加成員: ${member.user_id} (${member.role})`); + await DatabaseServiceBase.safeInsert( + 'INSERT INTO team_members (team_id, user_id, role, joined_at) VALUES (?, ?, ?, NOW())', + [id, member.user_id, member.role] + ); + } + } + console.log('團隊成員更新完成'); + } else { + console.log('成員列表為空,只刪除現有成員'); + } + } catch (memberError) { + console.error('更新團隊成員時發生錯誤:', memberError); + // 成員更新失敗不應該影響整個更新操作 + // 可以選擇繼續或回滾 + } + } + + // 更新團隊應用(如果提供了應用信息) + if (updates.apps && Array.isArray(updates.apps)) { + try { + // 先移除現有應用的團隊關聯 + console.log('移除現有應用的團隊關聯...'); + await DatabaseServiceBase.safeUpdate('UPDATE apps SET team_id = NULL WHERE team_id = ?', [id]); + + // 添加新應用的團隊關聯(如果應用列表不為空) + if (updates.apps.length > 0) { + console.log('添加新應用的團隊關聯...'); + for (const appId of updates.apps) { + if (appId) { + console.log(`關聯應用: ${appId}`); + await DatabaseServiceBase.safeUpdate( + 'UPDATE apps SET team_id = ? WHERE id = ?', + [id, appId] + ); + } + } + console.log('團隊應用更新完成'); + } else { + console.log('應用列表為空,只移除現有關聯'); + } + } catch (appError) { + console.error('更新團隊應用時發生錯誤:', appError); + // 應用更新失敗不應該影響整個更新操作 + } + } + + return true; } catch (error) { console.error('更新團隊錯誤:', error); return false; @@ -1165,7 +1240,7 @@ export class TeamService extends DatabaseServiceBase { // 綁定應用到團隊 static async bindAppToTeam(teamId: string, appId: string): Promise { try { - const sql = 'UPDATE apps SET team_id = ? WHERE id = ? AND is_active = TRUE'; + const sql = 'UPDATE apps SET team_id = ? WHERE id = ? AND is_active = 1'; const result = await DatabaseServiceBase.safeUpdate(sql, [teamId, appId]); return result.affectedRows > 0; } catch (error) { @@ -1196,7 +1271,7 @@ export class TeamService extends DatabaseServiceBase { u.name as creator_name, u.department as creator_department FROM apps a LEFT JOIN users u ON a.creator_id = u.id - WHERE a.team_id = ? AND a.is_active = TRUE + WHERE a.team_id = ? AND a.is_active = 1 ORDER BY a.created_at DESC `; console.log('📝 getTeamApps SQL:', sql); @@ -1211,7 +1286,7 @@ export class TeamService extends DatabaseServiceBase { const sql = ` SELECT COUNT(*) as totalTeams, - COUNT(CASE WHEN is_active = TRUE THEN 1 END) as activeTeams, + COUNT(CASE WHEN is_active = 1 THEN 1 END) as activeTeams, COUNT(CASE WHEN is_active = FALSE THEN 1 END) as inactiveTeams, AVG(member_count) as avgMembersPerTeam FROM ( @@ -1278,7 +1353,7 @@ export class CompetitionService extends DatabaseServiceBase { // 根據名稱獲取競賽 static async getCompetitionByName(name: string): Promise { - const sql = 'SELECT * FROM competitions WHERE name = ? AND is_active = TRUE'; + const sql = 'SELECT * FROM competitions WHERE name = ? AND is_active = 1'; return await db.queryOne(sql, [name]); } @@ -1313,7 +1388,7 @@ export class CompetitionService extends DatabaseServiceBase { // 獲取所有競賽 static async getAllCompetitions(): Promise { - const sql = 'SELECT * FROM competitions WHERE is_active = TRUE ORDER BY year DESC, month DESC'; + const sql = 'SELECT * FROM competitions WHERE is_active = 1 ORDER BY year DESC, month DESC'; const competitions = await db.query(sql); // 轉換字段名稱以匹配前端期望的格式 @@ -1365,7 +1440,7 @@ export class CompetitionService extends DatabaseServiceBase { // 獲取當前競賽 static async getCurrentCompetition(): Promise { try { - const sql = 'SELECT * FROM competitions WHERE is_current = TRUE AND is_active = TRUE LIMIT 1'; + const sql = 'SELECT * FROM competitions WHERE is_current = 1 AND is_active = 1 LIMIT 1'; const competitions = await db.query(sql); if (competitions.length > 0) { @@ -1485,7 +1560,7 @@ export class CompetitionService extends DatabaseServiceBase { 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 + WHERE cj.competition_id = ? AND j.is_active = 1 ORDER BY cj.assigned_at ASC `; return await DatabaseServiceBase.safeQuery(sql, [competitionId]); @@ -1546,7 +1621,7 @@ export class CompetitionService extends DatabaseServiceBase { 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 + WHERE ct.competition_id = ? AND t.is_active = 1 ORDER BY ct.registered_at ASC `; return await DatabaseServiceBase.safeQuery(sql, [competitionId]); @@ -1577,7 +1652,7 @@ export class CompetitionService extends DatabaseServiceBase { 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 + WHERE a.team_id IN (${placeholders}) AND a.is_active = 1 ORDER BY a.created_at ASC `; apps = await DatabaseServiceBase.safeQuery(sql, teamIds); @@ -1590,7 +1665,7 @@ export class CompetitionService extends DatabaseServiceBase { 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 + WHERE ca.competition_id = ? AND a.is_active = 1 ORDER BY ca.submitted_at ASC `; apps = await DatabaseServiceBase.safeQuery(sql, [competitionId]); @@ -1934,7 +2009,7 @@ export class AppService extends DatabaseServiceBase { u.department as creator_department FROM apps a LEFT JOIN users u ON a.creator_id = u.id - WHERE a.id = ? AND a.is_active = TRUE + WHERE a.id = ? AND a.is_active = 1 `; return await this.queryOne(sql, [appId]); } @@ -1962,7 +2037,7 @@ export class AppService extends DatabaseServiceBase { u.department as creator_department FROM apps a LEFT JOIN users u ON a.creator_id = u.id - WHERE a.name = ? AND a.is_active = TRUE + WHERE a.name = ? AND a.is_active = 1 `; return await this.queryOne(sql, [name]); } @@ -1986,7 +2061,7 @@ export class AppService extends DatabaseServiceBase { // 根據狀態篩選 if (status && status !== 'all') { if (status === 'active') { - whereConditions.push('a.is_active = TRUE'); + whereConditions.push('a.is_active = 1'); } else if (status === 'inactive') { whereConditions.push('a.is_active = FALSE'); } @@ -2058,6 +2133,7 @@ export class AppService extends DatabaseServiceBase { app_url?: string; icon?: string; icon_color?: string; + department?: string; }): Promise<{ success: boolean; app?: any; error?: string }> { try { const updateFields = []; @@ -2092,20 +2168,41 @@ export class AppService extends DatabaseServiceBase { params.push(updates.icon_color); } - if (updateFields.length === 0) { + // 如果沒有要更新的欄位,檢查是否需要更新部門 + if (updateFields.length === 0 && !updates.department) { return { success: false, error: '沒有要更新的欄位' }; } + // 如果需要更新部門,先更新創建者的部門 + if (updates.department) { + // 先獲取應用的創建者 ID + const appSql = 'SELECT creator_id FROM apps WHERE id = ? AND is_active = 1'; + const appResult = await this.query(appSql, [appId]); + + if (appResult.length === 0) { + return { success: false, error: '應用不存在' }; + } + + const creatorId = appResult[0].creator_id; + + // 更新創建者的部門 + const userUpdateSql = 'UPDATE users SET department = ? WHERE id = ?'; + await this.query(userUpdateSql, [updates.department, creatorId]); + } + + // 如果有其他欄位需要更新 + if (updateFields.length > 0) { updateFields.push('updated_at = NOW()'); params.push(appId); const sql = ` UPDATE apps SET ${updateFields.join(', ')} - WHERE id = ? AND is_active = TRUE + WHERE id = ? AND is_active = 1 `; await this.query(sql, params); + } // 獲取更新後的應用 const updatedApp = await this.getAppById(appId); @@ -2173,7 +2270,7 @@ export class AppService extends DatabaseServiceBase { const sql = ` SELECT COUNT(*) as total_apps, - COUNT(CASE WHEN is_active = TRUE THEN 1 END) as active_apps, + COUNT(CASE WHEN is_active = 1 THEN 1 END) as active_apps, COUNT(CASE WHEN is_active = FALSE THEN 1 END) as inactive_apps, COALESCE(SUM(views_count), 0) as total_views, COALESCE(SUM(likes_count), 0) as total_likes, @@ -2605,7 +2702,7 @@ export class AppService extends DatabaseServiceBase { COUNT(DISTINCT a.id) as count FROM apps a JOIN users u ON a.creator_id = u.id - WHERE a.is_active = TRUE + WHERE a.is_active = 1 GROUP BY u.department ORDER BY count DESC, u.department ASC `; @@ -2629,7 +2726,7 @@ export class AppService extends DatabaseServiceBase { type, COUNT(*) as count FROM apps - WHERE is_active = TRUE + WHERE is_active = 1 GROUP BY type ORDER BY count DESC, type ASC `; @@ -2653,7 +2750,7 @@ export class AppService extends DatabaseServiceBase { category, COUNT(*) as count FROM apps - WHERE is_active = TRUE + WHERE is_active = 1 GROUP BY category ORDER BY count DESC, category ASC `; @@ -2847,7 +2944,7 @@ export class AppService extends DatabaseServiceBase { const appStatsSql = ` SELECT COUNT(*) as total_apps, - COUNT(CASE WHEN is_active = TRUE THEN 1 END) as active_apps, + COUNT(CASE WHEN is_active = 1 THEN 1 END) as active_apps, COUNT(CASE WHEN is_active = FALSE THEN 1 END) as inactive_apps, COUNT(CASE WHEN created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) THEN 1 END) as new_apps_this_month, COALESCE(SUM(views_count), 0) as total_views, @@ -2997,7 +3094,7 @@ export class AppService extends DatabaseServiceBase { SELECT COUNT(*) as total FROM user_favorites uf JOIN apps a ON uf.app_id = a.id - WHERE uf.user_id = ? AND a.is_active = TRUE + WHERE uf.user_id = ? AND a.is_active = 1 `; const countResult = await this.queryOne(countSql, [userId]); const total = countResult?.total || 0; @@ -3022,7 +3119,7 @@ export class AppService extends DatabaseServiceBase { FROM user_favorites uf JOIN apps a ON uf.app_id = a.id LEFT JOIN users u ON a.creator_id = u.id - WHERE uf.user_id = ? AND a.is_active = TRUE + WHERE uf.user_id = ? AND a.is_active = 1 ORDER BY uf.created_at DESC LIMIT ${limit} OFFSET ${offset} `; @@ -3334,7 +3431,7 @@ export class AppService extends DatabaseServiceBase { for (const activity of activities) { try { // 嘗試從應用表獲取類別 - const appSql = 'SELECT type FROM apps WHERE id = ? AND is_active = TRUE'; + const appSql = 'SELECT type FROM apps WHERE id = ? AND is_active = 1'; const appResult = await this.query(appSql, [activity.resource_id]); let category = '未分類'; diff --git a/scripts/manual-insert-virtual-apps.sql b/scripts/manual-insert-virtual-apps.sql index 2cce547..92d3e0b 100644 --- a/scripts/manual-insert-virtual-apps.sql +++ b/scripts/manual-insert-virtual-apps.sql @@ -3,7 +3,7 @@ -- ===================================================== -- 首先查看現有的團隊數據 -SELECT id, name FROM teams WHERE is_active = TRUE; +SELECT id, name FROM teams WHERE is_active = 1; -- 為每個團隊創建對應的虛擬應用記錄 -- 格式:team_{teamId} 以便識別這是團隊評分 @@ -38,7 +38,7 @@ INSERT INTO apps ( 0, 0, 0.00, - TRUE, + 1, NOW(), NOW() );