diff --git a/sage_datamart/dashboards.py b/sage_datamart/dashboards.py index a0278a0..01b4c69 100644 --- a/sage_datamart/dashboards.py +++ b/sage_datamart/dashboards.py @@ -491,3 +491,30 @@ async def get_distributor_financial(sor, org_filter, period='today'): 'total_calls': total_calls, 'period': period, } + + +async def get_customer_currency_stats(sor, org_filter, period='today'): + """客户费用统计:按币种+时间维度""" + today_str = _date_module_date.today().isoformat() + date_sql = "" + ns = {'org': org_filter, 'today': today_str} + if period == 'month': + date_sql = "AND f.call_date BETWEEN ${month_start}$ AND ${today}$" + ns['month_start'] = today_str[:7] + '-01' + elif period == 'year': + date_sql = "AND f.call_date BETWEEN ${year_start}$ AND ${today}$" + ns['year_start'] = today_str[:4] + '-01-01' + elif period == 'today': + date_sql = "AND f.call_date = ${today}$" + + sql = f"""SELECT COALESCE(f.currency, 'CNY') as currency, + COUNT(*) as call_count, + COALESCE(SUM(f.amount), 0) as revenue, + COALESCE(SUM(f.amount_cny), 0) as revenue_cny + 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] diff --git a/sage_datamart/i18n/en/i18n.json b/sage_datamart/i18n/en/i18n.json index 601d017..8894cf5 100644 --- a/sage_datamart/i18n/en/i18n.json +++ b/sage_datamart/i18n/en/i18n.json @@ -1,20 +1,26 @@ { - "API Key排行": "API Key Ranking", - "供应商性价比": "Provider Cost-Performance", - "供应商排行": "Provider Ranking", - "币种分布": "Currency Distribution", - "我的模型(按金额Top5)": "My Models (Top 5 by Amount)", - "模型性能排行": "Model Performance Ranking", - "热门模型": "Popular Models", - "用户排行": "User Ranking", - "财务概览": "Financial Overview", - "今天调用": "Today", - "用户": "Users", - "今日调用笔数": "Today Calls", - "今日交易金额": "Today Amount", - "用户总数": "Total Users", - "今日活跃": "Today Active", - "在线用户": "Online Users", - "成功率": "Success Rate", - "失败数": "Failed" -} + "API Key排行": "API Key Ranking", + "供应商性价比": "Provider Cost-Performance", + "供应商排行": "Provider Ranking", + "币种分布": "Currency Distribution", + "我的模型(按金额Top5)": "My Models (Top 5 by Amount)", + "模型性能排行": "Model Performance Ranking", + "热门模型": "Popular Models", + "用户排行": "User Ranking", + "财务概览": "Financial Overview", + "今天调用": "Today", + "用户": "Users", + "今日调用笔数": "Today Calls", + "今日交易金额": "Today Amount", + "用户总数": "Total Users", + "今日活跃": "Today Active", + "在线用户": "Online Users", + "成功率": "Success Rate", + "失败数": "Failed", + "币种成本收入": "Currency Cost & Revenue", + "币种费用": "Currency Expenses", + "今天": "Today", + "本月": "This Month", + "本年": "This Year", + "累计": "Cumulative" +} \ No newline at end of file diff --git a/sage_datamart/i18n/jp/i18n.json b/sage_datamart/i18n/jp/i18n.json index b49766a..976f2d3 100644 --- a/sage_datamart/i18n/jp/i18n.json +++ b/sage_datamart/i18n/jp/i18n.json @@ -1,20 +1,26 @@ { - "API Key排行": "APIキーランキング", - "供应商性价比": "プロバイダーコストパフォーマンス", - "供应商排行": "プロバイダーランキング", - "币种分布": "通貨分布", - "我的模型(按金额Top5)": "マイモデル(金額Top5)", - "模型性能排行": "モデルパフォーマンスランキング", - "热门模型": "人気モデル", - "用户排行": "ユーザーランキング", - "财务概览": "財務概要", - "今天调用": "本日", - "用户": "ユーザー", - "今日调用笔数": "本日コール数", - "今日交易金额": "本日取引額", - "用户总数": "総ユーザー数", - "今日活跃": "本日アクティブ", - "在线用户": "オンラインユーザー", - "成功率": "成功率", - "失败数": "失敗数" -} + "API Key排行": "APIキーランキング", + "供应商性价比": "プロバイダーコストパフォーマンス", + "供应商排行": "プロバイダーランキング", + "币种分布": "通貨分布", + "我的模型(按金额Top5)": "マイモデル(金額Top5)", + "模型性能排行": "モデルパフォーマンスランキング", + "热门模型": "人気モデル", + "用户排行": "ユーザーランキング", + "财务概览": "財務概要", + "今天调用": "本日", + "用户": "ユーザー", + "今日调用笔数": "本日コール数", + "今日交易金额": "本日取引額", + "用户总数": "総ユーザー数", + "今日活跃": "本日アクティブ", + "在线用户": "オンラインユーザー", + "成功率": "成功率", + "失败数": "失敗数", + "币种成本收入": "通貨コスト収入", + "币种费用": "通貨費用", + "今天": "今日", + "本月": "今月", + "本年": "今年", + "累计": "累計" +} \ No newline at end of file diff --git a/sage_datamart/i18n/ko/i18n.json b/sage_datamart/i18n/ko/i18n.json index 195fb2c..a1c1f34 100644 --- a/sage_datamart/i18n/ko/i18n.json +++ b/sage_datamart/i18n/ko/i18n.json @@ -1,20 +1,26 @@ { - "API Key排行": "API 키 랭킹", - "供应商性价比": "공급업체 가성비", - "供应商排行": "공급업체 랭킹", - "币种分布": "통화 분포", - "我的模型(按金额Top5)": "내 모델 (금액 Top5)", - "模型性能排行": "모델 성능 랭킹", - "热门模型": "인기 모델", - "用户排行": "사용자 랭킹", - "财务概览": "재무 개요", - "今天调用": "오늘", - "用户": "사용자", - "今日调用笔数": "오늘 호출", - "今日交易金额": "오늘 거래액", - "用户总数": "전체 사용자", - "今日活跃": "오늘 활성", - "在线用户": "온라인 사용자", - "成功率": "성공률", - "失败数": "실패수" -} + "API Key排行": "API 키 랭킹", + "供应商性价比": "공급업체 가성비", + "供应商排行": "공급업체 랭킹", + "币种分布": "통화 분포", + "我的模型(按金额Top5)": "내 모델 (금액 Top5)", + "模型性能排行": "모델 성능 랭킹", + "热门模型": "인기 모델", + "用户排行": "사용자 랭킹", + "财务概览": "재무 개요", + "今天调用": "오늘", + "用户": "사용자", + "今日调用笔数": "오늘 호출", + "今日交易金额": "오늘 거래액", + "用户总数": "전체 사용자", + "今日活跃": "오늘 활성", + "在线用户": "온라인 사용자", + "成功率": "성공률", + "失败数": "실패수", + "币种成本收入": "통화 비용 수익", + "币种费用": "통화 비용", + "今天": "오늘", + "本月": "이번 달", + "本年": "올해", + "累计": "누계" +} \ No newline at end of file diff --git a/sage_datamart/i18n/zh/i18n.json b/sage_datamart/i18n/zh/i18n.json index ae3a15a..9db34c4 100644 --- a/sage_datamart/i18n/zh/i18n.json +++ b/sage_datamart/i18n/zh/i18n.json @@ -1,20 +1,26 @@ { - "API Key排行": "API Key排行", - "供应商性价比": "供应商性价比", - "供应商排行": "供应商排行", - "币种分布": "币种分布", - "我的模型(按金额Top5)": "我的模型(按金额Top5)", - "模型性能排行": "模型性能排行", - "热门模型": "热门模型", - "用户排行": "用户排行", - "财务概览": "财务概览", - "今天调用": "今天调用", - "用户": "用户", - "今日调用笔数": "今日调用笔数", - "今日交易金额": "今日交易金额", - "用户总数": "用户总数", - "今日活跃": "今日活跃", - "在线用户": "在线用户", - "成功率": "成功率", - "失败数": "失败数" -} + "API Key排行": "API Key排行", + "供应商性价比": "供应商性价比", + "供应商排行": "供应商排行", + "币种分布": "币种分布", + "我的模型(按金额Top5)": "我的模型(按金额Top5)", + "模型性能排行": "模型性能排行", + "热门模型": "热门模型", + "用户排行": "用户排行", + "财务概览": "财务概览", + "今天调用": "今天调用", + "用户": "用户", + "今日调用笔数": "今日调用笔数", + "今日交易金额": "今日交易金额", + "用户总数": "用户总数", + "今日活跃": "今日活跃", + "在线用户": "在线用户", + "成功率": "成功率", + "失败数": "失败数", + "币种成本收入": "币种成本收入", + "币种费用": "币种费用", + "今天": "今天", + "本月": "本月", + "本年": "本年", + "累计": "累计" +} \ No newline at end of file diff --git a/sage_datamart/init.py b/sage_datamart/init.py index a732b56..f84aa88 100644 --- a/sage_datamart/init.py +++ b/sage_datamart/init.py @@ -462,6 +462,17 @@ async def j2_is_distributor(request): except Exception: return False + +async def j2_customer_currency(request, period='today'): + """Jinja2: 客户币种费用""" + try: + env = request._run_ns + scope, org_filter = await _resolve_view(request) + async with get_sor_context(env, MODULE_NAME) as sor: + return await get_customer_currency_stats(sor, org_filter, period) + except Exception: + return [] + # ── 监控组件 j2 函数,每段独立 .ui 通过 RefreshWidget 加载 ── async def j2_model_perf_data(request): @@ -581,6 +592,7 @@ def load_sage_datamart(): env.api_distributor_financial = api_distributor_financial env.j2_distributor_financial = j2_distributor_financial env.j2_is_distributor = j2_is_distributor + env.j2_customer_currency = j2_customer_currency env.j2_model_perf_data = j2_model_perf_data env.j2_currency_data = j2_currency_data env.j2_provider_roi_data = j2_provider_roi_data diff --git a/scripts/load_path.py b/scripts/load_path.py index e5828ac..ffcf370 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -42,6 +42,7 @@ PATHS_LOGINED = [ f"/{MOD}/sect_user_apikeys.ui", f"/{MOD}/sect_distributor_financial.ui", f"/{MOD}/sect_distributor_currency.ui", + f"/{MOD}/sect_customer_currency.ui", ] def run(role, path): diff --git a/wwwroot/index.ui b/wwwroot/index.ui index d843723..6200a62 100644 --- a/wwwroot/index.ui +++ b/wwwroot/index.ui @@ -99,8 +99,16 @@ "widgettype": "VBox", "options": {"css": "card", "width": "40%", "padding": "16px"}, "subwidgets": [ +{% if is_distributor %} + {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "币种成本收入", "i18n": true, "marginBottom": "8px"}}, + {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_distributor_currency.ui')}}", "width": "100%"}} +{% elif is_admin %} {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "币种分布", "i18n": true, "marginBottom": "8px"}}, {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_currency.ui')}}", "width": "100%"}} +{% else %} + {"widgettype": "Title4", "options": {"fontWeight": "600", "otext": "币种费用", "i18n": true, "marginBottom": "8px"}}, + {"widgettype": "RefreshWidget", "options": {"period_seconds": 60, "url": "{{entire_url('sect_customer_currency.ui')}}", "width": "100%"}} +{% endif %} ] } ] diff --git a/wwwroot/sect_customer_currency.ui b/wwwroot/sect_customer_currency.ui new file mode 100644 index 0000000..3d4a8da --- /dev/null +++ b/wwwroot/sect_customer_currency.ui @@ -0,0 +1,41 @@ +{% set today_data = j2_customer_currency(request, 'today') %} +{% set month_data = j2_customer_currency(request, 'month') %} +{% set year_data = j2_customer_currency(request, 'year') %} +{% set all_data = j2_customer_currency(request, 'all') %} + +{% macro sum_cny(data) %}{% set total = namespace(v=0) %}{% for r in data %}{% set total.v = total.v + r.revenue_cny %}{% endfor %}{{ '%.2f' % total.v }}{% endmacro %} + +{ + "widgettype": "VBox", + "options": {"width": "100%"}, + "subwidgets": [ + {"widgettype": "HBox", "options": {"width": "100%", "gap": "10px", "marginBottom": "12px"}, + "subwidgets": [ + {"widgettype": "VBox", "options": {"css": "card", "padding": "10px", "borderRadius": "6px", "width": "25%"}, + "subwidgets": [ + {"widgettype": "Text", "options": {"otext": "今天", "i18n": true, "fontWeight": "600", "fontSize": "12px"}}, + {"widgettype": "Text", "options": {"text": {{json.dumps('¥' + sum_cny(today_data), ensure_ascii=False)}},"fontWeight": "700", "fontSize": "16px", "marginTop": "4px"}} + ] + }, + {"widgettype": "VBox", "options": {"css": "card", "padding": "10px", "borderRadius": "6px", "width": "25%"}, + "subwidgets": [ + {"widgettype": "Text", "options": {"otext": "本月", "i18n": true, "fontWeight": "600", "fontSize": "12px"}}, + {"widgettype": "Text", "options": {"text": {{json.dumps('¥' + sum_cny(month_data), ensure_ascii=False)}},"fontWeight": "700", "fontSize": "16px", "marginTop": "4px"}} + ] + }, + {"widgettype": "VBox", "options": {"css": "card", "padding": "10px", "borderRadius": "6px", "width": "25%"}, + "subwidgets": [ + {"widgettype": "Text", "options": {"otext": "本年", "i18n": true, "fontWeight": "600", "fontSize": "12px"}}, + {"widgettype": "Text", "options": {"text": {{json.dumps('¥' + sum_cny(year_data), ensure_ascii=False)}},"fontWeight": "700", "fontSize": "16px", "marginTop": "4px"}} + ] + }, + {"widgettype": "VBox", "options": {"css": "card", "padding": "10px", "borderRadius": "6px", "width": "25%"}, + "subwidgets": [ + {"widgettype": "Text", "options": {"otext": "累计", "i18n": true, "fontWeight": "600", "fontSize": "12px"}}, + {"widgettype": "Text", "options": {"text": {{json.dumps('¥' + sum_cny(all_data), ensure_ascii=False)}},"fontWeight": "700", "fontSize": "16px", "marginTop": "4px"}} + ] + } + ] + } + ] +}