fix(web): 渠道冷却是按原因分类的,提示不再写死「连续失败 X/3」
后端冷却策略已改成按失败原因区分(余额/配额类首次失败即冷却 30 分钟,其余按连续 进冷却次数指数退避 30s→60s→120s 封顶 10 分钟),前端还停留在「满 3 次进冷却 + 原始 秒数」的旧假设上,管理员看到的提示与真实状态对不上。 - Channel 类型补 cooldown_reason(network | server_error | rate_limited | quota_exhausted) - 冷却提示带失败原因;配额类额外提示「需充值或换 key」——唯一重试不会自愈的场景, 应当让管理员直接处理而不是等 30 分钟再看 - 连续失败提示去掉硬编码的 /3(触发阈值随原因变化) - 新增 fmtDur:1800 → 30m、5400 → 1h30m,替代直接显示秒数(附 4 组用例)
This commit is contained in:
1 parent
979973ddde
commit
e7f8a5e5ad
4 files changed
+70
-6
No files matched your search
@@ -4,7 +4,7 @@ import { PhCaretDown, PhPulse, PhNotePencil, PhTrash, PhStack, PhArrowsClockwise
|
||||
import { http, errMsg } from '@/api/client'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { PROTOCOL_OPTIONS, protocolShort } from '@/lib/protocol'
|
||||
import { fmtCost } from '@/lib/format'
|
||||
import { fmtCost, fmtDur } from '@/lib/format'
|
||||
import ChannelModelsDrawer from '@/views/admin/ChannelModelsDrawer.vue'
|
||||
import Button from '@/components/ui/Button.vue'
|
||||
import Input from '@/components/ui/Input.vue'
|
||||
@@ -177,13 +177,41 @@ async function testChannel(ch: Channel) {
|
||||
}
|
||||
}
|
||||
|
||||
// 运行期冷却是网关进程内的状态,探测写的 health_status 看不到它,所以单独提示
|
||||
// 运行期冷却是网关进程内的状态,探测写的 health_status 看不到它,所以单独提示。
|
||||
// 冷却时长与触发条件都随失败原因变化(余额/配额类首次失败即 30 分钟,其余指数退避),
|
||||
// 不能再写死「/3」,也不能只给秒数。
|
||||
const cooldownReasonLabel: Record<string, string> = {
|
||||
network: '网络/超时',
|
||||
server_error: '上游 5xx',
|
||||
rate_limited: '上游限流',
|
||||
quota_exhausted: '余额/配额耗尽',
|
||||
}
|
||||
|
||||
function cooldownReason(ch: Channel): string {
|
||||
const r = ch.cooldown_reason ?? ''
|
||||
return cooldownReasonLabel[r] ?? r
|
||||
}
|
||||
|
||||
function runtimeHint(ch: Channel): string {
|
||||
if (ch.cooldown_seconds > 0) return `冷却中 ${ch.cooldown_seconds}s`
|
||||
if (ch.runtime_fails > 0) return `连续失败 ${ch.runtime_fails}/3`
|
||||
if (ch.cooldown_seconds > 0) {
|
||||
const reason = cooldownReason(ch)
|
||||
const base = `冷却中 ${fmtDur(ch.cooldown_seconds)}`
|
||||
if (isQuotaCooldown(ch)) return `${base} · ${reason},需充值或换 key`
|
||||
return reason ? `${base} · ${reason}` : base
|
||||
}
|
||||
if (ch.runtime_fails > 0) {
|
||||
const reason = cooldownReason(ch)
|
||||
return reason ? `连续失败 ${ch.runtime_fails} 次 · ${reason}` : `连续失败 ${ch.runtime_fails} 次`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// 余额/配额耗尽是唯一的「重试不会自愈」场景:冷却结束也经常还是坏的,值得到管理员面前
|
||||
// 直接点出来(充值或换 key),而不是让他等 30 分钟再看。
|
||||
function isQuotaCooldown(ch: Channel): boolean {
|
||||
return ch.cooldown_seconds > 0 && ch.cooldown_reason === 'quota_exhausted'
|
||||
}
|
||||
|
||||
function needRecover(ch: Channel): boolean {
|
||||
return ch.cooldown_seconds > 0 || ch.health_status !== 'healthy'
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user