增加订单实时状态同步

This commit is contained in:
yml2213
2026-08-17 14:51:51 +08:00
parent 5330b55b75
commit 18dad9b0fe
13 changed files with 798 additions and 16 deletions
@@ -65,6 +65,11 @@ import {
createWorkOrderMaterialAdminNotification,
resolveAdminNotificationEntity,
} from '../admin/admin-notification-service.js'
import {
publishRealtimeEvent,
publishWorkerFinanceRealtimeChange,
publishWorkOrderRealtimeChange,
} from '../realtime/realtime-event-service.js'
import {
consumeIndustryVouchersBeforeWorkOrderAssign,
consumeIndustryVouchersBeforeWorkOrderPublish,
@@ -512,6 +517,17 @@ export async function assignAdminWorkOrderToWorker(
},
)
}
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: [Number(worker.id)],
hallChanged: workOrder.status === WORK_ORDER_STATUS.OPEN,
})
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: Number(worker.id),
scopes: ['worker_wallet'],
workerIds: [Number(worker.id)],
})
return { order: mapWorkOrderAdmin(updated), voucherConsume }
}
@@ -540,6 +556,12 @@ export async function creditAdminWorkerWallet(workerId: number | string, payload
payloadJson: JSON.stringify({ source: 'admin_manual_credit' }),
now: nowIso(),
})
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: Number(workerId),
scopes: ['worker_wallet'],
workerIds: [Number(workerId)],
})
return { wallet: mapWallet(wallet) }
}
@@ -693,6 +715,11 @@ export async function reviewAdminWorkerFinanceRequest(
Number(reviewed.request.id),
'finance_request_reviewed',
)
publishWorkerFinanceRealtimeChange({
requestId: Number(reviewed.request.id),
workerId: Number(reviewed.request.worker_id),
walletChanged: reviewed.request.request_type === 'withdraw' && status === 'approved',
})
return {
request: mapFinanceRequest(reviewed.request),
@@ -817,6 +844,10 @@ export async function deductAdminWorkOrderPendingDeposit(
payloadJson: JSON.stringify({ amount, note }),
now: nowIso(),
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: await resolveWorkOrderRealtimeWorkerIds(workOrder),
})
return { deductedAmount: result.deductedAmount }
}
@@ -891,6 +922,10 @@ export async function updateAdminWorkOrderSharing(
}),
now,
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
hallChanged: workOrder.status === WORK_ORDER_STATUS.OPEN,
})
return { order: mapWorkOrderAdmin(updated || workOrder) }
}
@@ -983,6 +1018,10 @@ export async function submitAdminWorkOrderMaterial(
if (nextStatus !== WORK_ORDER_STATUS.PENDING_MATERIAL) {
await resolveAdminNotificationEntity('work_order', Number(workOrder.id), 'material_completed')
}
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: await resolveWorkOrderRealtimeWorkerIds(workOrder),
})
return { order: mapWorkOrderAdmin(updated || workOrder), complete }
}
@@ -1152,6 +1191,10 @@ export async function updateAdminWorkOrder(
}),
now,
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
hallChanged: workOrder.status === WORK_ORDER_STATUS.OPEN,
})
return { order: mapWorkOrderAdmin(updated || workOrder) }
}
@@ -1190,6 +1233,10 @@ export async function deleteAdminWorkOrder(workOrderId: number | string) {
errorCode: 'work_order_delete_failed',
})
}
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
hallChanged: workOrder.status === WORK_ORDER_STATUS.OPEN,
})
return { deleted: true }
}
@@ -1256,6 +1303,7 @@ export async function publishAdminWorkOrder(workOrderId: number | string, actorN
payloadJson: JSON.stringify({ voucherConsume }),
now,
})
publishWorkOrderRealtimeChange({ workOrderId: Number(workOrder.id), hallChanged: true })
return { order: mapWorkOrderAdmin(updated), voucherConsume }
}
@@ -1282,6 +1330,7 @@ export async function unpublishAdminWorkOrder(workOrderId: number | string, acto
toStatus: WORK_ORDER_STATUS.UNASSIGNED,
now,
})
publishWorkOrderRealtimeChange({ workOrderId: Number(workOrder.id), hallChanged: true })
return { order: mapWorkOrderAdmin(updated || workOrder) }
}
@@ -1312,6 +1361,7 @@ export async function pinAdminWorkOrder(
toStatus: workOrder.status,
now,
})
publishWorkOrderRealtimeChange({ workOrderId: Number(workOrder.id), hallChanged: true })
return { order: mapWorkOrderAdmin(updated || workOrder) }
}
@@ -1351,6 +1401,10 @@ export async function markAdminWorkOrderProblem(
if (workOrder.status === WORK_ORDER_STATUS.PENDING_ACCEPTANCE) {
await resolveAdminNotificationEntity('work_order', Number(workOrder.id), 'marked_problem')
}
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: await resolveWorkOrderRealtimeWorkerIds(workOrder),
})
return { order: mapWorkOrderAdmin(updated || workOrder) }
}
@@ -1389,6 +1443,10 @@ export async function resolveAdminProblemWorkOrder(
errorCode: 'work_order_problem_resolution_conflict',
})
}
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: await resolveWorkOrderRealtimeWorkerIds(workOrder),
})
return { order: mapWorkOrderAdmin(updated) }
}
@@ -1421,6 +1479,19 @@ export async function acceptAdminWorkOrder(workOrderId: number | string, actorNa
})
}
await resolveAdminNotificationEntity('work_order', Number(workOrder.id), 'accepted')
const workerIds = await resolveWorkOrderRealtimeWorkerIds(workOrder)
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds,
})
for (const workerId of workerIds) {
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: workerId,
scopes: ['worker_wallet'],
workerIds: [workerId],
})
}
return { order: mapWorkOrderAdmin(updated || workOrder) }
}
@@ -1498,6 +1569,16 @@ export async function unassignAdminWorkOrder(workOrderId: number | string, actor
errorCode: 'work_order_unassign_conflict',
})
}
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: [Number(workOrder.assigned_worker_id)],
})
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: Number(workOrder.assigned_worker_id),
scopes: ['worker_wallet'],
workerIds: [Number(workOrder.assigned_worker_id)],
})
return { order: mapWorkOrderAdmin(updated) }
}
@@ -1555,9 +1636,30 @@ export async function cancelAdminWorkOrder(
errorCode: 'work_order_cancel_conflict',
})
}
const workerId = Number(workOrder.assigned_worker_id)
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: workerId ? [workerId] : [],
hallChanged: action === 'return_to_hall',
})
if (workerId) {
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: workerId,
scopes: ['worker_wallet'],
workerIds: [workerId],
})
}
return { order: mapWorkOrderAdmin(updated), action }
}
async function resolveWorkOrderRealtimeWorkerIds(workOrder: WorkOrderRow) {
const shares = await listWorkOrderShares(workOrder.id)
return [Number(workOrder.assigned_worker_id), ...shares.map((share) => Number(share.worker_id))]
.filter((workerId) => Number.isInteger(workerId) && workerId > 0)
.filter((workerId, index, values) => values.indexOf(workerId) === index)
}
export async function getAdminWorkerPlatformSummary() {
const [pendingWorkers, pendingMaterial, openOrders, inProgressOrders] = await Promise.all([
listWorkerUsers({ page: 1, pageSize: 1, status: 'pending_review' }),
@@ -129,6 +129,9 @@ export async function syncWorkOrdersFromKuaishouSendCode(
},
})
createdWorkOrderCount = syncResult.createdCount
for (const workOrder of syncResult.created) {
workOrderIds.push(Number(workOrder.workOrderId))
}
for (const item of syncResult.skipped) {
skipped.push(`work_order:${item.reason}`)
}
@@ -42,6 +42,7 @@ import {
listWorkCategories,
listWorkOrders,
listWorkOrderEventsByOrderId,
listWorkOrderShares,
listWorkerLevels,
listDueDepositUnfreezes,
listOverdueWorkOrders,
@@ -75,6 +76,11 @@ import {
getWorkerAcceptanceReminder,
remindWorkerAcceptanceAdminNotification,
} from '../admin/admin-notification-service.js'
import {
publishRealtimeEvent,
publishWorkerFinanceRealtimeChange,
publishWorkOrderRealtimeChange,
} from '../realtime/realtime-event-service.js'
import {
DEFAULT_CATEGORY_KEY,
@@ -701,6 +707,10 @@ export async function createWorkerRechargeRequest(
workerName: worker.display_name || worker.username,
amountFen: Number(created.amount || amount),
})
publishWorkerFinanceRealtimeChange({
requestId: Number(created.id),
workerId: Number(worker.id),
})
return {
request: mapFinanceRequest(created),
}
@@ -797,6 +807,10 @@ export async function createWorkerWithdrawRequest(
amountFen: Number(created.amount || amount),
channel: created.account_channel,
})
publishWorkerFinanceRealtimeChange({
requestId: Number(created.id),
workerId: Number(worker.id),
})
return {
request: mapFinanceRequest(created),
availableForWithdraw,
@@ -1049,6 +1063,17 @@ export async function grabWorkerHallOrder(workOrderId: number | string, session:
}),
now: nowIso(),
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: [Number(worker.id)],
hallChanged: true,
})
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: Number(worker.id),
scopes: ['worker_wallet'],
workerIds: [Number(worker.id)],
})
return { order: mapWorkOrderForWorker(grabbed.order, permissions) }
}
@@ -1084,7 +1109,15 @@ export async function settleDueDepositUnfreezes(
unfreezeId: unfreeze.id,
now: nowIso(),
})
if (released) processedCount += 1
if (released) {
processedCount += 1
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: Number(released.worker_id),
scopes: ['worker_wallet'],
workerIds: [Number(released.worker_id)],
})
}
}
return {
checkedCount: due.length,
@@ -1117,6 +1150,21 @@ export async function settleOverdueWorkOrders(options: { limit?: number; workerI
})
if (result.order) {
processedCount += 1
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: Number(workOrder.assigned_worker_id)
? [Number(workOrder.assigned_worker_id)]
: [],
hallChanged: result.order.status === WORK_ORDER_STATUS.OPEN,
})
if (Number(workOrder.assigned_worker_id)) {
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: Number(workOrder.assigned_worker_id),
scopes: ['worker_wallet'],
workerIds: [Number(workOrder.assigned_worker_id)],
})
}
} else {
skippedCount += 1
}
@@ -1288,6 +1336,11 @@ export async function joinWorkerSharingOrder(
if (!result.share) {
throw resolveWorkOrderShareJoinError(result.failureReason)
}
await publishWorkOrderParticipantChange(Number(workOrderId), {
workerIds: [Number(worker.id)],
hallChanged: true,
walletChangedWorkerIds: [Number(worker.id)],
})
return { share: mapWorkOrderShare(result.share) }
}
@@ -1440,6 +1493,16 @@ export async function submitWorkerOrderAcceptance(
}),
now,
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: [Number(worker.id)],
})
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: Number(worker.id),
scopes: ['worker_wallet'],
workerIds: [Number(worker.id)],
})
return {
order: mapWorkOrderForWorker(acceptedOrder, resolveWorkerPermissions(worker)),
}
@@ -1451,6 +1514,10 @@ export async function submitWorkerOrderAcceptance(
workerName: worker.display_name || worker.username,
imageCount: imageUrls.length,
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: [Number(worker.id)],
})
return {
order: mapWorkOrderForWorker(updated || workOrder, resolveWorkerPermissions(worker)),
}
@@ -1645,6 +1712,7 @@ export async function supplementWorkerAcceptedOrderEvidence(
}),
now,
})
publishWorkOrderRealtimeChange({ workOrderId: Number(workOrder.id) })
return {
order: mapWorkOrderForWorker(updated || workOrder, resolveWorkerPermissions(worker)),
}
@@ -1697,6 +1765,9 @@ async function submitWorkerSharingAcceptance(
errorCode: 'work_order_sharing_share_not_found',
})
}
await publishWorkOrderParticipantChange(Number(workOrder.id), {
workerIds: [Number(session.workerId)],
})
return {
share: mapWorkOrderShare(result.share),
sharingPendingCount: await countWorkOrderPendingSharingSubmissions(Number(workOrder.id)),
@@ -1789,6 +1860,9 @@ export async function saveWorkerOrderAcceptanceDraft(
payloadJson: JSON.stringify({ imageCount: imageUrls.length }),
now,
})
await publishWorkOrderParticipantChange(Number(workOrder.id), {
workerIds: [Number(session.workerId)],
})
return {
order: mapWorkOrderForWorker(updated || workOrder, resolveWorkerPermissions(worker)),
}
@@ -1823,6 +1897,9 @@ export async function saveWorkerOrderAcceptanceDraft(
payloadJson: JSON.stringify({ imageCount: imageUrls.length }),
now,
})
await publishWorkOrderParticipantChange(Number(workOrder.id), {
workerIds: [Number(session.workerId)],
})
return {
order: mapWorkOrderForWorker(updated || workOrder, resolveWorkerPermissions(worker)),
}
@@ -1881,6 +1958,9 @@ async function saveWorkerSharingAcceptanceDraft(
}),
now,
})
await publishWorkOrderParticipantChange(Number(workOrder.id), {
workerIds: [Number(session.workerId)],
})
return { share: share ? mapWorkOrderShare(share) : mapWorkOrderShare(sharingShare) }
}
@@ -1915,6 +1995,9 @@ async function saveWorkerSharingAcceptanceDraft(
}),
now,
})
await publishWorkOrderParticipantChange(Number(workOrder.id), {
workerIds: [Number(session.workerId)],
})
return { share: share ? mapWorkOrderShare(share) : mapWorkOrderShare(sharingShare) }
}
@@ -2001,12 +2084,47 @@ export async function collectSubmitWorkOrder(payload: JsonObject = {}) {
payloadJson: JSON.stringify({ fields: submittedFields, complete }),
now,
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
})
return {
order: mapWorkOrderPublic(updated || workOrder),
complete,
}
}
/** 拼单工单同步所有参与打手,避免仅主打手页面更新。 */
async function publishWorkOrderParticipantChange(
workOrderId: number,
options: {
workerIds?: number[]
hallChanged?: boolean
walletChangedWorkerIds?: number[]
} = {},
) {
const shares = await listWorkOrderShares(workOrderId)
const workerIds = [
...(options.workerIds || []),
...shares.map((share) => Number(share.worker_id)),
].filter(
(workerId, index, values) =>
Number.isInteger(workerId) && workerId > 0 && values.indexOf(workerId) === index,
)
publishWorkOrderRealtimeChange({
workOrderId,
workerIds,
...(options.hallChanged ? { hallChanged: true } : {}),
})
for (const workerId of options.walletChangedWorkerIds || []) {
publishRealtimeEvent({
type: 'worker_wallet.changed',
entityId: workerId,
scopes: ['worker_wallet'],
workerIds: [workerId],
})
}
}
export function resolveCollectSubmitTargetWorkOrder(
orderNo: string,
workOrders: WorkOrderRow[],