fix(delivery): add transient retry, fix queue deadlock, and gate delivery submit by scan auth
This commit is contained in:
@@ -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`,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user