Skip to content

Commit 938dc95

Browse files
wans10Calcium-Ion
andauthored
fix(web): 修复阶梯计费 Base64 解码失败与标签不匹配导致的显示错误 (#4530)
* fix(web): 修复阶梯计费表达式解析与匹配逻辑 - 优化 Base64 解码逻辑:引入 UTF-8 感知的解码方法(使用 TextDecoder/Uint8Array),替换原有的简单 `atob`,修复包含非拉丁字符时解码失败的问题。 - 增强阶梯标签匹配机制:新增标签规范化处理(移除空格、统一大小写、转换 `<`/`≤`/`<=` 等符号),确保日志记录中的标签能够与配置中的标签准确匹配。 - 将上述修复同步应用于 default 和 classic 两套前端主题。 * refactor(web): 完善 Base64 解码函数的类型声明 - 根据 CodeRabbitAI 的代码审查建议,将 `decodeBillingExprB64` 方法中 `Array.prototype.map` 回调函数的参数类型由 `any` 替换为更精确的 `number`。 - 提高了代码的类型安全性与可读性。 * fix(web): 修复动态价格明细表中阶梯高亮未能正确匹配的问题 - 在 default 主题的 `DynamicPricingBreakdown` 组件中,引入 `normalizeTierLabel` 函数。 - 替换原有对 `matchedTierLabel` 的严格相等判定,确保在包含全半角符号(如 `≤`/`<=`)或存在空格等格式不一致的场景下,日志详情中的表格依然能准确高亮(Matched)当前命中的对应计费阶梯。 * refactor(web): 移除阶梯计费标签不匹配时的强制兜底逻辑 - 在 default 和 classic 主题中,修改 `resolveMatchedTier` 和相关的阶梯匹配方法,当日志中 `matched_tier` 无法与表达式中的阶梯标签严格对应时,直接返回 `null` 而不再默认退化展示第一阶梯(`tiers[0]`)的价格。 - 遵循“数据准确性优先”的计费展示准则,防止因匹配失败而向用户展示猜测出的单价,避免产生账单误导及客诉风险。 - 在 Classic 主题账单卡片中,对于无法匹配的异常账单明确展示“未匹配到对应阶梯”的提示。 * fix(web): 修复阶梯计费标签正则匹配的短路问题 - 根据 CodeRabbitAI 的代码审查反馈,修正了 `normalizeLabel`(以及 `normalizeTierLabel`)函数中的正则表达式分支顺序。 - 将原本的 `/<|≤|<=/` 调整为 `/<=|≤|</`,以修复 JavaScript 正则引擎从左到右匹配时,会将 `<=` 中的 `<` 优先短路匹配,导致残留 `=` 号的问题。 - 确保了双字符操作符(如 `<=`、`>=`)现在能够被正确完整地替换为单字符(`<`、`>`),保证了计费阶梯日志匹配的准确性。 * fix(web): 完善阶梯计费未匹配展示 --------- Co-authored-by: CaIon <i@caion.me>
1 parent 5114ad0 commit 938dc95

14 files changed

Lines changed: 1182 additions & 1062 deletions

File tree

web/classic/src/helpers/render.jsx

Lines changed: 1020 additions & 968 deletions
Large diffs are not rendered by default.

web/classic/src/i18n/locales/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3689,6 +3689,8 @@
36893689
"缓存创建-5分钟 (cc5)": "Cache Creation-5min (cc5)",
36903690
"缓存创建-1小时 (cc1h)": "Cache Creation-1hour (cc1h)",
36913691
"阶梯计费": "Tiered Billing",
3692+
"阶梯计费(表达式解析失败)": "Tiered Billing (expression parse failed)",
3693+
"阶梯计费(未匹配到对应阶梯)": "Tiered Billing (no matching tier)",
36923694
"输入 Tokens 阶梯": "Input Token Tiers",
36933695
"输出 Tokens 阶梯": "Output Token Tiers",
36943696
"固定阶梯": "Fixed Tier",

web/classic/src/i18n/locales/fr.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3642,6 +3642,8 @@
36423642
"默认折叠侧边栏": "Réduire la barre latérale par défaut",
36433643
"默认测试模型": "Modèle de test par défaut",
36443644
"默认用户消息": "Bonjour",
3645-
"默认补全倍率": "Taux de complétion par défaut"
3645+
"默认补全倍率": "Taux de complétion par défaut",
3646+
"阶梯计费(表达式解析失败)": "Facturation par paliers (échec de l'analyse de l'expression)",
3647+
"阶梯计费(未匹配到对应阶梯)": "Facturation par paliers (aucun palier correspondant)"
36463648
}
36473649
}

web/classic/src/i18n/locales/ja.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3611,6 +3611,8 @@
36113611
"默认折叠侧边栏": "サイドバーをデフォルトで折りたたむ",
36123612
"默认测试模型": "デフォルトテストモデル",
36133613
"默认用户消息": "こんにちは",
3614-
"默认补全倍率": "デフォルト補完倍率"
3614+
"默认补全倍率": "デフォルト補完倍率",
3615+
"阶梯计费(表达式解析失败)": "段階課金(式の解析に失敗)",
3616+
"阶梯计费(未匹配到对应阶梯)": "段階課金(一致する階層なし)"
36153617
}
36163618
}

