完善发货链接签名和作废恢复

This commit is contained in:
yml2213
2026-07-31 11:43:13 +08:00
parent 6ca226ce4b
commit e4c1306216
17 changed files with 527 additions and 109 deletions
+74 -27
View File
@@ -76,9 +76,6 @@ const eventOptions = [
{ value: 'order.cancelled', label: '订单取消' },
]
const deliveryBaseUrl = (import.meta.env.VITE_DELIVERY_BASE_URL || window.location.origin).replace(/\/+$/, '')
const deliveryChannel = 'dlc'
const testOrderStatusOptions = [
{ value: 'pending', label: '已支付,可发货' },
{ value: 'failed', label: '发货失败,可重试' },
@@ -125,11 +122,26 @@ export default function MerchantCenter() {
})), [testOrderProducts])
const copyDeliveryLink = useCallback((orderNo: string) => {
navigator.clipboard.writeText(buildDeliveryLink(orderNo)).then(() => message.success('发货链接已复制'))
merchantApi.getDeliveryLink(orderNo)
.then((data) => navigator.clipboard.writeText(data.delivery_url).then(() => message.success('发货链接已复制')))
.catch((e) => message.error(e instanceof Error ? e.message : '复制失败'))
}, [])
const openDeliveryLink = useCallback((orderNo: string) => {
window.open(buildDeliveryLink(orderNo), '_blank', 'noopener,noreferrer')
merchantApi.getDeliveryLink(orderNo)
.then((data) => window.open(data.delivery_url, '_blank', 'noopener,noreferrer'))
.catch((e) => message.error(e instanceof Error ? e.message : '打开失败'))
}, [])
const patchDeliveryLinkState = useCallback((orderNo: string, patch: Partial<FulfillmentOrder>) => {
setOrders((prev) => ({
...prev,
list: prev.list.map((item) => item.order_no === orderNo ? { ...item, ...patch } : item),
}))
setTestOrderResult((prev) => prev?.order.order_no === orderNo ? {
...prev,
order: { ...prev.order, ...patch },
} : prev)
}, [])
const loadCurrent = useCallback(async () => {
@@ -149,6 +161,28 @@ export default function MerchantCenter() {
setOrders(data)
}, [orders.page, orders.size])
const revokeDeliveryLink = useCallback((orderNo: string) => {
merchantApi.revokeDeliveryLink(orderNo)
.then(() => {
const revokedAt = new Date().toISOString()
message.success('发货链接已作废')
patchDeliveryLinkState(orderNo, { delivery_link_revoked_at: revokedAt })
})
.catch((e) => message.error(e instanceof Error ? e.message : '作废失败'))
}, [patchDeliveryLinkState])
const restoreDeliveryLink = useCallback((orderNo: string) => {
merchantApi.restoreDeliveryLink(orderNo)
.then((data) => {
message.success('发货链接已恢复')
patchDeliveryLinkState(orderNo, {
delivery_link_expires_at: data.expires_at,
delivery_link_revoked_at: null,
})
})
.catch((e) => message.error(e instanceof Error ? e.message : '恢复失败'))
}, [patchDeliveryLinkState])
const loadWallet = useCallback(async (
page = ledger.page,
size = ledger.size,
@@ -435,15 +469,24 @@ export default function MerchantCenter() {
{ title: '支付', dataIndex: 'payment_status', width: 90, render: paymentStatusTag },
{ title: '履约', dataIndex: 'fulfillment_status', width: 100, render: fulfillmentStatusTag },
{ title: '上游单号', dataIndex: 'provider_order_no', width: 140, ellipsis: true, render: (v) => v || '-' },
{
title: '链接有效期',
dataIndex: 'delivery_link_expires_at',
width: 160,
render: (_, record) => record.delivery_link_revoked_at ? <Tag color="red"></Tag> : formatTime(record.delivery_link_expires_at),
},
{ title: '时间', dataIndex: 'created_at', width: 160, render: formatTime },
{
title: '发货链接',
key: 'delivery_link',
width: 150,
render: (_, record) => (
<Space size={4}>
width: 220,
render: (_, record) => record.delivery_link_revoked_at ? (
<Button type="link" size="small" onClick={() => restoreDeliveryLink(record.order_no)}></Button>
) : (
<Space size={4} wrap>
<Button type="link" size="small" onClick={() => copyDeliveryLink(record.order_no)}></Button>
<Button type="link" size="small" onClick={() => openDeliveryLink(record.order_no)}></Button>
<Button type="link" danger size="small" onClick={() => revokeDeliveryLink(record.order_no)}></Button>
</Space>
),
},
@@ -552,7 +595,7 @@ export default function MerchantCenter() {
children: (
<Space direction="vertical" style={{ width: '100%' }} size="middle">
<Space style={{ width: '100%', justifyContent: 'space-between' }}>
<Typography.Text type="secondary"> UID</Typography.Text>
<Typography.Text type="secondary"> UID</Typography.Text>
{canManage && (
<Space>
<Button type="primary" icon={<PlusOutlined />} onClick={openTestOrderCreate}>
@@ -567,7 +610,7 @@ export default function MerchantCenter() {
columns={orderColumns}
dataSource={orders.list}
tableLayout="fixed"
scroll={{ x: 1450 }}
scroll={{ x: 1500 }}
pagination={pageConfig(orders, loadOrders)}
/>
</Space>
@@ -748,15 +791,22 @@ export default function MerchantCenter() {
footer={(
<Space>
<Button onClick={() => setTestOrderResult(null)}></Button>
<Button
icon={<CopyOutlined />}
onClick={() => copyDeliveryLink(testOrderResult?.order.order_no || '')}
>
</Button>
<Button type="primary" icon={<LinkOutlined />} onClick={() => openDeliveryLink(testOrderResult?.order.order_no || '')}>
</Button>
{testOrderResult?.order.delivery_link_revoked_at ? (
<Button onClick={() => restoreDeliveryLink(testOrderResult?.order.order_no || '')}></Button>
) : (
<>
<Button
icon={<CopyOutlined />}
onClick={() => copyDeliveryLink(testOrderResult?.order.order_no || '')}
>
</Button>
<Button type="primary" icon={<LinkOutlined />} onClick={() => openDeliveryLink(testOrderResult?.order.order_no || '')}>
</Button>
<Button danger onClick={() => revokeDeliveryLink(testOrderResult?.order.order_no || '')}></Button>
</>
)}
</Space>
)}
>
@@ -773,10 +823,11 @@ export default function MerchantCenter() {
{testOrderResult.can_ship ? 'can_ship=true' : 'can_ship=false'}
</Tag>
</Descriptions.Item>
<Descriptions.Item label="发货链接">
<Typography.Link href={buildDeliveryLink(testOrderResult.order.order_no)} target="_blank" rel="noreferrer">
{buildDeliveryLink(testOrderResult.order.order_no)}
</Typography.Link>
<Descriptions.Item label="链接有效期">
{testOrderResult.order.delivery_link_revoked_at ? <Tag color="red"></Tag> : formatTime(testOrderResult.order.delivery_link_expires_at)}
</Descriptions.Item>
<Descriptions.Item label="链接状态">
<Typography.Text type="secondary"></Typography.Text>
</Descriptions.Item>
</Descriptions>
)}
@@ -935,10 +986,6 @@ function productOptionLabel(item: MerchantProduct) {
return name === item.sku ? item.sku : `${name} / ${item.sku}`
}
function buildDeliveryLink(orderNo: string) {
return `${deliveryBaseUrl}/delivery/${deliveryChannel}/${encodeURIComponent(orderNo)}`
}
function paymentStatusTag(value: string) {
const item = paymentStatusMap[value] || { color: 'default', text: value }
return <Tag color={item.color}>{item.text}</Tag>