feat(接单): 支持客服手动结束赠送冷却

This commit is contained in:
yml2213
2026-08-28 20:39:41 +08:00
parent e680792161
commit 5d5d63bb59
11 changed files with 255 additions and 2 deletions
@@ -479,6 +479,7 @@ export type WorkOrderCapabilities = {
canResolveProblem: boolean
canDeduct: boolean
canConfirmFriend: boolean
canCompleteGiftCooldown: boolean
canResetGiftFlow: boolean
}
@@ -162,6 +162,82 @@ export async function confirmWorkOrderFriendAdded(input: {
})
}
/** 后台人工确认赠送冷却已完成:立即开放打手提交赠送凭证,并从当前重新计算任务时限。 */
export async function completeWorkOrderGiftCooldown(input: {
workOrderId: number
note?: string
actorName?: string
now: string
}): Promise<{
order: WorkOrderRow | null
failureReason:
| 'work_order_not_found'
| 'work_order_mode_invalid'
| 'work_order_status_invalid'
| 'work_order_gift_phase_invalid'
| 'work_order_gift_cooldown_completed'
| null
}> {
return withTransaction(async (client) => {
const currentResult = await client.query<WorkOrderRow>(
'SELECT * FROM work_orders WHERE id = $1 FOR UPDATE',
[input.workOrderId],
)
const workOrder = currentResult.rows[0] || null
if (!workOrder) return { order: null, failureReason: 'work_order_not_found' }
if (
normalizeWorkOrderAcceptanceMode(workOrder.acceptance_mode) !==
WORK_ORDER_ACCEPTANCE_MODE.FRIEND_GIFT
) {
return { order: null, failureReason: 'work_order_mode_invalid' }
}
if (!['in_progress', 'problem'].includes(workOrder.status)) {
return { order: null, failureReason: 'work_order_status_invalid' }
}
if (workOrder.gift_phase !== WORK_ORDER_GIFT_PHASE.FRIEND_COUNTDOWN) {
return { order: null, failureReason: 'work_order_gift_phase_invalid' }
}
const availableAt = Date.parse(String(workOrder.gift_available_at || ''))
if (Number.isFinite(availableAt) && availableAt <= Date.parse(input.now)) {
return { order: null, failureReason: 'work_order_gift_cooldown_completed' }
}
await client.query(
`
UPDATE work_orders
SET
gift_available_at = $1::timestamptz,
deadline_at = CASE
WHEN timeout_minutes > 0
THEN $1::timestamptz + (timeout_minutes * INTERVAL '1 minute')
ELSE NULL
END,
updated_at = $1::timestamptz
WHERE id = $2
`,
[input.now, input.workOrderId],
)
await createWorkOrderEventWithClient(client, {
workOrderId: input.workOrderId,
actorType: 'admin',
actorId: input.actorName || '',
eventType: 'gift_cooldown_completed_by_admin',
fromStatus: workOrder.status,
toStatus: workOrder.status,
payloadJson: JSON.stringify({
previousGiftAvailableAt: workOrder.gift_available_at,
completedAt: input.now,
note: String(input.note || '').trim(),
}),
now: input.now,
})
return {
order: await getWorkOrderByIdWithClient(client, input.workOrderId),
failureReason: null,
}
})
}
/** 后台重置打手资料(资料有误或误确认好友):清空好友状态并让打手重新提交。 */
export async function resetWorkOrderGiftMaterial(input: {
workOrderId: number
@@ -841,7 +841,13 @@ export function buildWorkOrderWhere({
)`)
} else if (Object.values(WORK_ORDER_GIFT_PHASE).includes(normalizedGiftPhase as never)) {
params.push(normalizedGiftPhase)
filters.push(`wo.acceptance_mode = 'friend_gift' AND wo.gift_phase = $${params.length}`)
const giftPhaseFilter = `wo.acceptance_mode = 'friend_gift' AND wo.gift_phase = $${params.length}`
// 待打手提交资料仅代表已接单后的赠送流程;待抢单/未分配模板单不应混入该筛选。
filters.push(
normalizedGiftPhase === WORK_ORDER_GIFT_PHASE.MATERIAL_REQUIRED
? `(${giftPhaseFilter} AND wo.assigned_worker_id IS NOT NULL AND wo.status IN ('in_progress', 'problem'))`
: giftPhaseFilter,
)
}
}
if (hallCandidateLimit > 0) {