fix: 修复释放回库存的上游限频误判与不可用强制释放

- 110001/请求过于频繁归为 rate_limited,冷却期直接返回剩余秒数且不再请求上游
- 释放校验区分限频、校验失败与可强制释放:仅 upstream_status=bound 允许强制
- 弹窗对限频/校验失败改为可重试或关闭,不再展示必然失败的强制释放输入
- 提交前重新校验按钮复用 checkSeq,避免重复打开弹窗
- 修正绑定刷新测试夹具的上游时间未按业务时区格式化导致的失败
This commit is contained in:
2026-11-15 14:13:39 +08:00
parent 6a87fe5228
commit 3ea93c0e0d
7 changed files with 180 additions and 13 deletions
+2
View File
@@ -822,6 +822,8 @@ export interface RockReleaseCheckResult {
role_name?: string
role_id?: string
message: string
// 仅上游限频(110001)时返回,冷却剩余秒数
retry_after_seconds?: number
}
export interface RockTokenStatusLog {
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import { Alert, Descriptions, Grid, Input, Modal, Space, Tag, Typography, message } from 'antd'
import { Alert, Button, Descriptions, Grid, Input, Modal, Space, Tag, Typography, message } from 'antd'
import { ExclamationCircleOutlined, SafetyCertificateOutlined } from '@ant-design/icons'
import {
rockApi,
@@ -36,6 +36,8 @@ export function ReleaseTokenModal({
const [submitting, setSubmitting] = useState(false)
const [submitError, setSubmitError] = useState('')
const [forceText, setForceText] = useState('')
// 重新校验触发器:上游限频或校验失败后无需关闭弹窗即可重试。
const [checkSeq, setCheckSeq] = useState(0)
useEffect(() => {
if (!open || !token) return
@@ -61,16 +63,30 @@ export function ReleaseTokenModal({
return () => {
cancelled = true
}
}, [open, token])
}, [open, token, checkSeq])
if (!token) return null
const needForce = Boolean(check && !check.can_release)
// 只有「上游已绑定」属于人工可确认后强制释放的范围;
// 已发货、失效、卡密不存在、校验失败、限频即使强制也不能放行。
const forceAllowed = check?.upstream_status === 'bound'
const blocked = Boolean(check && !check.can_release && !forceAllowed)
const rateLimited = check?.upstream_status === 'rate_limited'
// 校验直接失败(如本地状态已变化)时只允许关闭,避免出现灰掉的释放按钮。
const closeOnly = blocked || Boolean(checkError)
const forceConfirmed = forceText.trim() === FORCE_CONFIRM_TEXT
const canSubmit =
Boolean(check) && !loadingCheck && !submitting && (!needForce || forceConfirmed)
Boolean(check) &&
!loadingCheck &&
!submitting &&
(Boolean(check?.can_release) || (forceAllowed && forceConfirmed))
const handleSubmit = async () => {
if (closeOnly) {
onCancel()
return
}
if (!canSubmit) return
setSubmitting(true)
setSubmitError('')
@@ -103,16 +119,22 @@ export function ReleaseTokenModal({
width={isMobile ? 'calc(100vw - 24px)' : 640}
style={isMobile ? { top: 16 } : undefined}
onCancel={submitting ? undefined : onCancel}
onOk={handleSubmit}
onOk={closeOnly ? onCancel : handleSubmit}
okText={
submitting
? '释放中…'
: needForce
? '确认强制释放'
: '确认释放回库存'
: closeOnly
? '关闭'
: needForce
? '确认强制释放'
: '确认释放回库存'
}
cancelText="取消"
okButtonProps={{ danger: needForce, disabled: !canSubmit, loading: submitting }}
okButtonProps={{
danger: needForce && forceAllowed,
disabled: closeOnly ? false : !canSubmit,
loading: submitting,
}}
confirmLoading={submitting}
destroyOnHidden
>
@@ -173,7 +195,54 @@ export function ReleaseTokenModal({
/>
)}
{needForce && check && (
{rateLimited && check && (
<Alert
type="warning"
showIcon
message="上游限频,暂时无法校验"
description={
<Space direction="vertical" size={6}>
<span>{check.message}</span>
<span>
这是上游全局频控(110001),不是 Token 状态异常;冷却结束后重新校验即可,
限频期间无法释放回库存。
</span>
<Button size="small" onClick={() => setCheckSeq((n) => n + 1)} loading={loadingCheck}>
重新校验
</Button>
</Space>
}
/>
)}
{blocked && check && !rateLimited && (
<Alert
type="error"
showIcon
icon={<ExclamationCircleOutlined />}
message={`红色警告:${rockUpstreamStatusLabel(check.upstream_status)},禁止释放回库存`}
description={
<Space direction="vertical" size={4}>
<span>{check.message}</span>
{roleText && (
<span>
上游绑定角色:<Text strong>{roleText}</Text>
</span>
)}
{check.upstream_status === 'check_failed' && (
<>
<span>该状态即使输入「强制释放」也不会放行,请先恢复上游连通性后重新校验。</span>
<Button size="small" onClick={() => setCheckSeq((n) => n + 1)} loading={loadingCheck}>
重新校验
</Button>
</>
)}
</Space>
}
/>
)}
{needForce && forceAllowed && check && (
<>
<Alert
type="error"
@@ -212,7 +281,7 @@ export function ReleaseTokenModal({
</>
)}
{!needForce && !checkError && check && (
{check?.can_release && !checkError && (
<Alert
type="warning"
showIcon
+1
View File
@@ -31,6 +31,7 @@ export const ROCK_UPSTREAM_STATUS_LABELS: Record<string, string> = {
abnormal: '上游已上报失效',
not_found: '上游卡密不存在',
check_failed: '上游状态校验失败',
rate_limited: '上游限频',
}
export function rockUpstreamStatusLabel(status?: string): string {