diff --git a/src/routes/admin.js b/src/routes/admin.js index 26849f40..353f95b4 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -1293,7 +1293,8 @@ router.get('/dashboard', authenticateAdmin, async (req, res) => { claudeAccountsHealthy: activeClaudeAccounts > 0, geminiAccountsHealthy: activeGeminiAccounts > 0, uptime: process.uptime() - } + }, + systemTimezone: config.system.timezoneOffset || 8 }; res.json({ success: true, data: dashboard }); @@ -1452,6 +1453,14 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => { // 使用自定义时间范围 startTime = new Date(startDate); endTime = new Date(endDate); + + // 调试日志 + logger.info(`📊 Usage trend hour granularity - received times:`); + logger.info(` startDate (raw): ${startDate}`); + logger.info(` endDate (raw): ${endDate}`); + logger.info(` startTime (parsed): ${startTime.toISOString()}`); + logger.info(` endTime (parsed): ${endTime.toISOString()}`); + logger.info(` System timezone offset: ${config.system.timezoneOffset || 8}`); } else { // 默认最近24小时 endTime = new Date(); @@ -1471,7 +1480,8 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => { currentHour.setMinutes(0, 0, 0); while (currentHour <= endTime) { - // 使用时区转换后的时间来生成键 + // 注意:前端发送的时间已经是UTC时间,不需要再次转换 + // 直接从currentHour生成对应系统时区的日期和小时 const tzCurrentHour = redis.getDateInTimezone(currentHour); const dateStr = redis.getDateStringInTimezone(currentHour); const hour = String(tzCurrentHour.getUTCHours()).padStart(2, '0'); @@ -1545,11 +1555,11 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => { hourCost = costResult.costs.total; } - // 格式化时间标签 - const tzDate = redis.getDateInTimezone(currentHour); - const month = String(tzDate.getUTCMonth() + 1).padStart(2, '0'); - const day = String(tzDate.getUTCDate()).padStart(2, '0'); - const hourStr = String(tzDate.getUTCHours()).padStart(2, '0'); + // 格式化时间标签 - 使用系统时区的显示 + const tzDateForLabel = redis.getDateInTimezone(currentHour); + const month = String(tzDateForLabel.getUTCMonth() + 1).padStart(2, '0'); + const day = String(tzDateForLabel.getUTCDate()).padStart(2, '0'); + const hourStr = String(tzDateForLabel.getUTCHours()).padStart(2, '0'); trendData.push({ // 对于小时粒度,只返回hour字段,不返回date字段 diff --git a/web/admin-spa/dist/index.html b/web/admin-spa/dist/index.html index cfb4c2ca..0721fb8c 100644 --- a/web/admin-spa/dist/index.html +++ b/web/admin-spa/dist/index.html @@ -18,7 +18,7 @@ - + diff --git a/web/admin-spa/src/stores/dashboard.js b/web/admin-spa/src/stores/dashboard.js index d29a8865..c7837448 100644 --- a/web/admin-spa/src/stores/dashboard.js +++ b/web/admin-spa/src/stores/dashboard.js @@ -27,7 +27,8 @@ export const useDashboardStore = defineStore('dashboard', () => { systemRPM: 0, systemTPM: 0, systemStatus: '正常', - uptime: 0 + uptime: 0, + systemTimezone: 8 // 默认 UTC+8 }) const costsData = ref({ @@ -81,6 +82,41 @@ export const useDashboardStore = defineStore('dashboard', () => { } }) + // 辅助函数:基于系统时区计算时间 + function getDateInSystemTimezone(date = new Date()) { + const offset = dashboardData.value.systemTimezone || 8 + // 将本地时间转换为UTC时间,然后加上系统时区偏移 + const utcTime = date.getTime() + (date.getTimezoneOffset() * 60000) + return new Date(utcTime + (offset * 3600000)) + } + + // 辅助函数:获取系统时区某一天的起止UTC时间 + // 输入:系统时区的日期(使用getDateInSystemTimezone转换后的) + // 输出:对应的UTC时间 + function getSystemTimezoneDay(systemTzDate, startOfDay = true) { + const offset = dashboardData.value.systemTimezone || 8 + + // 使用UTC方法获取系统时区日期的年月日 + const year = systemTzDate.getUTCFullYear() + const month = systemTzDate.getUTCMonth() + const day = systemTzDate.getUTCDate() + + // 创建一个新的Date对象 + const result = new Date() + + if (startOfDay) { + // 设置为系统时区的0点(UTC时间) + result.setUTCFullYear(year, month, day) + result.setUTCHours(0 - offset, 0, 0, 0) + } else { + // 设置为系统时区的23:59:59.999(UTC时间) + result.setUTCFullYear(year, month, day) + result.setUTCHours(23 - offset, 59, 59, 999) + } + + return result + } + // 方法 async function loadDashboardData() { loading.value = true @@ -118,7 +154,8 @@ export const useDashboardStore = defineStore('dashboard', () => { systemRPM: systemAverages.rpm || 0, systemTPM: systemAverages.tpm || 0, systemStatus: systemHealth.redisConnected ? '正常' : '异常', - uptime: systemHealth.uptime || 0 + uptime: systemHealth.uptime || 0, + systemTimezone: dashboardResponse.data.systemTimezone || 8 } } @@ -161,28 +198,20 @@ export const useDashboardStore = defineStore('dashboard', () => { startTime = new Date(now.getTime() - 24 * 60 * 60 * 1000) break case 'yesterday': - // 昨天:使用UTC时间,避免时区双重转换 - startTime = new Date(now) - startTime.setUTCDate(now.getUTCDate() - 1) - startTime.setUTCHours(0, 0, 0, 0) - endTime = new Date(startTime) - endTime.setUTCHours(23, 59, 59, 999) - - // 由于后端会加8小时,我们需要减去8小时 - startTime = new Date(startTime.getTime() - 8 * 60 * 60 * 1000) - endTime = new Date(endTime.getTime() - 8 * 60 * 60 * 1000) + // 昨天:基于系统时区的昨天 + const systemNow = getDateInSystemTimezone(now) + const yesterday = new Date(systemNow) + yesterday.setUTCDate(yesterday.getUTCDate() - 1) + startTime = getSystemTimezoneDay(yesterday, true) + endTime = getSystemTimezoneDay(yesterday, false) break case 'dayBefore': - // 前天:使用UTC时间 - startTime = new Date(now) - startTime.setUTCDate(now.getUTCDate() - 2) - startTime.setUTCHours(0, 0, 0, 0) - endTime = new Date(startTime) - endTime.setUTCHours(23, 59, 59, 999) - - // 由于后端会加8小时,我们需要减去8小时 - startTime = new Date(startTime.getTime() - 8 * 60 * 60 * 1000) - endTime = new Date(endTime.getTime() - 8 * 60 * 60 * 1000) + // 前天:基于系统时区的前天 + const systemNow2 = getDateInSystemTimezone(now) + const dayBefore = new Date(systemNow2) + dayBefore.setUTCDate(dayBefore.getUTCDate() - 2) + startTime = getSystemTimezoneDay(dayBefore, true) + endTime = getSystemTimezoneDay(dayBefore, false) break default: // 默认近24小时 @@ -249,28 +278,20 @@ export const useDashboardStore = defineStore('dashboard', () => { startTime = new Date(now.getTime() - 24 * 60 * 60 * 1000) break case 'yesterday': - // 昨天:使用UTC时间,避免时区双重转换 - startTime = new Date(now) - startTime.setUTCDate(now.getUTCDate() - 1) - startTime.setUTCHours(0, 0, 0, 0) - endTime = new Date(startTime) - endTime.setUTCHours(23, 59, 59, 999) - - // 由于后端会加8小时,我们需要减去8小时 - startTime = new Date(startTime.getTime() - 8 * 60 * 60 * 1000) - endTime = new Date(endTime.getTime() - 8 * 60 * 60 * 1000) + // 昨天:基于系统时区的昨天 + const systemNow3 = getDateInSystemTimezone(now) + const yesterday = new Date(systemNow3) + yesterday.setUTCDate(yesterday.getUTCDate() - 1) + startTime = getSystemTimezoneDay(yesterday, true) + endTime = getSystemTimezoneDay(yesterday, false) break case 'dayBefore': - // 前天:使用UTC时间 - startTime = new Date(now) - startTime.setUTCDate(now.getUTCDate() - 2) - startTime.setUTCHours(0, 0, 0, 0) - endTime = new Date(startTime) - endTime.setUTCHours(23, 59, 59, 999) - - // 由于后端会加8小时,我们需要减去8小时 - startTime = new Date(startTime.getTime() - 8 * 60 * 60 * 1000) - endTime = new Date(endTime.getTime() - 8 * 60 * 60 * 1000) + // 前天:基于系统时区的前天 + const systemNow4 = getDateInSystemTimezone(now) + const dayBefore = new Date(systemNow4) + dayBefore.setUTCDate(dayBefore.getUTCDate() - 2) + startTime = getSystemTimezoneDay(dayBefore, true) + endTime = getSystemTimezoneDay(dayBefore, false) break default: // 默认近24小时 @@ -329,28 +350,20 @@ export const useDashboardStore = defineStore('dashboard', () => { startDate = new Date(now.getTime() - 24 * 60 * 60 * 1000) break case 'yesterday': - // 昨天:使用UTC时间,避免时区双重转换 - startDate = new Date(now) - startDate.setUTCDate(now.getUTCDate() - 1) - startDate.setUTCHours(0, 0, 0, 0) - endDate = new Date(startDate) - endDate.setUTCHours(23, 59, 59, 999) - - // 由于后端会加8小时,我们需要减去8小时 - startDate = new Date(startDate.getTime() - 8 * 60 * 60 * 1000) - endDate = new Date(endDate.getTime() - 8 * 60 * 60 * 1000) + // 昨天:基于系统时区的昨天 + const systemNow5 = getDateInSystemTimezone(now) + const yesterday2 = new Date(systemNow5) + yesterday2.setUTCDate(yesterday2.getUTCDate() - 1) + startDate = getSystemTimezoneDay(yesterday2, true) + endDate = getSystemTimezoneDay(yesterday2, false) break case 'dayBefore': - // 前天:使用UTC时间 - startDate = new Date(now) - startDate.setUTCDate(now.getUTCDate() - 2) - startDate.setUTCHours(0, 0, 0, 0) - endDate = new Date(startDate) - endDate.setUTCHours(23, 59, 59, 999) - - // 由于后端会加8小时,我们需要减去8小时 - startDate = new Date(startDate.getTime() - 8 * 60 * 60 * 1000) - endDate = new Date(endDate.getTime() - 8 * 60 * 60 * 1000) + // 前天:基于系统时区的前天 + const systemNow6 = getDateInSystemTimezone(now) + const dayBefore2 = new Date(systemNow6) + dayBefore2.setUTCDate(dayBefore2.getUTCDate() - 2) + startDate = getSystemTimezoneDay(dayBefore2, true) + endDate = getSystemTimezoneDay(dayBefore2, false) break } } else {