diff --git a/controller/model_meta.go b/controller/model_meta.go
index b097c80a..72ba4a41 100644
--- a/controller/model_meta.go
+++ b/controller/model_meta.go
@@ -3,8 +3,10 @@ package controller
import (
"encoding/json"
"strconv"
+ "strings"
"one-api/common"
+ "one-api/constant"
"one-api/model"
"github.com/gin-gonic/gin"
@@ -162,17 +164,105 @@ func DeleteModelMeta(c *gin.Context) {
// 辅助函数:填充 Endpoints 和 BoundChannels 和 EnableGroups
func fillModelExtra(m *model.Model) {
- if m.Endpoints == "" {
- eps := model.GetModelSupportEndpointTypes(m.ModelName)
+ // 若为精确匹配,保持原有逻辑
+ if m.NameRule == model.NameRuleExact {
+ if m.Endpoints == "" {
+ eps := model.GetModelSupportEndpointTypes(m.ModelName)
+ if b, err := json.Marshal(eps); err == nil {
+ m.Endpoints = string(b)
+ }
+ }
+ if channels, err := model.GetBoundChannels(m.ModelName); err == nil {
+ m.BoundChannels = channels
+ }
+ m.EnableGroups = model.GetModelEnableGroups(m.ModelName)
+ m.QuotaType = model.GetModelQuotaType(m.ModelName)
+ return
+ }
+
+ // 非精确匹配:计算并集
+ pricings := model.GetPricing()
+
+ // 端点去重集合
+ endpointSet := make(map[constant.EndpointType]struct{})
+ // 已绑定渠道去重集合
+ channelSet := make(map[string]model.BoundChannel)
+ // 分组去重集合
+ groupSet := make(map[string]struct{})
+ // 计费类型(若有任意模型为 1,则返回 1)
+ quotaTypeSet := make(map[int]struct{})
+
+ for _, p := range pricings {
+ var matched bool
+ switch m.NameRule {
+ case model.NameRulePrefix:
+ matched = strings.HasPrefix(p.ModelName, m.ModelName)
+ case model.NameRuleSuffix:
+ matched = strings.HasSuffix(p.ModelName, m.ModelName)
+ case model.NameRuleContains:
+ matched = strings.Contains(p.ModelName, m.ModelName)
+ }
+ if !matched {
+ continue
+ }
+
+ // 收集端点
+ for _, et := range p.SupportedEndpointTypes {
+ endpointSet[et] = struct{}{}
+ }
+
+ // 收集分组
+ for _, g := range p.EnableGroup {
+ groupSet[g] = struct{}{}
+ }
+
+ // 收集计费类型
+ quotaTypeSet[p.QuotaType] = struct{}{}
+
+ // 收集渠道
+ if channels, err := model.GetBoundChannels(p.ModelName); err == nil {
+ for _, ch := range channels {
+ key := ch.Name + "_" + strconv.Itoa(ch.Type)
+ channelSet[key] = ch
+ }
+ }
+ }
+
+ // 序列化端点
+ if len(endpointSet) > 0 && m.Endpoints == "" {
+ eps := make([]constant.EndpointType, 0, len(endpointSet))
+ for et := range endpointSet {
+ eps = append(eps, et)
+ }
if b, err := json.Marshal(eps); err == nil {
m.Endpoints = string(b)
}
}
- if channels, err := model.GetBoundChannels(m.ModelName); err == nil {
+
+ // 序列化渠道
+ if len(channelSet) > 0 {
+ channels := make([]model.BoundChannel, 0, len(channelSet))
+ for _, ch := range channelSet {
+ channels = append(channels, ch)
+ }
m.BoundChannels = channels
}
- // 填充启用分组
- m.EnableGroups = model.GetModelEnableGroups(m.ModelName)
- // 填充计费类型
- m.QuotaType = model.GetModelQuotaType(m.ModelName)
+
+ // 序列化分组
+ if len(groupSet) > 0 {
+ groups := make([]string, 0, len(groupSet))
+ for g := range groupSet {
+ groups = append(groups, g)
+ }
+ m.EnableGroups = groups
+ }
+
+ // 确定计费类型:仅当所有匹配模型计费类型一致时才返回该类型,否则返回 -1 表示未知/不确定
+ if len(quotaTypeSet) == 1 {
+ for k := range quotaTypeSet {
+ m.QuotaType = k
+ }
+ } else {
+ m.QuotaType = -1
+ }
}
diff --git a/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx b/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx
index f856eecb..89c952ea 100644
--- a/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx
+++ b/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx
@@ -63,7 +63,7 @@ const ModelPricingTable = ({
key: group,
group: group,
ratio: groupRatioValue,
- billingType: modelData?.quota_type === 0 ? t('按量计费') : t('按次计费'),
+ billingType: modelData?.quota_type === 0 ? t('按量计费') : (modelData?.quota_type === 1 ? t('按次计费') : '-'),
inputPrice: modelData?.quota_type === 0 ? priceData.inputPrice : '-',
outputPrice: modelData?.quota_type === 0 ? (priceData.completionPrice || priceData.outputPrice) : '-',
fixedPrice: modelData?.quota_type === 1 ? priceData.price : '-',
@@ -100,11 +100,16 @@ const ModelPricingTable = ({
columns.push({
title: t('计费类型'),
dataIndex: 'billingType',
- render: (text) => (
-
- {text}
-
- ),
+ render: (text) => {
+ let color = 'white';
+ if (text === t('按量计费')) color = 'violet';
+ else if (text === t('按次计费')) color = 'teal';
+ return (
+
+ {text || '-'}
+
+ );
+ },
});
// 根据计费类型添加价格列
diff --git a/web/src/components/table/model-pricing/view/card/PricingCardView.jsx b/web/src/components/table/model-pricing/view/card/PricingCardView.jsx
index 2a3e4109..9eab7d08 100644
--- a/web/src/components/table/model-pricing/view/card/PricingCardView.jsx
+++ b/web/src/components/table/model-pricing/view/card/PricingCardView.jsx
@@ -144,13 +144,24 @@ const PricingCardView = ({
// 渲染标签
const renderTags = (record) => {
// 计费类型标签(左边)
- const billingType = record.quota_type === 1 ? 'teal' : 'violet';
- const billingText = record.quota_type === 1 ? t('按次计费') : t('按量计费');
- const billingTag = (
-
- {billingText}
+ let billingTag = (
+
+ -
);
+ if (record.quota_type === 1) {
+ billingTag = (
+
+ {t('按次计费')}
+
+ );
+ } else if (record.quota_type === 0) {
+ billingTag = (
+
+ {t('按量计费')}
+
+ );
+ }
// 自定义标签(右边)
const customTags = [];
diff --git a/web/src/components/table/models/ModelsColumnDefs.js b/web/src/components/table/models/ModelsColumnDefs.js
index 6514e752..4306730e 100644
--- a/web/src/components/table/models/ModelsColumnDefs.js
+++ b/web/src/components/table/models/ModelsColumnDefs.js
@@ -137,7 +137,8 @@ const renderQuotaType = (qt, t) => {
);
}
- return qt ?? '-';
+ // 未知
+ return '-';
};
// Render bound channels
diff --git a/web/src/helpers/utils.js b/web/src/helpers/utils.js
index 71903ab8..d41d426e 100644
--- a/web/src/helpers/utils.js
+++ b/web/src/helpers/utils.js
@@ -632,12 +632,22 @@ export const calculateModelPrice = ({
};
}
- // 按次计费
- const priceUSD = parseFloat(record.model_price) * usedGroupRatio;
- const displayVal = displayPrice(priceUSD);
+ if (record.quota_type === 1) {
+ // 按次计费
+ const priceUSD = parseFloat(record.model_price) * usedGroupRatio;
+ const displayVal = displayPrice(priceUSD);
+ return {
+ price: displayVal,
+ isPerToken: false,
+ usedGroup,
+ usedGroupRatio,
+ };
+ }
+
+ // 未知计费类型,返回占位信息
return {
- price: displayVal,
+ price: '-',
isPerToken: false,
usedGroup,
usedGroupRatio,