123 lines
4.9 KiB
JavaScript
123 lines
4.9 KiB
JavaScript
|
|
// ======================
|
|||
|
|
// 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)';
|
|||
|
|
}
|
|||
|
|
}
|