修复拼单工单打手展示

This commit is contained in:
yml2213
2026-08-19 19:31:55 +08:00
parent df90b67d8e
commit f1575aec85
6 changed files with 83 additions and 7 deletions
@@ -255,7 +255,7 @@ async function publishWorkOrderSnapshots(input: {
.reduce((sum, share) => sum + Number(share.quantity || 0), 0) .reduce((sum, share) => sum + Number(share.quantity || 0), 0)
const pendingSubmissionCount = shares.filter((share) => share.status === 'joined').length const pendingSubmissionCount = shares.filter((share) => share.status === 'joined').length
const adminSnapshot = { const adminSnapshot = {
...mapWorkOrderAdmin(workOrder), ...mapWorkOrderAdmin(workOrder, shares),
pendingUnfreezeAmount: pendingUnfreezeByOrderId.get(workOrderId) || 0, pendingUnfreezeAmount: pendingUnfreezeByOrderId.get(workOrderId) || 0,
sharingProgress: { sharingProgress: {
joinedQuantity, joinedQuantity,
@@ -45,6 +45,7 @@ import {
listWorkerUsers, listWorkerUsers,
listWorkerWithdrawalAccounts, listWorkerWithdrawalAccounts,
listWorkOrderShares, listWorkOrderShares,
listWorkOrderSharesByOrderIds,
sumPendingUnfreezeByOrderIds, sumPendingUnfreezeByOrderIds,
resolveProblemWorkOrder, resolveProblemWorkOrder,
reviewWorkerFinanceRequest, reviewWorkerFinanceRequest,
@@ -1300,10 +1301,18 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
const pendingUnfreezeByOrderId = await sumPendingUnfreezeByOrderIds( const pendingUnfreezeByOrderId = await sumPendingUnfreezeByOrderIds(
items.map((item) => Number(item.id)), items.map((item) => Number(item.id)),
) )
const shares = await listWorkOrderSharesByOrderIds(items.map((item) => Number(item.id)))
const sharesByOrderId = new Map<number, typeof shares>()
for (const share of shares) {
const workOrderId = Number(share.work_order_id)
const current = sharesByOrderId.get(workOrderId) || []
current.push(share)
sharesByOrderId.set(workOrderId, current)
}
return { return {
items: items.map((item) => { items: items.map((item) => {
return { return {
...mapWorkOrderAdmin(item), ...mapWorkOrderAdmin(item, sharesByOrderId.get(Number(item.id)) || []),
pendingUnfreezeAmount: pendingUnfreezeByOrderId.get(Number(item.id)) || 0, pendingUnfreezeAmount: pendingUnfreezeByOrderId.get(Number(item.id)) || 0,
} }
}), }),
@@ -1377,7 +1386,7 @@ export async function getAdminWorkOrderSharing(workOrderId: number | string) {
const workOrder = await getRequiredWorkOrder(workOrderId) const workOrder = await getRequiredWorkOrder(workOrderId)
const shares = await listWorkOrderShares(workOrder.id) const shares = await listWorkOrderShares(workOrder.id)
return { return {
order: mapWorkOrderAdmin(workOrder), order: mapWorkOrderAdmin(workOrder, shares),
shares: shares.map(mapWorkOrderShare), shares: shares.map(mapWorkOrderShare),
} }
} }
@@ -2428,7 +2437,7 @@ export async function syncWorkerOrdersForSourceOrder(
} }
return { return {
created: created.map(mapWorkOrderAdmin), created: created.map((workOrder) => mapWorkOrderAdmin(workOrder)),
skipped, skipped,
createdCount: created.length, createdCount: created.length,
skippedCount: skipped.length, skippedCount: skipped.length,
@@ -341,7 +341,33 @@ export function resolveWorkOrderOperationalStatus(workOrder: WorkOrderRow): stri
return workOrder.status return workOrder.status
} }
export function mapWorkOrderAdmin(workOrder: WorkOrderRow) { /** 汇总拼单参与者,已撤销份额不应继续展示为当前打手。 */
export function mapWorkOrderSharingWorkers(shares: WorkOrderShareRow[]) {
const workers = new Map<
number,
{ workerId: number; username: string; displayName: string; quantity: number }
>()
for (const share of shares) {
if (share.status === 'cancelled') continue
const workerId = Number(share.worker_id || 0)
if (workerId <= 0) continue
const existing = workers.get(workerId)
if (existing) {
existing.quantity += Number(share.quantity || 0)
continue
}
const username = String(share.worker_username || '').trim()
workers.set(workerId, {
workerId,
username,
displayName: String(share.worker_display_name || username || `打手#${workerId}`).trim(),
quantity: Number(share.quantity || 0),
})
}
return [...workers.values()]
}
export function mapWorkOrderAdmin(workOrder: WorkOrderRow, shares?: WorkOrderShareRow[]) {
const activeShareQuantity = Math.max(0, Number(workOrder.active_share_quantity || 0)) const activeShareQuantity = Math.max(0, Number(workOrder.active_share_quantity || 0))
const pendingShareCount = Math.max(0, Number(workOrder.pending_share_count || 0)) const pendingShareCount = Math.max(0, Number(workOrder.pending_share_count || 0))
const sharingTotalQuantity = Math.max(1, Number(workOrder.sharing_total_quantity || 1)) const sharingTotalQuantity = Math.max(1, Number(workOrder.sharing_total_quantity || 1))
@@ -437,6 +463,7 @@ export function mapWorkOrderAdmin(workOrder: WorkOrderRow) {
displayName: workOrder.worker_display_name || '', displayName: workOrder.worker_display_name || '',
} }
: null, : null,
...(shares ? { sharingWorkers: mapWorkOrderSharingWorkers(shares) } : {}),
publishedAt: workOrder.published_at, publishedAt: workOrder.published_at,
pinnedAt: workOrder.pinned_at, pinnedAt: workOrder.pinned_at,
assignedAt: workOrder.assigned_at, assignedAt: workOrder.assigned_at,
@@ -22,6 +22,7 @@ import {
normalizeRequirementFields, normalizeRequirementFields,
mapWorkOrderForWorker, mapWorkOrderForWorker,
mapWorkOrderAdmin, mapWorkOrderAdmin,
mapWorkOrderSharingWorkers,
maskWorkerLeaderboardDisplayName, maskWorkerLeaderboardDisplayName,
resolveCollectSubmitTargetWorkOrder, resolveCollectSubmitTargetWorkOrder,
resolveAdminMaterialRewardAmount, resolveAdminMaterialRewardAmount,
@@ -733,6 +734,20 @@ test('后台拼单聚合状态同时收敛筛选语义和操作能力', () => {
assert.equal(filled.capabilities.canUnpublish, false) assert.equal(filled.capabilities.canUnpublish, false)
}) })
test('拼单打手汇总展示当前参与者并合并同一打手的份数', () => {
const workers = mapWorkOrderSharingWorkers([
buildWorkOrderShareRow({ worker_id: 1, quantity: 2, worker_username: 'alpha' }),
buildWorkOrderShareRow({ id: 2, worker_id: 1, quantity: 3, worker_username: 'alpha' }),
buildWorkOrderShareRow({ id: 3, worker_id: 2, quantity: 1, worker_display_name: 'Beta' }),
buildWorkOrderShareRow({ id: 4, worker_id: 3, quantity: 9, status: 'cancelled' }),
])
assert.deepEqual(workers, [
{ workerId: 1, username: 'alpha', displayName: 'alpha', quantity: 5 },
{ workerId: 2, username: '', displayName: 'Beta', quantity: 1 },
])
})
test('拼单份额撤单只允许未验收份额并让母工单回到大厅状态', () => { test('拼单份额撤单只允许未验收份额并让母工单回到大厅状态', () => {
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'joined'), 'open') assert.equal(resolveWorkOrderShareCancellationStatus('open', 'joined'), 'open')
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'submitted'), 'open') assert.equal(resolveWorkOrderShareCancellationStatus('open', 'submitted'), 'open')
@@ -793,7 +793,7 @@ export default function WorkOrdersPanel() {
{ {
title: '打手', title: '打手',
width: 140, width: 140,
render: (_, row) => row.worker?.displayName || row.worker?.username || '-', render: (_, row) => renderWorkOrderWorkers(row),
}, },
{ {
title: '资料', title: '资料',
@@ -1125,7 +1125,7 @@ export default function WorkOrdersPanel() {
</Tag> </Tag>
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="打手"> <Descriptions.Item label="打手">
{detailOrder.worker?.displayName || detailOrder.worker?.username || '-'} {renderWorkOrderWorkers(detailOrder)}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="接单金额"> <Descriptions.Item label="接单金额">
{formatMoney(detailOrder.rewardAmount)} {formatMoney(detailOrder.rewardAmount)}
@@ -1928,6 +1928,24 @@ function formatUserPaymentAmount(order: WorkOrder): string {
return paymentFen === null ? '-' : formatMoney(paymentFen) return paymentFen === null ? '-' : formatMoney(paymentFen)
} }
/** 拼单母单没有单一 assigned worker,改为展示当前参与打手及其承接份数。 */
function renderWorkOrderWorkers(order: WorkOrder) {
if (order.worker) return order.worker.displayName || order.worker.username || '-'
if (!order.sharing?.enabled || !order.sharingWorkers?.length) return '-'
const names = order.sharingWorkers
.map((worker) => worker.displayName || worker.username || `打手#${worker.workerId}`)
.join('、')
const quantity = order.sharingWorkers.reduce((sum, worker) => sum + Number(worker.quantity || 0), 0)
return (
<div className="cell-stack">
<Typography.Text ellipsis={{ tooltip: names }}>{names}</Typography.Text>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{order.sharingWorkers.length} · {quantity}
</Typography.Text>
</div>
)
}
function getDisplayOrderNo(order: Pick<WorkOrder, 'platformOrderId'> | null | undefined): string { function getDisplayOrderNo(order: Pick<WorkOrder, 'platformOrderId'> | null | undefined): string {
return String(order?.platformOrderId || '').trim() || '-' return String(order?.platformOrderId || '').trim() || '-'
} }
@@ -400,6 +400,13 @@ export type WorkOrder = {
username: string username: string
displayName: string displayName: string
} }
/** 拼单订单的当前参与打手,普通整单不返回该字段。 */
sharingWorkers?: Array<{
workerId: number
username: string
displayName: string
quantity: number
}>
publishedAt: string | null publishedAt: string | null
/** 置顶时间;非空表示该工单在抢单大厅置顶展示 */ /** 置顶时间;非空表示该工单在抢单大厅置顶展示 */
pinnedAt?: string | null pinnedAt?: string | null