fix: exclude failed accounting from today stats + add click nav to failed records

1. load_dashboard.py: get_today_usage/amount and trend functions now
   filter out accounting_status='failed' records so failed transactions
   are not counted as successful.
2. stat_errors.ui: added click bind to navigate to llmage failed
   accounting records page (app.sage_main_content target).
This commit is contained in:
Hermes Agent 2026-06-19 14:45:26 +08:00
parent 2fcf32c863
commit 35ba31a554
3 changed files with 65 additions and 14 deletions

View File

@ -6,22 +6,22 @@ from datetime import datetime, timedelta, date
async def get_today_usage(request): async def get_today_usage(request):
"""获取当天llmusage笔数""" """获取当天llmusage笔数(排除记账失败)"""
env = request._run_ns env = request._run_ns
today = env.curDateString() today = env.curDateString()
async with get_sor_context(env, 'sage') as sor: async with get_sor_context(env, 'sage') as sor:
sql = "SELECT COUNT(*) as cnt FROM llmusage WHERE use_date = ${today}$" sql = "SELECT COUNT(*) as cnt FROM llmusage WHERE use_date = ${today}$ AND accounting_status != 'failed'"
recs = await sor.sqlExe(sql, {'today': today}) recs = await sor.sqlExe(sql, {'today': today})
cnt = int(recs[0].get('cnt', 0)) if recs else 0 cnt = int(recs[0].get('cnt', 0)) if recs else 0
return cnt return cnt
async def get_today_amount(request): async def get_today_amount(request):
"""获取当天交易金额""" """获取当天交易金额(排除记账失败)"""
env = request._run_ns env = request._run_ns
today = env.curDateString() today = env.curDateString()
async with get_sor_context(env, 'sage') as sor: async with get_sor_context(env, 'sage') as sor:
sql = "SELECT COALESCE(SUM(amount), 0) as total_amount FROM llmusage WHERE use_date = ${today}$" sql = "SELECT COALESCE(SUM(amount), 0) as total_amount FROM llmusage WHERE use_date = ${today}$ AND accounting_status != 'failed'"
recs = await sor.sqlExe(sql, {'today': today}) recs = await sor.sqlExe(sql, {'today': today})
amount = float(recs[0].get('total_amount', 0)) if recs else 0.0 amount = float(recs[0].get('total_amount', 0)) if recs else 0.0
return amount return amount
@ -34,12 +34,12 @@ async def get_usage_trend(request):
yesterday = (date.today() - timedelta(days=1)).isoformat() yesterday = (date.today() - timedelta(days=1)).isoformat()
async with get_sor_context(env, 'sage') as sor: async with get_sor_context(env, 'sage') as sor:
# 今日 # 今日
sql_today = "SELECT COUNT(*) as cnt FROM llmusage WHERE use_date = ${today}$" sql_today = "SELECT COUNT(*) as cnt FROM llmusage WHERE use_date = ${today}$ AND accounting_status != 'failed'"
recs_today = await sor.sqlExe(sql_today, {'today': today}) recs_today = await sor.sqlExe(sql_today, {'today': today})
today_cnt = int(recs_today[0].get('cnt', 0)) if recs_today else 0 today_cnt = int(recs_today[0].get('cnt', 0)) if recs_today else 0
# 昨日 # 昨日
sql_yesterday = "SELECT COUNT(*) as cnt FROM llmusage WHERE use_date = ${yesterday}$" sql_yesterday = "SELECT COUNT(*) as cnt FROM llmusage WHERE use_date = ${yesterday}$ AND accounting_status != 'failed'"
recs_yesterday = await sor.sqlExe(sql_yesterday, {'yesterday': yesterday}) recs_yesterday = await sor.sqlExe(sql_yesterday, {'yesterday': yesterday})
yesterday_cnt = int(recs_yesterday[0].get('cnt', 0)) if recs_yesterday else 0 yesterday_cnt = int(recs_yesterday[0].get('cnt', 0)) if recs_yesterday else 0
@ -66,28 +66,28 @@ async def get_amount_trend(request):
yesterday = (date.today() - timedelta(days=1)).isoformat() yesterday = (date.today() - timedelta(days=1)).isoformat()
async with get_sor_context(env, 'sage') as sor: async with get_sor_context(env, 'sage') as sor:
# 今日 # 今日
sql_today = "SELECT COALESCE(SUM(amount), 0) as total_amount FROM llmusage WHERE use_date = ${today}$" sql_today = "SELECT COALESCE(SUM(amount), 0) as total_amount FROM llmusage WHERE use_date = ${today}$ AND accounting_status != 'failed'"
recs_today = await sor.sqlExe(sql_today, {'today': today}) recs_today = await sor.sqlExe(sql_today, {'today': today})
today_amount = float(recs_today[0].get('total_amount', 0)) if recs_today else 0.0 today_amount = float(recs_today[0].get('total_amount', 0)) if recs_today else 0.0
# 昨日 # 昨日
sql_yesterday = "SELECT COALESCE(SUM(amount), 0) as total_amount FROM llmusage WHERE use_date = ${yesterday}$" sql_yesterday = "SELECT COALESCE(SUM(amount), 0) as total_amount FROM llmusage WHERE use_date = ${yesterday}$ AND accounting_status != 'failed'"
recs_yesterday = await sor.sqlExe(sql_yesterday, {'yesterday': yesterday}) recs_yesterday = await sor.sqlExe(sql_yesterday, {'yesterday': yesterday})
yesterday_amount = float(recs_yesterday[0].get('total_amount', 0)) if recs_yesterday else 0.0 yesterday_amount = float(recs_yesterday[0].get('total_amount', 0)) if recs_yesterday else 0.0
# 计算趋势 # 计算趋势
if yesterday_amount == 0: if yesterday_amount == 0:
return {'trend': 'flat', 'percentage': 0, 'value': today_amount} return {'trend': 'flat', 'percentage': 0, 'value': today_amount}
change_pct = ((today_amount - yesterday_amount) / yesterday_amount) * 100 change_pct = ((today_amount - yesterday_amount) / yesterday_amount) * 100
if change_pct > 5: if change_pct > 5:
trend = 'up' trend = 'up'
elif change_pct < -5: elif change_pct < -5:
trend = 'down' trend = 'down'
else: else:
trend = 'flat' trend = 'flat'
return {'trend': trend, 'percentage': abs(change_pct), 'value': today_amount} return {'trend': trend, 'percentage': abs(change_pct), 'value': today_amount}

