refactor: consolidate rankings into combined charts

- Change top models from Top 3 to Top 5
- Simplify titles: remove 'Top X' and parenthetical details
- Replace separate user tables (by amount/by count) with single ChartBar
- Replace separate provider tables with single ChartBar
- Add get_top_users_combined and get_top_providers_combined functions
This commit is contained in:
yumoqing 2026-06-01 15:41:40 +08:00
parent c9e860c691
commit 855f376671
6 changed files with 95 additions and 76 deletions

View File

@ -70,7 +70,7 @@ async def get_top_models(request):
WHERE a.use_date = ${today}$
GROUP BY a.llmid, b.name
ORDER BY cnt DESC
LIMIT 3
LIMIT 5
"""
recs = await sor.sqlExe(sql, {'today': today})
result = []
@ -200,6 +200,59 @@ async def get_top_providers_by_count(request):
return result
async def get_top_users_combined(request):
"""Top 5 users by amount with count - for combined ChartBar"""
env = request._run_ns
async with get_sor_context(env, 'sage') as sor:
sql = """
SELECT
COALESCE(b.nick_name, b.username) as user_name,
COUNT(*) as cnt,
COALESCE(SUM(a.amount), 0) as total_amount
FROM llmusage a
LEFT JOIN users b ON a.userid = b.id
GROUP BY a.userid, b.nick_name, b.username
ORDER BY total_amount DESC
LIMIT 5
"""
recs = await sor.sqlExe(sql, {})
result = []
for r in recs:
result.append({
'user_name': r.get('user_name', 'Unknown'),
'cnt': int(r.get('cnt', 0)),
'total_amount': round(float(r.get('total_amount', 0)), 2)
})
return result
async def get_top_providers_combined(request):
"""Top 5 providers by amount with count - for combined ChartBar"""
env = request._run_ns
async with get_sor_context(env, 'sage') as sor:
sql = """
SELECT
COALESCE(c.orgname, 'Unknown') as provider_name,
COUNT(*) as cnt,
COALESCE(SUM(a.amount), 0) as total_amount
FROM llmusage a
LEFT JOIN llm b ON a.llmid = b.id
LEFT JOIN organization c ON b.providerid = c.id
GROUP BY b.providerid, c.orgname
ORDER BY total_amount DESC
LIMIT 5
"""
recs = await sor.sqlExe(sql, {})
result = []
for r in recs:
result.append({
'provider_name': r.get('provider_name', 'Unknown'),
'cnt': int(r.get('cnt', 0)),
'total_amount': round(float(r.get('total_amount', 0)), 2)
})
return result
async def get_active_users_today(request):
"""获取今日活跃用户数今日有llmusage记录的去重用户"""
env = request._run_ns
@ -481,6 +534,8 @@ def load_dashboard():
g.get_top_users_by_count = get_top_users_by_count
g.get_top_providers_by_amount = get_top_providers_by_amount
g.get_top_providers_by_count = get_top_providers_by_count
g.get_top_users_combined = get_top_users_combined
g.get_top_providers_combined = get_top_providers_combined
g.get_active_users_today = get_active_users_today
g.get_new_users_month = get_new_users_month
g.get_total_orgs = get_total_orgs

View File

@ -0,0 +1,6 @@
# coding=utf-8
"""Top providers data API for ChartBar"""
import json
providers = await get_top_providers_combined(request)
return json.dumps(providers, ensure_ascii=False, default=str)

View File

@ -0,0 +1,6 @@
# coding=utf-8
"""Top users data API for ChartBar"""
import json
users = await get_top_users_combined(request)
return json.dumps(users, ensure_ascii=False, default=str)

View File

@ -0,0 +1,9 @@
{
"widgettype": "ChartBar",
"options": {
"height": "300px",
"data_url": "api/top_providers.dspy",
"nameField": "provider_name",
"valueFields": ["total_amount", "cnt"]
}
}

View File

@ -0,0 +1,9 @@
{
"widgettype": "ChartBar",
"options": {
"height": "300px",
"data_url": "api/top_users.dspy",
"nameField": "user_name",
"valueFields": ["total_amount", "cnt"]
}
}

View File

@ -175,7 +175,7 @@
"widgettype": "Title4",
"options": {
"fontWeight": "600",
"otext": "Top 3 模型(今日调用)",
"otext": "热门模型",
"i18n": true
}
},
@ -372,16 +372,16 @@
"options": {
"fontWeight": "600",
"marginBottom": "16px",
"otext": "用户消费排行Top 5",
"otext": "用户排行",
"i18n": true
}
},
{
"widgettype": "RefreshWidget",
"id": "table_top_users",
"id": "chart_top_users",
"options": {
"period_seconds": 30,
"url": "{{entire_url('table_top_users.ui')}}"
"period_seconds": 60,
"url": "{{entire_url('chart_top_users.ui')}}"
}
}
]
@ -401,85 +401,19 @@
"options": {
"fontWeight": "600",
"marginBottom": "16px",
"otext": "用户调用排行Top 5",
"otext": "供应商排行",
"i18n": true
}
},
{
"widgettype": "RefreshWidget",
"id": "table_top_users_count",
"id": "chart_top_providers",
"options": {
"period_seconds": 30,
"url": "{{entire_url('table_top_users_count.ui')}}"
"period_seconds": 60,
"url": "{{entire_url('chart_top_providers.ui')}}"
}
}
]
},
{
"widgettype": "HBox",
"options": {
"width": "100%",
"gap": "20px",
"marginTop": "20px"
},
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"css": "card",
"width": "50%",
"borderRadius": "12px",
"padding": "20px"
},
"subwidgets": [
{
"widgettype": "Title4",
"options": {
"fontWeight": "600",
"marginBottom": "16px",
"otext": "供应商交易排行(金额 Top 5",
"i18n": true
}
},
{
"widgettype": "RefreshWidget",
"id": "table_top_providers_amount",
"options": {
"period_seconds": 30,
"url": "{{entire_url('table_top_providers_amount.ui')}}"
}
}
]
},
{
"widgettype": "VBox",
"options": {
"css": "card",
"width": "50%",
"borderRadius": "12px",
"padding": "20px"
},
"subwidgets": [
{
"widgettype": "Title4",
"options": {
"fontWeight": "600",
"marginBottom": "16px",
"otext": "供应商调用排行(数量 Top 5",
"i18n": true
}
},
{
"widgettype": "RefreshWidget",
"id": "table_top_providers_count",
"options": {
"period_seconds": 30,
"url": "{{entire_url('table_top_providers_count.ui')}}"
}
}
]
}
]
}
{% endif %}
]