点“关闭任务”,就会达到你要的效果:链接失效,预占释放。

This commit is contained in:
yml
2026-04-14 18:29:25 +08:00
parent 813c733860
commit 215f8bb710
6 changed files with 351 additions and 6 deletions
@@ -22,6 +22,7 @@ import { confirmClaimRoleForAdminTask, redeemClaimTaskForAdminTask } from '../cl
import { reserveInventoryForTask } from '../order/inventory-service.js'
import { replayAgisoTradeWebhookEvent } from '../order/webhook-service.js'
import { ensureAgisoXianyuAutoDeliveryForDeliveredTask } from '../platforms/agiso/xianyu/auto-delivery-service.js'
import { closeTencentBrowserSession } from '../session/session.js'
import { createHttpError } from '../../utils/http.js'
import { nowIso } from '../../utils/time.js'
import {
@@ -394,7 +395,25 @@ export async function redeemAdminTaskAssisted(taskId, session = null) {
/** @returns {Promise<AdminTaskActionResponse>} */
/** @param {AdminEntityIdInput} taskId */
export async function closeAdminTask(taskId) {
const task = await getRequiredTask(taskId)
return closeAdminTaskWithDeps(taskId)
}
/** @returns {Promise<AdminTaskActionResponse>} */
/** @param {AdminEntityIdInput} taskId */
export async function closeAdminTaskWithDeps(
taskId,
{
getRequiredTask: getTask = getRequiredTask,
listTaskInventoryBindingsByTaskId: listBindings = listTaskInventoryBindingsByTaskId,
releaseReservedInventoryItem: releaseReserved = releaseReservedInventoryItem,
updateClaimToken: updateToken = updateClaimToken,
updateTask: updateTaskRecord = updateTask,
createTaskEvent: createEvent = createTaskEvent,
closeTencentBrowserSession: closeSession = closeTencentBrowserSession,
nowIso: getNowIso = nowIso,
} = {},
) {
const task = await getTask(taskId)
if (task.task_status === 'redeemed') {
throw createHttpError('已兑换任务不能关闭', {
@@ -403,13 +422,63 @@ export async function closeAdminTask(taskId) {
})
}
const updatedTask = await updateTask(task.id, {
const now = getNowIso()
const bindings = await listBindings(task.id)
const reservedBindings = bindings.filter((binding) => String(binding.binding_status || '').trim() === 'reserved')
const releasedInventoryItemIds = Array.from(new Set(reservedBindings.map((binding) => Number(binding.inventory_item_id)).filter((id) => id > 0)))
const hasConsumedBindings = bindings.some((binding) => String(binding.binding_status || '').trim() === 'consumed')
const primaryClaimTokenId = getTaskPrimaryClaimTokenId(task)
let browserSessionClosed = false
if (primaryClaimTokenId) {
await updateToken(primaryClaimTokenId, {
status: 'revoked',
expired_at: now,
updated_at: now,
})
}
if (task.browser_session_id) {
try {
await closeSession(task.browser_session_id)
browserSessionClosed = true
} catch (error) {
if (!isRecoverableTaskSessionCloseError(error)) {
throw error
}
}
}
for (const inventoryItemId of releasedInventoryItemIds) {
await releaseReserved(inventoryItemId, now)
}
const closeReasonParts = ['已手动关闭任务']
if (primaryClaimTokenId) {
closeReasonParts.push('领取链接已失效')
}
if (releasedInventoryItemIds.length > 0) {
closeReasonParts.push('预占库存已释放')
}
const updatedTask = await updateTaskRecord(task.id, {
task_status: 'closed',
inventory_status: hasConsumedBindings ? 'consumed' : 'pending',
delivery_status: 'closed',
last_error: task.last_error || '已手动关闭任务',
updated_at: nowIso(),
user_action_status: 'closed',
claim_expires_at: primaryClaimTokenId ? now : task.claim_expires_at,
browser_session_id: '',
last_error: task.last_error || closeReasonParts.join(''),
updated_at: now,
})
await createEvent(task.id, 'task_closed', {
claimTokenRevoked: Boolean(primaryClaimTokenId),
releasedInventoryItemIds,
releasedInventoryCount: releasedInventoryItemIds.length,
browserSessionClosed,
}, now)
return {
task: mapTaskActionPayload(updatedTask),
}
@@ -697,6 +766,11 @@ function ensureViewerCanOperateAssistedTask(task, viewerContext, action) {
}
}
function isRecoverableTaskSessionCloseError(error) {
const errorCode = String(error?.errorCode || error?.code || '').trim()
return errorCode === 'session_not_found' || errorCode === 'session_closed'
}
function normalizeManualDispatchOutcome(value) {
const normalized = String(value || '').trim().toLowerCase()