38
i18n/zh/msg.txt Normal file
View File

@ -0,0 +1,38 @@
Cancel: Cancel
Conform: Conform
Discard: Discard
Reset: Reset
Submit: Submit
今日交易金额: 今日交易金额
今日各模型调用: 今日各模型调用
今日活跃用户: 今日活跃用户
今日消费金额: 今日消费金额
今日调用次数: 今日调用次数
今日调用笔数: 今日调用笔数
供应商排行: 供应商排行
刷新: 刷新
在线用户: 在线用户
客户专属监控: 客户专属监控
当前并发用户: 当前并发用户
我的今日模型使用: 我的今日模型使用
排名: 排名
数据概览: 数据概览
数据看板: 数据看板
暂无数据: 暂无数据
本月各模型调用: 本月各模型调用
本月新增用户: 本月新增用户
本月每日调用趋势: 本月每日调用趋势
本月消费金额: 本月消费金额
本月调用次数: 本月调用次数
查看本组织各模型每日/每月调用次数与金额统计: 查看本组织各模型每日/每月调用次数与金额统计
热门模型: 热门模型
用户: 用户
用户总数: 用户总数
用户排行: 用户排行
用户金额 TOP 5今日: 用户金额 TOP 5今日
笔数: 笔数
组织机构数: 组织机构数
记账异常: 记账异常
记账错误笔数: 记账错误笔数
返回首页: 返回首页
金额: 金额

View File

@ -6,8 +6,21 @@
"borderRadius": "12px", "borderRadius": "12px",
"flex": "1", "flex": "1",
"minHeight": "90px", "minHeight": "90px",
"cursor": "pointer",
"borderLeft": "4px solid #ef4444" "borderLeft": "4px solid #ef4444"
}, },
"binds": [
{
"wid": "self",
"event": "click",
"actiontype": "urlwidget",
"target": "app.sage_main_content",
"options": {
"url": "{{entire_url('/llmage/failed_accounting.ui')}}"
},
"mode": "replace"
}
],
"subwidgets": [ "subwidgets": [
{ {
"widgettype": "HBox", "widgettype": "HBox",