mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 00:53:33 +00:00
fix(admin-spa): 修复仪表板图表时区处理问题
- 修复前端时间计算逻辑,正确处理不同时区用户的时间范围选择 - 添加系统时区信息到dashboard API响应 - 修改getSystemTimezoneDay函数,确保正确处理系统时区的日期转换 - 修复"昨天"和"前天"时间范围计算,基于系统时区而非用户时区 - 添加后端调试日志以便追踪时间转换问题 问题描述: - 选择"昨天"时显示29号7:00到30号6:00(错误) - 选择"近24小时"时显示29号17:00到30号17:00(错误) 修复后: - "昨天"正确显示完整一天(如29号0:00到29号23:59) - "近24小时"正确显示从当前时间往前24小时 - 支持所有时区的用户,不再硬编码UTC+8偏移 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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字段
|
||||
|
||||
2
web/admin-spa/dist/index.html
vendored
2
web/admin-spa/dist/index.html
vendored
@@ -18,7 +18,7 @@
|
||||
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
|
||||
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
|
||||
<script type="module" crossorigin src="/admin-next/assets/index-8QtnjTcX.js"></script>
|
||||
<script type="module" crossorigin src="/admin-next/assets/index-B6gXCBSA.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/admin-next/assets/vue-vendor-CKToUHZx.js">
|
||||
<link rel="modulepreload" crossorigin href="/admin-next/assets/vendor-BDiMbLwQ.js">
|
||||
<link rel="modulepreload" crossorigin href="/admin-next/assets/element-plus-B8Fs_0jW.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 {
|
||||
|
||||
Reference in New Issue
Block a user