拆分打手大厅操作服务

This commit is contained in:
yml2213
2026-08-21 17:02:31 +08:00
parent e3089e20a6
commit 3ebfb0991a
3 changed files with 191 additions and 230 deletions
@@ -15,6 +15,7 @@ export * from './worker-order-realtime-service.js'
export * from './worker-collect-service.js'
export * from './worker-acceptance-service.js'
export * from './worker-my-order-service.js'
export * from './worker-hall-action-service.js'
export * from './worker-platform-defaults.js'
export * from './work-order-timeout-policy.js'
export * from './after-sales-service.js'
@@ -0,0 +1,188 @@
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import {
countWorkerActiveOrders,
createWorkOrderEvent,
grabWorkOrder,
joinWorkOrderShare,
type WorkOrderRow,
} 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 {
mapWorkOrderForWorker,
mapWorkOrderShare,
normalizePositiveInteger,
resolveFreezeDepositAmount,
resolveWorkerPermissions,
throwGrabWorkOrderFailure,
} from './mappers.js'
import { getWorkerHallConfig } from './worker-hall-config-service.js'
import { publishWorkOrderParticipantChange } from './worker-order-realtime-service.js'
import {
getRequiredWorkOrder,
getRequiredWorker,
requireActiveWorkerSession,
type WorkerSession,
} from './worker-session-context-service.js'
import {
publishWorkerWalletRealtimeChange,
publishWorkOrderRealtimeChange,
} from '../realtime/realtime-event-service.js'
export async function grabWorkerHallOrder(workOrderId: number | string, session: WorkerSession) {
requireActiveWorkerSession(session)
const worker = await getRequiredWorker(session.workerId)
const permissions = resolveWorkerPermissions(worker)
const hallConfig = getWorkerHallConfig()
const now = nowIso()
const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
const visibleAfterIso =
visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
const workOrder = await getRequiredWorkOrder(workOrderId)
if (workOrder.status !== WORK_ORDER_STATUS.OPEN || workOrder.assigned_worker_id) {
throw createHttpError('该订单已不可抢', {
statusCode: 409,
errorCode: 'work_order_not_open',
})
}
if (workOrder.sharing_enabled) {
const result = await joinWorkOrderShare({
workOrderId: Number(workOrder.id),
workerId: Number(worker.id),
quantity: 0,
takeRemaining: true,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
visibleAfterIso,
now,
})
if (!result.share) {
throw resolveWorkOrderShareJoinError(result.failureReason)
}
await publishWorkOrderParticipantChange(Number(workOrder.id), {
workerIds: [Number(worker.id)],
hallChanged: true,
walletChangedWorkerIds: [Number(worker.id)],
})
return { order: mapWorkOrderForWorker(workOrder, permissions, result.share) }
}
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 grabbed = await grabWorkOrder({
workOrderId: Number(workOrder.id),
workerId: Number(worker.id),
depositAmount: freezeAmount,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
visibleAfterIso,
deadlineAt: resolveWorkOrderDeadlineAt(workOrder, now),
now,
})
if (!grabbed.order) {
throwGrabWorkOrderFailure(grabbed.failureReason)
}
await createWorkOrderEvent({
workOrderId: workOrder.id,
actorType: 'worker',
actorId: String(worker.id),
eventType: 'grabbed',
fromStatus: workOrder.status,
toStatus: grabbed.order.status,
payloadJson: JSON.stringify({
workerId: worker.id,
workerName: worker.display_name || worker.username,
internal: worker.worker_type === 'internal',
}),
now: nowIso(),
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: [Number(worker.id)],
hallChanged: true,
})
publishWorkerWalletRealtimeChange(Number(worker.id))
return { order: mapWorkOrderForWorker(grabbed.order, permissions) }
}
export async function joinWorkerSharingOrder(
workOrderId: number | string,
payload: JsonObject = {},
session: WorkerSession,
) {
requireActiveWorkerSession(session)
const worker = await getRequiredWorker(session.workerId)
const permissions = resolveWorkerPermissions(worker)
const hallConfig = getWorkerHallConfig()
const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
const visibleAfterIso =
visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
const quantity = normalizePositiveInteger(payload.quantity, 1)
const now = nowIso()
const result = await joinWorkOrderShare({
workOrderId: Number(workOrderId),
workerId: Number(worker.id),
quantity,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
visibleAfterIso,
now,
})
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) }
}
function resolveWorkOrderDeadlineAt(workOrder: WorkOrderRow, now: string): string | null {
const timeoutMinutes = Number(workOrder.timeout_minutes || 0)
if (timeoutMinutes <= 0) return null
return new Date(new Date(now).getTime() + timeoutMinutes * 60 * 1000).toISOString()
}
function resolveWorkOrderShareJoinError(
failureReason:
| 'work_order_not_open'
| 'sharing_disabled'
| 'work_order_owner_conflict'
| 'sharing_quantity_full'
| 'worker_active_order_limit'
| 'worker_deposit_insufficient'
| null,
): Error {
const messages: Record<string, string> = {
work_order_not_open: '该订单当前不可拼单',
sharing_disabled: '该订单未启用拼单',
work_order_owner_conflict: '你已整单接取该订单,无需拼单',
sharing_quantity_full: '拼单数量已满或超出剩余数量',
worker_active_order_limit: '已达到当前等级最大同时接单量',
worker_deposit_insufficient: '余额不足,无法冻结所需押金',
}
return createHttpError(messages[failureReason || ''] || '拼单失败', {
statusCode: 409,
errorCode: `work_order_sharing_${failureReason || 'failed'}`,
})
}
@@ -1,78 +1,3 @@
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
import {
countWorkerActiveOrders,
consumeWorkerSmsCode,
createWorkOrderEvent,
getLatestWorkerSmsCode,
recordWorkerSmsCodeFailure,
getWorkerUserById,
getWorkerUserByPhone,
getWorkerUserByUsername,
getWorkerUserByDisplayName,
getWorkOrderById,
grabWorkOrder,
createWorkerSessionRecord,
getWorkerSessionBySessionId,
listWorkerSessions,
revokeWorkerSession,
touchWorkerSession,
joinWorkOrderShare,
type WorkOrderRow,
type WorkerSessionRow,
} 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 { getWorkerHallConfig } from './worker-hall-config-service.js'
import {
getRequiredWorkOrder,
getRequiredWorker,
requireActiveWorkerSession,
type WorkerSession,
} from './worker-session-context-service.js'
import {
publishWorkerWalletRealtimeChange,
publishWorkOrderRealtimeChange,
} from '../realtime/realtime-event-service.js'
import {
mapWorkOrderForWorker,
mapWorkOrderShare,
normalizePositiveInteger,
resolveFreezeDepositAmount,
resolveWorkerPermissions,
throwGrabWorkOrderFailure,
} from './mappers.js'
import {
assertWorkerLoginAllowed,
normalizeWorkerDeviceId,
normalizeWorkerDeviceName,
normalizeWorkerDeviceType,
} from './worker-auth-policy.js'
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
import {
deleteWorkerSessionWithCredentials,
loginWorker,
loginWorkerBySmsCode,
manageWorkerSessions,
} from './worker-login-service.js'
import { registerWorker, resetWorkerPassword } from './worker-registration-service.js'
import {
sendWorkerLoginSmsCode,
sendWorkerPasswordResetSmsCode,
sendWorkerSmsCode,
} from './worker-sms-service.js'
import { changeWorkerPassword } from './worker-credentials-service.js'
import {
deleteWorkerSession,
getWorkerSessionSummary,
getWorkerSessions,
logoutWorkerSession,
verifyWorkerSessionToken,
} from './worker-session-auth-service.js'
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
import { publishWorkOrderParticipantChange } from './worker-order-realtime-service.js'
export {
getRequiredWorkOrder,
getRequiredWorker,
@@ -124,104 +49,7 @@ export {
maskWorkerLeaderboardDisplayName,
} from './worker-hall-service.js'
export async function grabWorkerHallOrder(workOrderId: number | string, session: WorkerSession) {
requireActiveWorkerSession(session)
const worker = await getRequiredWorker(session.workerId)
const permissions = resolveWorkerPermissions(worker)
const hallConfig = getWorkerHallConfig()
const now = nowIso()
const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
const visibleAfterIso =
visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
const workOrder = await getRequiredWorkOrder(workOrderId)
if (workOrder.status !== WORK_ORDER_STATUS.OPEN || workOrder.assigned_worker_id) {
throw createHttpError('该订单已不可抢', {
statusCode: 409,
errorCode: 'work_order_not_open',
})
}
if (workOrder.sharing_enabled) {
const result = await joinWorkOrderShare({
workOrderId: Number(workOrder.id),
workerId: Number(worker.id),
quantity: 0,
takeRemaining: true,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
visibleAfterIso,
now,
})
if (!result.share) {
throw resolveWorkOrderShareJoinError(result.failureReason)
}
await publishWorkOrderParticipantChange(Number(workOrder.id), {
workerIds: [Number(worker.id)],
hallChanged: true,
walletChangedWorkerIds: [Number(worker.id)],
})
return { order: mapWorkOrderForWorker(workOrder, permissions, result.share) }
}
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 grabbed = await grabWorkOrder({
workOrderId: Number(workOrder.id),
workerId: Number(worker.id),
depositAmount: freezeAmount,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
visibleAfterIso,
deadlineAt: resolveWorkOrderDeadlineAt(workOrder, now),
now,
})
if (!grabbed.order) {
throwGrabWorkOrderFailure(grabbed.failureReason)
}
await createWorkOrderEvent({
workOrderId: workOrder.id,
actorType: 'worker',
actorId: String(worker.id),
eventType: 'grabbed',
fromStatus: workOrder.status,
toStatus: grabbed.order.status,
payloadJson: JSON.stringify({
workerId: worker.id,
workerName: worker.display_name || worker.username,
internal: worker.worker_type === 'internal',
}),
now: nowIso(),
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: [Number(worker.id)],
hallChanged: true,
})
publishWorkerWalletRealtimeChange(Number(worker.id))
return { order: mapWorkOrderForWorker(grabbed.order, permissions) }
}
function resolveWorkOrderDeadlineAt(workOrder: WorkOrderRow, now: string): string | null {
const timeoutMinutes = Number(workOrder.timeout_minutes || 0)
if (timeoutMinutes <= 0) return null
return new Date(new Date(now).getTime() + timeoutMinutes * 60 * 1000).toISOString()
}
export { grabWorkerHallOrder } from './worker-hall-action-service.js'
export { settleDueDepositUnfreezes } from './worker-profile-service.js'
@@ -240,63 +68,7 @@ export {
submitWorkerOrderFeedback,
} from './worker-my-order-service.js'
export async function joinWorkerSharingOrder(
workOrderId: number | string,
payload: JsonObject = {},
session: WorkerSession,
) {
requireActiveWorkerSession(session)
const worker = await getRequiredWorker(session.workerId)
const permissions = resolveWorkerPermissions(worker)
const hallConfig = getWorkerHallConfig()
const visibleDelaySeconds = Number(permissions.visibleDelaySeconds || 0)
const visibleAfterIso =
visibleDelaySeconds > 0 ? new Date(Date.now() - visibleDelaySeconds * 1000).toISOString() : ''
const quantity = normalizePositiveInteger(payload.quantity, 1)
const now = nowIso()
const result = await joinWorkOrderShare({
workOrderId: Number(workOrderId),
workerId: Number(worker.id),
quantity,
maxActiveOrders: permissions.maxActiveOrders,
hallCandidateLimit: hallConfig.maxVisibleOrders,
visibleAfterIso,
now,
})
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) }
}
function resolveWorkOrderShareJoinError(
failureReason:
| 'work_order_not_open'
| 'sharing_disabled'
| 'work_order_owner_conflict'
| 'sharing_quantity_full'
| 'worker_active_order_limit'
| 'worker_deposit_insufficient'
| null,
): Error {
const messages: Record<string, string> = {
work_order_not_open: '该订单当前不可拼单',
sharing_disabled: '该订单未启用拼单',
work_order_owner_conflict: '你已整单接取该订单,无需拼单',
sharing_quantity_full: '拼单数量已满或超出剩余数量',
worker_active_order_limit: '已达到当前等级最大同时接单量',
worker_deposit_insufficient: '余额不足,无法冻结所需押金',
}
return createHttpError(messages[failureReason || ''] || '拼单失败', {
statusCode: 409,
errorCode: `work_order_sharing_${failureReason || 'failed'}`,
})
}
export { joinWorkerSharingOrder } from './worker-hall-action-service.js'
export {
remindWorkerOrderAcceptance,