實現得獎資訊與資料庫整合

This commit is contained in:
2025-09-26 01:42:52 +08:00
parent 9bed168238
commit 0675fe63b0
8 changed files with 1531 additions and 409 deletions

View File

@@ -98,6 +98,7 @@ export async function POST(request: NextRequest) {
// 獲取獎項列表 // 獲取獎項列表
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
try { try {
console.log('🔍 開始獲取獎項列表...');
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const competitionId = searchParams.get('competitionId'); const competitionId = searchParams.get('competitionId');
const awardType = searchParams.get('awardType'); const awardType = searchParams.get('awardType');
@@ -105,14 +106,20 @@ export async function GET(request: NextRequest) {
const year = searchParams.get('year'); const year = searchParams.get('year');
const month = searchParams.get('month'); const month = searchParams.get('month');
console.log('📋 查詢參數:', { competitionId, awardType, category, year, month });
let awards; let awards;
if (competitionId) { if (competitionId) {
console.log('🎯 根據競賽ID獲取獎項:', competitionId);
awards = await AwardService.getAwardsByCompetition(competitionId); awards = await AwardService.getAwardsByCompetition(competitionId);
} else { } else {
console.log('📊 獲取所有獎項');
awards = await AwardService.getAllAwards(); awards = await AwardService.getAllAwards();
} }
console.log('✅ 獲取到獎項數量:', awards?.length || 0);
// 應用篩選條件 // 應用篩選條件
if (awardType) { if (awardType) {
awards = awards.filter(award => award.award_type === 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)); 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({ return NextResponse.json({
success: true, success: true,
message: '獎項列表獲取成功', message: '獎項列表獲取成功',
data: awards data: processedAwards
}); });
} catch (error) { } catch (error) {

View File

@@ -1,94 +1,42 @@
// ===================================================== // =====================================================
// 競賽評審關聯管理 API // 競賽評審 API
// ===================================================== // =====================================================
import { NextRequest, NextResponse } from 'next/server'; 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 { try {
const { id } = await params; const { id: competitionId } = await params;
const judges = await CompetitionService.getCompetitionJudges(id); if (!competitionId) {
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) {
return NextResponse.json({ return NextResponse.json({
success: false, success: false,
message: '缺少評審ID列表', message: '缺少競賽ID參數'
error: 'judgeIds 必須是非空陣列'
}, { status: 400 }); }, { 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({ return NextResponse.json({
success: true, success: true,
message: '評審添加成功', message: '評審列表獲取成功',
data: result data: judges || []
}); });
} catch (error) { } catch (error) {
console.error('添加競賽評審失敗:', error); console.error('❌ 獲取評審失敗:', error);
return NextResponse.json({ return NextResponse.json({
success: false, 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 : '未知錯誤' error: error instanceof Error ? error.message : '未知錯誤'
}, { status: 500 }); }, { status: 500 });
} }

View File

@@ -1,94 +1,36 @@
// ===================================================== // =====================================================
// 競賽團隊關聯管理 API // 競賽參賽團隊 API
// ===================================================== // =====================================================
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { CompetitionService } from '@/lib/services/database-service'; 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 { try {
const { id } = await params; const { id: competitionId } = await params;
const teams = await CompetitionService.getCompetitionTeams(id); if (!competitionId) {
return NextResponse.json({
success: false,
message: '缺少競賽ID參數'
}, { status: 400 });
}
// 獲取競賽的參賽團隊
const teams = await CompetitionService.getCompetitionTeams(competitionId);
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
message: '賽團隊列表獲取成功', message: '賽團隊獲取成功',
data: teams data: teams
}); });
} catch (error) { } catch (error) {
console.error('獲取賽團隊失敗:', error); console.error('獲取賽團隊失敗:', error);
return NextResponse.json({ return NextResponse.json({
success: false, 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 : '未知錯誤' error: error instanceof Error ? error.message : '未知錯誤'
}, { status: 500 }); }, { status: 500 });
} }

View 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 }
)
}
}

View 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

View File

