fix: 修复管理员散点图只显示部分用户的问题

- 改为按比例采样,保持各用户数据量比例不变
- 散点图默认时间从7天改为当天(24小时)
- limit 从 2000 提高到 10000
This commit is contained in:
fawney19
2025-12-11 19:34:56 +08:00
parent 3b05f0b269
commit 6e8107e340
2 changed files with 42 additions and 17 deletions

View File

@@ -41,7 +41,7 @@ const props = withDefaults(defineProps<{
isAdmin: boolean isAdmin: boolean
hours?: number hours?: number
}>(), { }>(), {
hours: 168 // 默认7 hours: 24 // 默认
}) })
const loading = ref(false) const loading = ref(false)
@@ -174,17 +174,17 @@ async function loadData() {
loading.value = true loading.value = true
try { try {
if (props.isAdmin) { if (props.isAdmin) {
// 管理员:获取所有用户数据 // 管理员:获取所有用户数据(按比例采样)
timelineData.value = await cacheAnalysisApi.getIntervalTimeline({ timelineData.value = await cacheAnalysisApi.getIntervalTimeline({
hours: props.hours, hours: props.hours,
include_user_info: true, include_user_info: true,
limit: 2000, limit: 10000,
}) })
} else { } else {
// 普通用户:获取自己的数据 // 普通用户:获取自己的数据
timelineData.value = await meApi.getIntervalTimeline({ timelineData.value = await meApi.getIntervalTimeline({
hours: props.hours, hours: props.hours,
limit: 1000, limit: 5000,
}) })
} }
} catch (error) { } catch (error) {

View File

@@ -1730,8 +1730,8 @@ class UsageService:
@staticmethod @staticmethod
def get_interval_timeline( def get_interval_timeline(
db: Session, db: Session,
hours: int = 168, hours: int = 24,
limit: int = 1000, limit: int = 10000,
user_id: Optional[str] = None, user_id: Optional[str] = None,
include_user_info: bool = False, include_user_info: bool = False,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
@@ -1740,8 +1740,8 @@ class UsageService:
Args: Args:
db: 数据库会话 db: 数据库会话
hours: 分析最近多少小时的数据 hours: 分析最近多少小时的数据默认24小时
limit: 最大返回数据点数量 limit: 最大返回数据点数量默认10000
user_id: 指定用户 ID可选为空则返回所有用户 user_id: 指定用户 ID可选为空则返回所有用户
include_user_info: 是否包含用户信息(用于管理员多用户视图) include_user_info: 是否包含用户信息(用于管理员多用户视图)
@@ -1758,6 +1758,7 @@ class UsageService:
# 根据是否需要用户信息选择不同的查询 # 根据是否需要用户信息选择不同的查询
if include_user_info and not user_id: if include_user_info and not user_id:
# 管理员视图:返回带用户信息的数据点 # 管理员视图:返回带用户信息的数据点
# 使用按比例采样,保持每个用户的数据量比例不变
sql = text(f""" sql = text(f"""
WITH request_intervals AS ( WITH request_intervals AS (
SELECT SELECT
@@ -1774,17 +1775,41 @@ class UsageService:
AND u.created_at > :start_date AND u.created_at > :start_date
AND u.user_id IS NOT NULL AND u.user_id IS NOT NULL
{user_filter} {user_filter}
),
filtered_intervals AS (
SELECT
created_at,
user_id,
username,
EXTRACT(EPOCH FROM (created_at - prev_request_at)) / 60.0 as interval_minutes,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at) as rn
FROM request_intervals
WHERE prev_request_at IS NOT NULL
AND EXTRACT(EPOCH FROM (created_at - prev_request_at)) / 60.0 <= 120
),
total_count AS (
SELECT COUNT(*) as cnt FROM filtered_intervals
),
user_totals AS (
SELECT user_id, COUNT(*) as user_cnt FROM filtered_intervals GROUP BY user_id
),
user_limits AS (
SELECT
ut.user_id,
CASE WHEN tc.cnt <= :limit THEN ut.user_cnt
ELSE GREATEST(CEIL(ut.user_cnt::float * :limit / tc.cnt), 1)::int
END as user_limit
FROM user_totals ut, total_count tc
) )
SELECT SELECT
created_at, fi.created_at,
user_id, fi.user_id,
username, fi.username,
EXTRACT(EPOCH FROM (created_at - prev_request_at)) / 60.0 as interval_minutes fi.interval_minutes
FROM request_intervals FROM filtered_intervals fi
WHERE prev_request_at IS NOT NULL JOIN user_limits ul ON fi.user_id = ul.user_id
AND EXTRACT(EPOCH FROM (created_at - prev_request_at)) / 60.0 <= 120 WHERE fi.rn <= ul.user_limit
ORDER BY created_at ORDER BY fi.created_at
LIMIT :limit
""") """)
else: else:
# 普通视图:只返回时间和间隔 # 普通视图:只返回时间和间隔