修复拼单明细并支持参与者撤单

This commit is contained in:
yml2213
2026-08-19 14:35:23 +08:00
parent 4b1dcd47ea
commit ab6387759e
9 changed files with 409 additions and 43 deletions
@@ -310,11 +310,13 @@ async function publishWorkOrderSnapshots(input: {
const viewedAt =
views.find((item) => Number(item.work_order_id) === workOrderId)?.viewed_at || null
const share = shares.find((item) => Number(item.worker_id) === workerId)
const isAssignedWorker = Number(workOrder.assigned_worker_id || 0) === workerId
const workerOrderVisible = isAssignedWorker || Boolean(share && share.status !== 'cancelled')
publishRealtimeEvent({
type: 'work_order.changed',
entityId: workOrderId,
scopes: ['worker_my_orders'],
operation: 'upsert',
operation: workerOrderVisible ? 'upsert' : 'remove',
data: mapWorkOrderForWorker(
workOrder,
resolveWorkerPermissions(worker),
@@ -6,6 +6,7 @@ import {
addWorkerWalletCredit,
assignWorkOrderToWorker,
cancelAssignedWorkOrder,
cancelWorkOrderShare,
returnAssignedWorkOrderToHall,
unassignWorkOrder,
countWorkerActiveOrders,
@@ -2040,6 +2041,52 @@ export async function acceptAdminWorkOrderShare(
}
}
export async function cancelAdminWorkOrderShare(
workOrderId: number | string,
shareId: number | string,
payload: JsonObject = {},
actorName = '',
) {
const result = await cancelWorkOrderShare({
workOrderId: Number(workOrderId),
shareId: Number(shareId),
now: nowIso(),
reason: String(payload.reason || '').trim(),
actorName,
})
if (result.failureReason === 'work_order_not_cancellable') {
throw createHttpError('当前拼单状态不能撤销参与者', {
statusCode: 409,
errorCode: 'work_order_sharing_cancel_status_invalid',
})
}
if (result.failureReason === 'share_not_found') {
throw createHttpError('未找到该拼单参与记录', {
statusCode: 404,
errorCode: 'work_order_sharing_share_not_found',
})
}
if (result.failureReason === 'share_status_not_cancellable' || !result.share || !result.order) {
throw createHttpError('只有已参与或已提交验收的拼单份额可以撤单', {
statusCode: 409,
errorCode: 'work_order_sharing_share_cancel_status_invalid',
})
}
const workerId = Number(result.share.worker_id || 0)
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrderId),
workerIds: await resolveWorkOrderRealtimeWorkerIds(result.order),
hallChanged: true,
})
if (workerId > 0) publishWorkerWalletRealtimeChange(workerId)
return {
order: mapWorkOrderAdmin(result.order),
share: mapWorkOrderShare(result.share),
releasedDepositAmount: result.releasedDepositAmount,
}
}
export async function acceptAdminWorkOrders(payload: JsonObject = {}, actorName = '') {
const workOrderIds = [
...new Set(
@@ -8,7 +8,10 @@ import type {
WorkProductRuleRow,
WorkerUserRow,
} from '../../repositories/worker-platform/index.js'
import { resolveWorkOrderShareJoinQuantity } from '../../repositories/worker-platform/index.js'
import {
resolveWorkOrderShareCancellationStatus,
resolveWorkOrderShareJoinQuantity,
} from '../../repositories/worker-platform/index.js'
import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js'
import {
assertWithdrawChannelAllowedForAmount,
@@ -729,6 +732,15 @@ test('后台拼单聚合状态同时收敛筛选语义和操作能力', () => {
assert.equal(filled.capabilities.canUnpublish, false)
})
test('拼单份额撤单只允许未验收份额并让母工单回到大厅状态', () => {
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'joined'), 'open')
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'submitted'), 'open')
assert.equal(resolveWorkOrderShareCancellationStatus('pending_acceptance', 'submitted'), 'open')
assert.equal(resolveWorkOrderShareCancellationStatus('pending_acceptance', 'accepted'), null)
assert.equal(resolveWorkOrderShareCancellationStatus('accepted', 'submitted'), null)
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'cancelled'), null)
})
test('resolveCollectSubmitTargetWorkOrder returns the only pending work order when no id is provided', () => {
const workOrder = buildWorkOrderRow()
const selected = resolveCollectSubmitTargetWorkOrder('ORDER-001', [workOrder], '')