From ee96a5b8668ae3cc49cfe81ccd2cba9ef67302dd Mon Sep 17 00:00:00 2001 From: qian Date: Thu, 20 Aug 2026 00:44:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(model):=E4=BC=98=E5=8C=96=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E4=BB=B7=E6=A0=BC=E8=AE=BE=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/admin/v1/model.go | 8 +++ api/tenant/v1/models.go | 4 ++ internal/dao/internal/mdl_pricing.go | 6 ++ internal/logic/admin/model.go | 66 +++++++++++++++++-- internal/logic/tenant/model.go | 23 ++++++- internal/model/do/mdl_pricing.go | 3 + internal/model/entity/mdl_pricing.go | 3 + .../000017_add_mdl_pricing_display_fields.sql | 16 +++++ .../src/components/ModelPricingModal.vue | 43 +++++++++++- web/tenant/src/views/service/ModelsPage.vue | 20 +++++- 10 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 migrations/000017_add_mdl_pricing_display_fields.sql diff --git a/api/admin/v1/model.go b/api/admin/v1/model.go index 60bf6cf..2a945ea 100644 --- a/api/admin/v1/model.go +++ b/api/admin/v1/model.go @@ -128,6 +128,10 @@ type PricingGetReq struct { type PricingGetRes struct { List []PricingItem `json:"list"` TimeSegments []TimeSegmentItem `json:"time_segments" dc:"时段定价列表(锚点行 time_segments)"` + // 以下为锚点行展示字段(仅 min_tokens=0 行生效) + PriceNote string `json:"price_note" dc:"价格说明(仅管理后台可见的内部备注)"` + DiscountLabel string `json:"discount_label" dc:"折扣标签(对外展示,如 7折起)"` + PriceChangeNote string `json:"price_change_note" dc:"价格调整说明(对外展示,提示价格有变动)"` } // PricingSetReq 设置模型定价(全量替换) @@ -136,6 +140,10 @@ type PricingSetReq struct { ModelID int64 `json:"model_id" in:"path" v:"required" dc:"模型ID"` Items []PricingItem `json:"items" v:"required" dc:"定价列表"` TimeSegments []TimeSegmentItem `json:"time_segments" dc:"时段定价(可选,全量替换;空数组清除时段配置)"` + // 以下为锚点行展示字段(全量替换语义:空=清除;price_note 不透出到租户端) + PriceNote string `json:"price_note" dc:"价格说明(仅内部可见)"` + DiscountLabel string `json:"discount_label" dc:"折扣标签(对外展示)"` + PriceChangeNote string `json:"price_change_note" dc:"价格调整说明(对外展示)"` } type PricingSetRes struct{} diff --git a/api/tenant/v1/models.go b/api/tenant/v1/models.go index ad5d1e3..4226778 100644 --- a/api/tenant/v1/models.go +++ b/api/tenant/v1/models.go @@ -60,6 +60,10 @@ type TenantAvailableModelItem struct { PricingTiers []PricingTierItem `json:"pricing_tiers"` // TimePrices 时段价目(平台配置了时段定价的模型才有;价格为换算后的展示价) TimePrices []TimePriceItem `json:"time_prices"` + // DiscountLabel 折扣标签(平台定价锚点行配置的营销展示文案,如"7折起";NULL=不展示) + DiscountLabel *string `json:"discount_label"` + // PriceChangeNote 价格调整说明(对外提示价格有变动;NULL=不展示) + PriceChangeNote *string `json:"price_change_note"` // AsyncImage 图片模型异步端点是否可用(提交 /v1/images/generations/async 后轮询取图)。 // 仅图片分类模型有意义;真异步厂商,或同步厂商且「同步图片异步化」开关开启时为 true。 AsyncImage bool `json:"async_image"` diff --git a/internal/dao/internal/mdl_pricing.go b/internal/dao/internal/mdl_pricing.go index 5467d00..dcdf6d2 100644 --- a/internal/dao/internal/mdl_pricing.go +++ b/internal/dao/internal/mdl_pricing.go @@ -34,6 +34,9 @@ type MdlPricingColumns struct { CacheReadPrice string // 缓存读取每 1M token 价格(直接定价) CacheCreationPrice string // 缓存创建每 1M token 价格(直接定价) TimeSegments string // 时段定价(JSONB 有序数组,仅 min_tokens=0 锚点行生效):[{"name":"闲时","days":[1,2,3,4,5],"start_time":"00:00","end_time":"08:00","valid_from":"","valid_to":"","multiplier":0.5}],按序先命中先生效,未命中=默认价(乘数 1.0),days 1=周一..7=周日 空=每天,end([]) +// 展示字段(挂锚点行:内部备注 + 对外营销文案) +const editorPriceNote = ref('') +const editorDiscountLabel = ref('') +const editorPriceChangeNote = ref('') + const billingModeOptions = [ { label: '按量计费 (Token)', value: 'token' }, { label: '按次计费', value: 'per_request' }, @@ -46,6 +51,9 @@ function resetEditorDefaults() { editorBillingMode.value = 'token' editorItems.length = 0 editorItems.push(createEmptyItem('token')) + editorPriceNote.value = '' + editorDiscountLabel.value = '' + editorPriceChangeNote.value = '' resetTimeSegments() } @@ -124,6 +132,9 @@ async function loadPricing() { try { const res: any = await request.get(`/admin/models/${props.modelId}/pricing`) const list: any[] = res.data?.data?.list || [] + editorPriceNote.value = res.data?.data?.price_note || '' + editorDiscountLabel.value = res.data?.data?.discount_label || '' + editorPriceChangeNote.value = res.data?.data?.price_change_note || '' if (list.length > 0) { editorBillingMode.value = list[0].billing_mode || 'token' editorItems.length = 0 @@ -379,7 +390,13 @@ async function savePricing() { cache_read_price: item.cache_read_price, cache_creation_price: item.cache_creation_price, })) - await request.put(`/admin/models/${props.modelId}/pricing`, { items, time_segments: segmentsPayload() }) + await request.put(`/admin/models/${props.modelId}/pricing`, { + items, + time_segments: segmentsPayload(), + price_note: editorPriceNote.value.trim(), + discount_label: editorDiscountLabel.value.trim(), + price_change_note: editorPriceChangeNote.value.trim(), + }) Message.success('定价已保存') emit('update:visible', false) emit('saved') @@ -748,6 +765,30 @@ watch(() => props.visible, (val) => { + +
+
+

展示信息

+ 折扣标签与调整说明会展示给租户;价格说明仅管理后台可见 +
+
+ + + + + + +
+ + + +
+ diff --git a/web/tenant/src/views/service/ModelsPage.vue b/web/tenant/src/views/service/ModelsPage.vue index a57155e..7e08982 100644 --- a/web/tenant/src/views/service/ModelsPage.vue +++ b/web/tenant/src/views/service/ModelsPage.vue @@ -51,6 +51,8 @@ interface ModelItem { cache_creation_price: number | null pricing_tiers: PricingTierItem[] time_prices?: TimePriceItem[] | null + discount_label?: string | null + price_change_note?: string | null } const capabilityLabel: Record = { @@ -217,7 +219,7 @@ function timeWindow(tp: TimePriceItem): string { function timePriceText(m: ModelItem, tp: TimePriceItem): string { if (m.billing_mode === 'per_request') return `${formatPrice(tp.per_request_price ?? null)} /次` - if (m.billing_mode === 'tiered') return `${formatPrice(tp.input_price ?? null)} 起` + if (m.billing_mode === 'tiered') return `输入 ${formatPrice(tp.input_price ?? null)} · 输出 ${formatPrice(tp.output_price ?? null)} 起` return `输入 ${formatPrice(tp.input_price ?? null)} · 输出 ${formatPrice(tp.output_price ?? null)}` } @@ -334,6 +336,13 @@ onMounted(fetchModels) {{ categoryLabel[m.category] || m.category }}

{{ m.model_name || m.model_id }}

+ + {{ m.discount_label }} + @@ -382,6 +391,15 @@ onMounted(fetchModels) + +
+ + {{ m.price_change_note }} +
+