實現得獎資訊與資料庫整合
This commit is contained in:
@@ -98,6 +98,7 @@ export async function POST(request: NextRequest) {
|
||||
// 獲取獎項列表
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
console.log('🔍 開始獲取獎項列表...');
|
||||
const { searchParams } = new URL(request.url);
|
||||
const competitionId = searchParams.get('competitionId');
|
||||
const awardType = searchParams.get('awardType');
|
||||
@@ -105,14 +106,20 @@ export async function GET(request: NextRequest) {
|
||||
const year = searchParams.get('year');
|
||||
const month = searchParams.get('month');
|
||||
|
||||
console.log('📋 查詢參數:', { competitionId, awardType, category, year, month });
|
||||
|
||||
let awards;
|
||||
|
||||
if (competitionId) {
|
||||
console.log('🎯 根據競賽ID獲取獎項:', competitionId);
|
||||
awards = await AwardService.getAwardsByCompetition(competitionId);
|
||||
} else {
|
||||
console.log('📊 獲取所有獎項');
|
||||
awards = await AwardService.getAllAwards();
|
||||
}
|
||||
|
||||
console.log('✅ 獲取到獎項數量:', awards?.length || 0);
|
||||
|
||||
// 應用篩選條件
|
||||
if (awardType) {
|
||||
awards = awards.filter(award => award.award_type === awardType);
|
||||
@@ -127,10 +134,27 @@ export async function GET(request: NextRequest) {
|
||||
awards = awards.filter(award => award.month === parseInt(month));
|
||||
}
|
||||
|
||||
// 解析 JSON 欄位
|
||||
const processedAwards = awards.map(award => {
|
||||
console.log('🔍 處理獎項:', {
|
||||
id: award.id,
|
||||
competition_name: (award as any).competition_name,
|
||||
competition_type: (award as any).competition_type,
|
||||
competition_id: award.competition_id
|
||||
});
|
||||
|
||||
return {
|
||||
...award,
|
||||
application_links: award.application_links ? JSON.parse(award.application_links) : null,
|
||||
documents: award.documents ? JSON.parse(award.documents) : [],
|
||||
photos: award.photos ? JSON.parse(award.photos) : [],
|
||||
};
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '獎項列表獲取成功',
|
||||
data: awards
|
||||
data: processedAwards
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
@@ -141,4 +165,4 @@ export async function GET(request: NextRequest) {
|
||||
error: error instanceof Error ? error.message : '未知錯誤'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,95 +1,43 @@
|
||||
// =====================================================
|
||||
// 競賽評審關聯管理 API
|
||||
// 競賽評審 API
|
||||
// =====================================================
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { CompetitionService } from '@/lib/services/database-service';
|
||||
import { AwardService } from '@/lib/services/database-service';
|
||||
|
||||
// 獲取競賽的評審列表
|
||||
export async function GET(request: NextRequest, { params }: { params: { id: string } }) {
|
||||
// 獲取競賽評審列表
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const { id: competitionId } = await params;
|
||||
|
||||
const judges = await CompetitionService.getCompetitionJudges(id);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '競賽評審列表獲取成功',
|
||||
data: judges
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('獲取競賽評審失敗:', error);
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: '獲取競賽評審失敗',
|
||||
error: error instanceof Error ? error.message : '未知錯誤'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// 為競賽添加評審
|
||||
export async function POST(request: NextRequest, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const body = await request.json();
|
||||
const { judgeIds } = body;
|
||||
|
||||
if (!judgeIds || !Array.isArray(judgeIds) || judgeIds.length === 0) {
|
||||
if (!competitionId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: '缺少評審ID列表',
|
||||
error: 'judgeIds 必須是非空陣列'
|
||||
message: '缺少競賽ID參數'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await CompetitionService.addCompetitionJudges(id, judgeIds);
|
||||
console.log('🔍 獲取競賽評審:', competitionId);
|
||||
|
||||
const judges = await AwardService.getCompetitionJudges(competitionId);
|
||||
|
||||
console.log('✅ 獲取到評審數量:', judges?.length || 0);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '評審添加成功',
|
||||
data: result
|
||||
message: '評審列表獲取成功',
|
||||
data: judges || []
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('添加競賽評審失敗:', error);
|
||||
console.error('❌ 獲取評審失敗:', error);
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: '添加競賽評審失敗',
|
||||
message: '獲取評審失敗',
|
||||
error: error instanceof Error ? error.message : '未知錯誤'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// 從競賽中移除評審
|
||||
export async function DELETE(request: NextRequest, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const { searchParams } = new URL(request.url);
|
||||
const judgeId = searchParams.get('judgeId');
|
||||
|
||||
if (!judgeId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: '缺少評審ID',
|
||||
error: 'judgeId 參數是必需的'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await CompetitionService.removeCompetitionJudge(id, judgeId);
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,95 +1,37 @@
|
||||
// =====================================================
|
||||
// 競賽團隊關聯管理 API
|
||||
// 競賽參賽團隊 API
|
||||
// =====================================================
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { CompetitionService } from '@/lib/services/database-service';
|
||||
|
||||
// 獲取競賽的團隊列表
|
||||
export async function GET(request: NextRequest, { params }: { params: { id: string } }) {
|
||||
// 獲取競賽的參賽團隊
|
||||
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
|
||||
const teams = await CompetitionService.getCompetitionTeams(id);
|
||||
|
||||
const { id: competitionId } = await params;
|
||||
|
||||
if (!competitionId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: '缺少競賽ID參數'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
// 獲取競賽的參賽團隊
|
||||
const teams = await CompetitionService.getCompetitionTeams(competitionId);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '競賽團隊列表獲取成功',
|
||||
message: '參賽團隊獲取成功',
|
||||
data: teams
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('獲取競賽團隊失敗:', error);
|
||||
console.error('獲取參賽團隊失敗:', error);
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: '獲取競賽團隊失敗',
|
||||
message: '獲取參賽團隊失敗',
|
||||
error: error instanceof Error ? error.message : '未知錯誤'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// 為競賽添加團隊
|
||||
export async function POST(request: NextRequest, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const body = await request.json();
|
||||
const { teamIds } = body;
|
||||
|
||||
if (!teamIds || !Array.isArray(teamIds) || teamIds.length === 0) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: '缺少團隊ID列表',
|
||||
error: 'teamIds 必須是非空陣列'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await CompetitionService.addCompetitionTeams(id, teamIds);
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
// 從競賽中移除團隊
|
||||
export async function DELETE(request: NextRequest, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const { searchParams } = new URL(request.url);
|
||||
const teamId = searchParams.get('teamId');
|
||||
|
||||
if (!teamId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
message: '缺少團隊ID',
|
||||
error: 'teamId 參數是必需的'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await CompetitionService.removeCompetitionTeam(id, teamId);
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
74
app/api/upload/document/route.ts
Normal file
74
app/api/upload/document/route.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { writeFile, mkdir } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { existsSync } from 'fs'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const data = await request.formData()
|
||||
const file: File | null = data.get('file') as unknown as File
|
||||
const awardId = data.get('awardId') as string
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json({ error: '沒有選擇文件' }, { status: 400 })
|
||||
}
|
||||
|
||||
// 驗證文件類型
|
||||
const allowedTypes = [
|
||||
'application/pdf',
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
||||
]
|
||||
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
return NextResponse.json({
|
||||
error: '不支持的文件格式,請上傳 PDF、DOC、DOCX、PPT、PPTX 格式的文件'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
// 驗證文件大小 (10MB)
|
||||
const maxSize = 10 * 1024 * 1024
|
||||
if (file.size > maxSize) {
|
||||
return NextResponse.json({
|
||||
error: '文件大小超過 10MB 限制'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
// 創建上傳目錄
|
||||
const uploadDir = join(process.cwd(), 'public', 'uploads', 'documents')
|
||||
if (!existsSync(uploadDir)) {
|
||||
await mkdir(uploadDir, { recursive: true })
|
||||
}
|
||||
|
||||
// 生成唯一文件名
|
||||
const timestamp = Date.now()
|
||||
const fileExtension = file.name.split('.').pop()
|
||||
const fileName = `${awardId}_${timestamp}.${fileExtension}`
|
||||
const filePath = join(uploadDir, fileName)
|
||||
|
||||
// 保存文件
|
||||
const bytes = await file.arrayBuffer()
|
||||
const buffer = Buffer.from(bytes)
|
||||
await writeFile(filePath, buffer)
|
||||
|
||||
// 返回文件信息
|
||||
const fileUrl = `/uploads/documents/${fileName}`
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
url: fileUrl,
|
||||
fileName: file.name,
|
||||
size: file.size,
|
||||
type: file.type
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('文件上傳錯誤:', error)
|
||||
return NextResponse.json(
|
||||
{ error: '文件上傳失敗' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
74
app/api/upload/photo/route.ts
Normal file
74
app/api/upload/photo/route.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { writeFile, mkdir } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { existsSync } from 'fs'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const data = await request.formData()
|
||||
const file: File | null = data.get('file') as unknown as File
|
||||
const awardId = data.get('awardId') as string
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json({ error: '沒有選擇照片' }, { status: 400 })
|
||||
}
|
||||
|
||||
// 驗證文件類型
|
||||
const allowedTypes = [
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp'
|
||||
]
|
||||
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
return NextResponse.json({
|
||||
error: '不支持的照片格式,請上傳 JPG、PNG、GIF、WEBP 格式的照片'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
// 驗證文件大小 (5MB)
|
||||
const maxSize = 5 * 1024 * 1024
|
||||
if (file.size > maxSize) {
|
||||
return NextResponse.json({
|
||||
error: '照片大小超過 5MB 限制'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
// 創建上傳目錄
|
||||
const uploadDir = join(process.cwd(), 'public', 'uploads', 'photos')
|
||||
if (!existsSync(uploadDir)) {
|
||||
await mkdir(uploadDir, { recursive: true })
|
||||
}
|
||||
|
||||
// 生成唯一文件名
|
||||
const timestamp = Date.now()
|
||||
const fileExtension = file.name.split('.').pop()
|
||||
const fileName = `${awardId}_${timestamp}.${fileExtension}`
|
||||
const filePath = join(uploadDir, fileName)
|
||||
|
||||
// 保存文件
|
||||
const bytes = await file.arrayBuffer()
|
||||
const buffer = Buffer.from(bytes)
|
||||
await writeFile(filePath, buffer)
|
||||
|
||||
// 返回文件信息
|
||||
const fileUrl = `/uploads/photos/${fileName}`
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
url: fileUrl,
|
||||
fileName: file.name,
|
||||
size: file.size,
|
||||
type: file.type
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('照片上傳錯誤:', error)
|
||||
return NextResponse.json(
|
||||
{ error: '照片上傳失敗' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -62,6 +62,8 @@ interface CompetitionContextType {
|
||||
|
||||
// Awards
|
||||
awards: Award[]
|
||||
loadingAwards: boolean
|
||||
loadAwards: () => Promise<void>
|
||||
addAward: (award: Omit<Award, "id">) => void
|
||||
getAwardsByYear: (year: number) => Award[]
|
||||
getAwardsByMonth: (year: number, month: number) => Award[]
|
||||
@@ -221,20 +223,9 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
|
||||
return mockProposalScores
|
||||
})
|
||||
|
||||
// Load awards from localStorage
|
||||
const [awards, setAwards] = useState<Award[]>(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
const saved = localStorage.getItem("competitionAwards")
|
||||
if (saved) {
|
||||
return JSON.parse(saved)
|
||||
}
|
||||
}
|
||||
|
||||
// 獎項資料
|
||||
const mockAwards = []
|
||||
|
||||
return mockAwards
|
||||
})
|
||||
// Load awards from database
|
||||
const [awards, setAwards] = useState<Award[]>([])
|
||||
const [loadingAwards, setLoadingAwards] = useState(false)
|
||||
|
||||
// Save to localStorage when data changes
|
||||
useEffect(() => {
|
||||
@@ -249,11 +240,10 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
}, [proposalJudgeScores])
|
||||
|
||||
// 載入獎項數據
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem("competitionAwards", JSON.stringify(awards))
|
||||
}
|
||||
}, [awards])
|
||||
loadAwards()
|
||||
}, [])
|
||||
|
||||
|
||||
const addJudge = (judge: Omit<Judge, "id">) => {
|
||||
@@ -533,12 +523,49 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
}
|
||||
|
||||
// 載入獎項數據
|
||||
const loadAwards = async () => {
|
||||
setLoadingAwards(true)
|
||||
try {
|
||||
const response = await fetch('/api/admin/awards')
|
||||
const data = await response.json()
|
||||
|
||||
if (data.success) {
|
||||
console.log('📊 載入獎項數據:', data.data.length, '個')
|
||||
setAwards(data.data)
|
||||
} else {
|
||||
console.error('載入獎項失敗:', data.message)
|
||||
setAwards([])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('載入獎項失敗:', error)
|
||||
setAwards([])
|
||||
} finally {
|
||||
setLoadingAwards(false)
|
||||
}
|
||||
}
|
||||
|
||||
const addAward = (award: Omit<Award, "id">) => {
|
||||
const newAward: Award = {
|
||||
...award,
|
||||
id: `a${Date.now()}`,
|
||||
}
|
||||
setAwards((prev) => [...prev, newAward])
|
||||
setAwards((prev) => {
|
||||
// 檢查是否已存在相同的獎項(基於競賽ID、參賽者ID和獎項名稱)
|
||||
const exists = prev.some((existingAward: any) =>
|
||||
existingAward.competitionId === newAward.competitionId &&
|
||||
existingAward.participantId === newAward.participantId &&
|
||||
existingAward.awardName === newAward.awardName
|
||||
)
|
||||
|
||||
if (exists) {
|
||||
console.log('⚠️ 獎項已存在,跳過重複添加:', newAward)
|
||||
return prev
|
||||
}
|
||||
|
||||
console.log('✅ 添加新獎項:', newAward)
|
||||
return [...prev, newAward]
|
||||
})
|
||||
}
|
||||
|
||||
const getAwardsByYear = (year: number): Award[] => {
|
||||
@@ -804,6 +831,8 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
|
||||
getAppDetailedScores,
|
||||
getProposalDetailedScores,
|
||||
awards,
|
||||
loadingAwards,
|
||||
loadAwards,
|
||||
addAward,
|
||||
getAwardsByYear,
|
||||
getAwardsByMonth,
|
||||
|
@@ -1733,11 +1733,21 @@ export class CompetitionService extends DatabaseServiceBase {
|
||||
// 獲取競賽的團隊列表
|
||||
static async getCompetitionTeams(competitionId: string): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT t.*, ct.registered_at, u.name as leader_name, u.phone as leader_phone
|
||||
SELECT
|
||||
t.*,
|
||||
ct.registered_at,
|
||||
u.name as leader_name,
|
||||
u.phone as leader_phone,
|
||||
COUNT(tm.id) as actual_member_count,
|
||||
a.id as app_id,
|
||||
a.name as app_name
|
||||
FROM competition_teams ct
|
||||
JOIN teams t ON ct.team_id = t.id
|
||||
LEFT JOIN users u ON t.leader_id = u.id
|
||||
LEFT JOIN team_members tm ON t.id = tm.team_id
|
||||
LEFT JOIN apps a ON t.id = a.team_id AND a.is_active = 1
|
||||
WHERE ct.competition_id = ? AND t.is_active = 1
|
||||
GROUP BY t.id, ct.registered_at, u.name, u.phone, a.id, a.name
|
||||
ORDER BY ct.registered_at ASC
|
||||
`;
|
||||
return await DatabaseServiceBase.safeQuery(sql, [competitionId]);
|
||||
@@ -1757,7 +1767,7 @@ export class CompetitionService extends DatabaseServiceBase {
|
||||
if (teams.length > 0) {
|
||||
// 過濾掉 undefined 或 null 的 team_id 值
|
||||
const teamIds = teams
|
||||
.map(t => t.team_id)
|
||||
.map(t => t.id)
|
||||
.filter(id => id !== undefined && id !== null);
|
||||
|
||||
if (teamIds.length > 0) {
|
||||
@@ -4459,10 +4469,10 @@ export class ScoringService extends DatabaseServiceBase {
|
||||
// =====================================================
|
||||
export class AwardService extends DatabaseServiceBase {
|
||||
// 創建獎項
|
||||
static async createAward(awardData: Omit<Award, 'id' | 'created_at'>): Promise<Award> {
|
||||
static async createAward(awardData: any): Promise<Award> {
|
||||
const sql = `
|
||||
INSERT INTO awards (id, competition_id, app_id, team_id, proposal_id, app_name, team_name, proposal_title, creator, award_type, award_name, score, year, month, icon, custom_award_type_id, competition_type, rank, category)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO awards (id, competition_id, app_id, team_id, proposal_id, app_name, team_name, proposal_title, creator, award_type, award_name, score, year, month, icon, custom_award_type_id, competition_type, \`rank\`, category, description, judge_comments, application_links, documents, photos)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`;
|
||||
const params = [
|
||||
awardData.competition_id,
|
||||
@@ -4482,11 +4492,19 @@ export class AwardService extends DatabaseServiceBase {
|
||||
awardData.custom_award_type_id || null,
|
||||
awardData.competition_type,
|
||||
awardData.rank,
|
||||
awardData.category
|
||||
awardData.category,
|
||||
awardData.description || null,
|
||||
awardData.judge_comments || null,
|
||||
awardData.application_links ? JSON.stringify(awardData.application_links) : null,
|
||||
awardData.documents ? JSON.stringify(awardData.documents) : null,
|
||||
awardData.photos ? JSON.stringify(awardData.photos) : null
|
||||
];
|
||||
|
||||
await DatabaseServiceBase.safeInsert(sql, params);
|
||||
return await this.getAwardByCompetitionAndCreator(awardData.competition_id, awardData.creator) as Award;
|
||||
const result = await DatabaseServiceBase.safeInsert(sql, params);
|
||||
|
||||
// 獲取創建的獎項(使用最後創建的獎項)
|
||||
const createdAward = await this.getAwardByCompetitionAndCreator(awardData.competition_id, awardData.creator);
|
||||
return createdAward as Award;
|
||||
}
|
||||
|
||||
// 根據競賽和創作者獲取獎項
|
||||
@@ -4495,6 +4513,149 @@ export class AwardService extends DatabaseServiceBase {
|
||||
return await db.queryOne<Award>(sql, [competitionId, creator]);
|
||||
}
|
||||
|
||||
// 獲取所有獎項
|
||||
static async getAllAwards(): Promise<Award[]> {
|
||||
try {
|
||||
console.log('🔍 開始查詢所有獎項...');
|
||||
|
||||
// 先測試簡單查詢
|
||||
const simpleSql = 'SELECT * FROM awards ORDER BY created_at DESC LIMIT 5';
|
||||
console.log('📝 執行簡單查詢:', simpleSql);
|
||||
const simpleResult = await db.query<Award>(simpleSql);
|
||||
console.log('✅ 簡單查詢結果:', simpleResult?.length || 0, '個獎項');
|
||||
|
||||
// 再執行 JOIN 查詢
|
||||
const sql = `
|
||||
SELECT
|
||||
a.*,
|
||||
c.name as competition_name,
|
||||
c.type as competition_type
|
||||
FROM awards a
|
||||
LEFT JOIN competitions c ON a.competition_id = c.id
|
||||
ORDER BY a.created_at DESC
|
||||
`;
|
||||
console.log('📝 執行JOIN查詢:', sql);
|
||||
const result = await db.query<Award>(sql);
|
||||
console.log('✅ JOIN查詢結果:', result?.length || 0, '個獎項');
|
||||
|
||||
// 檢查第一個獎項的數據
|
||||
if (result && result.length > 0) {
|
||||
console.log('🔍 第一個獎項數據:', {
|
||||
id: result[0].id,
|
||||
competition_name: (result[0] as any).competition_name,
|
||||
competition_type: (result[0] as any).competition_type,
|
||||
competition_id: result[0].competition_id
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ 查詢獎項失敗:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 根據競賽獲取獎項
|
||||
static async getAwardsByCompetition(competitionId: string): Promise<Award[]> {
|
||||
const sql = 'SELECT * FROM awards WHERE competition_id = ? ORDER BY created_at DESC';
|
||||
return await db.query<Award>(sql, [competitionId]);
|
||||
}
|
||||
|
||||
// 根據ID獲取獎項
|
||||
static async getAwardById(id: string): Promise<Award | null> {
|
||||
const sql = 'SELECT * FROM awards WHERE id = ?';
|
||||
return await db.queryOne<Award>(sql, [id]);
|
||||
}
|
||||
|
||||
// 根據競賽ID獲取評審信息
|
||||
static async getCompetitionJudges(competitionId: string): Promise<any[]> {
|
||||
try {
|
||||
console.log('🔍 查詢競賽評審:', competitionId);
|
||||
const sql = `
|
||||
SELECT
|
||||
j.id,
|
||||
j.name,
|
||||
j.title,
|
||||
j.department,
|
||||
j.expertise,
|
||||
j.avatar
|
||||
FROM competition_judges cj
|
||||
LEFT JOIN judges j ON cj.judge_id = j.id
|
||||
WHERE cj.competition_id = ? AND j.is_active = TRUE
|
||||
ORDER BY j.name
|
||||
`;
|
||||
console.log('📝 執行評審查詢SQL:', sql);
|
||||
const result = await db.query<any>(sql, [competitionId]);
|
||||
console.log('✅ 查詢評審結果:', result?.length || 0, '位評審');
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ 查詢評審失敗:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新獎項
|
||||
static async updateAward(id: string, updates: Partial<Award>): Promise<boolean> {
|
||||
const fields = Object.keys(updates).filter(key =>
|
||||
key !== 'id' &&
|
||||
key !== 'created_at'
|
||||
);
|
||||
|
||||
if (fields.length === 0) return true;
|
||||
|
||||
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
||||
const values = fields.map(field => (updates as any)[field]);
|
||||
|
||||
const sql = `UPDATE awards SET ${setClause} WHERE id = ?`;
|
||||
const result = await DatabaseServiceBase.safeUpdate(sql, [...values, id]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 刪除獎項
|
||||
static async deleteAward(id: string): Promise<boolean> {
|
||||
const sql = 'DELETE FROM awards WHERE id = ?';
|
||||
const result = await DatabaseServiceBase.safeDelete(sql, [id]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 獲取獎項統計
|
||||
static async getAwardStats(competitionId?: string): Promise<{
|
||||
total: number;
|
||||
byType: Record<string, number>;
|
||||
byCategory: Record<string, number>;
|
||||
byYear: Record<number, number>;
|
||||
}> {
|
||||
let sql = 'SELECT award_type, category, year FROM awards';
|
||||
const params: any[] = [];
|
||||
|
||||
if (competitionId) {
|
||||
sql += ' WHERE competition_id = ?';
|
||||
params.push(competitionId);
|
||||
}
|
||||
|
||||
const awards = await DatabaseServiceBase.safeQuery(sql, params);
|
||||
|
||||
const stats = {
|
||||
total: awards.length,
|
||||
byType: {} as Record<string, number>,
|
||||
byCategory: {} as Record<string, number>,
|
||||
byYear: {} as Record<number, number>,
|
||||
};
|
||||
|
||||
awards.forEach((award: any) => {
|
||||
// 統計獎項類型
|
||||
stats.byType[award.award_type] = (stats.byType[award.award_type] || 0) + 1;
|
||||
|
||||
// 統計獎項類別
|
||||
stats.byCategory[award.category] = (stats.byCategory[award.category] || 0) + 1;
|
||||
|
||||
// 統計年份
|
||||
stats.byYear[award.year] = (stats.byYear[award.year] || 0) + 1;
|
||||
});
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
// 根據年份獲取獎項
|
||||
static async getAwardsByYear(year: number): Promise<Award[]> {
|
||||
const sql = 'SELECT * FROM awards WHERE year = ? ORDER BY month DESC, rank ASC';
|
||||
|
Reference in New Issue
Block a user