2026-05-10 13:12:44 +08:00

123 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ======================
// HoloLake DevBoard · API Client v2.0
// DEV-004 之之 · 环节5 · 真实API对接
// 协议SYSLOG-v4.0
// ======================
const API = {
// ====== 配置区 ======
// 页页的后端API地址如果地址不对问知秋确认
BASE_URL: 'https://guanghulab.com/api/devboard',
TIMEOUT: 8000,
FALLBACK: true, // API挂了自动切换模拟数据
REFRESH_MS: 30000, // 30秒自动刷新
_cache: {}
};
// ====== 通用请求(带超时+缓存+降级) =====================
async function apiFetch(path) {
const url = API.BASE_URL + path;
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), API.TIMEOUT);
try {
const res = await fetch(url, {
signal: ctrl.signal,
headers: { 'Accept': 'application/json' }
});
clearTimeout(timer);
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
API._cache[path] = { data: data, time: Date.now() };
return data;
} catch (err) {
clearTimeout(timer);
console.warn(' 🚨 [DevBoard API] ' + path + ' 失败: ', err.message);
if (API._cache[path]) {
console.info(' 💾 [DevBoard API] 使用缓存');
return API._cache[path].data;
}
if (API.FALLBACK) {
console.info(' 🛡️ [DevBoard API] 降级到模拟数据');
return getMockData(path);
}
throw err;
}
}
// ========== 公开接口 ==========
async function apiGetDevelopers() {
return apiFetch('/developers');
}
async function apiGetDeveloperDetail(devId) {
return apiFetch('/developers/' + devId);
}
async function apiGetStats() {
return apiFetch('/stats');
}
async function apiGetLeaderboard() {
return apiFetch('/leaderboard');
}
// ========== 健康检查 ==========
async function apiHealthCheck() {
try {
const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), 3000);
const res = await fetch(API.BASE_URL + '/health', { signal: ctrl.signal });
clearTimeout(t);
return res.ok;
} catch (e) {
return false;
}
}
// ========== 模拟数据(降级保护) ==========
function getMockData(path) {
var devs = [
{ dev_id:'DEV-001', name:'页页', module:'BC-集成', status:'进行中', streak:5, el:'EL-6', pca:{EXE:75,TEC:60,SYS:70,COL:80,INI:65} },
{ dev_id:'DEV-002', name:'肥猫', module:'M-STATUS', status:'进行中', streak:9, el:'EL-5', pca:{EXE:72,TEC:40,SYS:68,COL:82,INI:70} },
{ dev_id:'DEV-003', name:'燕樊', module:'M-MEMORY', status:'进行中', streak:7, el:'EL-5', pca:{EXE:70,TEC:55,SYS:65,COL:75,INI:68} },
{ dev_id:'DEV-004', name:'之之', module:'M-DEVBOARD', status:'进行中', streak:14, el:'EL-8', pca:{EXE:90,TEC:55,SYS:80,COL:85,INI:88} },
{ dev_id:'DEV-009', name:'花尔', module:'M20', status:'进行中', streak:4, el:'EL-8', pca:{EXE:65,TEC:45,SYS:55,COL:70,INI:60} },
{ dev_id:'DEV-010', name:'桔子', module:'M-CHANNEL', status:'进行中', streak:14, el:'EL-6', pca:{EXE:85,TEC:50,SYS:75,COL:80,INI:82} },
{ dev_id:'DEV-011', name:'匆匆那年',module:'M16', status:'等待中', streak:1, el:'EL-3', pca:{EXE:50,TEC:30,SYS:40,COL:55,INI:45} },
{ dev_id:'DEV-012', name:'Awen', module:'M22', status:'进行中', streak:13, el:'EL-6', pca:{EXE:88,TEC:45,SYS:78,COL:90,INI:80} },
{ dev_id:'DEV-013', name:'小兴', module:'M-AUTH', status:'进行中', streak:1, el:'EL-3', pca:{EXE:55,TEC:25,SYS:35,COL:60,INI:50} }
];
var stats = { total_developers:10, active:8, modules:12, code_lines:32000, longest_streak:14, avg_streak:7.5 };
if (path === '/developers') return devs;
if (path.indexOf('/developers/') === 0) {
var id = path.split('/').pop();
return devs.find(function(d){ return d.dev_id === id; }) || devs[0];
}
if (path === '/stats') return stats;
if (path === '/leaderboard') return devs.slice().sort(function(a,b){ return b.streak - a.streak; });
return [];
}
// ========== API状态指示器 ==========
function showApiStatus(online) {
var el = document.getElementById('api-status');
if (!el) {
el = document.createElement('div');
el.id = 'api-status';
el.style.cssText = 'position:fixed;bottom:12px;right:12px;padding:6px 14px;border-radius:20px;font-size:12px;font-weight:600;z-index:9999;transition:all 0.3s;';
document.body.appendChild(el);
}
if (online) {
el.textContent = '🟢 实时数据';
el.style.backgroundColor = 'rgba(52,211,153,0.15)';
el.style.color = '#34d399';
el.style.border = '1px solid rgba(52,211,153,0.3)';
} else {
el.textContent = '🟡 模拟数据';
el.style.backgroundColor = 'rgba(251,191,36,0.15)';
el.style.color = '#fbbf24';
el.style.border = '1px solid rgba(251,191,36,0.3)';
}
}