fix(worker-platform): 修复寄售匹配工单详情中卖家上架截图丢失问题,并收敛手动匹配为内部现货直接履约

1. 修复文件归一化器 normalizeUploadedFiles 遇到纯字符串 URL 数组被误过滤为空的问题,自动提取 objectKey 并构造规范 UploadedFile 结构;
2. 优化工单展示映射器 resolveWorkOrderEffectiveBoosterMaterial:当工单资料中截图为空时,自动回退并加载寄售卖家上架/主页截图;
3. 前端管理后台详情抽屉与打手端详情抽屉支持读取 detailOrder.gift.boosterMaterial.screenshots 与 consignmentInfo.screenshots 降级兜底渲染;
4. 手动匹配弹窗与发布拦截弹窗全面收敛为单一标准的【内部现货直接履约】,废除旧版指派打手带来的重复扣押金与二次填资料问题;
5. 新增 consignment-booster-material 单元测试,更新寄卖库存优化现状与进度追踪文档,前后端全套测试通过。
This commit is contained in:
yml2213
2026-09-21 11:35:38 +08:00
parent 040b3db84c
commit c80bd0e337
9 changed files with 372 additions and 102 deletions
@@ -16,9 +16,9 @@ import {
import { SearchOutlined, TeamOutlined } from '@ant-design/icons'
import {
assignWorkOrderWithConsignmentWorker,
fetchInventoryMatchableOrders,
fetchWorkOrderConsignmentMatchOptions,
fulfillWorkOrderWithConsignment,
} from '@/services/admin/consignment'
import type { ConsignmentMatchOptionView, ConsignmentMatchableOrder } from '@/types/consignment'
import {
@@ -39,7 +39,7 @@ type Selected = {
inventoryLabel: string
matchType: ManualMatchType
conflicts: string[]
/** 供货打手(接单平台指派履约对象) */
/** 供货打手(用于回显供货来源) */
workerId?: number | null
workerName?: string
}
@@ -57,7 +57,7 @@ export type ConsignmentManualMatchModalProps = {
/**
* 「手动指定现货」弹窗:订单侧与库存侧共用一套交互与落地逻辑。
*
* 统一走接单平台指派供货打手履约(自动冻结押金、开启交付倒计时与履约监控)。
* 统一走内部现货直接履约(原子占库、免外派佣金、不冻结重复押金、跳过打手资料直接进入待加好友)。
*/
export default function ConsignmentManualMatchModal({
open,
@@ -106,7 +106,6 @@ export default function ConsignmentManualMatchModal({
? `${res.data.inventory.itemName}(${res.data.inventory.sourceSubmissionNo || res.data.inventory.inventoryNo})`
: inventory.label,
)
// 指派线需要现货的供货打手:库存侧入口从响应里记下来,选中订单时带上
setInventoryWorker({
workerId: res.data?.inventory?.workerId ?? null,
workerName: res.data?.inventory?.workerName || '',
@@ -165,7 +164,6 @@ export default function ConsignmentManualMatchModal({
const requirement = resolveManualMatchRequirement(selected?.matchType || 'exact')
const canSubmit =
Boolean(selected) &&
Boolean(selected?.workerId) &&
canSubmitManualMatch({
matchType: selected?.matchType || 'exact',
confirmedPartial,
@@ -175,16 +173,11 @@ export default function ConsignmentManualMatchModal({
async function handleSubmit() {
if (!selected) return
if (!selected.workerId) {
message.error('该现货未关联供货打手,无法走平台指派履约;请先关联打手')
return
}
setSubmitting(true)
try {
const res = await assignWorkOrderWithConsignmentWorker({
const res = await fulfillWorkOrderWithConsignment({
workOrderId: selected.workOrderId,
inventoryId: selected.inventoryId,
workerId: selected.workerId,
allowPartial: selected.matchType !== 'exact' || confirmedPartial,
allowSpecMismatch: selected.matchType === 'conflict' || confirmedMismatch,
matchNote: matchNote.trim(),
@@ -194,7 +187,7 @@ export default function ConsignmentManualMatchModal({
const voucher = (res.data as any)?.voucherConsume
notifyVoucherConsume(voucher)
if (!voucher || voucher.ok) {
message.success('已手动匹配并指派供货打手履约,履约押金已冻结')
message.success('已使用内部寄售现货直接履约,已进入待加好友阶段')
}
onMatched?.()
resetState()
@@ -221,7 +214,7 @@ export default function ConsignmentManualMatchModal({
void load()
return
}
message.error(err.message || '手动匹配指派失败')
message.error(err.message || '内部现货履约失败')
} finally {
setSubmitting(false)
}
@@ -232,7 +225,7 @@ export default function ConsignmentManualMatchModal({
title={mode === 'order' ? `手动指定现货 · ${workOrder?.label || ''}` : '手动匹配订单'}
open={open}
width={860}
okText="确认指派履约"
okText="确认内部现货履约"
okButtonProps={{
disabled: !canSubmit,
loading: submitting,
@@ -252,13 +245,13 @@ export default function ConsignmentManualMatchModal({
>
<Space direction="vertical" size={12} style={{ width: '100%' }}>
<Alert
type="warning"
type="info"
showIcon
message="手动匹配将直接占用现货并指派供货打手履约(冻结履约押金并纳入交付监控)。"
message="手动匹配将直接选用内部寄售现货履约(原子占库、免外派佣金、不冻结重复押金、自动跳过打手资料)。"
description={
mode === 'order'
? '下面是该订单的全部在库现货(含规格不一致的),请核对差异后再决定。'
: `当前现货:${inventoryLabel || inventory?.label || ''}。下面是可匹配的订单列表,请核对规格后指派。`
? '下面是该订单的全部在库现货(含规格不一致的),核对差异后直接内部履约。'
: `当前现货:${inventoryLabel || inventory?.label || ''}。下面是可匹配的订单列表,核对后直接内部履约。`
}
/>
@@ -442,18 +435,18 @@ export default function ConsignmentManualMatchModal({
<Descriptions size="small" column={1} style={{ marginBottom: 8 }}>
<Descriptions.Item label="订单">{selected.workOrderLabel || '—'}</Descriptions.Item>
<Descriptions.Item label="现货">{selected.inventoryLabel || '—'}</Descriptions.Item>
<Descriptions.Item label="指派打手">
<Descriptions.Item label="供货来源">
{selected.workerId ? (
<Space size={6}>
<Tag icon={<TeamOutlined />} color="blue">
{selected.workerName || `打手 #${selected.workerId}`}
{selected.workerName || `供货打手 #${selected.workerId}`}
</Tag>
<Text type="secondary" style={{ fontSize: 12 }}>
(工单将进入该打手“我的接单”并冻结履约押金)
(验收后将自动为该打手发放货款)
</Text>
</Space>
) : (
<Text type="danger">该现货未关联供货打手,无法指派</Text>
<Tag color="default">内部库存现货(无打手关联)</Tag>
)}
</Descriptions.Item>
<Descriptions.Item label="匹配判定">
@@ -469,15 +462,6 @@ export default function ConsignmentManualMatchModal({
</Descriptions>
<Space direction="vertical" size={8} style={{ width: '100%' }}>
{!selected.workerId ? (
<Alert
type="error"
showIcon
message="无法指派打手"
description="该现货未绑定任何供货打手,无法在接单平台中执行指派与押金冻结流程。请先在寄卖管理中关联打手。"
/>
) : null}
{requirement.needsPartialConfirm ? (
<Checkbox
checked={confirmedPartial}
@@ -516,7 +500,7 @@ export default function ConsignmentManualMatchModal({
) : null}
<Paragraph type="secondary" style={{ fontSize: 12, marginBottom: 0 }}>
确认后现货立即被占用:内部现货履约进入「待加好友」,指派打手会冻结履约押金。
确认后现货立即被占用并进入「待加好友」,双方加好买家好友后点确认加好友进入赠送冷却期。
如需退回请使用「撤销寄售履约」。
</Paragraph>
</Space>