web/classic/src/i18n/locales/ru.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3662,6 +3662,8 @@
36623662
"默认折叠侧边栏": "Сворачивать боковую панель по умолчанию",
36633663
"默认测试模型": "Модель для тестирования по умолчанию",
36643664
"默认用户消息": "Здравствуйте",
3665-
"默认补全倍率": "Коэффициент завершения по умолчанию"
3665+
"默认补全倍率": "Коэффициент завершения по умолчанию",
3666+
"阶梯计费(表达式解析失败)": "Многоуровневая тарификация (ошибка разбора выражения)",
3667+
"阶梯计费(未匹配到对应阶梯)": "Многоуровневая тарификация (подходящий уровень не найден)"
36663668
}
36673669
}

web/classic/src/i18n/locales/vi.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4176,6 +4176,8 @@
41764176
"默认折叠侧边栏": "Mặc định thu gọn thanh bên",
41774177
"默认测试模型": "Mô hình kiểm tra mặc định",
41784178
"默认用户消息": "Xin chào",
4179-
"默认补全倍率": "Tỷ lệ hoàn thành mặc định"
4179+
"默认补全倍率": "Tỷ lệ hoàn thành mặc định",
4180+
"阶梯计费(表达式解析失败)": "Thanh toán theo bậc (không phân tích được biểu thức)",
4181+
"阶梯计费(未匹配到对应阶梯)": "Thanh toán theo bậc (không tìm thấy bậc phù hợp)"
41804182
}
41814183
}

web/classic/src/i18n/locales/zh-CN.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3676,6 +3676,8 @@
36763676
"缓存创建-5分钟 (cc5)": "缓存创建-5分钟 (cc5)",
36773677
"缓存创建-1小时 (cc1h)": "缓存创建-1小时 (cc1h)",
36783678
"阶梯计费": "阶梯计费",
3679+
"阶梯计费(表达式解析失败)": "阶梯计费(表达式解析失败)",
3680+
"阶梯计费(未匹配到对应阶梯)": "阶梯计费(未匹配到对应阶梯)",
36793681
"输入 Tokens 阶梯": "输入 Tokens 阶梯",
36803682
"输出 Tokens 阶梯": "输出 Tokens 阶梯",
36813683
"固定阶梯": "固定阶梯",

web/classic/src/i18n/locales/zh-TW.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3635,6 +3635,8 @@
36353635
"默认折叠侧边栏": "預設摺疊側邊欄",
36363636
"默认测试模型": "預設測試模型",
36373637
"默认用户消息": "你好",
3638-
"默认补全倍率": "預設補全倍率"
3638+
"默认补全倍率": "預設補全倍率",
3639+
"阶梯计费(表达式解析失败)": "階梯計費(表達式解析失敗)",
3640+
"阶梯计费(未匹配到对应阶梯)": "階梯計費(未匹配到對應階梯)"
36393641
}
36403642
}

web/classic/src/i18n/locales/zh.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,8 @@
25882588
"关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?",
25892589
"关闭提示": "关闭提示",
25902590
"说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。",
2591-
"提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。"
2591+
"提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。",
2592+
"阶梯计费(表达式解析失败)": "阶梯计费(表达式解析失败)",
2593+
"阶梯计费(未匹配到对应阶梯)": "阶梯计费(未匹配到对应阶梯)"
25922594
}
25932595
}

web/default/src/features/pricing/components/dynamic-pricing-breakdown.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
MATCH_LT,
2222
MATCH_RANGE,
2323
SOURCE_TIME,
24+
normalizeTierLabel,
2425
parseTiersFromExpr,
2526
splitBillingExprAndRequestRules,
2627
tryParseRequestRuleExpr,
@@ -168,6 +169,9 @@ export function DynamicPricingBreakdown({
168169

169170
const hasTiers = tiers.length > 0
170171
const hasRules = ruleGroups.length > 0
172+
const normalizedMatchedTierLabel = normalizeTierLabel(
173+
matchedTierLabel ?? undefined
174+
)
171175

172176
if (!expr) return null
173177

@@ -307,9 +311,9 @@ export function DynamicPricingBreakdown({
307311
{tiers.map((tier, i) => {
308312
const condSummary = formatConditionSummary(tier.conditions, t)
309313
const isMatched =
310-
matchedTierLabel != null &&
311-
matchedTierLabel !== '' &&
312-
tier.label === matchedTierLabel
314+
normalizedMatchedTierLabel !== '' &&
315+
normalizeTierLabel(tier.label) ===
316+
normalizedMatchedTierLabel
313317
return (
314318
<TableRow
315319
key={`tier-${i}`}

0 commit comments

Comments
 (0)