function json(data, status = 200) { return new Response(JSON.stringify(data), { status, headers: { "content-type": "application/json; charset=UTF-8" }, }); } function ratio(correct, total) { if (!total) return 0; return Math.round((correct / total) * 10000) / 100; } export async function onRequestGet(context) { const db = context.env.DB; if (!db) { return json({ success: false, error: "缺少 D1 绑定 DB" }, 500); } try { const url = new URL(context.request.url); const month = (url.searchParams.get("month") || new Date().toISOString().slice(0, 7)).trim(); const sql = ` SELECT SUM(CASE WHEN actual_result IS NOT NULL AND ensemble_prediction IS NOT NULL THEN 1 ELSE 0 END) AS ens_total, SUM(CASE WHEN ensemble_correct = 1 THEN 1 ELSE 0 END) AS ens_correct, SUM(CASE WHEN actual_result IS NOT NULL AND dlinear_prediction IS NOT NULL THEN 1 ELSE 0 END) AS dl_total, SUM(CASE WHEN dlinear_correct = 1 THEN 1 ELSE 0 END) AS dl_correct, SUM(CASE WHEN actual_result IS NOT NULL AND nbeats_prediction IS NOT NULL THEN 1 ELSE 0 END) AS nb_total, SUM(CASE WHEN nbeats_correct = 1 THEN 1 ELSE 0 END) AS nb_correct, SUM(CASE WHEN actual_result IS NOT NULL AND ttm_prediction IS NOT NULL THEN 1 ELSE 0 END) AS ttm_total, SUM(CASE WHEN ttm_correct = 1 THEN 1 ELSE 0 END) AS ttm_correct FROM backup_hourly_model_predictions WHERE date LIKE ? `; const row = await db.prepare(sql).bind(`${month}%`).first(); const ensTotal = Number(row?.ens_total || 0); const ensCorrect = Number(row?.ens_correct || 0); const dlTotal = Number(row?.dl_total || 0); const dlCorrect = Number(row?.dl_correct || 0); const nbTotal = Number(row?.nb_total || 0); const nbCorrect = Number(row?.nb_correct || 0); const ttmTotal = Number(row?.ttm_total || 0); const ttmCorrect = Number(row?.ttm_correct || 0); return json({ success: true, month, data: { Ensemble: { total: ensTotal, correct: ensCorrect, incorrect: Math.max(0, ensTotal - ensCorrect), accuracy: ratio(ensCorrect, ensTotal), }, DLinear: { total: dlTotal, correct: dlCorrect, incorrect: Math.max(0, dlTotal - dlCorrect), accuracy: ratio(dlCorrect, dlTotal), }, "N-BEATS": { total: nbTotal, correct: nbCorrect, incorrect: Math.max(0, nbTotal - nbCorrect), accuracy: ratio(nbCorrect, nbTotal), }, "TTM-R2": { total: ttmTotal, correct: ttmCorrect, incorrect: Math.max(0, ttmTotal - ttmCorrect), accuracy: ratio(ttmCorrect, ttmTotal), }, }, }); } catch (error) { return json({ success: false, error: String(error) }, 500); } }