Browse Source

💰 feat: Add model billing type (`quota_type`) support across backend & frontend

Summary
• Backend
  1. model/model_meta.go
     – Added `QuotaType` field to `Model` struct (JSON only, gorm `-`).
  2. model/model_groups.go
     – Implemented `GetModelQuotaType(modelName)` leveraging cached pricing map.
  3. controller/model_meta.go
     – Enhanced `fillModelExtra` to populate `QuotaType` using new helper.

• Frontend
  1. web/src/components/table/models/ModelsColumnDefs.js
     – Introduced `renderQuotaType` helper that visualises billing mode with coloured tags (`teal = per-call`, `violet = per-token`).
     – Added “计费类型” column (`quota_type`) to models table.

Why
Providing the billing mode alongside existing pricing/group information gives administrators instant visibility into whether each model is priced per call or per token, aligning UI with new backend metadata.

Notes
No database migration required – `quota_type` is transient, delivered via API. Frontend labels/colours can be adjusted via i18n or theme tokens if necessary.
t0ng7u 7 months ago
parent
commit
5e70274003

+ 2 - 0
controller/model_meta.go

@@ -147,5 +147,7 @@ func fillModelExtra(m *model.Model) {
     }
     // 填充启用分组
     m.EnableGroups = model.GetModelEnableGroups(m.ModelName)
+    // 填充计费类型
+    m.QuotaType = model.GetModelQuotaType(m.ModelName)
 
 }

+ 24 - 0
model/model_extra.go

@@ -0,0 +1,24 @@
+package model
+
+// GetModelEnableGroups 返回指定模型名称可用的用户分组列表。
+// 复用缓存的定价映射,避免额外的数据库查询。
+func GetModelEnableGroups(modelName string) []string {
+    for _, p := range GetPricing() {
+        if p.ModelName == modelName {
+            return p.EnableGroup
+        }
+    }
+    return make([]string, 0)
+}
+
+// GetModelQuotaType 返回指定模型的计费类型(quota_type)。
+// 复用缓存的定价映射,避免额外数据库查询。
+// 如果未找到对应模型,默认返回 0。
+func GetModelQuotaType(modelName string) int {
+    for _, p := range GetPricing() {
+        if p.ModelName == modelName {
+            return p.QuotaType
+        }
+    }
+    return 0
+}

+ 0 - 12
model/model_groups.go

@@ -1,12 +0,0 @@
-package model
-
-// GetModelEnableGroups 返回指定模型名称可用的用户分组列表。
-// 复用缓存的定价映射,避免额外的数据库查询。
-func GetModelEnableGroups(modelName string) []string {
-    for _, p := range GetPricing() {
-        if p.ModelName == modelName {
-            return p.EnableGroup
-        }
-    }
-    return make([]string, 0)
-}

+ 1 - 0
model/model_meta.go

@@ -39,6 +39,7 @@ type Model struct {
 
     BoundChannels []BoundChannel `json:"bound_channels,omitempty" gorm:"-"`
     EnableGroups []string       `json:"enable_groups,omitempty" gorm:"-"`
+    QuotaType    int            `json:"quota_type" gorm:"-"`
 }
 
 // Insert 创建新的模型元数据记录

+ 24 - 0
web/src/components/table/models/ModelsColumnDefs.js

@@ -98,6 +98,25 @@ const renderEndpoints = (text) => {
   });
 };
 
+// Render quota type
+const renderQuotaType = (qt, t) => {
+  if (qt === 1) {
+    return (
+      <Tag color='teal' size='small' shape='circle'>
+        {t('按次计费')}
+      </Tag>
+    );
+  }
+  if (qt === 0) {
+    return (
+      <Tag color='violet' size='small' shape='circle'>
+        {t('按量计费')}
+      </Tag>
+    );
+  }
+  return qt ?? '-';
+};
+
 // Render bound channels
 const renderBoundChannels = (channels) => {
   if (!channels || channels.length === 0) return '-';
@@ -213,6 +232,11 @@ export const getModelsColumns = ({
       dataIndex: 'enable_groups',
       render: renderGroups,
     },
+    {
+      title: t('计费类型'),
+      dataIndex: 'quota_type',
+      render: (qt) => renderQuotaType(qt, t),
+    },
     {
       title: t('创建时间'),
       dataIndex: 'created_time',