拆分后台工单指派服务
This commit is contained in:
@@ -2,18 +2,15 @@ import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
|
||||
import {
|
||||
acceptWorkOrderAndSettle,
|
||||
acceptWorkOrderShareAndSettle,
|
||||
assignWorkOrderToWorker,
|
||||
cancelAssignedWorkOrder,
|
||||
cancelWorkOrderShare,
|
||||
returnAssignedWorkOrderToHall,
|
||||
unassignWorkOrder,
|
||||
countWorkerActiveOrders,
|
||||
countWorkOrderPendingSharingSubmissions,
|
||||
createWorkProductMatchLog,
|
||||
createWorkOrder,
|
||||
createWorkOrderEvent,
|
||||
deductPendingDepositUnfreeze,
|
||||
getWorkerUserById,
|
||||
getWorkOrderByOrderItemId,
|
||||
listWorkOrders,
|
||||
listWorkProductRules,
|
||||
@@ -37,7 +34,6 @@ import {
|
||||
publishWorkerWalletRealtimeChange,
|
||||
publishWorkOrderRealtimeChange,
|
||||
} from '../realtime/realtime-event-service.js'
|
||||
import { consumeIndustryVouchersBeforeWorkOrderAssign } from './publish-voucher-service.js'
|
||||
|
||||
import {
|
||||
mapWorkOrderAdmin,
|
||||
@@ -47,13 +43,11 @@ import {
|
||||
normalizeOptionalId,
|
||||
normalizeRequirementFields,
|
||||
normalizePositiveInteger,
|
||||
resolveFreezeDepositAmount,
|
||||
resolveMatchingProductRuleDecision,
|
||||
resolveKuaishouWorkProductMatchContext,
|
||||
resolveSkuNameQuantity,
|
||||
resolveWorkOrderRulePricing,
|
||||
resolveWorkOrderSharingEnabled,
|
||||
resolveWorkerPermissions,
|
||||
resolveWorkOrderOperationalStatus,
|
||||
} from './mappers.js'
|
||||
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
||||
@@ -66,132 +60,7 @@ export {
|
||||
reviewAdminWorkerUser,
|
||||
} from './admin-worker-user-service.js'
|
||||
|
||||
export async function assignAdminWorkOrderToWorker(
|
||||
workOrderId: number | string,
|
||||
payload: JsonObject = {},
|
||||
actorName = '',
|
||||
) {
|
||||
const workOrder = await getRequiredWorkOrder(workOrderId)
|
||||
const capabilities = mapWorkOrderAdmin(workOrder).capabilities
|
||||
if (!capabilities.canAssign) {
|
||||
throw createHttpError('只有未分配或待抢单且未指派打手的订单才能指派', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_not_assignable',
|
||||
})
|
||||
}
|
||||
const workerId = normalizeOptionalId(payload.workerId ?? payload.worker_id)
|
||||
if (!workerId) {
|
||||
throw createHttpError('请选择要指派的打手', {
|
||||
statusCode: 400,
|
||||
errorCode: 'work_order_assign_worker_required',
|
||||
})
|
||||
}
|
||||
const worker = await getWorkerUserById(workerId)
|
||||
if (!worker || worker.status !== 'active') {
|
||||
throw createHttpError('打手账号不存在或不可用', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_assign_worker_invalid',
|
||||
})
|
||||
}
|
||||
const permissions = resolveWorkerPermissions(worker)
|
||||
const activeCount = await countWorkerActiveOrders(worker.id)
|
||||
if (activeCount >= permissions.maxActiveOrders) {
|
||||
throw createHttpError('该打手已达到最大同时接单量', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_active_order_limit',
|
||||
})
|
||||
}
|
||||
const freezeAmount = resolveFreezeDepositAmount(workOrder, permissions)
|
||||
if (Number(worker.available_amount || 0) < freezeAmount) {
|
||||
throw createHttpError('该打手余额不足,无法冻结所需押金', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_deposit_insufficient',
|
||||
})
|
||||
}
|
||||
|
||||
const voucherConsume = await consumeIndustryVouchersBeforeWorkOrderAssign(workOrder)
|
||||
if (!voucherConsume.ok) {
|
||||
const failedAt = nowIso()
|
||||
await createWorkOrderEvent({
|
||||
workOrderId: workOrder.id,
|
||||
actorType: 'admin',
|
||||
actorId: actorName,
|
||||
eventType: 'assign_voucher_consume_failed',
|
||||
fromStatus: workOrder.status,
|
||||
toStatus: workOrder.status,
|
||||
payloadJson: JSON.stringify({ workerId: worker.id, voucherConsume }),
|
||||
now: failedAt,
|
||||
})
|
||||
throw createHttpError(voucherConsume.errorMessage || '电子凭证核销失败,订单未指派', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_assign_voucher_consume_failed',
|
||||
context: voucherConsume,
|
||||
})
|
||||
}
|
||||
|
||||
const now = nowIso()
|
||||
const { order: updated, failureReason } = await assignWorkOrderToWorker({
|
||||
workOrderId: workOrder.id,
|
||||
workerId: worker.id,
|
||||
depositAmount: freezeAmount,
|
||||
maxActiveOrders: permissions.maxActiveOrders,
|
||||
deadlineAt: resolveAssignedDeadlineAt(Number(workOrder.timeout_minutes || 0), now),
|
||||
now,
|
||||
actorName,
|
||||
})
|
||||
if (failureReason === 'work_order_not_assignable') {
|
||||
throw createHttpError(
|
||||
resolveAssignFailureMessage(voucherConsume.voucherCount, '订单状态已变化,无法指派'),
|
||||
{
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_not_assignable',
|
||||
},
|
||||
)
|
||||
}
|
||||
if (failureReason === 'worker_active_order_limit') {
|
||||
throw createHttpError(
|
||||
resolveAssignFailureMessage(voucherConsume.voucherCount, '该打手已达到最大同时接单量'),
|
||||
{
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_active_order_limit',
|
||||
},
|
||||
)
|
||||
}
|
||||
if (failureReason === 'worker_deposit_insufficient') {
|
||||
throw createHttpError(
|
||||
resolveAssignFailureMessage(voucherConsume.voucherCount, '该打手余额不足,无法冻结所需押金'),
|
||||
{
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_deposit_insufficient',
|
||||
},
|
||||
)
|
||||
}
|
||||
if (!updated) {
|
||||
throw createHttpError(
|
||||
resolveAssignFailureMessage(voucherConsume.voucherCount, '指派失败,订单状态可能已变化'),
|
||||
{
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_assign_conflict',
|
||||
},
|
||||
)
|
||||
}
|
||||
publishWorkOrderRealtimeChange({
|
||||
workOrderId: Number(workOrder.id),
|
||||
workerIds: [Number(worker.id)],
|
||||
hallChanged: workOrder.status === WORK_ORDER_STATUS.OPEN,
|
||||
})
|
||||
publishWorkerWalletRealtimeChange(Number(worker.id))
|
||||
return { order: mapWorkOrderAdmin(updated), voucherConsume }
|
||||
}
|
||||
|
||||
function resolveAssignFailureMessage(voucherCount: number, fallback: string) {
|
||||
return voucherCount > 0 ? `电子凭证已核销,但${fallback},请重试指派` : fallback
|
||||
}
|
||||
|
||||
function resolveAssignedDeadlineAt(timeoutMinutes: number, now: string): string | null {
|
||||
if (timeoutMinutes <= 0) return null
|
||||
return new Date(new Date(now).getTime() + timeoutMinutes * 60 * 1000).toISOString()
|
||||
}
|
||||
export { assignAdminWorkOrderToWorker } from './admin-work-order-assignment-service.js'
|
||||
|
||||
export { getAdminWorkOrderEvents, listAdminWorkOrders } from './admin-work-order-query-service.js'
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
|
||||
import {
|
||||
assignWorkOrderToWorker,
|
||||
countWorkerActiveOrders,
|
||||
createWorkOrderEvent,
|
||||
getWorkerUserById,
|
||||
} from '../../repositories/worker-platform/index.js'
|
||||
import type { JsonObject } from '../../types/json.js'
|
||||
import { createHttpError } from '../../utils/http.js'
|
||||
import { nowIso } from '../../utils/time.js'
|
||||
import {
|
||||
publishWorkerWalletRealtimeChange,
|
||||
publishWorkOrderRealtimeChange,
|
||||
} from '../realtime/realtime-event-service.js'
|
||||
import { consumeIndustryVouchersBeforeWorkOrderAssign } from './publish-voucher-service.js'
|
||||
import {
|
||||
mapWorkOrderAdmin,
|
||||
normalizeOptionalId,
|
||||
resolveFreezeDepositAmount,
|
||||
resolveWorkerPermissions,
|
||||
} from './mappers.js'
|
||||
import { getRequiredWorkOrder } from './worker-session-context-service.js'
|
||||
|
||||
export async function assignAdminWorkOrderToWorker(
|
||||
workOrderId: number | string,
|
||||
payload: JsonObject = {},
|
||||
actorName = '',
|
||||
) {
|
||||
const workOrder = await getRequiredWorkOrder(workOrderId)
|
||||
const capabilities = mapWorkOrderAdmin(workOrder).capabilities
|
||||
if (!capabilities.canAssign) {
|
||||
throw createHttpError('只有未分配或待抢单且未指派打手的订单才能指派', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_not_assignable',
|
||||
})
|
||||
}
|
||||
const workerId = normalizeOptionalId(payload.workerId ?? payload.worker_id)
|
||||
if (!workerId) {
|
||||
throw createHttpError('请选择要指派的打手', {
|
||||
statusCode: 400,
|
||||
errorCode: 'work_order_assign_worker_required',
|
||||
})
|
||||
}
|
||||
const worker = await getWorkerUserById(workerId)
|
||||
if (!worker || worker.status !== 'active') {
|
||||
throw createHttpError('打手账号不存在或不可用', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_assign_worker_invalid',
|
||||
})
|
||||
}
|
||||
const permissions = resolveWorkerPermissions(worker)
|
||||
const activeCount = await countWorkerActiveOrders(worker.id)
|
||||
if (activeCount >= permissions.maxActiveOrders) {
|
||||
throw createHttpError('该打手已达到最大同时接单量', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_active_order_limit',
|
||||
})
|
||||
}
|
||||
const freezeAmount = resolveFreezeDepositAmount(workOrder, permissions)
|
||||
if (Number(worker.available_amount || 0) < freezeAmount) {
|
||||
throw createHttpError('该打手余额不足,无法冻结所需押金', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_deposit_insufficient',
|
||||
})
|
||||
}
|
||||
|
||||
const voucherConsume = await consumeIndustryVouchersBeforeWorkOrderAssign(workOrder)
|
||||
if (!voucherConsume.ok) {
|
||||
const failedAt = nowIso()
|
||||
await createWorkOrderEvent({
|
||||
workOrderId: workOrder.id,
|
||||
actorType: 'admin',
|
||||
actorId: actorName,
|
||||
eventType: 'assign_voucher_consume_failed',
|
||||
fromStatus: workOrder.status,
|
||||
toStatus: workOrder.status,
|
||||
payloadJson: JSON.stringify({ workerId: worker.id, voucherConsume }),
|
||||
now: failedAt,
|
||||
})
|
||||
throw createHttpError(voucherConsume.errorMessage || '电子凭证核销失败,订单未指派', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_order_assign_voucher_consume_failed',
|
||||
context: voucherConsume,
|
||||
})
|
||||
}
|
||||
|
||||
const now = nowIso()
|
||||
const { order: updated, failureReason } = await assignWorkOrderToWorker({
|
||||
workOrderId: workOrder.id,
|
||||
workerId: worker.id,
|
||||
depositAmount: freezeAmount,
|
||||
maxActiveOrders: permissions.maxActiveOrders,
|
||||
deadlineAt: resolveAssignedDeadlineAt(Number(workOrder.timeout_minutes || 0), now),
|
||||
now,
|
||||
actorName,
|
||||
})
|
||||
if (failureReason === 'work_order_not_assignable') {
|
||||
throw createHttpError(
|
||||
resolveAssignFailureMessage(voucherConsume.voucherCount, '订单状态已变化,无法指派'),
|
||||
{ statusCode: 409, errorCode: 'work_order_not_assignable' },
|
||||
)
|
||||
}
|
||||
if (failureReason === 'worker_active_order_limit') {
|
||||
throw createHttpError(
|
||||
resolveAssignFailureMessage(voucherConsume.voucherCount, '该打手已达到最大同时接单量'),
|
||||
{ statusCode: 409, errorCode: 'worker_active_order_limit' },
|
||||
)
|
||||
}
|
||||
if (failureReason === 'worker_deposit_insufficient') {
|
||||
throw createHttpError(
|
||||
resolveAssignFailureMessage(voucherConsume.voucherCount, '该打手余额不足,无法冻结所需押金'),
|
||||
{ statusCode: 409, errorCode: 'worker_deposit_insufficient' },
|
||||
)
|
||||
}
|
||||
if (!updated) {
|
||||
throw createHttpError(
|
||||
resolveAssignFailureMessage(voucherConsume.voucherCount, '指派失败,订单状态可能已变化'),
|
||||
{ statusCode: 409, errorCode: 'work_order_assign_conflict' },
|
||||
)
|
||||
}
|
||||
publishWorkOrderRealtimeChange({
|
||||
workOrderId: Number(workOrder.id),
|
||||
workerIds: [Number(worker.id)],
|
||||
hallChanged: workOrder.status === WORK_ORDER_STATUS.OPEN,
|
||||
})
|
||||
publishWorkerWalletRealtimeChange(Number(worker.id))
|
||||
return { order: mapWorkOrderAdmin(updated), voucherConsume }
|
||||
}
|
||||
|
||||
function resolveAssignFailureMessage(voucherCount: number, fallback: string) {
|
||||
return voucherCount > 0 ? `电子凭证已核销,但${fallback},请重试指派` : fallback
|
||||
}
|
||||
|
||||
function resolveAssignedDeadlineAt(timeoutMinutes: number, now: string): string | null {
|
||||
if (timeoutMinutes <= 0) return null
|
||||
return new Date(new Date(now).getTime() + timeoutMinutes * 60 * 1000).toISOString()
|
||||
}
|
||||
@@ -34,6 +34,7 @@ export * from './admin-work-order-material-service.js'
|
||||
export * from './admin-mock-work-order-service.js'
|
||||
export * from './admin-work-order-management-service.js'
|
||||
export * from './admin-worker-platform-summary-service.js'
|
||||
export * from './admin-work-order-assignment-service.js'
|
||||
export {
|
||||
acceptAdminWorkOrder,
|
||||
acceptAdminWorkOrderShare,
|
||||
|
||||
Reference in New Issue
Block a user