From 387f6c1ae01aef634579c1ed374c40f07ab9c718 Mon Sep 17 00:00:00 2001 From: fengsilin Date: Mon, 20 Apr 2026 11:47:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(pricing):=20=E5=AE=9A=E4=BB=B7=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=BA=90=E5=88=87=E6=8D=A2=E5=88=B0=E6=B8=A0=E9=81=93?= =?UTF-8?q?=E8=A1=A8=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=BC=93=E5=AD=98=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 pricing API 从 channel_pricings 表获取实际定价,选取最便宜渠道 - 提取 applyGlobalDefault 辅助函数消除全局回退逻辑重复 - price.go 重构扩展比率为局部变量,简化回退逻辑 - 定价卡片新增缓存读取/创建价格,改为两行布局防止溢出 - ChannelPricingCard 缓存列显示实际价格而非倍率 - 修复移动端 hero 区域 padding 过大 - 默认标签页标题改为 Loading... - 新增缓存读取/创建 i18n 翻译(7 语言) Co-Authored-By: Claude --- model/pricing.go | 100 ++++++++++++-- model/pricing_test.go | 122 ++++++++++++++++++ relay/helper/price.go | 71 +++++----- web/index.html | 2 +- .../modal/components/ChannelPricingCard.jsx | 17 ++- .../view/card/PricingCardView.jsx | 2 +- web/src/helpers/utils.jsx | 50 +++++-- web/src/i18n/locales/en.json | 2 + web/src/i18n/locales/fr.json | 2 + web/src/i18n/locales/ja.json | 2 + web/src/i18n/locales/ru.json | 2 + web/src/i18n/locales/vi.json | 2 + web/src/i18n/locales/zh-CN.json | 2 + web/src/i18n/locales/zh-TW.json | 2 + web/src/pages/Home/components/HeroSection.jsx | 2 +- 15 files changed, 323 insertions(+), 57 deletions(-) create mode 100644 model/pricing_test.go diff --git a/model/pricing.go b/model/pricing.go index e98c892..183c183 100644 --- a/model/pricing.go +++ b/model/pricing.go @@ -25,6 +25,8 @@ type Pricing struct { ModelPrice float64 `json:"model_price"` OwnerBy string `json:"owner_by"` CompletionRatio float64 `json:"completion_ratio"` + CacheRatio float64 `json:"cache_ratio"` + CacheCreationRatio float64 `json:"cache_creation_ratio"` EnableGroup []string `json:"enable_groups"` SupportedEndpointTypes []constant.EndpointType `json:"supported_endpoint_types"` PricingVersion string `json:"pricing_version,omitempty"` @@ -269,6 +271,18 @@ func updatePricing() { } } + // 从渠道定价表加载实际定价数据(仅启用渠道) + var allCPs []ChannelPricing + DB.Table("channel_pricings"). + Select("channel_pricings.*"). + Joins("JOIN channels ON channel_pricings.channel_id = channels.id"). + Where("channels.status = 1 AND channel_pricings.deleted_at IS NULL"). + Find(&allCPs) + cpMap := make(map[string][]ChannelPricing) + for i := range allCPs { + cpMap[allCPs[i].ModelName] = append(cpMap[allCPs[i].ModelName], allCPs[i]) + } + pricingMap = make([]Pricing, 0) for model, groups := range modelGroupsMap { pricing := Pricing{ @@ -289,16 +303,10 @@ func updatePricing() { pricing.VendorID = meta.VendorID pricing.Type = meta.Type } - modelPrice, findPrice := ratio_setting.GetModelPrice(model, false) - if findPrice { - pricing.ModelPrice = modelPrice - pricing.QuotaType = 1 - } else { - modelRatio, _, _ := ratio_setting.GetModelRatio(model) - pricing.ModelRatio = modelRatio - pricing.CompletionRatio = ratio_setting.GetCompletionRatio(model) - pricing.QuotaType = 0 - } + + // 使用渠道定价表中的实际数据,选取最便宜的渠道 + applyBestChannelPricing(&pricing, cpMap[model], model) + pricingMap = append(pricingMap, pricing) } @@ -324,3 +332,75 @@ func updatePricing() { func GetSupportedEndpointMap() map[string]common.EndpointInfo { return supportedEndpointMap } + +// applyGlobalDefault 用全局默认值填充 Pricing(无渠道定价时的回退) +func applyGlobalDefault(pricing *Pricing, model string) { + modelPrice, findPrice := ratio_setting.GetModelPrice(model, false) + if findPrice { + pricing.ModelPrice = modelPrice + pricing.QuotaType = 1 + } else { + modelRatio, _, _ := ratio_setting.GetModelRatio(model) + pricing.ModelRatio = modelRatio + pricing.CompletionRatio = ratio_setting.GetCompletionRatio(model) + pricing.QuotaType = 0 + } + pricing.CacheRatio, _ = ratio_setting.GetCacheRatio(model) + pricing.CacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(model) +} + +// applyBestChannelPricing 从渠道定价中选取最优(最便宜)的价格填充 Pricing +// 优先选按量计费(quota_type=0)的渠道,因为缓存价格仅对按量计费有意义 +// 扩展比率字段(cache_ratio 等)为 0 表示未设置,需回退到全局默认值 +func applyBestChannelPricing(pricing *Pricing, cps []ChannelPricing, model string) { + if len(cps) == 0 { + applyGlobalDefault(pricing, model) + return + } + + // 优先选按量计费 (quota_type=0) 中 model_ratio 最低的渠道 + var bestPerToken *ChannelPricing + for i := range cps { + cp := &cps[i] + if cp.QuotaType == 0 { + if bestPerToken == nil || cp.ModelRatio < bestPerToken.ModelRatio { + bestPerToken = cp + } + } + } + + if bestPerToken != nil { + pricing.QuotaType = 0 + pricing.ModelRatio = bestPerToken.ModelRatio + pricing.CompletionRatio = bestPerToken.CompletionRatio + if bestPerToken.CacheRatio > 0 { + pricing.CacheRatio = bestPerToken.CacheRatio + } else { + pricing.CacheRatio, _ = ratio_setting.GetCacheRatio(model) + } + if bestPerToken.CacheCreationRatio > 0 { + pricing.CacheCreationRatio = bestPerToken.CacheCreationRatio + } else { + pricing.CacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(model) + } + return + } + + // 没有按量渠道,选按次计费 (quota_type=1) 中 model_price 最低的渠道 + var bestPerCall *ChannelPricing + for i := range cps { + cp := &cps[i] + if cp.QuotaType == 1 { + if bestPerCall == nil || cp.ModelPrice < bestPerCall.ModelPrice { + bestPerCall = cp + } + } + } + if bestPerCall != nil { + pricing.QuotaType = 1 + pricing.ModelPrice = bestPerCall.ModelPrice + return + } + + applyGlobalDefault(pricing, model) +} diff --git a/model/pricing_test.go b/model/pricing_test.go new file mode 100644 index 0000000..6364d53 --- /dev/null +++ b/model/pricing_test.go @@ -0,0 +1,122 @@ +package model + +import ( + "testing" + + "github.com/QuantumNous/new-api/setting/ratio_setting" + "github.com/glebarez/sqlite" + "github.com/stretchr/testify/require" + "gorm.io/gorm" +) + +const testPricingModel = "test-pricing-model-apply" + +func setupPricingTest(t *testing.T) { + t.Helper() + db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + require.NoError(t, err) + sqlDB, _ := db.DB() + sqlDB.SetMaxOpenConns(1) + + origDB := DB + DB = db + require.NoError(t, db.AutoMigrate(&ChannelPricing{})) + + // 全局默认定价 + require.NoError(t, ratio_setting.UpdateModelRatioByJSONString(`{"`+testPricingModel+`":10}`)) + require.NoError(t, ratio_setting.UpdateCompletionRatioByJSONString(`{"`+testPricingModel+`":3}`)) + require.NoError(t, ratio_setting.UpdateCacheRatioByJSONString(`{"`+testPricingModel+`":0.5}`)) + require.NoError(t, ratio_setting.UpdateCreateCacheRatioByJSONString(`{"`+testPricingModel+`":0.75}`)) + + t.Cleanup(func() { + DB = origDB + sqlDB.Close() + ratio_setting.UpdateModelRatioByJSONString(`{}`) + ratio_setting.UpdateCompletionRatioByJSONString(`{}`) + ratio_setting.UpdateCacheRatioByJSONString(`{}`) + ratio_setting.UpdateCreateCacheRatioByJSONString(`{}`) + ratio_setting.UpdateModelPriceByJSONString(`{}`) + }) +} + +func TestApplyBestChannelPricing_NoChannelPricing(t *testing.T) { + setupPricingTest(t) + + p := &Pricing{} + applyBestChannelPricing(p, nil, testPricingModel) + + require.Equal(t, 0, p.QuotaType, "应使用全局按量计费") + require.Equal(t, 10.0, p.ModelRatio, "应使用全局 model_ratio") + require.Equal(t, 3.0, p.CompletionRatio, "应使用全局 completion_ratio") + require.Equal(t, 0.5, p.CacheRatio, "应使用全局 cache_ratio") + require.Equal(t, 0.75, p.CacheCreationRatio, "应使用全局 cache_creation_ratio") +} + +func TestApplyBestChannelPricing_PerTokenCheapest(t *testing.T) { + setupPricingTest(t) + + // 渠道A: model_ratio=8(更便宜) + require.NoError(t, (&ChannelPricing{ + ModelName: testPricingModel, ChannelId: 1, + QuotaType: QuotaTypeByTokens, ModelRatio: 8, CompletionRatio: 2, + CacheRatio: 0.3, CacheCreationRatio: 0.6, + }).Insert()) + // 渠道B: model_ratio=12(更贵) + require.NoError(t, (&ChannelPricing{ + ModelName: testPricingModel, ChannelId: 2, + QuotaType: QuotaTypeByTokens, ModelRatio: 12, CompletionRatio: 4, + CacheRatio: 0.8, CacheCreationRatio: 1.0, + }).Insert()) + + cps := []ChannelPricing{ + {ModelRatio: 8, CompletionRatio: 2, CacheRatio: 0.3, CacheCreationRatio: 0.6}, + {ModelRatio: 12, CompletionRatio: 4, CacheRatio: 0.8, CacheCreationRatio: 1.0}, + } + + p := &Pricing{} + applyBestChannelPricing(p, cps, testPricingModel) + + require.Equal(t, 0, p.QuotaType) + require.Equal(t, 8.0, p.ModelRatio, "应选最便宜的渠道A") + require.Equal(t, 2.0, p.CompletionRatio) + require.Equal(t, 0.3, p.CacheRatio) + require.Equal(t, 0.6, p.CacheCreationRatio) +} + +func TestApplyBestChannelPricing_ExtendedRatioZeroFallback(t *testing.T) { + setupPricingTest(t) + + // 渠道定价中扩展比率为 0,应回退到全局值 + require.NoError(t, (&ChannelPricing{ + ModelName: testPricingModel, ChannelId: 1, + QuotaType: QuotaTypeByTokens, ModelRatio: 5, CompletionRatio: 1, + CacheRatio: 0, CacheCreationRatio: 0, // 0 = 未设置 + }).Insert()) + + cps := []ChannelPricing{ + {ModelRatio: 5, CompletionRatio: 1, CacheRatio: 0, CacheCreationRatio: 0}, + } + + p := &Pricing{} + applyBestChannelPricing(p, cps, testPricingModel) + + require.Equal(t, 0.5, p.CacheRatio, "cache_ratio=0 应回退全局 0.5") + require.Equal(t, 0.75, p.CacheCreationRatio, "cache_creation_ratio=0 应回退全局 0.75") +} + +func TestApplyBestChannelPricing_PerCallCheapest(t *testing.T) { + setupPricingTest(t) + require.NoError(t, ratio_setting.UpdateModelPriceByJSONString(`{"`+testPricingModel+`":0.8}`)) + + // 只有按次计费的渠道 + cps := []ChannelPricing{ + {QuotaType: QuotaTypeByCall, ModelPrice: 0.3}, + {QuotaType: QuotaTypeByCall, ModelPrice: 0.5}, + } + + p := &Pricing{} + applyBestChannelPricing(p, cps, testPricingModel) + + require.Equal(t, 1, p.QuotaType) + require.Equal(t, 0.3, p.ModelPrice, "应选最便宜的按次渠道") +} diff --git a/relay/helper/price.go b/relay/helper/price.go index 3523086..7138b18 100644 --- a/relay/helper/price.go +++ b/relay/helper/price.go @@ -49,6 +49,7 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens var modelRatio float64 var completionRatio float64 var channelPricingFound bool + var cacheRatio, cacheCreationRatio, imageRatio, audioRatio, audioCompletionRatio float64 // 尝试获取渠道定价(优先于全局定价) channelMetaAvailable := info != nil && info.ChannelMeta != nil && info.ChannelId > 0 @@ -60,6 +61,12 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens modelPrice = cp.ModelPrice usePrice = cp.QuotaType == model.QuotaTypeByCall channelPricingFound = true + // 渠道定价的扩展比率(非零值直接使用) + cacheRatio = cp.CacheRatio + cacheCreationRatio = cp.CacheCreationRatio + imageRatio = cp.ImageRatio + audioRatio = cp.AudioRatio + audioCompletionRatio = cp.AudioCompletionRatio if common.DebugEnabled { println(fmt.Sprintf("[ChannelPricing] hit: model=%s channel=%d source=cache", info.OriginModelName, info.ChannelId)) } @@ -105,6 +112,24 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens preConsumedQuota = int(modelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio) } + // 全局比率作为回退(仅当渠道未设置、值为 0 时生效) + // 必须放在 usePrice 判断之外,因为 UpdatePriceDataForChannelPricing 可能改变 UsePrice + if cacheRatio == 0 { + cacheRatio, _ = ratio_setting.GetCacheRatio(info.OriginModelName) + } + if cacheCreationRatio == 0 { + cacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(info.OriginModelName) + } + if imageRatio == 0 { + imageRatio, _ = ratio_setting.GetImageRatio(info.OriginModelName) + } + if audioRatio == 0 { + audioRatio = ratio_setting.GetAudioRatio(info.OriginModelName) + } + if audioCompletionRatio == 0 { + audioCompletionRatio = ratio_setting.GetAudioCompletionRatio(info.OriginModelName) + } + // check if free model pre-consume is disabled if !operation_setting.GetQuotaSetting().EnableFreeModelPreConsume { // if model price or ratio is 0, do not pre-consume quota @@ -125,38 +150,20 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens } priceData := types.PriceData{ - FreeModel: freeModel, - ModelPrice: modelPrice, - ModelRatio: modelRatio, - CompletionRatio: completionRatio, - GroupRatioInfo: groupRatioInfo, - UsePrice: usePrice, - QuotaToPreConsume: preConsumedQuota, - } - - // 应用渠道定价的扩展比率(非零值覆盖),然后回退全局比率 - if channelMetaAvailable { - if cp, found := model.GetEffectivePricing(info.OriginModelName, info.ChannelId); found { - priceData.ApplyChannelPricingRatios(cp.CacheRatio, cp.CacheCreationRatio, cp.ImageRatio, cp.AudioRatio, cp.AudioCompletionRatio) - } - } - // 全局比率作为回退(仅当对应字段仍为 0 时生效) - if priceData.CacheRatio == 0 { - priceData.CacheRatio, _ = ratio_setting.GetCacheRatio(info.OriginModelName) - } - if priceData.CacheCreationRatio == 0 { - priceData.CacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(info.OriginModelName) - priceData.CacheCreation5mRatio = priceData.CacheCreationRatio - priceData.CacheCreation1hRatio = priceData.CacheCreationRatio * types.ClaudeCacheCreation1hMultiplier - } - if priceData.ImageRatio == 0 { - priceData.ImageRatio, _ = ratio_setting.GetImageRatio(info.OriginModelName) - } - if priceData.AudioRatio == 0 { - priceData.AudioRatio = ratio_setting.GetAudioRatio(info.OriginModelName) - } - if priceData.AudioCompletionRatio == 0 { - priceData.AudioCompletionRatio = ratio_setting.GetAudioCompletionRatio(info.OriginModelName) + FreeModel: freeModel, + ModelPrice: modelPrice, + ModelRatio: modelRatio, + CompletionRatio: completionRatio, + GroupRatioInfo: groupRatioInfo, + UsePrice: usePrice, + QuotaToPreConsume: preConsumedQuota, + CacheRatio: cacheRatio, + ImageRatio: imageRatio, + AudioRatio: audioRatio, + AudioCompletionRatio: audioCompletionRatio, + CacheCreationRatio: cacheCreationRatio, + CacheCreation5mRatio: cacheCreationRatio, + CacheCreation1hRatio: cacheCreationRatio * types.ClaudeCacheCreation1hMultiplier, } if common.DebugEnabled { diff --git a/web/index.html b/web/index.html index 3946f66..485d8b1 100644 --- a/web/index.html +++ b/web/index.html @@ -10,7 +10,7 @@ content="OpenAI 接口聚合管理,支持多种渠道包括 Azure,可用于二次分发管理 key,仅单可执行文件,已打包好 Docker 镜像,一键部署,开箱即用" /> - New API + Loading... diff --git a/web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx b/web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx index 786f886..c953b58 100644 --- a/web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx +++ b/web/src/components/table/model-pricing/modal/components/ChannelPricingCard.jsx @@ -212,17 +212,26 @@ const ChannelPricingCard = ({ (item) => item.cacheRatio > 0 || item.cacheCreationRatio > 0 ); + const renderCachePrice = (v, record) => { + if (record.quotaType !== 0 || v <= 0) return '-'; + return ( +
+ {formatPrice(record.modelRatio * v * 2)} +
+ ); + }; + const advancedColumns = hasAdvancedPricing ? [ { - title: t('缓存读取'), + title: t('缓存读取') + ` / ${tokenUnit === 'K' ? '1K' : '1M'} tokens`, dataIndex: 'cacheRatio', - render: (v) => (v > 0 ? v : '-'), + render: renderCachePrice, }, { - title: t('缓存创建'), + title: t('缓存创建') + ` / ${tokenUnit === 'K' ? '1K' : '1M'} tokens`, dataIndex: 'cacheCreationRatio', - render: (v) => (v > 0 ? v : '-'), + render: renderCachePrice, }, ] : []; 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 98a4b58..dad1f7f 100644 --- a/web/src/components/table/model-pricing/view/card/PricingCardView.jsx +++ b/web/src/components/table/model-pricing/view/card/PricingCardView.jsx @@ -265,7 +265,7 @@ const PricingCardView = ({

{model.model_name}

-
+
{formatPriceInfo(priceData, t)}
diff --git a/web/src/helpers/utils.jsx b/web/src/helpers/utils.jsx index 263bb5a..6215da4 100644 --- a/web/src/helpers/utils.jsx +++ b/web/src/helpers/utils.jsx @@ -679,9 +679,27 @@ export const calculateModelPrice = ({ symbol = '¤'; } } + + const cacheReadRatio = record.cache_ratio || 0; + const cacheCreationRatio = record.cache_creation_ratio || 0; + let cacheReadPrice = null; + let cacheCreationPrice = null; + if (cacheReadRatio > 0) { + const cacheReadUSD = record.model_ratio * cacheReadRatio * 2 * usedGroupRatio; + const numCacheRead = parseFloat(displayPrice(cacheReadUSD).replace(/[^0-9.]/g, '')) / unitDivisor; + cacheReadPrice = `${symbol}${numCacheRead.toFixed(precision)}`; + } + if (cacheCreationRatio > 0) { + const cacheCreationUSD = record.model_ratio * cacheCreationRatio * 2 * usedGroupRatio; + const numCacheCreation = parseFloat(displayPrice(cacheCreationUSD).replace(/[^0-9.]/g, '')) / unitDivisor; + cacheCreationPrice = `${symbol}${numCacheCreation.toFixed(precision)}`; + } + return { inputPrice: `${symbol}${numInput.toFixed(precision)}`, completionPrice: `${symbol}${numCompletion.toFixed(precision)}`, + cacheReadPrice, + cacheCreationPrice, unitLabel, isPerToken: true, usedGroup, @@ -715,14 +733,30 @@ export const calculateModelPrice = ({ export const formatPriceInfo = (priceData, t) => { if (priceData.isPerToken) { return ( - <> - - {t('输入')} {priceData.inputPrice}/{priceData.unitLabel} - - - {t('输出')} {priceData.completionPrice}/{priceData.unitLabel} - - +
+
+ + {t('输入')} {priceData.inputPrice}/{priceData.unitLabel} + + + {t('输出')} {priceData.completionPrice}/{priceData.unitLabel} + +
+ {(priceData.cacheReadPrice || priceData.cacheCreationPrice) && ( +
+ {priceData.cacheReadPrice && ( + + {t('缓存读取')} {priceData.cacheReadPrice}/{priceData.unitLabel} + + )} + {priceData.cacheCreationPrice && ( + + {t('缓存创建')} {priceData.cacheCreationPrice}/{priceData.unitLabel} + + )} +
+ )} +
); } diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index d5f8877..a6da31e 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -2260,6 +2260,8 @@ "缓存目录": "Cache Directory", "缓存目录磁盘空间": "Cache Directory Disk Space", "缓存读": "Cache Read", + "缓存读取": "Cache Read", + "缓存创建": "Cache Creation", "编辑": "Edit", "编辑API": "Edit API", "编辑产品": "Edit Product", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index 0fcce1f..e3c46ec 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -2139,6 +2139,8 @@ "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Ratio de création de cache 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", "缓存创建倍率 {{cacheCreationRatio}}": "Ratio de création de cache {{cacheCreationRatio}}", "缓存读": "Lecture cache", + "缓存读取": "Lecture cache", + "缓存创建": "Création cache", "编辑": "Modifier", "编辑API": "Modifier l'API", "编辑产品": "Modifier le produit", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index 5d7c200..9f881eb 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -2122,6 +2122,8 @@ "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation ratio 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", "缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}", "缓存读": "キャッシュ読取", + "缓存读取": "キャッシュ読取", + "缓存创建": "キャッシュ作成", "编辑": "編集", "编辑API": "API編集", "编辑产品": "Edit Product", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index addbb46..b4e6c98 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -2152,6 +2152,8 @@ "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Коэффициент создания кэша 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", "缓存创建倍率 {{cacheCreationRatio}}": "Коэффициент создания кэша {{cacheCreationRatio}}", "缓存读": "Чтение кэша", + "缓存读取": "Чтение кэша", + "缓存创建": "Создание кэша", "编辑": "Редактировать", "编辑API": "Редактировать API", "编辑产品": "Редактировать продукт", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index 3332dfe..282f417 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -2423,6 +2423,8 @@ "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation ratio 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", "缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}", "缓存读": "Đọc bộ nhớ đệm", + "缓存读取": "Đọc bộ nhớ đệm", + "缓存创建": "Tạo bộ nhớ đệm", "编辑": "Chỉnh sửa", "编辑API": "Chỉnh sửa API", "编辑产品": "Chỉnh sửa sản phẩm", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index c315407..2a478df 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -2242,6 +2242,8 @@ "缓存目录": "缓存目录", "缓存目录磁盘空间": "缓存目录磁盘空间", "缓存读": "缓存读", + "缓存读取": "缓存读取", + "缓存创建": "缓存创建", "编辑": "编辑", "编辑API": "编辑API", "编辑产品": "编辑产品", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index 08ae8b5..f2b695a 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -2180,6 +2180,8 @@ "缓存创建倍率 {{cacheCreationRatio}}": "快取建立倍率 {{cacheCreationRatio}}", "缓存目录": "快取目錄", "缓存目录磁盘空间": "快取目錄磁碟空間", + "缓存读取": "快取讀取", + "缓存创建": "快取建立", "编辑": "編輯", "编辑API": "編輯API", "编辑产品": "編輯產品", diff --git a/web/src/pages/Home/components/HeroSection.jsx b/web/src/pages/Home/components/HeroSection.jsx index eb3243c..37bacad 100644 --- a/web/src/pages/Home/components/HeroSection.jsx +++ b/web/src/pages/Home/components/HeroSection.jsx @@ -81,7 +81,7 @@ const HeroSection = () => {
-
+
{/* 左侧内容 */}