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 = """
SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at
FROM model_usage
WHERE %s
WHERE %s AND bill_status != '0'
ORDER BY created_at DESC
""" % where_clause
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):
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']

View File

@ -95,7 +95,7 @@ def _normalize_usage_row(row, bill_amount_map=None):
'prompt_tokens': int(usage.get('prompt_tokens') or 0),
'completion_tokens': int(usage.get('completion_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'),
'orderid': orderid,
'usage_time': row.get('created_at'),
@ -155,7 +155,7 @@ async def _query_model_usage_rows(sor, conditions, limit=None, offset=None):
sql = """
SELECT id, userid, llmid, original_price, orderid, bill_status, usage_content, created_at
FROM model_usage
WHERE %s
WHERE %s AND bill_status != '0'
ORDER BY created_at DESC
""" % where_clause
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):
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']

View File

@ -260,6 +260,13 @@ async def process_user_billing(ns={}):
'status': 'error',
'msg': 'llmid必传'
}
model_name = ns.get('model')
if not model_name:
debug(f"{userid} process_user_billing model必传")
return {
'status': 'error',
'msg': 'model必传'
}
try:
amount = round(float(amount), 12)
@ -275,6 +282,9 @@ async def process_user_billing(ns={}):
async with db.sqlorContext('kboss') as sor:
try:
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:
debug(f"{userid} process_user_billing 未找到对应产品,请确认")
return {

View File

@ -7,7 +7,7 @@
<span class="title-icon">
<i class="el-icon-data-line"></i>
</span>
<h2>Token用量</h2>
<h2>Token用量</h2>
</div>
<p>查看模型调用 Token 消耗调用次数和费用趋势</p>
</div>
@ -17,7 +17,7 @@
</div>
</div> -->
<div class="stat-row">
<div v-for="item in statCards" :key="item.label" class="stat-card" :class="item.type">
<div class="stat-card-head">
@ -71,7 +71,7 @@
></el-input>
<el-date-picker
v-model="dateRange"
type="daterange"
type="daterange"
size="small"
value-format="yyyy-MM-dd"
range-separator="至"
@ -296,6 +296,8 @@ export default {
formatter: '{b}: {c} Token ({d}%)'
},
legend: {
orient: 'horizontal',
left: 'center',
bottom: 0,
icon: 'circle',
textStyle: {
@ -307,11 +309,20 @@ export default {
{
name: 'Token占比',
type: 'pie',
radius: ['52%', '72%'],
center: ['50%', '44%'],
radius: ['55%', '70%'],
center: ['50%', '48%'],
avoidLabelOverlap: true,
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
? [
@ -321,7 +332,7 @@ export default {
: [{ name: '暂无数据', value: 1 }],
itemStyle: {
borderColor: '#fff',
borderWidth: 3
borderWidth: 2
}
}
]
@ -332,10 +343,11 @@ export default {
const rankList = this.modelUsageRank.slice().reverse()
this.modelRankChart.setOption({
grid: {
top: 12,
right: 24,
bottom: 20,
left: 92
top: 24,
right: 30,
bottom: 28,
left: 100,
containLabel: false
},
tooltip: {
trigger: 'axis',
@ -371,15 +383,16 @@ export default {
axisTick: { show: false },
axisLabel: {
color: '#606266',
width: 82,
overflow: 'truncate'
width: 88,
overflow: 'truncate',
margin: 8
}
},
series: [
{
name: 'Token消耗',
type: 'bar',
barWidth: 12,
barWidth: 14,
data: rankList.map(item => item.total_tokens),
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
@ -392,6 +405,7 @@ export default {
show: true,
position: 'right',
color: '#344054',
fontSize: 11,
formatter: params => this.formatNumber(params.value)
}
}
@ -657,11 +671,11 @@ export default {
.chart-box {
width: 100%;
height: 240px;
height: 260px;
}
.model-rank-chart {
height: 300px;
height: 320px;
}
.ratio-list,