fix(delivery): add transient retry, fix queue deadlock, and gate delivery submit by scan auth

This commit is contained in:
yml2213
2026-09-18 14:49:54 +08:00
parent 848d3da809
commit 20bde97d7a
8 changed files with 404 additions and 40 deletions
+4
View File
@@ -175,6 +175,10 @@ export const deliveryApi = {
},
{ params: auth },
).then((r) => r.data.data as DeliveryBindResult),
getBindResult: (orderNo: string, bindUUID: string, auth?: { exp?: string; sign?: string }) =>
request.get(`/delivery/v1/orders/${encodeURIComponent(orderNo)}/bind-result`, {
params: { ...auth, bind_uuid: bindUUID },
}).then((r) => r.data.data as DeliveryBindResult),
submit: (orderNo: string, gameAccount: string, bindUUID: string, auth?: { exp?: string; sign?: string }) =>
request.post(
`/delivery/v1/orders/${encodeURIComponent(orderNo)}/submit`,
+104 -9
View File
@@ -61,6 +61,13 @@ export default function Delivery() {
const [form] = Form.useForm<{ game_account: string }>()
const [order, setOrder] = useState<DeliveryOrderInfo | null>(null)
const [bindResult, setBindResult] = useState<DeliveryBindResult | null>(null)
const [boundInfo, setBoundInfo] = useState<{
bound: boolean
role_name?: string
game_account?: string
game_channel?: string
} | null>(null)
const [checkingBind, setCheckingBind] = useState(false)
const [submitResult, setSubmitResult] = useState<DeliverySubmitResult | null>(null)
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
@@ -197,14 +204,55 @@ export default function Delivery() {
}
}
const checkBindStatus = useCallback(
async (silent = false) => {
if (!bindResult?.bind_uuid) return
if (!silent) setCheckingBind(true)
try {
const res = await deliveryApi.getBindResult(decodedOrderNo, bindResult.bind_uuid, deliveryAuth)
if (res.bound) {
setBoundInfo({
bound: true,
role_name: res.role_name,
game_account: res.game_account,
game_channel: res.game_channel,
})
if (!silent) {
message.success(`扫码授权成功!已绑定角色:${res.role_name || res.game_account}`)
}
} else if (!silent) {
message.info(res.message || '尚未检测到扫码授权,请使用微信/QQ扫码后重试')
}
} catch (e) {
if (!silent) {
message.error(e instanceof Error ? e.message : '检查绑定状态失败')
}
} finally {
if (!silent) setCheckingBind(false)
}
},
[bindResult?.bind_uuid, decodedOrderNo, deliveryAuth]
)
useEffect(() => {
if (!bindResult?.bind_uuid || boundInfo?.bound || submitting || submitResult) {
return
}
const timer = setInterval(() => {
checkBindStatus(true)
}, 2500)
return () => clearInterval(timer)
}, [bindResult?.bind_uuid, boundInfo?.bound, submitting, submitResult, checkBindStatus])
const bindAccount = async () => {
const values = await form.validateFields()
setBinding(true)
setSubmitResult(null)
setBoundInfo(null)
try {
const result = await deliveryApi.bind(decodedOrderNo, values.game_account, deliveryAuth)
setBindResult(result)
message.success('绑定二维码已生成')
message.success('绑定二维码已生成,请使用手机微信/QQ扫码授权')
} catch (e) {
message.error(e instanceof Error ? e.message : '生成二维码失败')
} finally {
@@ -218,6 +266,10 @@ export default function Delivery() {
message.warning('请先生成绑定二维码')
return
}
if (!boundInfo?.bound) {
message.warning('请先完成手机微信/QQ扫码授权后再提交发货')
return
}
setSubmitting(true)
try {
const result = await deliveryApi.submit(decodedOrderNo, values.game_account, bindResult.bind_uuid, deliveryAuth)
@@ -229,13 +281,13 @@ export default function Delivery() {
status: result.status,
can_ship: false,
game_uid: values.game_account,
role_name: (result.game_account as Record<string, string>)?.role_name || prev.role_name,
role_name: (result.game_account as Record<string, string>)?.role_name || boundInfo?.role_name || prev.role_name,
}
: prev
)
message.success(result.message || '已提交')
message.success(result.message || '发货已提交')
} catch (e) {
message.error(e instanceof Error ? e.message : '提交失败')
message.error(e instanceof Error ? e.message : '提交发货失败')
} finally {
setSubmitting(false)
}
@@ -258,7 +310,7 @@ export default function Delivery() {
}
const status = statusMap[order.status] || { color: 'default', text: order.status }
const step = submitResult ? 2 : bindResult ? 1 : 0
const step = submitResult ? 2 : boundInfo?.bound ? 2 : bindResult ? 1 : 0
return (
<main style={{ minHeight: '100vh', background: '#f5f7fb', padding: '32px 16px' }}>
@@ -535,23 +587,66 @@ export default function Delivery() {
<Space wrap>
<Button
type="primary"
type={bindResult ? 'default' : 'primary'}
icon={<QrcodeOutlined />}
loading={binding}
onClick={bindAccount}
>
生成绑定二维码
{bindResult ? '重新生成二维码' : '生成绑定二维码'}
</Button>
{bindResult && !boundInfo?.bound && (
<Button
icon={<ReloadOutlined />}
loading={checkingBind}
onClick={() => checkBindStatus(false)}
>
检查扫码状态
</Button>
)}
<Button
type="primary"
icon={<SendOutlined />}
loading={submitting}
disabled={!bindResult || !gameAccount}
disabled={!bindResult || !gameAccount || !boundInfo?.bound}
onClick={submitDelivery}
style={
boundInfo?.bound
? { background: '#16a34a', borderColor: '#16a34a' }
: undefined
}
>
我已扫码,提交发货
{boundInfo?.bound ? '扫码已确认,立即发货' : '请先微信/QQ扫码授权'}
</Button>
</Space>
{bindResult && !boundInfo?.bound && (
<Alert
type="info"
showIcon
icon={<Spin size="small" />}
message="正在等待扫码授权"
description="请使用手机微信或手机QQ扫描下方二维码完成角色授权。系统检测到授权成功后将自动解锁发货按钮。"
/>
)}
{boundInfo?.bound && (
<Alert
type="success"
showIcon
message="✅ 扫码授权成功!已绑定游戏角色"
description={
<div style={{ marginTop: 4 }}>
<div>角色名称:<strong>{boundInfo.role_name || '-'}</strong></div>
<div>游戏账号 UID:<strong>{boundInfo.game_account || gameAccount || '-'}</strong></div>
{boundInfo.game_channel && <div>游戏区服:{boundInfo.game_channel}</div>}
<div style={{ color: '#16a34a', fontWeight: 500, marginTop: 6 }}>
已确认绑定无误,请点击上方「扫码已确认,立即发货」按钮完成充值发货。
</div>
</div>
}
/>
)}
{bindResult && (
<div style={{ display: 'flex', gap: 24, flexWrap: 'wrap', alignItems: 'center' }}>
<Image
+7
View File
@@ -201,6 +201,13 @@ export interface DeliveryBindResult {
bind_uuid: string
bind_url: string
qr_url: string
bound?: boolean
game_account?: string
role_name?: string
game_channel?: string
expected_game_account?: string
mismatch?: boolean
message?: string
}
export interface DeliverySubmitResult {