修复拼单工单打手展示

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)
const pendingSubmissionCount = shares.filter((share) => share.status === 'joined').length
const adminSnapshot = {
...mapWorkOrderAdmin(workOrder),
...mapWorkOrderAdmin(workOrder, shares),
pendingUnfreezeAmount: pendingUnfreezeByOrderId.get(workOrderId) || 0,
sharingProgress: {
joinedQuantity,
@@ -45,6 +45,7 @@ import {
listWorkerUsers,
listWorkerWithdrawalAccounts,
listWorkOrderShares,
listWorkOrderSharesByOrderIds,
sumPendingUnfreezeByOrderIds,
resolveProblemWorkOrder,
reviewWorkerFinanceRequest,
@@ -1300,10 +1301,18 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
const pendingUnfreezeByOrderId = await sumPendingUnfreezeByOrderIds(
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 {
items: items.map((item) => {
return {
...mapWorkOrderAdmin(item),
...mapWorkOrderAdmin(item, sharesByOrderId.get(Number(item.id)) || []),
pendingUnfreezeAmount: pendingUnfreezeByOrderId.get(Number(item.id)) || 0,
}
}),
@@ -1377,7 +1386,7 @@ export async function getAdminWorkOrderSharing(workOrderId: number | string) {
const workOrder = await getRequiredWorkOrder(workOrderId)
const shares = await listWorkOrderShares(workOrder.id)
return {
order: mapWorkOrderAdmin(workOrder),
order: mapWorkOrderAdmin(workOrder, shares),
shares: shares.map(mapWorkOrderShare),
}
}
@@ -2428,7 +2437,7 @@ export async function syncWorkerOrdersForSourceOrder(
}
return {
created: created.map(mapWorkOrderAdmin),
created: created.map((workOrder) => mapWorkOrderAdmin(workOrder)),
skipped,
createdCount: created.length,
skippedCount: skipped.length,
@@ -341,7 +341,33 @@ export function resolveWorkOrderOperationalStatus(workOrder: WorkOrderRow): stri
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 pendingShareCount = Math.max(0, Number(workOrder.pending_share_count || 0))
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 || '',
}
: null,
...(shares ? { sharingWorkers: mapWorkOrderSharingWorkers(shares) } : {}),
publishedAt: workOrder.published_at,
pinnedAt: workOrder.pinned_at,
assignedAt: workOrder.assigned_at,
@@ -22,6 +22,7 @@ import {
normalizeRequirementFields,
mapWorkOrderForWorker,
mapWorkOrderAdmin,
mapWorkOrderSharingWorkers,
maskWorkerLeaderboardDisplayName,
resolveCollectSubmitTargetWorkOrder,
resolveAdminMaterialRewardAmount,
@@ -733,6 +734,20 @@ test('后台拼单聚合状态同时收敛筛选语义和操作能力', () => {
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('拼单份额撤单只允许未验收份额并让母工单回到大厅状态', () => {
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'joined'), 'open')
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'submitted'), 'open')
@@ -793,7 +793,7 @@ export default function WorkOrdersPanel() {
{
title: '打手',
width: 140,
render: (_, row) => row.worker?.displayName || row.worker?.username || '-',
render: (_, row) => renderWorkOrderWorkers(row),
},
{
title: '资料',
@@ -1125,7 +1125,7 @@ export default function WorkOrdersPanel() {
</Tag>
</Descriptions.Item>
<Descriptions.Item label="打手">
{detailOrder.worker?.displayName || detailOrder.worker?.username || '-'}
{renderWorkOrderWorkers(detailOrder)}
</Descriptions.Item>
<Descriptions.Item label="接单金额">
{formatMoney(detailOrder.rewardAmount)}
@@ -1928,6 +1928,24 @@ function formatUserPaymentAmount(order: WorkOrder): string {
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 {
return String(order?.platformOrderId || '').trim() || '-'
}
@@ -400,6 +400,13 @@ export type WorkOrder = {
username: string
displayName: string
}
/** 拼单订单的当前参与打手,普通整单不返回该字段。 */
sharingWorkers?: Array<{
workerId: number
username: string
displayName: string
quantity: number
}>
publishedAt: string | null
/** 置顶时间;非空表示该工单在抢单大厅置顶展示 */
pinnedAt?: string | null