Files
Toolbox/dev_test_scripts/debug/debug_cloud_load_timing.js
T
yangxiangyuan e4f84caa23 feat: 重构定投评估工具并优化Cloud工具体验
- 重构市场定投评估逻辑:优先读取本地估值快照,必要时通过探针补抓海外数据,后端接口新增force参数支持强制刷新,更新前后端注释与交互文案
- 优化Cloud工具全链路:
  - 新增运营商登录页地址字段,支持一键复制账号密码并打开对应登录页
  - 缓存密钥派生结果,避免重复阻塞事件循环,提升列表加载性能
  - 完善接口错误处理,新增面板级加载失败提示与自动重试功能
  - 修复云资源代理转发时的Cookie缺失问题
  - 并行加载所有面板数据,优化首屏渲染速度
  - 新增登录按钮样式与错误提示样式
- 新增4个Cloud工具调试脚本,用于性能测试、解密审计、回归验证与加载耗时统计
- 启动定投评估工具的定时调度任务
2026-08-17 16:33:41 +08:00

81 lines
3.9 KiB
JavaScript

// 调试脚本:测量 cloud 工具初始化各接口耗时 + 拉取前端加载日志,定位刷新"顿一下"的卡点
// 用法:node dev_test_scripts/debug/debug_cloud_load_timing.js
const BASE = process.env.CLOUD_TIMING_BASE || 'http://localhost:8976'
const getCookieFromGo = async () => {
const r1 = await fetch(`${BASE}/go?systemId=Tools-cloud`, { redirect: 'manual' })
const loc = r1.headers.get('location')
if (!loc) throw new Error('no location from /go, status=' + r1.status)
const r2 = await fetch(new URL(loc, BASE).toString(), { redirect: 'manual' })
const setc = String(r2.headers.get('set-cookie') || '')
if (!/cloud_gate=1/.test(setc)) throw new Error('no cloud_gate cookie, status=' + r2.status + ' set-cookie=' + setc)
return 'cloud_gate=1'
}
const timeIt = async (name, fn) => {
const t0 = Date.now()
try {
const r = await fn()
await r.text()
return { name, ms: Date.now() - t0, status: r.status }
} catch (e) {
return { name, ms: Date.now() - t0, error: String((e && e.message) || e) }
}
}
const main = async () => {
const cookie = await getCookieFromGo()
const get = url => fetch(`${BASE}${url}`, { headers: { Cookie: cookie }, cache: 'no-store' })
const post = url => fetch(`${BASE}${url}`, { method: 'POST', headers: { Cookie: cookie, 'Content-Type': 'application/json' }, body: JSON.stringify({ source: 'cloud', action: 'timing_probe' }) })
// 1) 拉取前端真实加载日志(用户刷新时上报的 start/done/error 序列)
try {
const logs = await (await get('/api/debug/logs?limit=150&channel=cloud')).json()
console.log('=== 最近 cloud 前端事件(旧→新) ===')
;(logs.lines || []).forEach(l => {
const o = typeof l === 'string' ? (() => { try { return JSON.parse(l) } catch { return { raw: l } } })() : l
console.log(`${o.ts || o.time || o.t || ''} | ${o.action || o.type || o.event || ''} | count=${o.count != null ? o.count : ''} ${o.error ? 'err=' + String(o.error).slice(0, 80) : ''}`)
})
} catch (e) { console.log('读取 debug logs 失败:', e.message) }
// 2) 顺序测量各接口(2 轮)
const endpoints = [
['GET /api/cloud/operators', () => get('/api/cloud/operators')],
['GET /api/cloud/ecs', () => get('/api/cloud/ecs')],
['GET /api/cloud/vas', () => get('/api/cloud/vas')],
['GET /api/cloud/renew', () => get('/api/cloud/renew')],
['GET /api/cloud/notes', () => get('/api/cloud/notes')],
['GET /api/cloud/balance_alerts/status', () => get('/api/cloud/balance_alerts/status')],
['POST /api/debug/event', () => post('/api/debug/event')],
['GET /api/flags', () => get('/api/flags')]
]
for (let round = 1; round <= 2; round++) {
console.log(`=== 顺序测量 第${round}轮 ===`)
for (const [name, fn] of endpoints) {
const r = await timeIt(name, fn)
console.log(`${String(r.ms).padStart(5)}ms ${r.status || ''} ${r.error || ''} ${name}`)
}
}
// 3) 模拟浏览器初始化并发突发(2 轮)
for (let round = 1; round <= 2; round++) {
console.log(`=== 并发突发 第${round}轮 ===`)
const t0 = Date.now()
const results = await Promise.all([
timeIt('POST boot', () => post('/api/debug/event')),
timeIt('POST ops_start', () => post('/api/debug/event')),
timeIt('GET operators', () => get('/api/cloud/operators')),
timeIt('GET ecs', () => get('/api/cloud/ecs')),
timeIt('GET vas', () => get('/api/cloud/vas')),
timeIt('GET renew', () => get('/api/cloud/renew')),
timeIt('GET notes', () => get('/api/cloud/notes')),
timeIt('GET balance_status', () => get('/api/cloud/balance_alerts/status')),
timeIt('GET flags', () => get('/api/flags'))
])
results.sort((a, b) => a.ms - b.ms).forEach(r => console.log(`${String(r.ms).padStart(5)}ms ${r.status || ''} ${r.error || ''} ${r.name}`))
console.log(`突发总耗时: ${Date.now() - t0}ms`)
}
}
main().catch(e => { console.error('FATAL', e); process.exit(1) })