main #118

Merged
charles merged 7 commits from main into prod 2026-06-08 17:39:38 +08:00
4 changed files with 45 additions and 21 deletions

View File

@ -178,7 +178,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
sql = """ sql = """
SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at
FROM model_usage FROM model_usage
WHERE %s WHERE %s AND bill_status != '0'
ORDER BY created_at DESC ORDER BY created_at DESC
""" % where_clause """ % where_clause
if limit is not None: if limit is not None:
@ -188,7 +188,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
async def _count_model_usage(sor, conditions): async def _count_model_usage(sor, conditions):
where_clause = ' AND '.join(conditions) if conditions else '1 = 1' where_clause = ' AND '.join(conditions) if conditions else '1 = 1'
sql = 'SELECT COUNT(*) AS total_count FROM model_usage WHERE %s' % where_clause sql = 'SELECT COUNT(*) AS total_count FROM model_usage WHERE %s AND bill_status != "0"' % where_clause
return (await sor.sqlExe(sql, {}))[0]['total_count'] return (await sor.sqlExe(sql, {}))[0]['total_count']

View File

@ -95,7 +95,7 @@ def _normalize_usage_row(row, bill_amount_map=None):
'prompt_tokens': int(usage.get('prompt_tokens') or 0), 'prompt_tokens': int(usage.get('prompt_tokens') or 0),
'completion_tokens': int(usage.get('completion_tokens') or 0), 'completion_tokens': int(usage.get('completion_tokens') or 0),
'total_tokens': int(usage.get('total_tokens') or 0), 'total_tokens': int(usage.get('total_tokens') or 0),
'amount': round(amount, 4), 'amount': round(amount, 2),
'bill_status': row.get('bill_status'), 'bill_status': row.get('bill_status'),
'orderid': orderid, 'orderid': orderid,
'usage_time': row.get('created_at'), 'usage_time': row.get('created_at'),
@ -155,7 +155,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
sql = """ sql = """
SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at
FROM model_usage FROM model_usage
WHERE %s WHERE %s AND bill_status != '0'
ORDER BY created_at DESC ORDER BY created_at DESC
""" % where_clause """ % where_clause
if limit is not None: if limit is not None:
@ -165,7 +165,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
async def _count_model_usage(sor, conditions): async def _count_model_usage(sor, conditions):
where_clause = ' AND '.join(conditions) if conditions else '1 = 1' where_clause = ' AND '.join(conditions) if conditions else '1 = 1'
sql = 'SELECT COUNT(*) AS total_count FROM model_usage WHERE %s' % where_clause sql = 'SELECT COUNT(*) AS total_count FROM model_usage WHERE %s AND bill_status != "0"' % where_clause
return (await sor.sqlExe(sql, {}))[0]['total_count'] return (await sor.sqlExe(sql, {}))[0]['total_count']

View File

@ -260,6 +260,13 @@ async def process_user_billing(ns={}):
'status': 'error', 'status': 'error',
'msg': 'llmid必传' 'msg': 'llmid必传'
} }
model_name = ns.get('model')
if not model_name:
debug(f"{userid} process_user_billing model必传")
return {
'status': 'error',
'msg': 'model必传'
}
try: try:
amount = round(float(amount), 12) amount = round(float(amount), 12)
@ -275,6 +282,9 @@ async def process_user_billing(ns={}):
async with db.sqlorContext('kboss') as sor: async with db.sqlorContext('kboss') as sor:
try: try:
product_li = await sor.R('product', {'providerpid': llmid, 'del_flg': '0'}) product_li = await sor.R('product', {'providerpid': llmid, 'del_flg': '0'})
if not product_li:
product_li = await sor.R('product', {'product_code': model_name, 'del_flg': '0'})
if not product_li: if not product_li:
debug(f"{userid} process_user_billing 未找到对应产品,请确认") debug(f"{userid} process_user_billing 未找到对应产品,请确认")
return { return {

View File

@ -296,6 +296,8 @@ export default {
formatter: '{b}: {c} Token ({d}%)' formatter: '{b}: {c} Token ({d}%)'
}, },
legend: { legend: {
orient: 'horizontal',
left: 'center',
bottom: 0, bottom: 0,
icon: 'circle', icon: 'circle',
textStyle: { textStyle: {
@ -307,11 +309,20 @@ export default {
{ {
name: 'Token占比', name: 'Token占比',
type: 'pie', type: 'pie',
radius: ['52%', '72%'], radius: ['55%', '70%'],
center: ['50%', '44%'], center: ['50%', '48%'],
avoidLabelOverlap: true, avoidLabelOverlap: true,
label: { label: {
formatter: '{b}\n{d}%' show: true,
position: 'outside',
formatter: '{b}\n{d}%',
lineHeight: 18,
fontSize: 12
},
labelLine: {
length: 8,
length2: 12,
smooth: true
}, },
data: hasData data: hasData
? [ ? [
@ -321,7 +332,7 @@ export default {
: [{ name: '暂无数据', value: 1 }], : [{ name: '暂无数据', value: 1 }],
itemStyle: { itemStyle: {
borderColor: '#fff', borderColor: '#fff',
borderWidth: 3 borderWidth: 2
} }
} }
] ]
@ -332,10 +343,11 @@ export default {
const rankList = this.modelUsageRank.slice().reverse() const rankList = this.modelUsageRank.slice().reverse()
this.modelRankChart.setOption({ this.modelRankChart.setOption({
grid: { grid: {
top: 12, top: 24,
right: 24, right: 30,
bottom: 20, bottom: 28,
left: 92 left: 100,
containLabel: false
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
@ -371,15 +383,16 @@ export default {
axisTick: { show: false }, axisTick: { show: false },
axisLabel: { axisLabel: {
color: '#606266', color: '#606266',
width: 82, width: 88,
overflow: 'truncate' overflow: 'truncate',
margin: 8
} }
}, },
series: [ series: [
{ {
name: 'Token消耗', name: 'Token消耗',
type: 'bar', type: 'bar',
barWidth: 12, barWidth: 14,
data: rankList.map(item => item.total_tokens), data: rankList.map(item => item.total_tokens),
itemStyle: { itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
@ -392,6 +405,7 @@ export default {
show: true, show: true,
position: 'right', position: 'right',
color: '#344054', color: '#344054',
fontSize: 11,
formatter: params => this.formatNumber(params.value) formatter: params => this.formatNumber(params.value)
} }
} }
@ -657,11 +671,11 @@ export default {
.chart-box { .chart-box {
width: 100%; width: 100%;
height: 240px; height: 260px;
} }
.model-rank-chart { .model-rank-chart {
height: 300px; height: 320px;
} }
.ratio-list, .ratio-list,