@@ -62,6 +62,8 @@ interface CompetitionContextType {
// Awards // Awards
awards: Award[] awards: Award[]
loadingAwards: boolean
loadAwards: () => Promise<void>
addAward: (award: Omit<Award, "id">) => void addAward: (award: Omit<Award, "id">) => void
getAwardsByYear: (year: number) => Award[] getAwardsByYear: (year: number) => Award[]
getAwardsByMonth: (year: number, month: number) => Award[] getAwardsByMonth: (year: number, month: number) => Award[]
@@ -221,20 +223,9 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
return mockProposalScores return mockProposalScores
}) })
// Load awards from localStorage // Load awards from database
const [awards, setAwards] = useState<Award[]>(() => { const [awards, setAwards] = useState<Award[]>([])
if (typeof window !== "undefined") { const [loadingAwards, setLoadingAwards] = useState(false)
const saved = localStorage.getItem("competitionAwards")
if (saved) {
return JSON.parse(saved)
}
}
// 獎項資料
const mockAwards = []
return mockAwards
})
// Save to localStorage when data changes // Save to localStorage when data changes
useEffect(() => { useEffect(() => {
@@ -249,11 +240,10 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
} }
}, [proposalJudgeScores]) }, [proposalJudgeScores])
// 載入獎項數據
useEffect(() => { useEffect(() => {
if (typeof window !== "undefined") { loadAwards()
localStorage.setItem("competitionAwards", JSON.stringify(awards)) }, [])
}
}, [awards])
const addJudge = (judge: Omit<Judge, "id">) => { 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 addAward = (award: Omit<Award, "id">) => {
const newAward: Award = { const newAward: Award = {
...award, ...award,
id: `a${Date.now()}`, 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[] => { const getAwardsByYear = (year: number): Award[] => {
@@ -804,6 +831,8 @@ export function CompetitionProvider({ children }: { children: ReactNode }) {
getAppDetailedScores, getAppDetailedScores,
getProposalDetailedScores, getProposalDetailedScores,
awards, awards,
loadingAwards,
loadAwards,
addAward, addAward,
getAwardsByYear, getAwardsByYear,
getAwardsByMonth, getAwardsByMonth,

View File

@@ -1733,11 +1733,21 @@ export class CompetitionService extends DatabaseServiceBase {
// 獲取競賽的團隊列表 // 獲取競賽的團隊列表
static async getCompetitionTeams(competitionId: string): Promise<any[]> { static async getCompetitionTeams(competitionId: string): Promise<any[]> {
const sql = ` 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 FROM competition_teams ct
JOIN teams t ON ct.team_id = t.id JOIN teams t ON ct.team_id = t.id
LEFT JOIN users u ON t.leader_id = u.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 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 ORDER BY ct.registered_at ASC
`; `;
return await DatabaseServiceBase.safeQuery(sql, [competitionId]); return await DatabaseServiceBase.safeQuery(sql, [competitionId]);
@@ -1757,7 +1767,7 @@ export class CompetitionService extends DatabaseServiceBase {
if (teams.length > 0) { if (teams.length > 0) {
// 過濾掉 undefined 或 null 的 team_id 值 // 過濾掉 undefined 或 null 的 team_id 值
const teamIds = teams const teamIds = teams
.map(t => t.team_id) .map(t => t.id)
.filter(id => id !== undefined && id !== null); .filter(id => id !== undefined && id !== null);
if (teamIds.length > 0) { if (teamIds.length > 0) {
@@ -4459,10 +4469,10 @@ export class ScoringService extends DatabaseServiceBase {
// ===================================================== // =====================================================
export class AwardService 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 = ` 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) 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(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`; `;
const params = [ const params = [
awardData.competition_id, awardData.competition_id,
@@ -4482,11 +4492,19 @@ export class AwardService extends DatabaseServiceBase {
awardData.custom_award_type_id || null, awardData.custom_award_type_id || null,
awardData.competition_type, awardData.competition_type,
awardData.rank, 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); const result = await DatabaseServiceBase.safeInsert(sql, params);
return await this.getAwardByCompetitionAndCreator(awardData.competition_id, awardData.creator) as Award;
// 獲取創建的獎項(使用最後創建的獎項)
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]); 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[]> { static async getAwardsByYear(year: number): Promise<Award[]> {
const sql = 'SELECT * FROM awards WHERE year = ? ORDER BY month DESC, rank ASC'; const sql = 'SELECT * FROM awards WHERE year = ? ORDER BY month DESC, rank ASC';