feat: i18n补全 + 币种分布改为收入/成本/毛利图表
1. i18n/zh/msg.txt: 补全所有缺失的英文翻译 2. dashboards.py: 新增 get_financial_summary(period) 平台级财务汇总 3. init.py: 注册 api_financial_summary + 导入 4. sect_currency.ui: 饼图改为累计/当年/当月/当天 收入/成本/毛利柱状图 5. 新增 api/financial_summary.dspy + api/financial_chart.dspy
This commit is contained in:
parent
72c522b5c9
commit
60059b304e
@ -1 +1 @@
|
||||
{"模型性能监控":"Model Performance","供应商性价比对比":"Provider ROI","平均 TTFT":"Avg TTFT","成功率":"Success Rate","今日调用":"Today Calls","失败数":"Failures","模型性能":"Model Perf","供应商性价比":"Provider ROI","刷新":"Refresh"}
|
||||
{"模型性能监控":"Model Performance","供应商性价比对比":"Provider ROI","平均 TTFT":"Avg TTFT","成功率":"Success Rate","今日调用":"Today Calls","失败数":"Failures","模型性能":"Model Perf","供应商性价比":"Provider ROI","刷新":"Refresh","今日调用笔数":"Today's Calls","今日交易金额":"Today's Amount","今日活跃":"Active Today","在线用户":"Online Users","用户总数":"Total Users","用户":"Users","模型性能排行":"Model Performance","币种分布":"Revenue/Cost/Profit","币种成本收入":"Currency Cost/Revenue","币种费用":"Currency Cost","热门模型":"Popular Models","用户排行":"User Ranking","供应商排行":"Supplier Ranking","我的模型(按金额Top5)":"My Models (Top 5)","API Key排行":"API Key Ranking","财务概览":"Financial Overview","收入":"Revenue","成本":"Cost","毛利":"Gross Profit","累计":"Cumulative","当年":"This Year","当月":"This Month","当天":"Today","今天调用":"Today Calls"}
|
||||
@ -514,7 +514,48 @@ async def get_customer_currency_stats(sor, org_filter, period='today'):
|
||||
FROM dm_model_call_fact f
|
||||
WHERE f.userorgid = ${{org}}$ {date_sql}
|
||||
GROUP BY f.currency ORDER BY revenue_cny DESC"""
|
||||
recs = await sor.sqlExe(sql, ns)
|
||||
return [{'currency': r.currency, 'call_count': int(r.call_count or 0),
|
||||
'revenue': round(float(r.revenue or 0), 2),
|
||||
'revenue_cny': round(float(r.revenue_cny or 0), 2)} for r in recs]
|
||||
async def get_financial_summary(sor, period='all'):
|
||||
"""平台级财务汇总:收入/成本/毛利(不分机构)
|
||||
period: 'today' | 'month' | 'year' | 'all'
|
||||
"""
|
||||
today_str = _date_module_date.today().isoformat()
|
||||
ns = {'today': today_str}
|
||||
date_sql = ""
|
||||
if period == 'month':
|
||||
date_sql = "AND call_date BETWEEN ${month_start}$ AND ${today}$"
|
||||
ns['month_start'] = today_str[:7] + '-01'
|
||||
elif period == 'year':
|
||||
date_sql = "AND call_date BETWEEN ${year_start}$ AND ${today}$"
|
||||
ns['year_start'] = today_str[:4] + '-01-01'
|
||||
elif period == 'today':
|
||||
date_sql = "AND call_date = ${today}$"
|
||||
|
||||
sql_rev = f"""SELECT
|
||||
COALESCE(SUM(amount_cny), 0) as revenue_cny,
|
||||
COUNT(*) as call_count
|
||||
FROM dm_model_call_fact
|
||||
WHERE 1=1 {date_sql}"""
|
||||
recs = await sor.sqlExe(sql_rev, ns)
|
||||
total_revenue_cny = round(float(recs[0].revenue_cny or 0), 2) if recs else 0
|
||||
total_calls = int(recs[0].call_count or 0) if recs else 0
|
||||
|
||||
total_cost_cny = 0.0
|
||||
try:
|
||||
cost_sql = f"""SELECT COALESCE(SUM(c.total_amount_cny), 0) as total_cost_cny
|
||||
FROM dm_provider_cost_daily c
|
||||
WHERE 1=1 {date_sql.replace('call_date', 'c.stat_date')}"""
|
||||
cost_recs = await sor.sqlExe(cost_sql, ns)
|
||||
if cost_recs:
|
||||
total_cost_cny = round(float(cost_recs[0].total_cost_cny or 0), 2)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
gross_profit_cny = round(total_revenue_cny - total_cost_cny, 2)
|
||||
|
||||
return {
|
||||
'total_revenue_cny': total_revenue_cny,
|
||||
'total_cost_cny': total_cost_cny,
|
||||
'gross_profit_cny': gross_profit_cny,
|
||||
'total_calls': total_calls,
|
||||
'period': period,
|
||||
}
|
||||
|
||||
@ -17,6 +17,8 @@ from .dashboards import (
|
||||
get_distributor_top_customers,
|
||||
get_scoped_today_usage, get_scoped_today_amount,
|
||||
get_scoped_success_rate, get_scoped_fail_count,
|
||||
get_distributor_financial, get_customer_currency_stats,
|
||||
get_financial_summary,
|
||||
)
|
||||
|
||||
MODULE_NAME = "sage_datamart"
|
||||
@ -529,6 +531,18 @@ async def j2_top_providers_data(request):
|
||||
return []
|
||||
|
||||
|
||||
async def api_financial_summary(request):
|
||||
"""平台级财务汇总 API"""
|
||||
try:
|
||||
env = request._run_ns
|
||||
period = (request._run_ns.params_kw or {}).get('period', 'all')
|
||||
async with get_sor_context(env, MODULE_NAME) as sor:
|
||||
return await get_financial_summary(sor, period)
|
||||
except Exception as e:
|
||||
debug("sage_datamart API error: " + str(e))
|
||||
return {}
|
||||
|
||||
|
||||
# ── Cron ETL ──
|
||||
|
||||
async def cron_etl_sync(request):
|
||||
@ -599,6 +613,7 @@ def load_sage_datamart():
|
||||
env.j2_top_models_data = j2_top_models_data
|
||||
env.j2_top_users_data = j2_top_users_data
|
||||
env.j2_top_providers_data = j2_top_providers_data
|
||||
env.api_financial_summary = api_financial_summary
|
||||
env.cron_etl_sync = cron_etl_sync
|
||||
env.cron_etl_aggregate = cron_etl_aggregate
|
||||
env.cron_etl_provider_cost = cron_etl_provider_cost
|
||||
|
||||
9
wwwroot/api/financial_chart.dspy
Normal file
9
wwwroot/api/financial_chart.dspy
Normal file
@ -0,0 +1,9 @@
|
||||
import json
|
||||
period = (params_kw or {}).get('period', 'all')
|
||||
data = await request._run_ns.api_financial_summary(request)
|
||||
chart_data = [
|
||||
{"metric": "收入", "value": data.get('total_revenue_cny', 0)},
|
||||
{"metric": "成本", "value": data.get('total_cost_cny', 0)},
|
||||
{"metric": "毛利", "value": data.get('gross_profit_cny', 0)},
|
||||
]
|
||||
return json.dumps(chart_data, ensure_ascii=False)
|
||||
5
wwwroot/api/financial_summary.dspy
Normal file
5
wwwroot/api/financial_summary.dspy
Normal file
@ -0,0 +1,5 @@
|
||||
import json
|
||||
env = request._run_ns
|
||||
period = (params_kw or {}).get('period', 'all')
|
||||
data = await env.api_financial_summary(request)
|
||||
return json.dumps(data, ensure_ascii=False)
|
||||
@ -1,10 +1,32 @@
|
||||
{
|
||||
"widgettype": "ChartPie",
|
||||
"options": {
|
||||
"height": "200px",
|
||||
"data_url": "/sage_datamart/api/currency_stats.dspy",
|
||||
"nameField": "currency",
|
||||
"valueFields": ["call_count"],
|
||||
"pie_options": {"type": "pie", "radius": "60%"}
|
||||
}
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "gap": "8px"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "4px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Button", "id": "btn_all", "options": {"label": "累计", "i18n": true}},
|
||||
{"widgettype": "Button", "id": "btn_year", "options": {"label": "当年", "i18n": true}},
|
||||
{"widgettype": "Button", "id": "btn_month", "options": {"label": "当月", "i18n": true}},
|
||||
{"widgettype": "Button", "id": "btn_today", "options": {"label": "当天", "i18n": true}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "ChartBar",
|
||||
"id": "financial_chart",
|
||||
"options": {
|
||||
"height": "180px",
|
||||
"data_url": "{{entire_url('/sage_datamart/api/financial_chart.dspy')}}?period=all",
|
||||
"nameField": "metric",
|
||||
"valueFields": ["value"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"binds": [
|
||||
{"wid": "btn_all", "event": "click", "actiontype": "urldata", "target": "financial_chart", "options": {"url": "{{entire_url('/sage_datamart/api/financial_chart.dspy')}}?period=all"}},
|
||||
{"wid": "btn_year", "event": "click", "actiontype": "urldata", "target": "financial_chart", "options": {"url": "{{entire_url('/sage_datamart/api/financial_chart.dspy')}}?period=year"}},
|
||||
{"wid": "btn_month", "event": "click", "actiontype": "urldata", "target": "financial_chart", "options": {"url": "{{entire_url('/sage_datamart/api/financial_chart.dspy')}}?period=month"}},
|
||||
{"wid": "btn_today", "event": "click", "actiontype": "urldata", "target": "financial_chart", "options": {"url": "{{entire_url('/sage_datamart/api/financial_chart.dspy')}}?period=today"}}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user